diff -Nru osgearth-2.0+dfsg/CMakeLists.txt osgearth-2.1.1+dfsg/CMakeLists.txt --- osgearth-2.0+dfsg/CMakeLists.txt 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/CMakeLists.txt 2011-11-04 19:44:43.000000000 +0000 @@ -10,10 +10,16 @@ cmake_policy(SET CMP0005 OLD) endif(COMMAND cmake_policy) +# +# Set up CMake to use Solution Folders in VS. +# +SET_PROPERTY( GLOBAL PROPERTY USE_FOLDERS ON ) +SET_PROPERTY( GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER "CMake Targets" ) + PROJECT(OSGEARTH) SET(OSGEARTH_MAJOR_VERSION 2) -SET(OSGEARTH_MINOR_VERSION 0) +SET(OSGEARTH_MINOR_VERSION 1) SET(OSGEARTH_PATCH_VERSION 0) SET(OSGEARTH_SOVERSION 1) @@ -30,6 +36,7 @@ SET(OSGEARTH_VERSION ${OSGEARTH_MAJOR_VERSION}.${OSGEARTH_MINOR_VERSION}.${OSGEARTH_PATCH_VERSION}) + # We want to build SONAMES shared librariess SET(OSGEARTH_SONAMES TRUE) SET(OPENTHREADS_SONAMES TRUE) @@ -73,6 +80,15 @@ FIND_PACKAGE(Sqlite3) FIND_PACKAGE(ZLIB) +SET (WITH_EXTERNAL_TINYXML FALSE CACHE BOOL "Use bundled or system wide version of TinyXML") +IF (WITH_EXTERNAL_TINYXML) + FIND_PACKAGE(TinyXML) +ENDIF (WITH_EXTERNAL_TINYXML) + +IF (ZLIB_FOUND) + FIND_PACKAGE(MiniZip) +ENDIF (ZLIB_FOUND) + IF(UNIX) # Not sure what this will do on Cygwin and Msys # Also, remember OS X X11 is a user installed option so it may not exist. @@ -137,6 +153,8 @@ SET(CMAKE_DEBUG_POSTFIX "d" CACHE STRING "add a postfix, usually d on windows") FIND_PACKAGE(OSG) + + #FIND_PACKAGE(GDAL) ################################################################################ @@ -225,6 +243,8 @@ DETECT_OSG_VERSION() +SET(OPENSCENEGRAPH_VERSION ${OPENSCENEGRAPH_MAJOR_VERSION}.${OPENSCENEGRAPH_MINOR_VERSION}.${OPENSCENEGRAPH_PATCH_VERSION}) + # OE Core ADD_SUBDIRECTORY(src) @@ -266,6 +286,8 @@ ENDIF(NOT OSGEARTH_CONFIG_HAS_BEEN_RUN_BEFORE) OPTION(OSGEARTH_BUILD_APPLICATION_BUNDLES "Enable the building of applications and examples as OSX Bundles" OFF) + OPTION(OSGEARTH_BUILD_FRAMEWORKS "Compile frameworks instead of dylibs" OFF) + SET(OSGEARTH_BUILD_FRAMEWORKS_INSTALL_NAME_DIR "@executable_path/../Frameworks" CACHE STRING "Install name dir for compiled frameworks") ENDIF(APPLE) @@ -287,3 +309,17 @@ ADD_CUSTOM_TARGET(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") + + + +#------------------------------------------------------------------------------ +# Packaging setup. + +SET(CPACK_PACKAGE_VERSION ${OSGEARTH_VERSION} ) +SET(CPACK_PACKAGE_VERSION_MAJOR ${OSGEARTH_MAJOR_VERSION} ) +SET(CPACK_PACKAGE_VERSION_MINOR ${OSGEARTH_MINOR_VERSION} ) +SET(CPACK_PACKAGE_VERSION_PATCH ${OSGEARTH_PATCH_VERSION} ) +SET(CPACK_RESOURCE_FILE_LICENSE ${PROJECT_SOURCE_DIR}/LICENSE.txt ) +SET(CPACK_MONOLITHIC_INSTALL 1 ) # create a single, monolithic package. + +INCLUDE(CPack) \ No newline at end of file diff -Nru osgearth-2.0+dfsg/CMakeModules/FindCURL.cmake osgearth-2.1.1+dfsg/CMakeModules/FindCURL.cmake --- osgearth-2.0+dfsg/CMakeModules/FindCURL.cmake 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/CMakeModules/FindCURL.cmake 2011-11-04 19:44:43.000000000 +0000 @@ -1,16 +1,16 @@ -FIND_PATH(CURL_INCLUDE_DIR curl.h +FIND_PATH(CURL_INCLUDE_DIR curl.h curl/curl.h $ENV{CURL_DIR} NO_DEFAULT_PATH PATH_SUFFIXES include ) -FIND_PATH(CURL_INCLUDE_DIR curl.h +FIND_PATH(CURL_INCLUDE_DIR curl.h curl/curl.h PATHS ${CMAKE_PREFIX_PATH} # Unofficial: We are proposing this. NO_DEFAULT_PATH PATH_SUFFIXES include ) -FIND_PATH(CURL_INCLUDE_DIR curl.h +FIND_PATH(CURL_INCLUDE_DIR curl.h curl/curl.h PATHS /usr/local/include/curl /usr/local/include/CURL @@ -19,7 +19,7 @@ /usr/include/CURL /usr/include /sw/include/curl - /sw/include/CURL + /sw/include/CURL /sw/include # Fink /opt/local/include/curl /opt/local/include/CURL @@ -33,21 +33,21 @@ ) #Find the CURL release library -FIND_LIBRARY(CURL_LIBRARY - NAMES curl curllib CURL +FIND_LIBRARY(CURL_LIBRARY + NAMES curl curllib CURL libcurl PATHS $ENV{CURL_DIR} NO_DEFAULT_PATH PATH_SUFFIXES lib64 lib ) -FIND_LIBRARY(CURL_LIBRARY - NAMES curl CURL curllib +FIND_LIBRARY(CURL_LIBRARY + NAMES curl CURL curllib libcurl PATHS ${CMAKE_PREFIX_PATH} # Unofficial: We are proposing this. NO_DEFAULT_PATH PATH_SUFFIXES lib64 lib ) -FIND_LIBRARY(CURL_LIBRARY - NAMES curl CURL curllib +FIND_LIBRARY(CURL_LIBRARY + NAMES curl CURL curllib libcurl PATHS ~/Library/Frameworks /Library/Frameworks @@ -64,20 +64,20 @@ #Find the CURL debug library FIND_LIBRARY(CURL_LIBRARY_DEBUG - NAMES curlD curllibD CURLD + NAMES curlD curld curllibD curllibd CURLD libcurlD libcurld PATHS $ENV{CURL_DIR} NO_DEFAULT_PATH PATH_SUFFIXES lib64 lib ) -FIND_LIBRARY(CURL_LIBRARY_DEBUG - NAMES curlD CURLD curllibD +FIND_LIBRARY(CURL_LIBRARY_DEBUG + NAMES curlD curld curllibD curllibd CURLD libcurlD libcurld PATHS ${CMAKE_PREFIX_PATH} # Unofficial: We are proposing this. NO_DEFAULT_PATH PATH_SUFFIXES lib64 lib ) -FIND_LIBRARY(CURL_LIBRARY_DEBUG - NAMES curlD CURLD curllibD +FIND_LIBRARY(CURL_LIBRARY_DEBUG + NAMES curlD curld curllibD curllibd CURLD libcurlD libcurld PATHS ~/Library/Frameworks /Library/Frameworks @@ -97,5 +97,3 @@ SET(CURL_FOUND "YES") ENDIF(CURL_LIBRARY AND CURL_INCLUDE_DIR) - - diff -Nru osgearth-2.0+dfsg/CMakeModules/FindGDAL.cmake osgearth-2.1.1+dfsg/CMakeModules/FindGDAL.cmake --- osgearth-2.0+dfsg/CMakeModules/FindGDAL.cmake 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/CMakeModules/FindGDAL.cmake 2011-11-04 19:44:43.000000000 +0000 @@ -57,7 +57,7 @@ ) FIND_LIBRARY(GDAL_LIBRARY - NAMES gdal gdal_i gdal1.7.0 gdal1.6.0 gdal1.5.0 gdal1.4.0 gdal1.3.2 GDAL + NAMES gdal gdal_i gdal1.8.0 gdal1.7.0 gdal1.6.0 gdal1.5.0 gdal1.4.0 gdal1.3.2 GDAL PATHS c:/Program Files/FWTools2.1.0/lib $ENV{GDAL_DIR} @@ -65,14 +65,14 @@ PATH_SUFFIXES lib64 lib ) FIND_LIBRARY(GDAL_LIBRARY - NAMES gdal gdal_i gdal1.7.0 gdal1.6.0 gdal1.5.0 gdal1.4.0 gdal1.3.2 GDAL + NAMES gdal gdal_i gdal1.8.0 gdal1.7.0 gdal1.6.0 gdal1.5.0 gdal1.4.0 gdal1.3.2 GDAL PATHS ${CMAKE_PREFIX_PATH} # Unofficial: We are proposing this. c:/Program Files/FWTools2.1.0/lib NO_DEFAULT_PATH PATH_SUFFIXES lib64 lib ) FIND_LIBRARY(GDAL_LIBRARY - NAMES gdal gdal_i gdal1.7.0 gdal1.6.0 gdal1.5.0 gdal1.4.0 gdal1.3.2 GDAL + NAMES gdal gdal_i gdal1.8.0 gdal1.7.0 gdal1.6.0 gdal1.5.0 gdal1.4.0 gdal1.3.2 GDAL PATHS ~/Library/Frameworks /Library/Frameworks diff -Nru osgearth-2.0+dfsg/CMakeModules/FindMiniZip.cmake osgearth-2.1.1+dfsg/CMakeModules/FindMiniZip.cmake --- osgearth-2.0+dfsg/CMakeModules/FindMiniZip.cmake 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/CMakeModules/FindMiniZip.cmake 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,54 @@ +# Locate minizip library +# This module defines +# MINIZIP_LIBRARY +# MINIZIP_FOUND, if false, do not try to link to libzip +# MINIZIP_INCLUDE_DIR, where to find the headers +# + +FIND_PATH(MINIZIP_INCLUDE_DIR zip.h + ${CMAKE_SOURCE_DIR}/src/3rdparty/minizip + $ENV{MINIZIP_DIR}/include + $ENV{MINIZIP_DIR} + $ENV{OSGDIR}/include + $ENV{OSGDIR} + $ENV{OSG_ROOT}/include + ~/Library/Frameworks + /Library/Frameworks + /usr/local/include + /usr/include + /sw/include # Fink + /opt/local/include # DarwinPorts + /opt/csw/include # Blastwave + /opt/include + [HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session\ Manager\\Environment;OSG_ROOT]/include + /usr/freeware/include +) + +FIND_LIBRARY(MINIZIP_LIBRARY + NAMES minizip + PATHS + ${CMAKE_SOURCE_DIR}/src/3rdparty/minizip + $ENV{MINIZIP_DIR}/lib + $ENV{MINIZIP_DIR} + $ENV{OSGDIR}/lib + $ENV{OSGDIR} + $ENV{OSG_ROOT}/lib + ~/Library/Frameworks + /Library/Frameworks + /usr/local/lib + /usr/lib + /sw/lib + /opt/local/lib + /opt/csw/lib + /opt/lib + [HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session\ Manager\\Environment;OSG_ROOT]/lib + /usr/freeware/lib64 +) + +SET(MINIZIP_FOUND "NO") +IF(MINIZIP_LIBRARY AND MINIZIP_INCLUDE_DIR) + SET(MINIZIP_FOUND "YES") + ADD_DEFINITIONS(-DOSGEARTH_HAVE_MINIZIP) +ENDIF(MINIZIP_LIBRARY AND MINIZIP_INCLUDE_DIR) + + diff -Nru osgearth-2.0+dfsg/CMakeModules/FindOSG.cmake osgearth-2.1.1+dfsg/CMakeModules/FindOSG.cmake --- osgearth-2.0+dfsg/CMakeModules/FindOSG.cmake 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/CMakeModules/FindOSG.cmake 2011-11-04 19:44:43.000000000 +0000 @@ -111,6 +111,9 @@ FIND_OSG_LIBRARY( OSGSHADOW_LIBRARY osgShadow ) FIND_OSG_LIBRARY( OSGSHADOW_LIBRARY_DEBUG osgShadowd ) +FIND_OSG_LIBRARY( OSGMANIPULATOR_LIBRARY osgManipulator ) +FIND_OSG_LIBRARY( OSGMANIPULATOR_LIBRARY_DEBUG osgManipulatord ) + FIND_OSG_LIBRARY( OPENTHREADS_LIBRARY OpenThreads ) FIND_OSG_LIBRARY( OPENTHREADS_LIBRARY_DEBUG OpenThreadsd ) diff -Nru osgearth-2.0+dfsg/CMakeModules/FindTinyXML.cmake osgearth-2.1.1+dfsg/CMakeModules/FindTinyXML.cmake --- osgearth-2.0+dfsg/CMakeModules/FindTinyXML.cmake 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/CMakeModules/FindTinyXML.cmake 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,24 @@ +# - Find TinyXML +# Find the native TinyXML includes and library +# +# TINYXML_FOUND - True if TinyXML found. +# TINYXML_INCLUDE_DIR - where to find tinyxml.h, etc. +# TINYXML_LIBRARY - TinyXML library. +# + +IF( TINYXML_INCLUDE_DIR ) + # Already in cache, be silent + SET( TinyXML_FIND_QUIETLY TRUE ) +ENDIF( TINYXML_INCLUDE_DIR ) + +FIND_PATH( TINYXML_INCLUDE_DIR "tinyxml.h" + PATH_SUFFIXES "tinyxml" ) + +FIND_LIBRARY( TINYXML_LIBRARY + NAMES "tinyxml" + PATH_SUFFIXES "tinyxml" ) + +SET(TINYXML_FOUND "NO") +IF(TINYXML_LIBRARY AND TINYXML_INCLUDE_DIR) + SET(TINYXML_FOUND "YES") +ENDIF(TINYXML_LIBRARY AND TINYXML_INCLUDE_DIR) diff -Nru osgearth-2.0+dfsg/CMakeModules/ModuleInstall.cmake osgearth-2.1.1+dfsg/CMakeModules/ModuleInstall.cmake --- osgearth-2.0+dfsg/CMakeModules/ModuleInstall.cmake 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/CMakeModules/ModuleInstall.cmake 2011-11-04 19:44:43.000000000 +0000 @@ -28,11 +28,25 @@ LIBRARY DESTINATION ${INSTALL_LIBDIR} ARCHIVE DESTINATION ${INSTALL_ARCHIVEDIR} ) -# FIXME: Do not run for OS X framework -INSTALL( - FILES ${LIB_PUBLIC_HEADERS} - DESTINATION ${INSTALL_INCDIR}/${LIB_NAME} -) + +IF(NOT OSGEARTH_BUILD_FRAMEWORKS) + INSTALL( + FILES ${LIB_PUBLIC_HEADERS} + DESTINATION ${INSTALL_INCDIR}/${LIB_NAME} + ) +ELSE() + SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) + SET(CMAKE_INSTALL_RPATH "${OSGEARTH_BUILD_FRAMEWORKS_INSTALL_NAME_DIR}") + + SET_TARGET_PROPERTIES(${LIB_NAME} PROPERTIES + FRAMEWORK TRUE + FRAMEWORK_VERSION ${OSGEARTH_MAJOR_VERSION} + PUBLIC_HEADER "${LIB_PUBLIC_HEADERS}" + INSTALL_NAME_DIR "${OSGEARTH_BUILD_FRAMEWORKS_INSTALL_NAME_DIR}" + ) + # MESSAGE("${OSG_COMPILE_FRAMEWORKS_INSTALL_NAME_DIR}") +ENDIF() + #Install the library to the OSG_DIR as well IF(OSGEARTH_INSTALL_TO_OSG_DIR AND OSG_DIR) diff -Nru osgearth-2.0+dfsg/CMakeModules/ModuleInstallOsgEarthDriverIncludes.cmake osgearth-2.1.1+dfsg/CMakeModules/ModuleInstallOsgEarthDriverIncludes.cmake --- osgearth-2.0+dfsg/CMakeModules/ModuleInstallOsgEarthDriverIncludes.cmake 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/CMakeModules/ModuleInstallOsgEarthDriverIncludes.cmake 2011-11-04 19:44:43.000000000 +0000 @@ -4,13 +4,6 @@ SET(INSTALL_INCDIR include) -SET(HEADERS_GROUP "Header Files") - -SOURCE_GROUP( - ${HEADERS_GROUP} - FILES ${LIB_PUBLIC_HEADERS} -) - # FIXME: Do not run for OS X framework INSTALL( FILES ${LIB_PUBLIC_HEADERS} diff -Nru osgearth-2.0+dfsg/CMakeModules/OsgEarthMacroUtils.cmake osgearth-2.1.1+dfsg/CMakeModules/OsgEarthMacroUtils.cmake --- osgearth-2.0+dfsg/CMakeModules/OsgEarthMacroUtils.cmake 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/CMakeModules/OsgEarthMacroUtils.cmake 2011-11-04 19:44:43.000000000 +0000 @@ -49,12 +49,12 @@ SET(OPENSCENEGRAPH_PATCH_VERSION "" CACHE STRING "OpenSceneGraph patch version number") SET(OPENSCENEGRAPH_SOVERSION "" CACHE STRING "OpenSceneGraph so version number") - if (OPENSCENEGRAPH_MAJOR_VERSION AND OPENSCENEGRAPH_MINOR_VERSION AND OPENSCENEGRAPH_PATCH_VERSION) + if (OPENSCENEGRAPH_MAJOR_VERSION AND NOT OPENSCENEGRAPH_MINOR_VERSION STREQUAL "" AND NOT OPENSCENEGRAPH_PATCH_VERSION STREQUAL "") SET(OPENSCENEGRAPH_VERSION ${OPENSCENEGRAPH_MAJOR_VERSION}.${OPENSCENEGRAPH_MINOR_VERSION}.${OPENSCENEGRAPH_PATCH_VERSION}) - else (OPENSCENEGRAPH_MAJOR_VERSION AND OPENSCENEGRAPH_MINOR_VERSION AND OPENSCENEGRAPH_PATCH_VERSION) + else (OPENSCENEGRAPH_MAJOR_VERSION AND NOT OPENSCENEGRAPH_MINOR_VERSION STREQUAL "" AND NOT OPENSCENEGRAPH_PATCH_VERSION STREQUAL "") #MESSAGE("osgversion was found at ${OSG_VERSION_EXE} but failed to run") SET(OPENSCENEGRAPH_VERSION) - endif (OPENSCENEGRAPH_MAJOR_VERSION AND OPENSCENEGRAPH_MINOR_VERSION AND OPENSCENEGRAPH_PATCH_VERSION) + endif (OPENSCENEGRAPH_MAJOR_VERSION AND NOT OPENSCENEGRAPH_MINOR_VERSION STREQUAL "" AND NOT OPENSCENEGRAPH_PATCH_VERSION STREQUAL "") MARK_AS_ADVANCED(OPENSCENEGRAPH_VERSION) @@ -192,6 +192,8 @@ SET(TARGET_NAME ${PLUGIN_NAME} ) #MESSAGE("in -->SETUP_PLUGIN<-- ${TARGET_NAME}-->${TARGET_SRC} <--> ${TARGET_H}<--") + + SOURCE_GROUP( "Header Files" FILES ${TARGET_H} ) ## we have set up the target label and targetname by taking into account global prfix (osgdb_) @@ -238,6 +240,10 @@ ENDIF(OSGEARTH_INSTALL_TO_OSG_DIR AND OSG_DIR) ENDIF(WIN32) + +#finally, set up the solution folder -gw + SET_PROPERTY(TARGET ${TARGET_TARGETNAME} PROPERTY FOLDER "Plugins") + ENDMACRO(SETUP_PLUGIN) @@ -319,6 +325,8 @@ 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") ENDMACRO(SETUP_APPLICATION) diff -Nru osgearth-2.0+dfsg/debian/changelog osgearth-2.1.1+dfsg/debian/changelog --- osgearth-2.0+dfsg/debian/changelog 2011-08-04 17:47:04.000000000 +0000 +++ osgearth-2.1.1+dfsg/debian/changelog 2011-11-30 22:54:13.000000000 +0000 @@ -1,4 +1,16 @@ -osgearth (2.0+dfsg-4) unstable; urgency=low +osgearth (2.1.1+dfsg-1~oneiric1) oneiric; urgency=low + + * Launchpad build for oneiric. + + -- Pirmin Kalberer Wed, 30 Nov 2011 23:53:30 +0100 + +osgearth (2.1.1+dfsg-1) unstable; urgency=low + + * Upstream release 2.1.1 + + -- Pirmin Kalberer Wed, 30 Nov 2011 22:27:40 +0100 + +osgearth (2.0-4) unstable; urgency=low * Changed maintainer to Debian GIS Project * Added package osgearth-data @@ -6,12 +18,13 @@ * Removed binaries from libosgearth-dev * Moved to sections devel/libs/libdevel * Fixed detection of OpenSceneGraph 3.0.0 + * Patch for obsolete Curl header * Enhanced package descriptions * Standards-Version 3.9.2 * Added source/format 3.0 (quilt) * Ready for Debian upload (closes: #633068) - -- Pirmin Kalberer Thu, 14 Jul 2011 22:13:36 +0200 + -- Pirmin Kalberer Wed, 03 Aug 2011 21:40:46 +0200 osgearth (2.0-3) unstable; urgency=low diff -Nru osgearth-2.0+dfsg/debian/patches/curlheaders osgearth-2.1.1+dfsg/debian/patches/curlheaders --- osgearth-2.0+dfsg/debian/patches/curlheaders 2011-08-04 17:46:11.000000000 +0000 +++ osgearth-2.1.1+dfsg/debian/patches/curlheaders 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -Index: osgearth-2.0/src/osgEarth/HTTPClient.cpp -=================================================================== ---- osgearth-2.0.orig/src/osgEarth/HTTPClient.cpp 2011-08-03 21:45:26.667654831 +0200 -+++ osgearth-2.0/src/osgEarth/HTTPClient.cpp 2011-08-03 21:45:30.347654833 +0200 -@@ -18,7 +18,6 @@ - */ - - #include --#include - #include - #include - #include diff -Nru osgearth-2.0+dfsg/debian/patches/gdal1.8 osgearth-2.1.1+dfsg/debian/patches/gdal1.8 --- osgearth-2.0+dfsg/debian/patches/gdal1.8 2011-08-04 17:46:11.000000000 +0000 +++ osgearth-2.1.1+dfsg/debian/patches/gdal1.8 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ ---- a/CMakeModules/FindGDAL.cmake -+++ b/CMakeModules/FindGDAL.cmake -@@ -57,7 +57,7 @@ FIND_PATH(GDAL_INCLUDE_DIR gdal.h - ) - - FIND_LIBRARY(GDAL_LIBRARY -- NAMES gdal gdal_i gdal1.7.0 gdal1.6.0 gdal1.5.0 gdal1.4.0 gdal1.3.2 GDAL -+ NAMES gdal gdal_i gdal1.8.0 gdal1.7.0 gdal1.6.0 gdal1.5.0 gdal1.4.0 gdal1.3.2 GDAL - PATHS - c:/Program Files/FWTools2.1.0/lib - $ENV{GDAL_DIR} -@@ -65,14 +65,14 @@ FIND_LIBRARY(GDAL_LIBRARY - PATH_SUFFIXES lib64 lib - ) - FIND_LIBRARY(GDAL_LIBRARY -- NAMES gdal gdal_i gdal1.7.0 gdal1.6.0 gdal1.5.0 gdal1.4.0 gdal1.3.2 GDAL -+ NAMES gdal gdal_i gdal1.8.0 gdal1.7.0 gdal1.6.0 gdal1.5.0 gdal1.4.0 gdal1.3.2 GDAL - PATHS ${CMAKE_PREFIX_PATH} # Unofficial: We are proposing this. - c:/Program Files/FWTools2.1.0/lib - NO_DEFAULT_PATH - PATH_SUFFIXES lib64 lib - ) - FIND_LIBRARY(GDAL_LIBRARY -- NAMES gdal gdal_i gdal1.7.0 gdal1.6.0 gdal1.5.0 gdal1.4.0 gdal1.3.2 GDAL -+ NAMES gdal gdal_i gdal1.8.0 gdal1.7.0 gdal1.6.0 gdal1.5.0 gdal1.4.0 gdal1.3.2 GDAL - PATHS - ~/Library/Frameworks - /Library/Frameworks - diff -Nru osgearth-2.0+dfsg/debian/patches/osg3 osgearth-2.1.1+dfsg/debian/patches/osg3 --- osgearth-2.0+dfsg/debian/patches/osg3 2011-08-04 17:46:11.000000000 +0000 +++ osgearth-2.1.1+dfsg/debian/patches/osg3 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ -Index: osgearth-2.0/CMakeModules/OsgEarthMacroUtils.cmake -=================================================================== ---- osgearth-2.0.orig/CMakeModules/OsgEarthMacroUtils.cmake 2011-07-12 20:48:49.782589187 +0200 -+++ osgearth-2.0/CMakeModules/OsgEarthMacroUtils.cmake 2011-07-12 20:49:32.802589207 +0200 -@@ -49,12 +49,12 @@ - SET(OPENSCENEGRAPH_PATCH_VERSION "" CACHE STRING "OpenSceneGraph patch version number") - SET(OPENSCENEGRAPH_SOVERSION "" CACHE STRING "OpenSceneGraph so version number") - -- if (OPENSCENEGRAPH_MAJOR_VERSION AND OPENSCENEGRAPH_MINOR_VERSION AND OPENSCENEGRAPH_PATCH_VERSION) -+ if (OPENSCENEGRAPH_MAJOR_VERSION AND NOT OPENSCENEGRAPH_MINOR_VERSION STREQUAL "" AND NOT OPENSCENEGRAPH_PATCH_VERSION STREQUAL "") - SET(OPENSCENEGRAPH_VERSION ${OPENSCENEGRAPH_MAJOR_VERSION}.${OPENSCENEGRAPH_MINOR_VERSION}.${OPENSCENEGRAPH_PATCH_VERSION}) -- else (OPENSCENEGRAPH_MAJOR_VERSION AND OPENSCENEGRAPH_MINOR_VERSION AND OPENSCENEGRAPH_PATCH_VERSION) -+ else (OPENSCENEGRAPH_MAJOR_VERSION AND NOT OPENSCENEGRAPH_MINOR_VERSION STREQUAL "" AND NOT OPENSCENEGRAPH_PATCH_VERSION STREQUAL "") - #MESSAGE("osgversion was found at ${OSG_VERSION_EXE} but failed to run") - SET(OPENSCENEGRAPH_VERSION) -- endif (OPENSCENEGRAPH_MAJOR_VERSION AND OPENSCENEGRAPH_MINOR_VERSION AND OPENSCENEGRAPH_PATCH_VERSION) -+ endif (OPENSCENEGRAPH_MAJOR_VERSION AND NOT OPENSCENEGRAPH_MINOR_VERSION STREQUAL "" AND NOT OPENSCENEGRAPH_PATCH_VERSION STREQUAL "") - - MARK_AS_ADVANCED(OPENSCENEGRAPH_VERSION) - diff -Nru osgearth-2.0+dfsg/debian/patches/series osgearth-2.1.1+dfsg/debian/patches/series --- osgearth-2.0+dfsg/debian/patches/series 2011-08-04 17:55:36.000000000 +0000 +++ osgearth-2.1.1+dfsg/debian/patches/series 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -gdal1.8 -osg3 -curlheaders diff -Nru osgearth-2.0+dfsg/debian/watch osgearth-2.1.1+dfsg/debian/watch --- osgearth-2.0+dfsg/debian/watch 2011-08-04 17:46:11.000000000 +0000 +++ osgearth-2.1.1+dfsg/debian/watch 2011-11-30 22:43:22.000000000 +0000 @@ -1,2 +1,2 @@ version=3 -http://githubredir.debian.net/github/gwaldron/osgearth .*osgearth_(.*)_.*.tar.gz +http://githubredir.debian.net/github/gwaldron/osgearth .*osgearth-(.*).tar.gz diff -Nru osgearth-2.0+dfsg/LICENSE.txt osgearth-2.1.1+dfsg/LICENSE.txt --- osgearth-2.0+dfsg/LICENSE.txt 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/LICENSE.txt 2011-11-04 19:44:43.000000000 +0000 @@ -1,65 +1,165 @@ -GNU LESSER GENERAL PUBLIC LICENSE + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 -Version 3, 29 June 2007 - -Copyright © 2007 Free Software Foundation, Inc. - -Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. - -This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. -0. Additional Definitions. - -As used herein, “this License” refers to version 3 of the GNU Lesser General Public License, and the “GNU GPL” refers to version 3 of the GNU General Public License. - -“The Library” refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. - -An “Application” is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. - -A “Combined Work” is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the “Linked Version”. - -The “Minimal Corresponding Source” for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. - -The “Corresponding Application Code” for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. -1. Exception to Section 3 of the GNU GPL. - -You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. -2. Conveying Modified Versions. - -If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: - - * a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or - * b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. - -3. Object Code Incorporating Material from Library Header Files. - -The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: - - * a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. - * b) Accompany the object code with a copy of the GNU GPL and this license document. - -4. Combined Works. - -You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: - - * a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. - * b) Accompany the Combined Work with a copy of the GNU GPL and this license document. - * c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. - * d) Do one of the following: - o 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. - o 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. - * e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) - -5. Combined Libraries. - -You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: - - * a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. - * b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. - -6. Revised Versions of the GNU Lesser General Public License. - -The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. - -If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff -Nru osgearth-2.0+dfsg/src/applications/CMakeLists.txt osgearth-2.1.1+dfsg/src/applications/CMakeLists.txt --- osgearth-2.0+dfsg/src/applications/CMakeLists.txt 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/applications/CMakeLists.txt 2011-11-04 19:44:43.000000000 +0000 @@ -27,17 +27,30 @@ ADD_SUBDIRECTORY(osgearth_viewer) ADD_SUBDIRECTORY(osgearth_manip) ADD_SUBDIRECTORY(osgearth_seed) +ADD_SUBDIRECTORY(osgearth_composite) ADD_SUBDIRECTORY(osgearth_clouds) ADD_SUBDIRECTORY(osgearth_ocean) ADD_SUBDIRECTORY(osgearth_toc) ADD_SUBDIRECTORY(osgearth_elevation) ADD_SUBDIRECTORY(osgearth_features) +ADD_SUBDIRECTORY(osgearth_featureinfo) +ADD_SUBDIRECTORY(osgearth_map) +ADD_SUBDIRECTORY(osgearth_annotation) + +IF(NOT ${OPENSCENEGRAPH_VERSION} VERSION_LESS "2.9.6") +ADD_SUBDIRECTORY(osgearth_featureeditor) +ENDIF() + +ADD_SUBDIRECTORY(osgearth_measure) ADD_SUBDIRECTORY(osgearth_version) ADD_SUBDIRECTORY(osgearth_controls) ADD_SUBDIRECTORY(osgearth_shadercomp) ADD_SUBDIRECTORY(osgearth_tilesource) +ADD_SUBDIRECTORY(osgearth_labels) +ADD_SUBDIRECTORY(osgearth_imageoverlay) + #ADD_SUBDIRECTORY(osgearth_symbology) -IF(NOT (${OPENSCENEGRAPH_VERSION} VERSION_LESS "2.9.10")) - ADD_SUBDIRECTORY(osgearth_seamless) -ENDIF(NOT (${OPENSCENEGRAPH_VERSION} VERSION_LESS "2.9.10")) + +#testing +add_subdirectory(osgearth_resources) \ No newline at end of file diff -Nru osgearth-2.0+dfsg/src/applications/osgearth_annotation/CMakeLists.txt osgearth-2.1.1+dfsg/src/applications/osgearth_annotation/CMakeLists.txt --- osgearth-2.0+dfsg/src/applications/osgearth_annotation/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/applications/osgearth_annotation/CMakeLists.txt 2011-11-04 19:44:43.000000000 +0000 @@ -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_annotation.cpp ) + +#### end var setup ### +SETUP_APPLICATION(osgearth_annotation) \ No newline at end of file diff -Nru osgearth-2.0+dfsg/src/applications/osgearth_annotation/osgearth_annotation.cpp osgearth-2.1.1+dfsg/src/applications/osgearth_annotation/osgearth_annotation.cpp --- osgearth-2.0+dfsg/src/applications/osgearth_annotation/osgearth_annotation.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/applications/osgearth_annotation/osgearth_annotation.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,213 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph +* Copyright 2008-2010 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 +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace osgEarth; +using namespace osgEarth::Util; +using namespace osgEarth::Util::Controls; +using namespace osgEarth::Util::Annotation; + +int +usage( char** argv ) +{ + OE_WARN << "Usage: " << argv[0] << " " << std::endl; + return -1; +} + +struct ToggleNode : public ControlEventHandler { + ToggleNode( osg::Node* node ) : _node( node ) { } + void onValueChanged( Control* src, bool value ) { + if ( _node.valid() ) + _node->setNodeMask( value ? ~0 : 0 ); + } + osg::observer_ptr _node; +}; + +int +main(int argc, char** argv) +{ + osg::Group* root = new osg::Group(); + + // try to load an earth file. + osg::ArgumentParser arguments(&argc,argv); + osg::Node* node = osgDB::readNodeFiles( arguments ); + if ( !node ) + return usage(argv); + + // find the map node that we loaded. + MapNode* mapNode = MapNode::findMapNode(node); + if ( !mapNode ) + return usage(argv); + + root->addChild( mapNode ); + + // make some annotations. + osg::Group* annoGroup = new osg::Group(); + root->addChild( annoGroup ); + + // a Placemark combines a 2D icon with a text label. + annoGroup->addChild( new PlacemarkNode( + mapNode, + osg::Vec3d(-74, 40.714, 0), + URI("../data/placemark32.png").readImage(), + "New York") ); + + // a Placemark combines a 2D icon with a text label. + annoGroup->addChild( new PlacemarkNode( + mapNode, + osg::Vec3d(139.75, 35.685, 0), + URI("../data/placemark32.png").readImage(), + "Tokyo" ) ); + + // a box that follows lines of latitude (rhumb line interpolation, the default) + Geometry* geom = new Ring(); + geom->push_back( osg::Vec3d(0, 40, 0) ); + geom->push_back( osg::Vec3d(-60, 40, 0) ); + geom->push_back( osg::Vec3d(-60, 60, 0) ); + geom->push_back( osg::Vec3d(0, 60, 0) ); + Style geomStyle; + + geomStyle.getOrCreate()->stroke()->color() = Color::Yellow; + geomStyle.getOrCreate()->stroke()->width() = 5.0f; + + FeatureNode* gnode = new FeatureNode(mapNode, new Feature(geom, geomStyle), true); + annoGroup->addChild( gnode ); + + // another line, this time using great-circle interpolation (flight path from New York to Tokyo) + Geometry* path = new LineString(); + path->push_back( osg::Vec3d(-74, 40.714, 0) ); // New York + path->push_back( osg::Vec3d(139.75, 35.685, 0) ); // Tokyo + + Style pathStyle; + pathStyle.getOrCreate()->stroke()->color() = Color::Red; + pathStyle.getOrCreate()->stroke()->width() = 10.0f; + + Feature* pathFeature = new Feature(path, pathStyle); + pathFeature->geoInterp() = GEOINTERP_GREAT_CIRCLE; + FeatureNode* pathNode = new FeatureNode(mapNode, pathFeature, true); + annoGroup->addChild( pathNode ); + + // a circle around New Orleans + Style circleStyle; + circleStyle.getOrCreate()->fill()->color() = Color(Color::Cyan, 0.5); + CircleNode* circle = new CircleNode( + mapNode, + osg::Vec3d(-90.25, 29.98, 0), + Linear(600, Units::KILOMETERS), + circleStyle ); + annoGroup->addChild( circle ); + + // an ellipse around Miami + Style ellipseStyle; + ellipseStyle.getOrCreate()->fill()->color() = Color(Color::Orange, 0.75); + EllipseNode* ellipse = new EllipseNode( + mapNode, + osg::Vec3d(-80.28,25.82,0), + Linear(200, Units::MILES), + Linear(100, Units::MILES), + Angular(45, Units::DEGREES), + ellipseStyle, + true); + annoGroup->addChild( ellipse ); + + // an extruded polygon roughly the shape of Utah + Geometry* utah = new Polygon(); + utah->push_back( osg::Vec3d(-114.052, 37, 0) ); + utah->push_back( osg::Vec3d(-109.054, 37, 0) ); + utah->push_back( osg::Vec3d(-109.054, 41, 0) ); + utah->push_back( osg::Vec3d(-111.04, 41, 0) ); + utah->push_back( osg::Vec3d(-111.08, 42.059, 0) ); + utah->push_back( osg::Vec3d(-114.08, 42.024, 0) ); + + Style utahStyle; + utahStyle.getOrCreate()->height() = 250000.0; // meters MSL + utahStyle.getOrCreate()->fill()->color() = Color(Color::White, 0.8); + + Feature* utahFeature = new Feature(utah, utahStyle); + FeatureNode* utahNode = new FeatureNode(mapNode, utahFeature, false); + annoGroup->addChild( utahNode ); + + // an image overlay + ImageOverlay* imageOverlay = 0L; + osg::Image* image = osgDB::readImageFile( "../data/USFLAG.TGA" ); + if ( image ) { + imageOverlay = new ImageOverlay(mapNode, image); + imageOverlay->setBounds( Bounds( -100.0, 50.0, -90.0, 55.0) ); + + //Add an editor + annoGroup->addChild( imageOverlay ); + + osg::Node* editor = new ImageOverlayEditor( imageOverlay, mapNode->getMap()->getProfile()->getSRS()->getEllipsoid(), mapNode ); + root->addChild( editor ); + + + + } + + + + + // initialize a viewer: + osgViewer::Viewer viewer(arguments); + viewer.setCameraManipulator( new EarthManipulator() ); + viewer.setSceneData( root ); + + // make a little HUD to toggle stuff: + VBox* vbox = new VBox(); + vbox->setBackColor( Color(Color::Black, 0.5) ); + vbox->setVertAlign( Control::ALIGN_TOP ); + vbox->addControl( new LabelControl("Annotation Example", 22.0f, Color::Yellow) ); + Grid* grid = new Grid(); + vbox->addControl( grid ); + grid->setChildSpacing( 5 ); + grid->setChildHorizAlign( Control::ALIGN_LEFT ); + grid->setChildVertAlign( Control::ALIGN_CENTER ); + grid->setControl( 0, 0, new CheckBoxControl(true, new ToggleNode(gnode)) ); + grid->setControl( 1, 0, new LabelControl("Line geoemtry") ); + grid->setControl( 0, 1, new CheckBoxControl(true, new ToggleNode(pathNode)) ); + grid->setControl( 1, 1, new LabelControl("Red flight path") ); + grid->setControl( 0, 2, new CheckBoxControl(true, new ToggleNode(circle)) ); + grid->setControl( 1, 2, new LabelControl("Blue circle") ); + grid->setControl( 0, 3, new CheckBoxControl(true, new ToggleNode(ellipse)) ); + grid->setControl( 1, 3, new LabelControl("Orange ellipse") ); + grid->setControl( 0, 4, new CheckBoxControl(true, new ToggleNode(utahNode)) ); + grid->setControl( 1, 4, new LabelControl("Extruded state") ); + if ( imageOverlay ) { + grid->setControl( 0, 5, new CheckBoxControl(true, new ToggleNode(imageOverlay)) ); + grid->setControl( 1, 5, new LabelControl("Image overlay") ); + } + ControlCanvas::get(&viewer,true)->addControl(vbox); + + // add some stock OSG handlers: + viewer.getDatabasePager()->setDoPreCompile( true ); + viewer.addEventHandler(new osgViewer::StatsHandler()); + viewer.addEventHandler(new osgViewer::WindowSizeHandler()); + viewer.addEventHandler(new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet())); + + return viewer.run(); +} diff -Nru osgearth-2.0+dfsg/src/applications/osgearth_composite/CMakeLists.txt osgearth-2.1.1+dfsg/src/applications/osgearth_composite/CMakeLists.txt --- osgearth-2.0+dfsg/src/applications/osgearth_composite/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/applications/osgearth_composite/CMakeLists.txt 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,8 @@ +INCLUDE_DIRECTORIES(${OSG_INCLUDE_DIRS} ) + +SET(TARGET_LIBRARIES_VARS OSG_LIBRARY OSGDB_LIBRARY OSGUTIL_LIBRARY OSGVIEWER_LIBRARY OPENTHREADS_LIBRARY) + +SET(TARGET_SRC osgearth_composite.cpp ) + +#### end var setup ### +SETUP_APPLICATION(osgearth_composite) \ No newline at end of file diff -Nru osgearth-2.0+dfsg/src/applications/osgearth_composite/osgearth_composite.cpp osgearth-2.1.1+dfsg/src/applications/osgearth_composite/osgearth_composite.cpp --- osgearth-2.0+dfsg/src/applications/osgearth_composite/osgearth_composite.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/applications/osgearth_composite/osgearth_composite.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,143 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph +* Copyright 2008-2010 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 +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +using namespace osgEarth; +using namespace osgEarth::Drivers; +using namespace osgEarth::Util; + +static void +getFiles(const std::string &file, const std::vector &exts, std::vector &files) +{ + if (osgDB::fileType(file) == osgDB::DIRECTORY) + { + osgDB::DirectoryContents contents = osgDB::getDirectoryContents(file); + for (osgDB::DirectoryContents::iterator itr = contents.begin(); itr != contents.end(); ++itr) + { + if (*itr == "." || *itr == "..") continue; + std::string f = osgDB::concatPaths(file, *itr); + getFiles(f, exts, files); + } + } + else + { + bool fileValid = false; + //If we have no _extensions specified, assume we should try everything + if (exts.size() == 0) + { + fileValid = true; + } + 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])) + { + fileValid = true; + break; + } + } + } + + if (fileValid) + { + files.push_back(osgDB::convertFileNameToNativeStyle(file)); + } + } +} + +// +// Loads a directory of images, demonstrates the programatic use of the CompositeTileSource +// usage: osgearth_composite --dir DIRECTORY_OF_IMAGES --ext EXTENSION [--ext EXTENSION] +// example: osgearth_composite --dir c:\myimages --ext jpg --ext png +// NOTE: run this sample from the repo/tests directory. +// +int main(int argc, char** argv) +{ + osg::ArgumentParser arguments(&argc,argv); + + osgViewer::Viewer viewer(arguments); + + std::vector files; + std::vector< std::string > exts; + + //Specify a directory + std::string directory = "."; + while (arguments.read("--dir", directory) ); + + //Specify the extensions that are valid + std::string ext; + while (arguments.read("--ext", ext)) { exts.push_back( ext ); }; + + OE_NOTICE << "directory=" << directory << std::endl; + + getFiles(directory, exts, files); + + // Start by creating the map: + Map* map = new Map(); + + //Add a base layer + GDALOptions basemapOpt; + basemapOpt.url() = URI("../data/world.tif"); + map->addImageLayer( new ImageLayer( ImageLayerOptions("basemap", basemapOpt) ) ); + + osgEarth::CompositeTileSourceOptions compositeOpt; + for (unsigned int i = 0; i < files.size(); i++) + { + GDALOptions gdalOpt; + gdalOpt.url() = files[i]; + ImageLayerOptions ilo(files[i], gdalOpt); + //Set the transparent color on each image + //ilo.transparentColor() = osg::Vec4ub(255, 255, 206, 0); + compositeOpt.add( ilo ); + OE_NOTICE << "Added file " << files[i] << std::endl; + } + + map->addImageLayer( new ImageLayer( ImageLayerOptions("composite", compositeOpt) ) ); + + MapNode* mapNode = new MapNode( map ); + + viewer.setSceneData( mapNode ); + viewer.setCameraManipulator( new EarthManipulator() ); + + + // add some stock OSG handlers: + viewer.addEventHandler(new osgViewer::StatsHandler()); + viewer.addEventHandler(new osgViewer::WindowSizeHandler()); + viewer.addEventHandler(new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet())); + + return viewer.run(); +} diff -Nru osgearth-2.0+dfsg/src/applications/osgearth_controls/osgearth_controls.cpp osgearth-2.1.1+dfsg/src/applications/osgearth_controls/osgearth_controls.cpp --- osgearth-2.0+dfsg/src/applications/osgearth_controls/osgearth_controls.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/applications/osgearth_controls/osgearth_controls.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -24,11 +24,15 @@ #include #include #include +#include +using namespace osgEarth::Symbology; using namespace osgEarth::Util::Controls; void createControls( ControlCanvas* ); +ImageControl* s_imageControl; + int main(int argc, char** argv) { @@ -41,7 +45,7 @@ root->addChild( node ); // create a surface to house the controls - ControlCanvas* cs = new ControlCanvas( &viewer ); + ControlCanvas* cs = ControlCanvas::get( &viewer ); root->addChild( cs ); viewer.setSceneData( root ); @@ -55,10 +59,10 @@ struct MyClickHandler : public ControlEventHandler { - void onClick( Control* control, int mouseButtonMask ) + void onClick( Control* control, const osg::Vec2f& pos, int mouseButtonMask ) { - OE_NOTICE << "Thank you for clicking on " << typeid(control).name() - << std::endl; + OE_NOTICE << "You clicked at (" << pos.x() << ", " << pos.y() << ") within the control." + << std::endl; } }; @@ -75,6 +79,14 @@ } }; +struct RotateImage : public ControlEventHandler +{ + void onValueChanged( Control* control, float value ) + { + s_imageControl->setRotation( Angular(value) ); + } +}; + void createControls( ControlCanvas* cs ) { @@ -88,27 +100,47 @@ center->setVertAlign( Control::ALIGN_CENTER ); // Add an image: - osg::Image* image = osgDB::readImageFile( "http://pelicanmapping.com/pelican.png" ); - if ( image ) { - ImageControl* imageCon = new ImageControl( image ); - center->addControl( imageCon ); + osg::ref_ptr image; + if ( HTTPClient::readImageFile("http://demo.pelicanmapping.com/rmweb/readymap_logo.png", image) == HTTPClient::RESULT_OK ) + { + s_imageControl = new ImageControl( image.get() ); + s_imageControl->setHorizAlign( Control::ALIGN_CENTER ); + s_imageControl->setFixSizeForRotation( true ); + //imageCon->addEventHandler( new ImageRotationHandler ); + center->addControl( s_imageControl ); center->setHorizAlign( Control::ALIGN_CENTER ); } // Add a text label: - LabelControl* label = new LabelControl( "osgEarth Controls Toolkit - new in osgEarth 1.4" ); + LabelControl* label = new LabelControl( "osgEarth Controls Toolkit" ); label->setFont( osgText::readFontFile( "arialbd.ttf" ) ); label->setFontSize( 24.0f ); label->setHorizAlign( Control::ALIGN_CENTER ); + label->setMargin( 5 ); center->addControl( label ); + // Rotation slider + HBox* rotateBox = new HBox(); + rotateBox->setChildVertAlign( Control::ALIGN_CENTER ); + rotateBox->setHorizFill( true ); + rotateBox->setBackColor( Color::Blue ); + { + rotateBox->addControl( new LabelControl("Rotate: ") ); + + HSliderControl* rotateSlider = new HSliderControl( -180.0, 180.0, 0.0 ); + rotateSlider->addEventHandler( new RotateImage() ); + rotateSlider->setHeight( 8.0f ); + rotateSlider->setHorizFill( true ); + rotateBox->addControl( rotateSlider ); + } + center->addControl( rotateBox ); + cs->addControl( center ); } // a simple vbox with absolute positioning in the upper left with two text labels. { VBox* ul = new VBox(); - ul->setFrame( new Frame() ); ul->setPosition( 20, 20 ); ul->setPadding( 10 ); { @@ -119,7 +151,7 @@ ul->addControl( content ); HBox* c2 = new HBox(); - c2->setSpacing( 10 ); + c2->setChildSpacing( 10 ); { HSliderControl* slider = new HSliderControl( 0, 100 ); slider->setBackColor( .6,0,0,1 ); @@ -136,10 +168,10 @@ HBox* c3 = new HBox(); c3->setHorizAlign( Control::ALIGN_CENTER ); - c3->setSpacing( 10 ); + c3->setChildSpacing( 10 ); { HBox* c4 = new HBox(); - c4->setSpacing( 5 ); + c4->setChildSpacing( 5 ); { c4->addControl( new CheckBoxControl( true ) ); c4->addControl( new LabelControl( "Checkbox 1" ) ); @@ -147,7 +179,7 @@ c3->addControl( c4 ); HBox* c5 = new HBox(); - c5->setSpacing( 5 ); + c5->setChildSpacing( 5 ); { c5->addControl( new CheckBoxControl( false ) ); c5->addControl( new LabelControl( "Checkbox 2" ) ); @@ -167,7 +199,7 @@ bottom->setFrame( new RoundedFrame() ); bottom->getFrame()->setBackColor(0,0,0,0.5); bottom->setMargin( 10 ); - bottom->setSpacing( 145 ); + bottom->setChildSpacing( 145 ); bottom->setVertAlign( Control::ALIGN_BOTTOM ); bottom->setHorizAlign( Control::ALIGN_CENTER ); diff -Nru osgearth-2.0+dfsg/src/applications/osgearth_elevation/osgearth_elevation.cpp osgearth-2.1.1+dfsg/src/applications/osgearth_elevation/osgearth_elevation.cpp --- osgearth-2.0+dfsg/src/applications/osgearth_elevation/osgearth_elevation.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/applications/osgearth_elevation/osgearth_elevation.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -31,57 +31,55 @@ #include #include #include +#include #include -#include -#include +#include +#include #include +#include -using namespace osgEarth::Drivers; +using namespace osgEarth; +using namespace osgEarth::Util; + +static osg::Node* s_flagNode; +static osgText::Text* s_flagText; static -osg::MatrixTransform* createFlag() +osg::Node* createFlag() { - osg::Cylinder* c = new osg::Cylinder( osg::Vec3d(0,0,0), 2.0f, 250.f ); osg::Geode* g = new osg::Geode(); - g->addDrawable( new osg::ShapeDrawable( c ) ); osgText::Text* text = new osgText::Text(); text->setCharacterSizeMode( osgText::Text::SCREEN_COORDS ); - text->setCharacterSize( 72.f ); + text->setCharacterSize( 24.f ); + text->setFont( osgText::readFontFile("arial.ttf") ); text->setBackdropType( osgText::Text::OUTLINE ); text->setText( "00000000000000" ); text->setAutoRotateToScreen( true ); - text->setPosition( osg::Vec3d( 0, 0, 125 ) ); + text->setPosition( osg::Vec3d( 0, 0, 0 ) ); text->setDataVariance( osg::Object::DYNAMIC ); g->addDrawable( text ); - osg::AutoTransform* at = new osg::AutoTransform(); - at->setAutoScaleToScreen( true ); - at->addChild( g ); - at->getOrCreateStateSet()->setMode( GL_LIGHTING, 0 ); - osg::MatrixTransform* xf = new osg::MatrixTransform(); - xf->addChild( at ); - xf->setDataVariance( osg::Object::DYNAMIC ); - return xf; -} - -static void -updateFlag( osg::MatrixTransform* xf, const osg::Matrix& mat, double elev ) -{ - osg::Geode* g = static_cast( xf->getChild(0)->asGroup()->getChild(0) ); - std::stringstream buf; - buf << elev; - std::string bufStr; - bufStr = buf.str(); - static_cast( g->getDrawable(1) )->setText( bufStr ); - xf->setMatrix( mat ); + g->getOrCreateStateSet()->setMode( GL_LIGHTING, 0 ); + g->getStateSet()->setAttribute( new osg::Depth(osg::Depth::ALWAYS), 1 ); + g->getStateSet()->setRenderBinDetails( 99, "RenderBin" ); + g->setDataVariance( osg::Object::DYNAMIC ); + g->setCullingActive( false ); + g->setNodeMask( 0 ); + s_flagText = text; + s_flagNode = g; + return g; } // An event handler that will print out the elevation at the clicked point struct QueryElevationHandler : public osgGA::GUIEventHandler { - QueryElevationHandler(osgEarth::Util::ElevationManager* elevMan, - const osgEarth::SpatialReference* mapSRS, - osg::MatrixTransform* flag ) - : _mouseDown(false), _elevMan(elevMan), _flag(flag), _mapSRS(mapSRS) { } + QueryElevationHandler(const Map* map, ObjectLocator* flagLocator ) + : _map(map), + _mouseDown( false ), + _flagLocator(flagLocator), + _query(map) + { + _query.setMaxTilesToCache(10); + } void update( float x, float y, osgViewer::View* view ) { @@ -91,33 +89,51 @@ // find the first hit under the mouse: osgUtil::LineSegmentIntersector::Intersection first = *(results.begin()); osg::Vec3d point = first.getWorldIntersectPoint(); + osg::Vec3d lla; // transform it to map coordinates: - double lat_rad, lon_rad, height; - _mapSRS->getEllipsoid()->convertXYZToLatLongHeight( point.x(), point.y(), point.z(), lat_rad, lon_rad, height ); - - // query the elevation at the map point: - double lat_deg = osg::RadiansToDegrees( lat_rad ); - double lon_deg = osg::RadiansToDegrees( lon_rad ); + _map->worldPointToMapPoint(point, lla); + + // find the elevation at that map point: osg::Matrixd out_mat; double query_resolution = 0.1; // 1/10th of a degree double out_elevation = 0.0; double out_resolution = 0.0; - if ( _elevMan->getPlacementMatrix( - lon_deg, lat_deg, 0, - query_resolution, NULL, - out_mat, out_elevation, out_resolution ) ) + bool ok = _query.getElevation( + lla, + _map->getProfile()->getSRS(), + out_elevation, + query_resolution, + &out_resolution ); + + if ( ok ) { - updateFlag( _flag.get(), out_mat, out_elevation ); + _flagLocator->setPosition( osg::Vec3d(lla.x(), lla.y(), out_elevation) ); + s_flagNode->setNodeMask( ~0 ); + + std::stringstream buf; + buf << std::fixed << std::setprecision(2) + << "Pos: " << lla.x() << ", " << lla.y() << std::endl + << "Elv: " << out_elevation << "m" << std::endl + << "X: " << point.x() << std::endl + << "Y: " << point.y() << std::endl + << "Z: " << point.z(); + s_flagText->setText( buf.str() ); + + OE_NOTICE << buf.str() << std::endl; } else { OE_NOTICE - << "getElevation FAILED! at (" << lat_deg << ", " << lon_deg << ")" << std::endl; + << "getElevation FAILED! at (" << point.x() << ", " << point.y() << ")" << std::endl; } } + else + { + s_flagNode->setNodeMask(0); + } } bool handle( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa ) @@ -131,10 +147,10 @@ return false; } - bool _mouseDown; - osg::ref_ptr _elevMan; - osg::ref_ptr _flag; - osg::ref_ptr _mapSRS; + const Map* _map; + bool _mouseDown; + ElevationQuery _query; + ObjectLocator* _flagLocator; }; @@ -144,35 +160,19 @@ osgViewer::Viewer viewer(arguments); - // install the programmable manipulator. - osgEarth::Util::EarthManipulator* manip = new osgEarth::Util::EarthManipulator(); - viewer.setCameraManipulator( manip ); - osgEarth::MapNode* mapNode = NULL; osg::Node* loadedNode = osgDB::readNodeFiles( arguments ); - if (!loadedNode) - { - // load up a map with an elevation layer: - osgEarth::Map* map = new osgEarth::Map(); - - // Add some imagery - { - TMSOptions tms( "http://demo.pelicanmapping.com/rmweb/data/bluemarble-tms/tms.xml" ); - map->addImageLayer( new osgEarth::ImageLayer( "BLUEMARBLE", tms ) ); - } - - // Add some elevation - { - TMSOptions tms( "http://demo.pelicanmapping.com/rmweb/data/srtm30_plus_tms/tms.xml" ); - map->addElevationLayer( new osgEarth::ElevationLayer( "SRTM", tms ) ); - } - mapNode = new osgEarth::MapNode( map ); - } - else - { + if (loadedNode) + { mapNode = findTopMostNodeOfType( loadedNode ); - } + } + + if ( !mapNode ) + { + OE_WARN << "Unable to load earth file." << std::endl; + return -1; + } osg::Group* root = new osg::Group(); @@ -181,24 +181,24 @@ root->addChild( mapNode ); // A flag so we can see where we clicked - osg::MatrixTransform* flag = createFlag(); + ObjectLocatorNode* flag = new ObjectLocatorNode( mapNode->getMap() ); + flag->addChild( createFlag() ); flag->setNodeMask( 0x02 ); root->addChild( flag ); viewer.setSceneData( root ); - // AN elevation manager that is tied to the map node: - osgEarth::Util::ElevationManager* elevMan = new osgEarth::Util::ElevationManager( mapNode->getMap() ); - elevMan->setTechnique( osgEarth::Util::ElevationManager::TECHNIQUE_PARAMETRIC ); - elevMan->setMaxTilesToCache( 10 ); - // An event handler that will respond to mouse clicks: - viewer.addEventHandler( new QueryElevationHandler( elevMan, mapNode->getMap()->getProfile()->getSRS(), flag ) ); + viewer.addEventHandler( new QueryElevationHandler( mapNode->getMap(), flag->getLocator() ) ); // add some stock OSG handlers: viewer.addEventHandler(new osgViewer::StatsHandler()); viewer.addEventHandler(new osgViewer::WindowSizeHandler()); viewer.addEventHandler(new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet())); + // install the programmable manipulator. + if ( mapNode->getMap()->isGeocentric() ) + viewer.setCameraManipulator( new osgEarth::Util::EarthManipulator() ); + return viewer.run(); } diff -Nru osgearth-2.0+dfsg/src/applications/osgearth_featureeditor/CMakeLists.txt osgearth-2.1.1+dfsg/src/applications/osgearth_featureeditor/CMakeLists.txt --- osgearth-2.0+dfsg/src/applications/osgearth_featureeditor/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/applications/osgearth_featureeditor/CMakeLists.txt 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,8 @@ +INCLUDE_DIRECTORIES(${OSG_INCLUDE_DIRS} ) + +SET(TARGET_LIBRARIES_VARS OSG_LIBRARY OSGDB_LIBRARY OSGUTIL_LIBRARY OSGVIEWER_LIBRARY OPENTHREADS_LIBRARY) + +SET(TARGET_SRC osgearth_featureeditor.cpp ) + +#### end var setup ### +SETUP_APPLICATION(osgearth_featureeditor) \ No newline at end of file diff -Nru osgearth-2.0+dfsg/src/applications/osgearth_featureeditor/osgearth_featureeditor.cpp osgearth-2.1.1+dfsg/src/applications/osgearth_featureeditor/osgearth_featureeditor.cpp --- osgearth-2.0+dfsg/src/applications/osgearth_featureeditor/osgearth_featureeditor.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/applications/osgearth_featureeditor/osgearth_featureeditor.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,307 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph +* Copyright 2008-2010 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 +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include + +using namespace osgEarth; +using namespace osgEarth::Features; +using namespace osgEarth::Drivers; +using namespace osgEarth::Symbology; +using namespace osgEarth::Util; +using namespace osgEarth::Util::Controls; + +osg::Vec4 +randomColor() +{ + float r = (float)rand() / (float)RAND_MAX; + float g = (float)rand() / (float)RAND_MAX; + float b = (float)rand() / (float)RAND_MAX; + return osg::Vec4(r,g,b,1.0f); +} + + +static int s_fid = 0; + +static osg::ref_ptr< AddPointHandler > s_addPointHandler; +static osg::ref_ptr< osg::Node > s_editor; +static osg::ref_ptr< Feature > s_activeFeature; +static osgViewer::Viewer* s_viewer; +static osg::ref_ptr< osg::Group > s_root; +static osg::ref_ptr< FeatureListSource > s_source; +static osg::ref_ptr< MapNode > s_mapNode; + +Grid* createToolBar() +{ + Grid* toolbar = new Grid(); + toolbar->setBackColor(0,0,0,0.5); + toolbar->setMargin( 10 ); + toolbar->setPadding( 10 ); + toolbar->setChildSpacing( 10 ); + toolbar->setChildVertAlign( Control::ALIGN_CENTER ); + toolbar->setAbsorbEvents( true ); + toolbar->setVertAlign( Control::ALIGN_TOP ); + return toolbar; +} + +struct AddVertsModeHandler : public ControlEventHandler +{ + AddVertsModeHandler( FeatureModelGraph* featureGraph) + : _featureGraph( featureGraph ) + { + } + + void onClick( Control* control, int mouseButtonMask ) { + + //remove the editor if it's valid + if (s_editor.valid()) + { + s_root->removeChild( s_editor.get() ); + s_editor = NULL; + + Style* style = _featureGraph->getStyles()->getDefaultStyle(); + if ( style ) + { + style->get()->stroke()->stipple().unset(); + _featureGraph->dirty(); + } + } + + //Add the new add point handler + if (!s_addPointHandler.valid() && s_activeFeature.valid()) + { + s_addPointHandler = new AddPointHandler(s_activeFeature.get(), s_source.get(), s_mapNode->getMap()->getProfile()->getSRS()); + s_addPointHandler->setIntersectionMask( 0x1 ); + s_viewer->addEventHandler( s_addPointHandler.get() ); + } + } + + osg::ref_ptr< FeatureModelGraph > _featureGraph; +}; + +struct EditModeHandler : public ControlEventHandler +{ + EditModeHandler( FeatureModelGraph* featureGraph) + : _featureGraph( featureGraph ) + { + } + + void onClick( Control* control, int mouseButtonMask ) { + + //Remove the add point handler if it's valid + if (s_addPointHandler.valid()) + { + osgEarth::removeEventHandler( s_viewer, s_addPointHandler.get() ); + s_addPointHandler = NULL; + } + + if (!s_editor.valid() && s_activeFeature.valid()) + { + Style* style = _featureGraph->getStyles()->getDefaultStyle(); + if ( style ) + { + style->get()->stroke()->stipple() = 0x00FF; + _featureGraph->dirty(); + } + s_editor = new FeatureEditor(s_activeFeature.get(), s_source.get(), s_mapNode.get()); + s_root->addChild( s_editor.get() ); + } + } + + osg::ref_ptr< FeatureModelGraph > _featureGraph; +}; + +struct ChangeStyleHandler : public ControlEventHandler +{ + ChangeStyleHandler( FeatureModelGraph* features, StyleSheet* styleSheet) + : _features( features), _styleSheet(styleSheet) + { + //nop + } + + void onClick( Control* control, int mouseButtonMask ) { + _features->setStyles( _styleSheet.get() ); + } + + osg::ref_ptr< FeatureModelGraph > _features; + osg::ref_ptr< StyleSheet > _styleSheet; +}; + +StyleSheet* buildStyleSheet( const osg::Vec4 &color, float width ) +{ + // Define a style for the feature data. Since we are going to render the + // vectors as lines, configure the line symbolizer: + Style style; + + LineSymbol* ls = style.getOrCreateSymbol(); + ls->stroke()->color() = color; + ls->stroke()->width() = width; + + //AltitudeSymbol* as = style.getOrCreate(); + //as->clamping() = AltitudeSymbol::CLAMP_TO_TERRAIN; + + StyleSheet* styleSheet = new StyleSheet(); + styleSheet->addStyle( style ); + return styleSheet; +} + + +// +// NOTE: run this sample from the repo/tests directory. +// +int main(int argc, char** argv) +{ + osg::ArgumentParser arguments(&argc,argv); + + osgViewer::Viewer viewer(arguments); + s_viewer = &viewer; + + // Start by creating the map: + s_mapNode = MapNode::load(arguments); + if ( !s_mapNode ) + { + Map* map = new Map(); + + // Start with a basemap imagery layer; we'll be using the GDAL driver + // to load a local GeoTIFF file: + 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; + + s_mapNode = new MapNode( map, mapNodeOptions ); + } + s_mapNode->setNodeMask( 0x01 ); + + + // Define a style for the feature data. Since we are going to render the + // vectors as lines, configure the line symbolizer: + StyleSheet* styleSheet = buildStyleSheet( Color::Yellow, 2.0f ); + + s_source = new FeatureListSource(); + + LineString* line = new LineString(); + line->push_back( osg::Vec3d(-60, 20, 0) ); + line->push_back( osg::Vec3d(-120, 20, 0) ); + line->push_back( osg::Vec3d(-120, 60, 0) ); + line->push_back( osg::Vec3d(-60, 60, 0) ); + Feature *feature = new Feature(s_fid++); + feature->setGeometry( line ); + s_source->insertFeature( feature ); + s_activeFeature = feature; + + s_root = new osg::Group; + s_root->addChild( s_mapNode.get() ); + + Session* session = new Session(s_mapNode->getMap(), styleSheet); + + FeatureModelGraph* graph = new FeatureModelGraph( + s_source.get(), + FeatureModelSourceOptions(), + new GeomFeatureNodeFactory(), + session ); + + graph->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); + graph->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); + + s_root->addChild( graph ); + + //Setup the controls + ControlCanvas* canvas = ControlCanvas::get( &viewer ); + s_root->addChild( canvas ); + Grid *toolbar = createToolBar( ); + canvas->addControl( toolbar ); + canvas->setNodeMask( 0x1 << 1 ); + + + + int col = 0; + LabelControl* addVerts = new LabelControl("Add Verts"); + toolbar->setControl(col++, 0, addVerts ); + addVerts->addEventHandler( new AddVertsModeHandler( graph )); + + LabelControl* edit = new LabelControl("Edit"); + toolbar->setControl(col++, 0, edit ); + edit->addEventHandler(new EditModeHandler( graph )); + + unsigned int row = 0; + Grid *styleBar = createToolBar( ); + styleBar->setPosition(0, 50); + canvas->addControl( styleBar ); + + //Make a list of styles + styleBar->setControl(0, row++, new LabelControl("Styles") ); + + unsigned int numStyles = 8; + for (unsigned int i = 0; i < numStyles; ++i) + { + float w = 50; + osg::Vec4 color = randomColor(); + + float widths[3] = {2, 4, 8}; + + unsigned int r = row++; + for (unsigned int j = 0; j < 3; j++) + { + Control* l = new Control(); + l->setBackColor( color ); + l->addEventHandler(new ChangeStyleHandler(graph, buildStyleSheet( color, widths[j] ) )); + l->setSize(w,5 * widths[j]); + styleBar->setControl(j, r, l); + } + } + + + viewer.setSceneData( s_root.get() ); + viewer.setCameraManipulator( new EarthManipulator() ); + viewer.addEventHandler( new osgEarth::Util::AutoClipPlaneHandler ); + + // add some stock OSG handlers: + viewer.addEventHandler(new osgViewer::StatsHandler()); + viewer.addEventHandler(new osgViewer::WindowSizeHandler()); + viewer.addEventHandler(new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet())); + + return viewer.run(); +} diff -Nru osgearth-2.0+dfsg/src/applications/osgearth_featureinfo/CMakeLists.txt osgearth-2.1.1+dfsg/src/applications/osgearth_featureinfo/CMakeLists.txt --- osgearth-2.0+dfsg/src/applications/osgearth_featureinfo/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/applications/osgearth_featureinfo/CMakeLists.txt 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,8 @@ +INCLUDE_DIRECTORIES(${OSG_INCLUDE_DIRS} ) + +SET(TARGET_LIBRARIES_VARS OSG_LIBRARY OSGDB_LIBRARY OSGUTIL_LIBRARY OSGVIEWER_LIBRARY OPENTHREADS_LIBRARY) + +SET(TARGET_SRC osgearth_featureinfo.cpp ) + +#### end var setup ### +SETUP_APPLICATION(osgearth_featureinfo) \ No newline at end of file diff -Nru osgearth-2.0+dfsg/src/applications/osgearth_featureinfo/osgearth_featureinfo.cpp osgearth-2.1.1+dfsg/src/applications/osgearth_featureinfo/osgearth_featureinfo.cpp --- osgearth-2.0+dfsg/src/applications/osgearth_featureinfo/osgearth_featureinfo.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/applications/osgearth_featureinfo/osgearth_featureinfo.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,207 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph +* Copyright 2008-2010 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 +*/ + +#include + +#include + +using namespace osgEarth::Features; +using namespace osgEarth::Drivers; +using namespace osgEarth::Symbology; + +#include + +std::string attributeTypeToString( AttributeType type ) +{ + switch (type) + { + case ATTRTYPE_BOOL: return "Boolean"; + case ATTRTYPE_DOUBLE: return "Double"; + case ATTRTYPE_INT: return "Integer"; + case ATTRTYPE_STRING: return "String"; + default: return "Unspecified"; + } +} + +std::string indent = " "; + +void printStats(FeatureSource* features) +{ + std::cout << "Feature Count: " << features->getFeatureCount() << std::endl; + std::cout << "Geometry Type: " << osgEarth::Symbology::Geometry::toString( features->getGeometryType() ) << std::endl; + + //Print the schema + const FeatureSchema schema = features->getSchema(); + std::cout << "Schema:" << std::endl; + for (FeatureSchema::const_iterator itr = schema.begin(); itr != schema.end(); ++itr) + { + std::cout << indent << itr->first << ": " << attributeTypeToString(itr->second) << std::endl; + } + std::cout << std::endl; +} + +void printFeature( Feature* feature ) +{ + std::cout << "FID: " << feature->getFID() << std::endl; + for (AttributeTable::const_iterator itr = feature->getAttrs().begin(); itr != feature->getAttrs().end(); ++itr) + { + std::cout + << indent + << itr->first << "=" << itr->second.getString() << " (" + << (itr->second.first == ATTRTYPE_INT? "integer" : + itr->second.first == ATTRTYPE_DOUBLE? "double" : + itr->second.first == ATTRTYPE_BOOL? "bool" : + "string") + << ")" << std::endl; + } + + //Print out the geometry + Geometry* geom = feature->getGeometry(); + if (geom) + { + std::cout << indent << geometryToWkt( geom ) << std::endl; + } + std::cout << std::endl; +} + +void printAllFeatures(FeatureSource* features) +{ + osg::ref_ptr< FeatureCursor > cursor = features->createFeatureCursor(); + while (cursor->hasMore()) + { + osg::ref_ptr< Feature > feature = cursor->nextFeature(); + printFeature( feature.get() ); + } +} + +int +usage( const std::string& msg ) +{ + if ( !msg.empty() ) + { + std::cout << msg << std::endl; + } + + std::cout + << std::endl + << "USAGE: osgearth_featureinfo [options] filename" << std::endl + << std::endl + << " --printfeatures ; Prints all features in the source" << std::endl + << " --delete fid ; Deletes the given FID from the source." << std::endl + << " --fid fid ; Displays the given FID." << std::endl + << std::endl; + + return -1; +} + +int main(int argc, char** argv) +{ + osg::ArgumentParser arguments(&argc,argv); + + if (argc < 2) + { + return usage(""); + } + + + std::vector< FeatureID > toDelete; + int fid; + while (arguments.read("--delete", fid)) + { + toDelete.push_back( fid ); + } + + std::vector< FeatureID > fids; + while (arguments.read("--fid", fid)) + { + fids.push_back( fid ); + } + + bool printFeatures = false; + if (arguments.read("--printfeatures" )) printFeatures = true; + + std::string filename; + + //Get the first argument that is not an option + for(int pos=1;pos 0; + + + + //Open the feature source + OGRFeatureOptions featureOpt; + featureOpt.url() = filename; + featureOpt.openWrite() = write; + + osg::ref_ptr< FeatureSource > features = FeatureSourceFactory::create( featureOpt ); + features->initialize(""); + features->getFeatureProfile(); + + //Delete any features if requested + if (toDelete.size() > 0) + { + for (unsigned int i = 0; i < toDelete.size(); ++i) + { + FeatureID fid = toDelete[i]; + std::cout << "Deleting Feature " << fid << std::endl; + features->deleteFeature( fid ); + } + } + else if (fids.size() > 0) + { + //Print out any specific FIDs + for (unsigned int i = 0; i < fids.size(); ++i) + { + FeatureID fid = fids[i]; + osg::ref_ptr< Feature > feature = features->getFeature( fid ); + if (feature.valid()) + { + printFeature( feature.get() ); + } + else + { + std::cout << "Couldn't get feature " << fid << std::endl; + } + } + } + else + { + //Print out feature info + printStats( features.get() ); + + if (printFeatures) + { + printAllFeatures( features.get() ); + } + } + + return 0; +} diff -Nru osgearth-2.0+dfsg/src/applications/osgearth_features/osgearth_features.cpp osgearth-2.1.1+dfsg/src/applications/osgearth_features/osgearth_features.cpp --- osgearth-2.0+dfsg/src/applications/osgearth_features/osgearth_features.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/applications/osgearth_features/osgearth_features.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -27,12 +27,12 @@ #include #include -#include #include #include #include #include +#include using namespace osgEarth; using namespace osgEarth::Features; @@ -47,7 +47,11 @@ { osg::ArgumentParser arguments(&argc,argv); - bool useGeom = arguments.read("--geom"); + bool useRaster = arguments.read("--rasterize"); + bool useOverlay = arguments.read("--overlay"); + bool useStencil = arguments.read("--stencil"); + bool useMem = arguments.read("--mem"); + bool useLabels = arguments.read("--labels"); osgViewer::Viewer viewer(arguments); @@ -63,48 +67,90 @@ // Next we add a feature layer. First configure a feature driver to // load the vectors from a shapefile: OGRFeatureOptions featureOpt; - featureOpt.url() = "../data/world.shp"; + if ( !useMem ) + { + featureOpt.url() = "../data/usa.shp"; + } + else + { + Ring* line = new Ring(); + line->push_back( osg::Vec3d(-60, 20, 0) ); + line->push_back( osg::Vec3d(-120, 20, 0) ); + line->push_back( osg::Vec3d(-120, 60, 0) ); + line->push_back( osg::Vec3d(-60, 60, 0) ); + featureOpt.geometry() = line; + } // Define a style for the feature data. Since we are going to render the // vectors as lines, configure the line symbolizer: - Style* style = new Style; + Style style; - LineSymbol* ls = new LineSymbol; - ls->stroke()->color() = osg::Vec4f( 1,1,0,0.1 ); // yellow - ls->stroke()->width() = 1.0f; - style->addSymbol(ls); + LineSymbol* ls = style.getOrCreateSymbol(); + ls->stroke()->color() = osg::Vec4f( 1,1,0,1 ); // yellow + ls->stroke()->width() = 2.0f; + + // Add some text labels. + if ( useLabels ) + { + TextSymbol* text = style.getOrCreateSymbol(); + text->provider() = "overlay"; + text->content() = StringExpression( "[name]" ); + text->priority() = NumericExpression( "[area]" ); + text->removeDuplicateLabels() = true; + text->size() = 16.0f; + text->fill()->color() = Color::White; + text->halo()->color() = Color::DarkGray; + } + + // That's it, the map is ready; now create a MapNode to render the Map: + MapNodeOptions mapNodeOptions; + mapNodeOptions.enableLighting() = false; + + MapNode* mapNode = new MapNode( map, mapNodeOptions ); // Now we'll choose the AGG-Lite driver to render the features. By the way, the // feature data is actually polygons, so we override that to treat it as lines. // We apply the feature driver and set the style as well. - if ( !useGeom ) + if (useStencil) + { + FeatureStencilModelOptions worldOpt; + worldOpt.featureOptions() = featureOpt; + worldOpt.geometryTypeOverride() = Geometry::TYPE_LINESTRING; + worldOpt.styles() = new StyleSheet(); + worldOpt.styles()->addStyle( style ); + worldOpt.enableLighting() = false; + worldOpt.depthTestEnabled() = false; + map->addModelLayer( new ModelLayer( "my features", worldOpt ) ); + } + else if (useRaster) { AGGLiteOptions worldOpt; worldOpt.featureOptions() = featureOpt; worldOpt.geometryTypeOverride() = Geometry::TYPE_LINESTRING; + worldOpt.styles() = new StyleSheet(); worldOpt.styles()->addStyle( style ); map->addImageLayer( new ImageLayer( ImageLayerOptions("world", worldOpt) ) ); } - else + else //if (useGeom || useOverlay) { FeatureGeomModelOptions worldOpt; worldOpt.featureOptions() = featureOpt; worldOpt.geometryTypeOverride() = Geometry::TYPE_LINESTRING; + worldOpt.styles() = new StyleSheet(); worldOpt.styles()->addStyle( style ); worldOpt.enableLighting() = false; worldOpt.depthTestEnabled() = false; - map->addModelLayer( new ModelLayer( "my features", worldOpt ) ); - } - - // That's it, the map is ready; now create a MapNode to render the Map: - MapNode* mapNode = new MapNode( map ); + ModelLayerOptions options( "my features", worldOpt ); + options.overlay() = useOverlay; + map->addModelLayer( new ModelLayer(options) ); + } viewer.setSceneData( mapNode ); - viewer.setCameraManipulator( new EarthManipulator() ); - viewer.addEventHandler( new osgEarth::Util::AutoClipPlaneHandler ); + if ( !useStencil && !useOverlay ) + viewer.addEventHandler( new osgEarth::Util::AutoClipPlaneHandler ); // add some stock OSG handlers: viewer.addEventHandler(new osgViewer::StatsHandler()); diff -Nru osgearth-2.0+dfsg/src/applications/osgearth_imageoverlay/CMakeLists.txt osgearth-2.1.1+dfsg/src/applications/osgearth_imageoverlay/CMakeLists.txt --- osgearth-2.0+dfsg/src/applications/osgearth_imageoverlay/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/applications/osgearth_imageoverlay/CMakeLists.txt 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,7 @@ +INCLUDE_DIRECTORIES(${OSG_INCLUDE_DIRS} ) +SET(TARGET_LIBRARIES_VARS OSG_LIBRARY OSGDB_LIBRARY OSGMANIPULATOR_LIBRARY OSGUTIL_LIBRARY OSGVIEWER_LIBRARY OPENTHREADS_LIBRARY) + +SET(TARGET_SRC osgearth_imageoverlay.cpp ) + +#### end var setup ### +SETUP_APPLICATION(osgearth_imageoverlay) \ No newline at end of file diff -Nru osgearth-2.0+dfsg/src/applications/osgearth_imageoverlay/osgearth_imageoverlay.cpp osgearth-2.1.1+dfsg/src/applications/osgearth_imageoverlay/osgearth_imageoverlay.cpp --- osgearth-2.0+dfsg/src/applications/osgearth_imageoverlay/osgearth_imageoverlay.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/applications/osgearth_imageoverlay/osgearth_imageoverlay.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,300 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph +* Copyright 2008-2010 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 +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#if OSG_MIN_VERSION_REQUIRED(2,9,6) +#include +#endif + +using namespace osgEarth; +using namespace osgEarth::Util; +using namespace osgEarth::Util::Controls; + +static Grid* s_layerBox = NULL; + +osg::Node* +createControlPanel( osgViewer::View* view ) +{ + ControlCanvas* canvas = ControlCanvas::get( view ); + + // the outer container: + s_layerBox = new Grid(); + s_layerBox->setBackColor(0,0,0,0.5); + s_layerBox->setMargin( 10 ); + s_layerBox->setPadding( 10 ); + s_layerBox->setChildSpacing( 10 ); + s_layerBox->setChildVertAlign( Control::ALIGN_CENTER ); + s_layerBox->setAbsorbEvents( true ); + s_layerBox->setVertAlign( Control::ALIGN_BOTTOM ); + + + + canvas->addControl( s_layerBox ); + return canvas; +} + +int +usage( const std::string& msg ) +{ + OE_NOTICE << msg << std::endl; + OE_NOTICE << "USAGE: osgearth_imageoverlay file.earth" << std::endl; + OE_NOTICE << " --image xmin ymin xmax ymax : An image to overlay and it's bounds" << std::endl; + OE_NOTICE << " --vert : Move individual verts when editing" << std::endl; + + + return -1; +} + +struct OpacityHandler : public ControlEventHandler +{ + OpacityHandler( ImageOverlay* overlay ) : _overlay(overlay) { } + void onValueChanged( Control* control, float value ) { + _overlay->setAlpha( value ); + } + ImageOverlay* _overlay; +}; + +struct EnabledHandler : public ControlEventHandler +{ + EnabledHandler( ImageOverlay* overlay ) : _overlay(overlay) { } + void onValueChanged( Control* control, bool value ) { + _overlay->setNodeMask( value ? ~0 : 0 ); + } + ImageOverlay* _overlay; +}; + + + +struct EditHandler : public ControlEventHandler +{ + EditHandler( ImageOverlay* overlay, osgViewer::Viewer* viewer, osg::Node* editor) : + _overlay(overlay), + _viewer(viewer), + _editor(editor){ } + + void onClick( Control* control, int mouseButtonMask ) { +#if OSG_MIN_VERSION_REQUIRED(2,9,6) + if (_editor->getNodeMask() != ~0) + { + static_cast(control)->setText( "Finish" ); + _editor->setNodeMask(~0); + } + else + { + static_cast(control)->setText( "Edit" ); + _editor->setNodeMask(0); + } + +#else + OE_NOTICE << "Use OSG 2.9.6 or greater to use editing" << std::endl; +#endif + } + ImageOverlay* _overlay; + osgViewer::Viewer* _viewer; + osg::Node* _editor; +}; + +struct ChangeImageHandler : public ControlEventHandler +{ + ChangeImageHandler( osg::Image* image, ImageOverlay* overlay, ImageControl* preview) : + _image(image), + _overlay(overlay), + _preview(preview){ } + + void onClick( Control* control, int mouseButtonMask ) { + _overlay->setImage( _image.get() ); + _preview->setImage( _image.get() ); + } + ImageOverlay* _overlay; + osg::ref_ptr< osg::Image > _image; + osg::ref_ptr< ImageControl> _preview; +}; + +struct UpdateLabelCallback : public ImageOverlay::ImageOverlayCallback +{ + UpdateLabelCallback(LabelControl* label, ImageOverlay* overlay, ImageOverlay::ControlPoint controlPoint): + _label(label), + _overlay(overlay), + _controlPoint(controlPoint) + { + + } + + virtual void onOverlayChanged() + { + osg::Vec2d location = _overlay->getControlPoint( _controlPoint ); + std::stringstream ss; + ss << location.y() << ", " << location.x(); + _label->setText( ss.str() ); + } + + + osg::ref_ptr< LabelControl > _label; + osg::ref_ptr< ImageOverlay > _overlay; + ImageOverlay::ControlPoint _controlPoint; +}; + + + +int +main(int argc, char** argv) +{ + osg::ArgumentParser arguments(&argc,argv); + osg::DisplaySettings::instance()->setMinimumNumStencilBits( 8 ); + + + std::vector< std::string > imageFiles; + std::vector< Bounds > imageBounds; + + //Read in the image files + std::string filename; + Bounds bounds; + while (arguments.read("--image", filename, bounds.xMin(), bounds.yMin(), bounds.xMax(), bounds.yMax())) + { + imageFiles.push_back( filename ); + imageBounds.push_back( bounds ); + } + + if (imageFiles.empty()) + { + imageFiles.push_back("../data/osgearth.gif"); + imageBounds.push_back( Bounds(-100, 30, -90, 40) ); + } + + + bool moveVert = arguments.read("--vert"); + + // load the .earth file from the command line. + osg::Node* earthNode = osgDB::readNodeFiles( arguments ); + if (!earthNode) + return usage( "Unable to load earth model." ); + + osgViewer::Viewer viewer(arguments); + + EarthManipulator* manip = new EarthManipulator(); + viewer.setCameraManipulator( manip ); + + osg::Group* root = new osg::Group(); + root->addChild( earthNode ); + + //Create the control panel + root->addChild( createControlPanel(&viewer) ); + + viewer.setSceneData( root ); + + osgEarth::MapNode* mapNode = osgEarth::MapNode::findMapNode( earthNode ); + if ( mapNode ) + { + + for (unsigned int i = 0; i < imageFiles.size(); i++) + { + std::string imageFile = imageFiles[i]; + //Read the image file and play it if it's a movie + osg::Image* image = osgDB::readImageFile(imageFile); + if (image) + { + osg::ImageStream* is = dynamic_cast(image); + if (is) + { + is->play(); + } + } + + //Create a new ImageOverlay and set it's bounds + //ImageOverlay* overlay = new ImageOverlay(mapNode->getMap()->getProfile()->getSRS()->getEllipsoid(), image); + ImageOverlay* overlay = new ImageOverlay(mapNode); + overlay->setImage( image ); + overlay->setBounds(imageBounds[i]); + + root->addChild( overlay ); + + + //Create a new ImageOverlayEditor and set it's node mask to 0 to hide it initially +#if OSG_MIN_VERSION_REQUIRED(2,9,6) + osg::Node* editor = new ImageOverlayEditor( overlay, mapNode->getMap()->getProfile()->getSRS()->getEllipsoid(), mapNode ); +#else + //Just make an empty group for pre-2.9.6 + osg::Node* editor = new osg::Group; +#endif + editor->setNodeMask( 0 ); + root->addChild( editor ); + + // Add an image preview + ImageControl* imageCon = new ImageControl( image ); + imageCon->setSize( 64, 64 ); + imageCon->setVertAlign( Control::ALIGN_CENTER ); + s_layerBox->setControl( 0, i, imageCon ); + + + //Add some controls + CheckBoxControl* enabled = new CheckBoxControl( true ); + enabled->addEventHandler( new EnabledHandler(overlay) ); + enabled->setVertAlign( Control::ALIGN_CENTER ); + s_layerBox->setControl( 1, i, enabled ); + + //The overlay name + LabelControl* name = new LabelControl( osgDB::getSimpleFileName( imageFile) ); + name->setVertAlign( Control::ALIGN_CENTER ); + s_layerBox->setControl( 2, i, name ); + + // an opacity slider + HSliderControl* opacity = new HSliderControl( 0.0f, 1.0f, overlay->getAlpha() ); + opacity->setWidth( 125 ); + opacity->setHeight( 12 ); + opacity->setVertAlign( Control::ALIGN_CENTER ); + opacity->addEventHandler( new OpacityHandler(overlay) ); + s_layerBox->setControl( 3, i, opacity ); + + // Add a text label: + LabelControl* edit = new LabelControl( "Edit" ); + edit->setVertAlign( Control::ALIGN_CENTER ); + edit->addEventHandler(new EditHandler(overlay, &viewer, editor)); + s_layerBox->setControl(4, i, edit ); + } + } + + // osgEarth benefits from pre-compilation of GL objects in the pager. In newer versions of + // OSG, this activates OSG's IncrementalCompileOpeartion in order to avoid frame breaks. + viewer.getDatabasePager()->setDoPreCompile( true ); + + // add some stock OSG handlers: + viewer.addEventHandler(new osgViewer::StatsHandler()); + viewer.addEventHandler(new osgViewer::WindowSizeHandler()); + viewer.addEventHandler(new osgViewer::LODScaleHandler()); + viewer.addEventHandler(new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet())); + viewer.addEventHandler(new osgViewer::HelpHandler(arguments.getApplicationUsage())); + + return viewer.run(); +} diff -Nru osgearth-2.0+dfsg/src/applications/osgearth_kml/CMakeLists.txt osgearth-2.1.1+dfsg/src/applications/osgearth_kml/CMakeLists.txt --- osgearth-2.0+dfsg/src/applications/osgearth_kml/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/applications/osgearth_kml/CMakeLists.txt 2011-11-04 19:44:43.000000000 +0000 @@ -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_kml.cpp ) + +#### end var setup ### +SETUP_APPLICATION(osgearth_kml) \ No newline at end of file diff -Nru osgearth-2.0+dfsg/src/applications/osgearth_kml/osgearth_kml.cpp osgearth-2.1.1+dfsg/src/applications/osgearth_kml/osgearth_kml.cpp --- osgearth-2.0+dfsg/src/applications/osgearth_kml/osgearth_kml.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/applications/osgearth_kml/osgearth_kml.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,82 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph +* Copyright 2008-2010 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 +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace osgEarth::Util; + +int +usage( const std::string& msg ) +{ + OE_NOTICE << msg << std::endl; + OE_NOTICE << std::endl; + OE_NOTICE << "USAGE: osgearth_kml file.earth file.kml" << std::endl; + return -1; +} + +int +main(int argc, char** argv) +{ + osg::ArgumentParser arguments(&argc,argv); + osgViewer::Viewer viewer(arguments); + + osg::Group* root = new osg::Group(); + + // load the .earth file from the command line. + MapNode* mapNode = MapNode::load( arguments ); + if (!mapNode) + return usage( "Unable to load earth model." ); + + root->addChild( mapNode ); + + for( int a = 1; a < argc; ++a ) + { + std::string kmlFile( argv[a] ); + if ( endsWith( kmlFile, ".kml" ) ) + { + osg::ref_ptr options = new osgDB::Options(); + options->setPluginData( "osgEarth::MapNode", mapNode ); + osg::Node* kml = osgDB::readNodeFile( kmlFile, options.get() ); + if ( kml ) + root->addChild( kml ); + } + } + + viewer.setCameraManipulator( new EarthManipulator() ); + viewer.setSceneData( root ); + viewer.getDatabasePager()->setDoPreCompile( true ); + + // add some stock OSG handlers: + viewer.addEventHandler(new osgViewer::StatsHandler()); + viewer.addEventHandler(new osgViewer::WindowSizeHandler()); + viewer.addEventHandler(new osgViewer::ThreadingHandler()); + viewer.addEventHandler(new osgViewer::LODScaleHandler()); + viewer.addEventHandler(new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet())); + viewer.addEventHandler(new osgViewer::HelpHandler(arguments.getApplicationUsage())); + + return viewer.run(); +} diff -Nru osgearth-2.0+dfsg/src/applications/osgearth_labels/CMakeLists.txt osgearth-2.1.1+dfsg/src/applications/osgearth_labels/CMakeLists.txt --- osgearth-2.0+dfsg/src/applications/osgearth_labels/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/applications/osgearth_labels/CMakeLists.txt 2011-11-04 19:44:43.000000000 +0000 @@ -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_labels.cpp) + +#### end var setup ### +SETUP_APPLICATION(osgearth_labels) \ No newline at end of file diff -Nru osgearth-2.0+dfsg/src/applications/osgearth_labels/osgearth_labels.cpp osgearth-2.1.1+dfsg/src/applications/osgearth_labels/osgearth_labels.cpp --- osgearth-2.0+dfsg/src/applications/osgearth_labels/osgearth_labels.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/applications/osgearth_labels/osgearth_labels.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,193 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph +* Copyright 2008-2010 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 +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LC "[osgearth_labels] " + +using namespace osgEarth::Util::Controls; +using namespace osgEarth::Drivers; +using namespace osgEarth::Features; +using namespace osgEarth::Symbology; + +osg::Group* createLabels( Map* ); + +std::string g_featureFile, g_labelAttr, g_priorityAttr; +bool g_removeDupes = true; + +/** + * Demonstrates the dynamic labeling engine in osgEarthUtil::Controls. + */ + +int main(int argc, char** argv) +{ + osg::ArgumentParser arguments(&argc,argv); + + if ( arguments.read( "--help" ) || argc < 2 ) + { + OE_NOTICE << LC << std::endl << std::endl + + << "osgearth_labels " << std::endl + << " --features : name of shapefile containing feature labels" << std::endl + << " --label-attr : attribute containing label text" << std::endl + << " --priority-attr : attribute containing priority value" << std::endl + << " --show-duplicates : draws duplicate labels (usually won't)" << std::endl; + + return 0; + } + + if ( !arguments.read( "--features", g_featureFile ) ) + g_featureFile = "../data/world.shp"; + + if ( !arguments.read( "--label-attr", g_labelAttr ) ) + g_labelAttr = "cntry_name"; + + if ( !arguments.read( "--priority-attr", g_priorityAttr ) ) + g_priorityAttr = "cntry_pop"; + + if ( arguments.read( "--show-duplicates" ) ) + g_removeDupes = false; + + osgViewer::Viewer viewer(arguments); + + osg::Group* root = new osg::Group(); + osg::Node* node = osgDB::readNodeFiles( arguments ); + if ( node ) + root->addChild( node ); + + MapNode* mapNode = MapNode::findMapNode(node); + if ( mapNode ) + { + viewer.setSceneData( root ); + viewer.setCameraManipulator( new osgEarth::Util::EarthManipulator ); + + //root->addChild( new ControlCanvas( &viewer ) ); + + // load up some labels. + root->addChild( createLabels(mapNode->getMap()) ); + } + + // add some stock OSG handlers: + viewer.addEventHandler(new osgViewer::StatsHandler()); + viewer.addEventHandler(new osgViewer::WindowSizeHandler()); + viewer.addEventHandler(new osgViewer::ThreadingHandler()); + viewer.addEventHandler(new osgViewer::LODScaleHandler()); + viewer.addEventHandler(new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet())); + viewer.addEventHandler(new osgViewer::HelpHandler(arguments.getApplicationUsage())); + + return viewer.run(); +} + +osg::Group* +createLabels( Map* map ) +{ + osg::ref_ptr labels = new osg::Group(); + + // first, open up the source shapefile + OGRFeatureOptions fo; + fo.url() = g_featureFile; + + osg::ref_ptr features = FeatureSourceFactory::create( fo ); + if ( !features.valid() ) + { + OE_WARN << LC << "Unable to load features!" << std::endl; + return 0L; + } + + features->initialize( "" ); + const FeatureProfile* featureProfile = features->getFeatureProfile(); + if ( !featureProfile || !featureProfile->getSRS() ) + { + OE_WARN << LC << "Feature data has no spatial reference!" << std::endl; + return 0L; + } + + osg::ref_ptr cursor = features->createFeatureCursor(); + if ( !cursor.valid() ) + { + OE_WARN << LC << "Failed to query the feature source!" << std::endl; + return 0L; + } + + //SceneControlBin* priorityBin = canvas->getSceneControls(); + + unsigned count = 0; + + std::set antiDupeSet; + + while( cursor->hasMore() ) + { + Feature* feature = cursor->nextFeature(); + Geometry* geom = feature->getGeometry(); + + if ( !geom ) + continue; + + // we will display the country name: + std::string text = feature->getString( g_labelAttr ); + if ( text.empty() ) + continue; + + // and use the population to prioritize labels: + float population = feature->getDouble(g_priorityAttr, 0.0); + + // remove duplicate labels: + if ( g_removeDupes ) + { + if ( antiDupeSet.find(text) != antiDupeSet.end() ) + continue; + antiDupeSet.insert(text); + } + + // calculate the world location of the label: + osg::Vec3d centerPoint = geom->getBounds().center(); + + osg::Vec3d mapPoint; + if ( !map->toMapPoint( centerPoint, featureProfile->getSRS(), mapPoint ) ) + continue; + + osg::Vec3d worldPoint; + if ( !map->mapPointToWorldPoint( mapPoint, worldPoint ) ) + continue; + + // create the label and place it: + osg::MatrixTransform* xform = new osg::MatrixTransform( osg::Matrix::translate(worldPoint) ); + xform->setCullCallback( new CullNodeByNormal(worldPoint) ); + xform->addChild( new ControlNode(new LabelControl(text)) ); + labels->addChild( xform ); + + ++count; + + //OE_NOTICE << LC << "Added: " << text << std::endl; + } + + OE_NOTICE << LC << "Found " << count << " features. " << std::endl; + + return labels.release(); +} + diff -Nru osgearth-2.0+dfsg/src/applications/osgearth_manip/osgearth_manip.cpp osgearth-2.1.1+dfsg/src/applications/osgearth_manip/osgearth_manip.cpp --- osgearth-2.0+dfsg/src/applications/osgearth_manip/osgearth_manip.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/applications/osgearth_manip/osgearth_manip.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -37,126 +37,114 @@ namespace { -class SwitchHandler : public osgGA::GUIEventHandler -{ -public: - SwitchHandler(char key = 0) - : _key(key) {} - - bool handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter& /*aa*/, osg::Object* object, osg::NodeVisitor* /*nv*/) + class SwitchHandler : public osgGA::GUIEventHandler { - osg::Switch* sw = dynamic_cast(object); - if (!sw) - return false; - if (ea.getHandled()) - return false; - switch(ea.getEventType()) + public: + SwitchHandler(char key = 0) : _key(key) {} + + bool handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter& aa, osg::Object* object, osg::NodeVisitor* /*nv*/) { - case osgGA::GUIEventAdapter::KEYDOWN: - if (ea.getKey() == _key) + if (ea.getHandled() || ea.getEventType() != osgGA::GUIEventAdapter::KEYDOWN ) + return false; + + if ( ea.getKey() == _key ) { - for (unsigned i = 0; i < sw->getNumChildren(); ++i) - { - bool state = sw->getValue(i); - sw->setValue(i, !state); - } - return true; + ControlCanvas* canvas = ControlCanvas::get(aa.asView()); + if ( canvas ) + canvas->setNodeMask( canvas->getNodeMask() ^ 0xFFFFFFFF ); } - break; - default: - break; + return false; } - return false; - } -protected: - char _key; -}; -} -static osg::Node* -createHelp( osgViewer::View* view ) -{ - static char s_help[] = - "left mouse: pan \n" - "middle mouse: tilt/slew \n" - "right mouse: zoom in/out continuous \n" - "double-click: zoom in \n" - "scroll wheel: zoom in/out \n" - "arrows: pan\n" - "1-6 : fly to preset viewpoints \n" - "shift-right-mouse: locked panning\n" - "u : toggle azimuth locking\n" - "h : toggle this help\n"; - - VBox* v = new VBox(); - v->addControl( new LabelControl( "EarthManipulator", osg::Vec4f(1,1,0,1) ) ); - v->addControl( new LabelControl( s_help ) ); - ControlCanvas* cc = new ControlCanvas( view ); - cc->addControl( v ); - osg::Switch* sw = new osg::Switch; - sw->addChild(cc); - sw->setEventCallback(new SwitchHandler('h')); - return sw; -} - -// some preset viewpoints. -static Viewpoint VPs[] = { - Viewpoint( "Africa", osg::Vec3d( 0.0, 0.0, 0.0 ), 0.0, -90.0, 10e6 ), - Viewpoint( "California", osg::Vec3d( -121.0, 34.0, 0.0 ), 0.0, -90.0, 6e6 ), - Viewpoint( "Europe", osg::Vec3d( 0.0, 45.0, 0.0 ), 0.0, -90.0, 4e6 ), - Viewpoint( "Washington DC", osg::Vec3d( -77.0, 38.0, 0.0 ), 0.0, -90.0, 1e6 ), - Viewpoint( "Australia", osg::Vec3d( 135.0, -20.0, 0.0 ), 0.0, -90.0, 2e6 ), - Viewpoint( "Boston", osg::Vec3d( -71.096936, 42.332771, 0 ), 0.0, -90, 1e5 ) -}; - -// a simple handler that demonstrates the "viewpoint" functionality in -// osgEarthUtil::EarthManipulator. Press a number key to fly to a viewpoint. -struct FlyToViewpointHandler : public osgGA::GUIEventHandler -{ - FlyToViewpointHandler( EarthManipulator* manip ) : _manip(manip) { } - - bool handle( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa ) + protected: + char _key; + }; + + osg::Node* createHelp( osgViewer::View* view ) + { + static char s_help[] = + "left mouse: pan \n" + "middle mouse: tilt/slew \n" + "right mouse: zoom in/out continuous \n" + "double-click: zoom in \n" + "scroll wheel: zoom in/out \n" + "arrows: pan\n" + "1-6 : fly to preset viewpoints \n" + "shift-right-mouse: locked panning\n" + "u : toggle azimuth locking\n" + "h : toggle this help\n"; + + VBox* v = new VBox(); + v->addControl( new LabelControl( "EarthManipulator", osg::Vec4f(1,1,0,1) ) ); + v->addControl( new LabelControl( s_help ) ); + ControlCanvas* canvas = ControlCanvas::get( view ); + canvas->addControl( v ); + canvas->setEventCallback(new SwitchHandler('h')); + + return canvas; + } + + // some preset viewpoints. + static Viewpoint VPs[] = { + Viewpoint( "Africa", osg::Vec3d( 0.0, 0.0, 0.0 ), 0.0, -90.0, 10e6 ), + Viewpoint( "California", osg::Vec3d( -121.0, 34.0, 0.0 ), 0.0, -90.0, 6e6 ), + Viewpoint( "Europe", osg::Vec3d( 0.0, 45.0, 0.0 ), 0.0, -90.0, 4e6 ), + Viewpoint( "Washington DC", osg::Vec3d( -77.0, 38.0, 0.0 ), 0.0, -90.0, 1e6 ), + Viewpoint( "Australia", osg::Vec3d( 135.0, -20.0, 0.0 ), 0.0, -90.0, 2e6 ), + Viewpoint( "Boston", osg::Vec3d( -71.096936, 42.332771, 0 ), 0.0, -90, 1e5 ) + }; + + // a simple handler that demonstrates the "viewpoint" functionality in + // osgEarthUtil::EarthManipulator. Press a number key to fly to a viewpoint. + struct FlyToViewpointHandler : public osgGA::GUIEventHandler { - if ( ea.getEventType() == ea.KEYDOWN && ea.getKey() >= '1' && ea.getKey() <= '6' ) + FlyToViewpointHandler( EarthManipulator* manip ) : _manip(manip) { } + + bool handle( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa ) { - _manip->setViewpoint( VPs[ea.getKey()-'1'], 4.0 ); + if ( ea.getEventType() == ea.KEYDOWN && ea.getKey() >= '1' && ea.getKey() <= '6' ) + { + _manip->setViewpoint( VPs[ea.getKey()-'1'], 4.0 ); + } + return false; } - return false; - } - osg::observer_ptr _manip; -}; + osg::observer_ptr _manip; + }; -struct LockAzimuthHandler : public osgGA::GUIEventHandler -{ - LockAzimuthHandler(char key, EarthManipulator* manip) - : _key(key), _manip(manip) + struct LockAzimuthHandler : public osgGA::GUIEventHandler { - } + LockAzimuthHandler(char key, EarthManipulator* manip) + : _key(key), _manip(manip) + { + } - bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa) - { - if (ea.getEventType() == ea.KEYDOWN && ea.getKey() == _key) + bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa) { - bool lockAzimuth - = _manip->getSettings()->getLockAzimuthWhilePanning(); - _manip->getSettings()->setLockAzimuthWhilePanning(!lockAzimuth); - return true; + if (ea.getEventType() == ea.KEYDOWN && ea.getKey() == _key) + { + bool lockAzimuth + = _manip->getSettings()->getLockAzimuthWhilePanning(); + _manip->getSettings()->setLockAzimuthWhilePanning(!lockAzimuth); + return true; + } + return false; } - return false; - } - void getUsage(osg::ApplicationUsage& usage) const - { - using namespace std; - usage.addKeyboardMouseBinding(string(1, _key), - string("Toggle azimuth locking")); - } + void getUsage(osg::ApplicationUsage& usage) const + { + using namespace std; + usage.addKeyboardMouseBinding(string(1, _key), + string("Toggle azimuth locking")); + } + + char _key; + osg::ref_ptr _manip; + + }; + +} - char _key; - osg::ref_ptr _manip; - -}; int main(int argc, char** argv) { @@ -177,7 +165,7 @@ osg::Group* root = new osg::Group(); root->addChild( earthNode ); - root->addChild( createHelp( &viewer ) ); + root->addChild( createHelp(&viewer) ); osgEarth::MapNode* mapNode = osgEarth::MapNode::findMapNode( earthNode ); if ( mapNode ) diff -Nru osgearth-2.0+dfsg/src/applications/osgearth_map/CMakeLists.txt osgearth-2.1.1+dfsg/src/applications/osgearth_map/CMakeLists.txt --- osgearth-2.0+dfsg/src/applications/osgearth_map/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/applications/osgearth_map/CMakeLists.txt 2011-11-04 19:44:43.000000000 +0000 @@ -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_map.cpp ) + +#### end var setup ### +SETUP_APPLICATION(osgearth_map) \ No newline at end of file diff -Nru osgearth-2.0+dfsg/src/applications/osgearth_map/osgearth_map.cpp osgearth-2.1.1+dfsg/src/applications/osgearth_map/osgearth_map.cpp --- osgearth-2.0+dfsg/src/applications/osgearth_map/osgearth_map.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/applications/osgearth_map/osgearth_map.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,75 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph +* Copyright 2008-2010 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 +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace osgEarth; +using namespace osgEarth::Drivers; +using namespace osgEarth::Util; + +int +main(int argc, char** argv) +{ + osg::ArgumentParser arguments(&argc,argv); + + // create the map. + Map* map = new Map(); + + // add a TMS imager layer: + TMSOptions imagery; + imagery.url() = "http://readymap.org/readymap/tiles/1.0.0/7/"; + map->addImageLayer( new ImageLayer("Imagery", imagery) ); + + // add a TMS elevation layer: + TMSOptions elevation; + elevation.url() = "http://readymap.org/readymap/tiles/1.0.0/9/"; + map->addElevationLayer( new ElevationLayer("Elevation", elevation) ); + + // make the map scene graph: + MapNode* node = new MapNode( map ); + + // initialize a viewer: + osgViewer::Viewer viewer(arguments); + viewer.setCameraManipulator( new EarthManipulator ); + viewer.setSceneData( node ); + + // osgEarth benefits from pre-compilation of GL objects in the pager. In newer versions of + // OSG, this activates OSG's IncrementalCompileOpeartion in order to avoid frame breaks. + viewer.getDatabasePager()->setDoPreCompile( true ); + + // add some stock OSG handlers: + viewer.addEventHandler(new osgViewer::StatsHandler()); + viewer.addEventHandler(new osgViewer::WindowSizeHandler()); + viewer.addEventHandler(new osgViewer::ThreadingHandler()); + viewer.addEventHandler(new osgViewer::LODScaleHandler()); + viewer.addEventHandler(new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet())); + viewer.addEventHandler(new osgViewer::HelpHandler(arguments.getApplicationUsage())); + + return viewer.run(); +} diff -Nru osgearth-2.0+dfsg/src/applications/osgearth_measure/CMakeLists.txt osgearth-2.1.1+dfsg/src/applications/osgearth_measure/CMakeLists.txt --- osgearth-2.0+dfsg/src/applications/osgearth_measure/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/applications/osgearth_measure/CMakeLists.txt 2011-11-04 19:44:43.000000000 +0000 @@ -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_measure.cpp ) + +#### end var setup ### +SETUP_APPLICATION(osgearth_measure) \ No newline at end of file diff -Nru osgearth-2.0+dfsg/src/applications/osgearth_measure/osgearth_measure.cpp osgearth-2.1.1+dfsg/src/applications/osgearth_measure/osgearth_measure.cpp --- osgearth-2.0+dfsg/src/applications/osgearth_measure/osgearth_measure.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/applications/osgearth_measure/osgearth_measure.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,192 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph +* Copyright 2008-2010 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 +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace osgEarth::Util; +using namespace osgEarth::Util::Controls; +using namespace osgEarth::Symbology; + +class MyMeasureToolCallback : public MeasureToolHandler::MeasureToolEventHandler +{ +public: + MyMeasureToolCallback(LabelControl* label): + _label(label) + { + } + + virtual void onDistanceChanged(MeasureToolHandler* sender, double distance) + { + std::stringstream ss; + ss << "Distance = " << std::setprecision(10) << distance << "m" << std::endl; + _label->setText( ss.str() ); + } + LabelControl* _label; +}; + +struct TogglePathHandler : public ControlEventHandler +{ + TogglePathHandler( MeasureToolHandler* tool) : + _tool( tool ) + { } + + virtual void onValueChanged(Control* control, bool value) { + _tool->setIsPath( value ); + } + + osg::ref_ptr _tool; +}; + +struct ToggleModeHandler : public ControlEventHandler +{ + ToggleModeHandler( MeasureToolHandler* tool) : + _tool( tool ) + { } + + virtual void onValueChanged(Control* control, bool value) { + if (_tool->getGeoInterpolation() == GEOINTERP_GREAT_CIRCLE) + { + _tool->setGeoInterpolation( GEOINTERP_RHUMB_LINE); + } + else + { + _tool->setGeoInterpolation( GEOINTERP_GREAT_CIRCLE); + } + } + + osg::ref_ptr _tool; +}; + + + +int +main(int argc, char** argv) +{ + osg::ArgumentParser arguments(&argc,argv); + osg::DisplaySettings::instance()->setMinimumNumStencilBits( 8 ); + + // load the .earth file from the command line. + osg::Node* earthNode = osgDB::readNodeFiles( arguments ); + if (!earthNode) + { + OE_NOTICE << "Unable to load earth model." << std::endl; + return 1; + } + + MapNode* mapNode = MapNode::findMapNode( earthNode ); + if ( !mapNode ) + { + OE_NOTICE << "Input file was not a .earth file" << std::endl; + return 1; + } + + earthNode->setNodeMask( 0x1 ); + + + osgViewer::Viewer viewer(arguments); + + osgEarth::Util::EarthManipulator* earthManip = new EarthManipulator(); + viewer.setCameraManipulator( earthManip ); + + osg::Group* root = new osg::Group(); + root->addChild( earthNode ); + + //Create the MeasureToolHandler + MeasureToolHandler* measureTool = new MeasureToolHandler(root, mapNode); + measureTool->setIntersectionMask( 0x1 ); + viewer.addEventHandler( measureTool ); + + //Create some controls to interact with the measuretool + ControlCanvas* canvas = new ControlCanvas( &viewer ); + root->addChild( canvas ); + canvas->setNodeMask( 0x1 << 1 ); + + Grid* grid = new Grid(); + grid->setBackColor(0,0,0,0.5); + grid->setMargin( 10 ); + grid->setPadding( 10 ); + grid->setChildSpacing( 10 ); + grid->setChildVertAlign( Control::ALIGN_CENTER ); + grid->setAbsorbEvents( true ); + grid->setVertAlign( Control::ALIGN_BOTTOM ); + grid->setFrame(new RoundedFrame()); + + canvas->addControl( grid ); + + //Add a label to display the distance + // Add a text label: + LabelControl* label = new LabelControl(); + label->setFont( osgText::readFontFile( "arialbd.ttf" ) ); + label->setFontSize( 24.0f ); + label->setHorizAlign( Control::ALIGN_LEFT ); + label->setText("Distance"); + grid->setControl( 0, 0, label); + + //Add a callback to update the label when the distance changes + measureTool->addEventHandler( new MyMeasureToolCallback(label) ); + + Style style; + style.getOrCreate()->stroke()->color() = Color::Yellow; + measureTool->setLineStyle(style); + + //Add a checkbox to control if we are doing path based measurement or just point to point + grid->setControl( 0, 1, new LabelControl("Path")); + CheckBoxControl* checkBox = new CheckBoxControl(false); + checkBox->setHorizAlign(Control::ALIGN_LEFT); + checkBox->addEventHandler( new TogglePathHandler(measureTool)); + grid->setControl( 1, 1, checkBox); + + //Add a toggle to set the mode of the measuring tool + grid->setControl( 0, 2, new LabelControl("Great Circle")); + CheckBoxControl* mode = new CheckBoxControl(true); + mode->setHorizAlign(Control::ALIGN_LEFT); + mode->addEventHandler( new ToggleModeHandler(measureTool)); + grid->setControl( 1, 2, mode); + + + + + + viewer.setSceneData( root ); + + // add some stock OSG handlers: + viewer.addEventHandler(new osgViewer::StatsHandler()); + viewer.addEventHandler(new osgViewer::WindowSizeHandler()); + viewer.addEventHandler(new osgViewer::ThreadingHandler()); + viewer.addEventHandler(new osgViewer::LODScaleHandler()); + viewer.addEventHandler(new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet())); + viewer.addEventHandler(new osgViewer::HelpHandler(arguments.getApplicationUsage())); + + return viewer.run(); +} diff -Nru osgearth-2.0+dfsg/src/applications/osgearth_ocean/osgearth_ocean.cpp osgearth-2.1.1+dfsg/src/applications/osgearth_ocean/osgearth_ocean.cpp --- osgearth-2.0+dfsg/src/applications/osgearth_ocean/osgearth_ocean.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/applications/osgearth_ocean/osgearth_ocean.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -68,12 +68,12 @@ { using namespace osgEarth::Util::Controls; - ControlCanvas* canvas = new ControlCanvas( view ); + ControlCanvas* canvas = ControlCanvas::get( view ); Grid* grid = new Grid(); grid->setBackColor( 0, 0, 0, 0.5 ); grid->setMargin( 5 ); - grid->setSpacing( 3 ); + grid->setChildSpacing( 3 ); grid->setVertAlign( Control::ALIGN_BOTTOM ); int row = 0; @@ -272,8 +272,8 @@ // assemble the rest of the scene graph and go osgViewer::Viewer viewer(arguments); - group->addChild( loadedModel ); - group->addChild( createMenu( &viewer ) ); + group->addChild( loadedModel.get() ); + group->addChild( createMenu(&viewer) ); viewer.setSceneData(group); viewer.setCameraManipulator( new osgEarth::Util::EarthManipulator() ); diff -Nru osgearth-2.0+dfsg/src/applications/osgearth_resources/CMakeLists.txt osgearth-2.1.1+dfsg/src/applications/osgearth_resources/CMakeLists.txt --- osgearth-2.0+dfsg/src/applications/osgearth_resources/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/applications/osgearth_resources/CMakeLists.txt 2011-11-04 19:44:43.000000000 +0000 @@ -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_resources.cpp ) + +#### end var setup ### +SETUP_APPLICATION(osgearth_resources) \ No newline at end of file diff -Nru osgearth-2.0+dfsg/src/applications/osgearth_resources/osgearth_resources.cpp osgearth-2.1.1+dfsg/src/applications/osgearth_resources/osgearth_resources.cpp --- osgearth-2.0+dfsg/src/applications/osgearth_resources/osgearth_resources.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/applications/osgearth_resources/osgearth_resources.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,36 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph +* Copyright 2008-2010 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 +*/ + +#include +#include +#include +#include +#include + +using namespace osgEarth; +using namespace osgEarth::Symbology; + +int +main(int argc, char** argv) +{ + XmlDocument* xml = XmlDocument::load( URI("e:/data/osgearth_resources/US/catalog.xml") ); + Config conf = xml->getConfig(); + + OE_NOTICE << conf.toString() << std::endl; +} diff -Nru osgearth-2.0+dfsg/src/applications/osgearth_seed/osgearth_seed.cpp osgearth-2.1.1+dfsg/src/applications/osgearth_seed/osgearth_seed.cpp --- osgearth-2.0+dfsg/src/applications/osgearth_seed/osgearth_seed.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/applications/osgearth_seed/osgearth_seed.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -122,6 +123,8 @@ std::string cacheType; while (args.read("--cache-type", cacheType)); + bool quiet = args.read("--quiet"); + //Read in the earth file. osg::ref_ptr node = osgDB::readNodeFiles( args ); if ( !node.valid() ) @@ -135,6 +138,10 @@ seeder.setMinLevel( minLevel ); seeder.setMaxLevel( maxLevel ); seeder.setBounds( bounds ); + if (!quiet) + { + seeder.setProgressCallback(new ConsoleProgressCallback); + } seeder.seed( mapNode->getMap() ); return 0; diff -Nru osgearth-2.0+dfsg/src/applications/osgearth_toc/osgearth_toc.cpp osgearth-2.1.1+dfsg/src/applications/osgearth_toc/osgearth_toc.cpp --- osgearth-2.0+dfsg/src/applications/osgearth_toc/osgearth_toc.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/applications/osgearth_toc/osgearth_toc.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -160,14 +160,14 @@ osg::Node* createControlPanel( osgViewer::View* view ) { - ControlCanvas* canvas = new ControlCanvas( view ); + ControlCanvas* canvas = ControlCanvas::get( view ); // the outer container: s_layerBox = new Grid(); s_layerBox->setBackColor(0,0,0,0.5); s_layerBox->setMargin( 10 ); s_layerBox->setPadding( 10 ); - s_layerBox->setSpacing( 10 ); + s_layerBox->setChildSpacing( 10 ); s_layerBox->setChildVertAlign( Control::ALIGN_CENTER ); s_layerBox->setAbsorbEvents( true ); s_layerBox->setVertAlign( Control::ALIGN_BOTTOM ); diff -Nru osgearth-2.0+dfsg/src/applications/osgearth_viewer/osgearth_viewer.cpp osgearth-2.1.1+dfsg/src/applications/osgearth_viewer/osgearth_viewer.cpp --- osgearth-2.0+dfsg/src/applications/osgearth_viewer/osgearth_viewer.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/applications/osgearth_viewer/osgearth_viewer.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -23,35 +23,293 @@ #include #include #include +#include #include #include +#include #include #include +#include +#include +#include +#include +#include using namespace osgEarth::Util; +using namespace osgEarth::Util::Annotation; +using namespace osgEarth::Util::Controls; +using namespace osgEarth::Symbology; +using namespace osgEarth::Drivers; int usage( const std::string& msg ) { OE_NOTICE << msg << std::endl; - OE_NOTICE << "USAGE: osgearth_viewer [--graticule] [--autoclip] file.earth" << std::endl; - OE_NOTICE << " --graticule : displays a lat/long grid in geocentric mode" << std::endl; + OE_NOTICE << std::endl; + OE_NOTICE << "USAGE: osgearth_viewer [options] file.earth" << std::endl; OE_NOTICE << " --sky : activates the atmospheric model" << std::endl; - OE_NOTICE << " --animateSky : animates the sun across the sky" << std::endl; OE_NOTICE << " --autoclip : activates the auto clip-plane handler" << std::endl; + OE_NOTICE << " --dms : format coordinates as degrees/minutes/seconds" << std::endl; + OE_NOTICE << " --mgrs : format coordinates as MGRS" << std::endl; + return -1; } -struct AnimateSunCallback : public osg::NodeCallback +static EarthManipulator* s_manip =0L; +static Control* s_controlPanel =0L; +static SkyNode* s_sky =0L; +static bool s_dms =false; +static bool s_mgrs =false; + +struct SkySliderHandler : public ControlEventHandler +{ + virtual void onValueChanged( class Control* control, float value ) + { + s_sky->setDateTime( 2011, 3, 6, value ); + } +}; + +struct ToggleNodeHandler : public ControlEventHandler +{ + ToggleNodeHandler( osg::Node* node ) : _node(node) { } + + virtual void onValueChanged( class Control* control, bool value ) + { + osg::ref_ptr safeNode = _node.get(); + if ( safeNode.valid() ) + safeNode->setNodeMask( value ? ~0 : 0 ); + } + + osg::observer_ptr _node; +}; + +struct ClickViewpointHandler : public ControlEventHandler +{ + ClickViewpointHandler( const Viewpoint& vp ) : _vp(vp) { } + Viewpoint _vp; + + virtual void onClick( class Control* control ) + { + s_manip->setViewpoint( _vp, 4.5 ); + } +}; + +struct MouseCoordsHandler : public osgGA::GUIEventHandler +{ + MouseCoordsHandler( LabelControl* label, osgEarth::MapNode* mapNode ) + : _label( label ), + _mapNode( mapNode ) + { + _mapNodePath.push_back( mapNode->getTerrainEngine() ); + } + + bool handle( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa ) + { + osgViewer::View* view = static_cast(aa.asView()); + if (ea.getEventType() == ea.MOVE || ea.getEventType() == ea.DRAG) + { + osgUtil::LineSegmentIntersector::Intersections results; + if ( view->computeIntersections( ea.getX(), ea.getY(), _mapNodePath, results ) ) + { + // find the first hit under the mouse: + osgUtil::LineSegmentIntersector::Intersection first = *(results.begin()); + osg::Vec3d point = first.getWorldIntersectPoint(); + osg::Vec3d lla; + + // transform it to map coordinates: + _mapNode->getMap()->worldPointToMapPoint(point, lla); + + std::stringstream ss; + + if ( s_mgrs ) + { + MGRSFormatter f( MGRSFormatter::PRECISION_1M ); + ss << "MGRS: " << f.format(lla.y(), lla.x()) << " "; + } + // lat/long + { + LatLongFormatter::AngularFormat fFormat = s_dms? + LatLongFormatter::FORMAT_DEGREES_MINUTES_SECONDS : + LatLongFormatter::FORMAT_DECIMAL_DEGREES; + + LatLongFormatter f( fFormat ); + + ss + << "Lat: " << f.format( Angular(lla.y(),Units::DEGREES), 4 ) << " " + << "Lon: " << f.format( Angular(lla.x(),Units::DEGREES), 5 ); + } + + _label->setText( ss.str() ); + } + else + { + //Clear the text + _label->setText( "" ); + } + } + return false; + } + + osg::ref_ptr< LabelControl > _label; + MapNode* _mapNode; + osg::NodePath _mapNodePath; +}; + + + +void +createControlPanel( osgViewer::View* view, std::vector& vps ) +{ + ControlCanvas* canvas = ControlCanvas::get( view ); + + VBox* main = new VBox(); + main->setBackColor(0,0,0,0.5); + main->setMargin( 10 ); + main->setPadding( 10 ); + main->setChildSpacing( 10 ); + main->setAbsorbEvents( true ); + main->setVertAlign( Control::ALIGN_BOTTOM ); + + if ( vps.size() > 0 ) + { + // the viewpoint container: + Grid* g = new Grid(); + g->setChildSpacing( 0 ); + g->setChildVertAlign( Control::ALIGN_CENTER ); + + unsigned i; + for( i=0; isetPadding( 4 ); + g->setControl( 0, i, num ); + + Control* vpc = new LabelControl(vp.getName().empty() ? "" : vp.getName(), 16.0f); + vpc->setPadding( 4 ); + vpc->setHorizFill( true ); + vpc->setActiveColor( Color::Blue ); + vpc->addEventHandler( new ClickViewpointHandler(vp) ); + g->setControl( 1, i, vpc ); + } + main->addControl( g ); + } + + // sky time slider: + if ( s_sky ) + { + HBox* skyBox = new HBox(); + skyBox->setChildVertAlign( Control::ALIGN_CENTER ); + skyBox->setChildSpacing( 10 ); + skyBox->setHorizFill( true ); + + skyBox->addControl( new LabelControl("Time: ", 16) ); + + HSliderControl* skySlider = new HSliderControl( 0.0f, 24.0f, 18.0f ); + skySlider->setBackColor( Color::Gray ); + skySlider->setHeight( 12 ); + skySlider->setHorizFill( true, 200 ); + skySlider->addEventHandler( new SkySliderHandler ); + skyBox->addControl( skySlider ); + + main->addControl( skyBox ); + } + + canvas->addControl( main ); + + s_controlPanel = main; +} + +/** + * Visitor that builds a UI control for a loaded KML file. + */ +struct KMLUIBuilder : public osg::NodeVisitor +{ + KMLUIBuilder( ControlCanvas* canvas ) : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), _canvas(canvas) + { + _grid = new Grid(); + _grid->setAbsorbEvents( true ); + _grid->setPadding( 5 ); + _grid->setVertAlign( Control::ALIGN_TOP ); + _grid->setHorizAlign( Control::ALIGN_LEFT ); + _grid->setBackColor( Color(Color::Black,0.5) ); + _canvas->addControl( _grid ); + } + + void apply( osg::Node& node ) + { + AnnotationData* data = dynamic_cast( node.getUserData() ); + if ( data ) + { + ControlVector row; + CheckBoxControl* cb = new CheckBoxControl( node.getNodeMask() != 0, new ToggleNodeHandler( &node ) ); + cb->setSize( 12, 12 ); + row.push_back( cb ); + std::string name = data->getName().empty() ? "" : data->getName(); + LabelControl* label = new LabelControl( name, 14.0f ); + label->setMargin(Gutter(0,0,0,(this->getNodePath().size()-3)*20)); + if ( data->getViewpoint() ) + { + label->addEventHandler( new ClickViewpointHandler(*data->getViewpoint()) ); + label->setActiveColor( Color::Blue ); + } + row.push_back( label ); + _grid->addControls( row ); + } + traverse(node); + } + + ControlCanvas* _canvas; + Grid* _grid; +}; + +void addMouseCoords(osgViewer::Viewer* viewer, osgEarth::MapNode* mapNode) { - void operator()( osg::Node* node, osg::NodeVisitor* nv ) + ControlCanvas* canvas = ControlCanvas::get( viewer ); + LabelControl* mouseCoords = new LabelControl(); + mouseCoords->setHorizAlign(Control::ALIGN_CENTER ); + mouseCoords->setVertAlign(Control::ALIGN_BOTTOM ); + mouseCoords->setBackColor(0,0,0,0.5); + mouseCoords->setSize(400,50); + mouseCoords->setMargin( 10 ); + canvas->addControl( mouseCoords ); + + viewer->addEventHandler( new MouseCoordsHandler(mouseCoords, mapNode ) ); +} + +struct ViewpointHandler : public osgGA::GUIEventHandler +{ + ViewpointHandler( const std::vector& viewpoints ) + : _viewpoints( viewpoints ) { } + + bool handle( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa ) { - SkyNode* skyNode = static_cast(node); - double hours = fmod( osg::Timer::instance()->time_s()/4.0, 24.0 ); - skyNode->setDateTime( 2011, 6, 6, hours ); - OE_INFO << "TIME: " << hours << std::endl; + if ( ea.getEventType() == ea.KEYDOWN ) + { + int index = (int)ea.getKey() - (int)'1'; + if ( index >= 0 && index < (int)_viewpoints.size() ) + { + s_manip->setViewpoint( _viewpoints[index], 4.5 ); + } + else if ( ea.getKey() == 'v' ) + { + Viewpoint vp = s_manip->getViewpoint(); + XmlDocument xml( vp.getConfig() ); + xml.store( std::cout ); + std::cout << std::endl; + } + else if ( ea.getKey() == '?' ) + { + s_controlPanel->setVisible( !s_controlPanel->visible() ); + } + } + return false; } + + std::vector _viewpoints; }; int @@ -59,18 +317,24 @@ { osg::ArgumentParser arguments(&argc,argv); osg::DisplaySettings::instance()->setMinimumNumStencilBits( 8 ); + osgViewer::Viewer viewer(arguments); - bool useGraticule = arguments.read( "--graticule" ); bool useAutoClip = arguments.read( "--autoclip" ); - bool animateSky = arguments.read( "--animateSky"); - bool useSky = arguments.read( "--sky" ) || animateSky; + bool useSky = arguments.read( "--sky" ); + s_dms = arguments.read( "--dms" ); + s_mgrs = arguments.read( "--mgrs" ); + + std::string kmlFile; + arguments.read( "--kml", kmlFile ); // load the .earth file from the command line. osg::Node* earthNode = osgDB::readNodeFiles( arguments ); if (!earthNode) return usage( "Unable to load earth model." ); - - osgViewer::Viewer viewer(arguments); + + s_manip = new EarthManipulator(); + s_manip->getSettings()->setArcViewpointTransitions( true ); + viewer.setCameraManipulator( s_manip ); osg::Group* root = new osg::Group(); root->addChild( earthNode ); @@ -80,34 +344,68 @@ osgEarth::MapNode* mapNode = osgEarth::MapNode::findMapNode( earthNode ); if ( mapNode ) { + const Config& externals = mapNode->externalConfig(); + if ( mapNode->getMap()->isGeocentric() ) { + // Sky model. + Config skyConf = externals.child( "sky" ); + if ( !skyConf.empty() ) + useSky = true; + + if ( useSky ) + { + double hours = skyConf.value( "hours", 12.0 ); + s_sky = new SkyNode( mapNode->getMap() ); + s_sky->setDateTime( 2011, 3, 6, hours ); + s_sky->attach( &viewer ); + root->addChild( s_sky ); + } + + if ( externals.hasChild("autoclip") ) + useAutoClip = externals.child("autoclip").boolValue( useAutoClip ); + // the AutoClipPlaneHandler will automatically adjust the near/far clipping // planes based on your view of the horizon. This prevents near clipping issues // when you are very close to the ground. If your app never brings a user very // close to the ground, you may not need this. - if ( useAutoClip ) - viewer.addEventHandler( new AutoClipPlaneHandler ); - - // the Graticule is a lat/long grid that overlays the terrain. It only works - // in a round-earth geocentric terrain. - if ( useGraticule ) + if ( useSky || useAutoClip ) { - graticule = new Graticule( mapNode->getMap() ); - root->addChild( graticule ); + viewer.getCamera()->addEventCallback( new AutoClipPlaneCallback() ); } + } - if ( useSky ) + // read in viewpoints, if any + std::vector viewpoints; + const ConfigSet children = externals.children("viewpoint"); + if ( children.size() > 0 ) + { + for( ConfigSet::const_iterator i = children.begin(); i != children.end(); ++i ) + viewpoints.push_back( Viewpoint(*i) ); + + viewer.addEventHandler( new ViewpointHandler(viewpoints) ); + } + + // Add a control panel to the scene + root->addChild( ControlCanvas::get( &viewer ) ); + if ( viewpoints.size() > 0 || s_sky ) + createControlPanel(&viewer, viewpoints); + + addMouseCoords( &viewer, mapNode ); + + // Load a KML file if specified + if ( !kmlFile.empty() ) + { + KMLOptions kmlo; + kmlo.defaultIconImage() = URI("http://www.osgearth.org/chrome/site/pushpin_yellow.png").readImage(); + + osg::Node* kml = KML::load( URI(kmlFile), mapNode, kmlo ); + if ( kml ) { - SkyNode* sky = new SkyNode( mapNode->getMap() ); - sky->setDateTime( 2011, 1, 6, 17.0 ); - //sky->setSunPosition( osg::Vec3(0,-1,0) ); - sky->attach( &viewer ); - root->addChild( sky ); - if (animateSky) - { - sky->setUpdateCallback( new AnimateSunCallback()); - } + root->addChild( kml ); + + KMLUIBuilder uibuilder( ControlCanvas::get(&viewer) ); + root->accept( uibuilder ); } } } @@ -118,16 +416,12 @@ viewer.setSceneData( root ); - EarthManipulator* manip = new EarthManipulator(); - viewer.setCameraManipulator( manip ); - // add some stock OSG handlers: viewer.addEventHandler(new osgViewer::StatsHandler()); viewer.addEventHandler(new osgViewer::WindowSizeHandler()); viewer.addEventHandler(new osgViewer::ThreadingHandler()); viewer.addEventHandler(new osgViewer::LODScaleHandler()); viewer.addEventHandler(new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet())); - viewer.addEventHandler(new osgViewer::HelpHandler(arguments.getApplicationUsage())); return viewer.run(); } diff -Nru osgearth-2.0+dfsg/src/CMakeLists.txt osgearth-2.1.1+dfsg/src/CMakeLists.txt --- osgearth-2.0+dfsg/src/CMakeLists.txt 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/CMakeLists.txt 2011-11-04 19:44:43.000000000 +0000 @@ -1,17 +1,18 @@ #the old construct SUBDIRS( was substituded by ADD_SUBDIRECTORY that is to be preferred according on CMake docs. -FOREACH( myfolder - osgEarth - osgEarthFeatures - osgEarthUtil - osgEarthDrivers - osgEarthSymbology - applications - ) +FOREACH( lib + osgEarth + osgEarthFeatures + osgEarthUtil + osgEarthSymbology ) + + ADD_SUBDIRECTORY(${lib}) + + SET_PROPERTY(TARGET ${lib} PROPERTY FOLDER "Libs") - ADD_SUBDIRECTORY(${myfolder}) - -ENDFOREACH( myfolder) +ENDFOREACH( lib ) +ADD_SUBDIRECTORY( osgEarthDrivers ) +ADD_SUBDIRECTORY( applications ) IF(MSVC80) OPTION(OSGEARTH_MSVC_GENERATE_PLUGINS_AND_WRAPPERS_MANIFESTS "Generate or not manifests files under VS8 for dynamically loaded dlls" ON) diff -Nru osgearth-2.0+dfsg/src/osgEarth/CacheSeed osgearth-2.1.1+dfsg/src/osgEarth/CacheSeed --- osgearth-2.0+dfsg/src/osgEarth/CacheSeed 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/CacheSeed 2011-11-04 19:44:43.000000000 +0000 @@ -23,7 +23,7 @@ #include #include #include -#include +#include namespace osgEarth { @@ -74,6 +74,11 @@ void setBounds(const double& minLon, const double& minLat, const double& maxLon, const double& maxLat) { _bounds = Bounds(minLon, minLat, maxLon, maxLat); } /** + * Set progress callback for reporting which tiles are seeded + */ + void setProgressCallback(osgEarth::ProgressCallback* progress) { _progress = progress? progress : new ProgressCallback; } + + /** * Performs the seed operation */ void seed( Map* map ); @@ -82,6 +87,7 @@ unsigned int _minLevel; unsigned int _maxLevel; Bounds _bounds; + osg::ref_ptr _progress; void processKey( const MapFrame& mapf, const TileKey& key ) const; void cacheTile( const MapFrame& mapf, const TileKey& key ) const; diff -Nru osgearth-2.0+dfsg/src/osgEarth/CacheSeed.cpp osgearth-2.1.1+dfsg/src/osgEarth/CacheSeed.cpp --- osgearth-2.0+dfsg/src/osgEarth/CacheSeed.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/CacheSeed.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -156,7 +156,10 @@ if ( _minLevel <= lod && _maxLevel >= lod ) { - OE_NOTICE << "Caching tile = " << key.str() << std::endl; //<< lod << " (" << x << ", " << y << ") " << std::endl; +// OE_NOTICE << "Caching tile = " << key.str() << std::endl; //<< lod << " (" << x << ", " << y << ") " << std::endl; + if ( _progress.valid() && _progress->reportProgress(0, 0, "Caching tile: " + key.str()) ) + return; // Task has been cancelled by user + cacheTile( mapf, key ); // bool validData; //osg::ref_ptr node = engine->createTile( map, terrain.get(), key, true, false, false, validData ); @@ -200,4 +203,3 @@ mapf.getHeightField( key, false, hf ); } } - diff -Nru osgearth-2.0+dfsg/src/osgEarth/Caching osgearth-2.1.1+dfsg/src/osgEarth/Caching --- osgearth-2.0+dfsg/src/osgEarth/Caching 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/Caching 2011-11-04 19:44:43.000000000 +0000 @@ -35,6 +35,8 @@ #include #include +#include +#include namespace osgEarth { @@ -52,8 +54,8 @@ } /** Whether to run exclusively off the cache (and not fetch files from tile sources) */ - optional cacheOnly() { return _cacheOnly; } - const optional cacheOnly() const { return _cacheOnly; } + optional& cacheOnly() { return _cacheOnly; } + const optional& cacheOnly() const { return _cacheOnly; } public: virtual Config getConfig() const { @@ -65,12 +67,19 @@ ConfigOptions::mergeConfig( conf ); fromConfig( conf ); } + + /** INTERNAL FUNCTION + TODO: this most definitely should not be in the Options structure */ + void setReferenceURI(const std::string& referenceURI) { _referenceURI = referenceURI; } + const std::string& getReferenceURI() const { return _referenceURI; } + private: void fromConfig( const Config& conf ) { conf.getIfSet( "cache_only", _cacheOnly ); } optional _cacheOnly; + std::string _referenceURI; }; //---------------------------------------------------------------------- @@ -83,7 +92,8 @@ public: DiskCacheOptions( const ConfigOptions& options =ConfigOptions() ) : CacheOptions( options ), - _writeWorldFiles( false ) + _writeWorldFiles( false ), + _imageWriterPluginOptions("") { fromConfig( _conf ); } @@ -96,11 +106,16 @@ optional& writeWorldFiles() { return _writeWorldFiles; } const optional& writeWorldFiles() const { return _writeWorldFiles; } + /** Options string to pass to an osgDB image plugin */ + optional& imageWriterPluginOptions() { return _imageWriterPluginOptions; } + const optional& imageWriterPluginOptions() const { return _imageWriterPluginOptions; } + public: virtual Config getConfig() const { Config conf = CacheOptions::getConfig(); conf.update("path", _path); conf.updateIfSet("write_world_files", _writeWorldFiles); + conf.updateIfSet("image_writer_plugin_options", _imageWriterPluginOptions); return conf; } virtual void mergeConfig( const Config& conf ) { @@ -112,10 +127,13 @@ void fromConfig( const Config& conf ) { _path = conf.value("path"); conf.getIfSet("write_world_files", _writeWorldFiles); + conf.getIfSet("image_writer_plugin_options", _imageWriterPluginOptions); + } - std::string _path; - optional _writeWorldFiles; + std::string _path; + optional _writeWorldFiles; + optional _imageWriterPluginOptions; }; //---------------------------------------------------------------------- @@ -475,7 +493,7 @@ class OSGEARTH_EXPORT CacheFactory { public: - static Cache* create( const CacheOptions& options ); + static Cache* create( const CacheOptions& options); }; } diff -Nru osgearth-2.0+dfsg/src/osgEarth/Caching.cpp osgearth-2.1.1+dfsg/src/osgEarth/Caching.cpp --- osgearth-2.0+dfsg/src/osgEarth/Caching.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/Caching.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -249,6 +249,9 @@ bool writingJpeg = (ext == "jpg" || ext == "jpeg"); + osg::ref_ptr op = new osgDB::ReaderWriter::Options(); + op->setOptionString(_options.imageWriterPluginOptions().value()); + //If we are trying to write a non RGB image to JPEG, convert it to RGB before we write it if ((image->getPixelFormat() != GL_RGB) && writingJpeg) { @@ -256,12 +259,12 @@ osg::ref_ptr rgb = ImageUtils::convertToRGB8( image ); if (rgb.valid()) { - osgDB::writeImageFile(*rgb.get(), filename); + osgDB::writeImageFile(*rgb.get(), filename, op.get()); } } else { - osgDB::writeImageFile(*image, filename); + osgDB::writeImageFile(*image, filename, op.get()); } } @@ -397,7 +400,6 @@ { //TODO: can this be a ReadWriteLock instead? - // MemCache does not support timestamps or async, so just clear it out altogether. OpenThreads::ScopedLock lock(_mutex); @@ -413,11 +415,7 @@ { OpenThreads::ScopedLock lock(_mutex); - //OE_NOTICE << "List contains: " << _images.size() << std::endl; - std::string id = key.str() + spec.cacheId(); - //Find the image in the cache - //ImageCache::iterator itr = _images.find(id); KeyToIteratorMap::iterator itr = _keyToIterMap.find(id); if (itr != _keyToIterMap.end()) { @@ -425,10 +423,8 @@ _objects.erase(itr->second); _objects.push_front(entry); itr->second = _objects.begin(); - //OE_NOTICE<<"Getting from memcache "<< key.str() <second->_object.get(); return output.valid(); - //return itr->second->_object.get(); } return false; } @@ -440,19 +436,23 @@ std::string id = key.str() + spec.cacheId(); - CachedObject entry; - //entry._object = referenced->clone( osg::CopyOp::DEEP_COPY_ALL ); + _objects.push_front(CachedObject()); + CachedObject& entry = _objects.front(); + + //CachedObject entry; entry._object = referenced; entry._key = id; - _objects.push_front(entry); + //_objects.push_front(entry); _keyToIterMap[id] = _objects.begin(); if (_objects.size() > _maxNumTilesInCache) { - CachedObject toRemove = _objects.back(); - _objects.pop_back(); - _keyToIterMap.erase(toRemove._key); + _keyToIterMap.erase( _objects.back()._key ); + _objects.pop_back(); + //CachedObject toRemove = _objects.back(); + //_objects.pop_back(); + //_keyToIterMap.erase(toRemove._key); } } @@ -514,7 +514,7 @@ #define CACHE_OPTIONS_TAG "__osgEarth::CacheOptions" Cache* -CacheFactory::create( const CacheOptions& options ) +CacheFactory::create( const CacheOptions& options) { osg::ref_ptr result =0L; OE_INFO << LC << "Initializing cache of type \"" << options.getDriver() << "\"" << std::endl; diff -Nru osgearth-2.0+dfsg/src/osgEarth/Capabilities osgearth-2.1.1+dfsg/src/osgEarth/Capabilities --- osgearth-2.0+dfsg/src/osgEarth/Capabilities 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/Capabilities 2011-11-04 19:44:43.000000000 +0000 @@ -44,6 +44,9 @@ /** maximum supported size (in pixels) of a texture */ int getMaxTextureSize() const { return _maxTextureSize; } + /** maximum texture size that doesn't cause a slowdown (vendor-specific) */ + int getMaxFastTextureSize() const { return _maxFastTextureSize; } + /** maximum number of openGL lights */ int getMaxLights() const { return _maxLights; } @@ -83,6 +86,7 @@ int _maxGPUTextureUnits; int _maxGPUTextureCoordSets; int _maxTextureSize; + int _maxFastTextureSize; int _maxLights; bool _supportsGLSL; float _GLSLversion; diff -Nru osgearth-2.0+dfsg/src/osgEarth/Capabilities.cpp osgearth-2.1.1+dfsg/src/osgEarth/Capabilities.cpp --- osgearth-2.0+dfsg/src/osgEarth/Capabilities.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/Capabilities.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -100,6 +100,7 @@ _maxGPUTextureUnits ( 1 ), _maxGPUTextureCoordSets ( 1 ), _maxTextureSize ( 256 ), +_maxFastTextureSize ( 256 ), _maxLights ( 1 ), _supportsGLSL ( false ), _GLSLversion ( 1.0f ), @@ -198,9 +199,18 @@ //_supportsTexture2DLod = osg::isGLExtensionSupported( id, "GL_ARB_shader_texture_lod" ); //OE_INFO << LC << " texture2DLod = " << SAYBOOL(_supportsTexture2DLod) << std::endl; - bool isATI = _vendor.length() >= 4 && ::strncmp(_vendor.c_str(), "ATI ", 4) == 0; + // ATI workarounds: + bool isATI = _vendor.find("ATI ") == 0; _supportsMipmappedTextureUpdates = isATI && enableATIworkarounds ? false : true; + OE_INFO << LC << " Mipmapped texture updates = " << SAYBOOL(_supportsMipmappedTextureUpdates) << std::endl; + + // Intel workarounds: + bool isIntel = + _vendor.find("Intel ") != std::string::npos || + _vendor.find("Intel(R)") != std::string::npos; + _maxFastTextureSize = isIntel ? std::min(2048, _maxTextureSize) : _maxTextureSize; + OE_INFO << LC << "Max Fast Texture Size = " << _maxFastTextureSize << std::endl; } } diff -Nru osgearth-2.0+dfsg/src/osgEarth/CMakeLists.txt osgearth-2.1.1+dfsg/src/osgEarth/CMakeLists.txt --- osgearth-2.0+dfsg/src/osgEarth/CMakeLists.txt 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/CMakeLists.txt 2011-11-04 19:44:43.000000000 +0000 @@ -26,7 +26,10 @@ CompositeTileSource Config Cube + DrapeableNode + ECEF ElevationLayer + ElevationQuery Export EGM FileUtils @@ -42,18 +45,22 @@ JsonUtils Layer Locators + LocalTangentPlane Map MapNode MapNodeOptions MapOptions MaskLayer MaskNode + MaskSource ModelLayer ModelSource + NodeUtils Notify OverlayDecorator Profile Progress + Random Registry Revisioning ShaderComposition @@ -72,26 +79,49 @@ TileKey TileSource ThreadingUtils - tinystr.h - tinyxml.h TMS Units + URI + Utils Version VerticalSpatialReference XmlUtils ) + +IF (NOT TINYXML_FOUND) + SET(LIB_PUBLIC_HEADERS + ${LIB_PUBLIC_HEADERS} + tinystr.h + tinyxml.h + ) +ENDIF (NOT TINYXML_FOUND) + +IF (NOT TINYXML_FOUND) + SET(TINYXML_SRC + tinystr.cpp + tinyxml.cpp + tinyxmlerror.cpp + tinyxmlparser.cpp + ) +ENDIF (NOT TINYXML_FOUND) +MESSAGE("${LIB_PUBLIC_HEADERS}") + ADD_LIBRARY(${LIB_NAME} SHARED # ${OSGEARTH_USER_DEFINED_DYNAMIC_OR_STATIC} ${LIB_PUBLIC_HEADERS} + ${TINYXML_SRC} Caching.cpp CacheSeed.cpp Capabilities.cpp CompositeTileSource.cpp Config.cpp Cube.cpp + DrapeableNode.cpp + ECEF.cpp EGM.cpp ElevationLayer.cpp + ElevationQuery.cpp FileUtils.cpp GeoData.cpp GeoMath.cpp @@ -104,24 +134,29 @@ JsonUtils.cpp Layer.cpp Locators.cpp + LocalTangentPlane.cpp Map.cpp MapNode.cpp MapNodeOptions.cpp MapOptions.cpp MaskLayer.cpp MaskNode.cpp + MaskSource.cpp MimeTypes.cpp ModelLayer.cpp ModelSource.cpp + NodeUtils.cpp Notify.cpp OverlayDecorator.cpp Profile.cpp Progress.cpp + Random.cpp Registry.cpp ShaderComposition.cpp ShaderUtils.cpp SparseTexture2DArray.cpp SpatialReference.cpp + StringUtils.cpp TaskService.cpp TerrainLayer.cpp TerrainOptions.cpp @@ -132,12 +167,10 @@ TileFactory.cpp TileKey.cpp TileSource.cpp - tinystr.cpp - tinyxml.cpp - tinyxmlerror.cpp - tinyxmlparser.cpp TMS.cpp Units.cpp + URI.cpp + Utils.cpp Version.cpp VerticalSpatialReference.cpp XmlUtils.cpp @@ -145,6 +178,10 @@ INCLUDE_DIRECTORIES(${GDAL_INCLUDE_DIR} ${CURL_INCLUDE_DIR} ${OSG_INCLUDE_DIR} ) +IF (TINYXML_FOUND) + INCLUDE_DIRECTORIES(${TINYXML_INCLUDE_DIR}) +ENDIF (TINYXML_FOUND) + IF (WIN32) LINK_EXTERNAL(${LIB_NAME} ${TARGET_EXTERNAL_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${MATH_LIBRARY} ) ELSE(WIN32) @@ -154,4 +191,10 @@ 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_CORELIB_DEFAULT(${LIB_NAME} ${CMAKE_THREAD_LIBS_INIT} ${MATH_LIBRARY}) +IF (TINYXML_FOUND) + LINK_WITH_VARIABLES(${LIB_NAME} TINYXML_LIBRARY) + get_directory_property(output INCLUDE_DIRECTORIES) + message(STATUS ${output}) +ENDIF (TINYXML_FOUND) + INCLUDE(ModuleInstall OPTIONAL) diff -Nru osgearth-2.0+dfsg/src/osgEarth/Common osgearth-2.1.1+dfsg/src/osgEarth/Common --- osgearth-2.0+dfsg/src/osgEarth/Common 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/Common 2011-11-04 19:44:43.000000000 +0000 @@ -26,12 +26,6 @@ #include #include #include -#include -#include -#include -#include -#include -#include // define the OSG Version checks for older OSG versions @@ -48,55 +42,7 @@ { // application-wide unique ID. typedef int UID; - - // converts a string to primitive using serialization - template inline T - as( const std::string& str, T default_value ) - { - T temp = default_value; - std::istringstream strin( str ); - if ( !strin.eof() ) strin >> temp; - return temp; - } - - // template specialization for a bool - template<> inline bool - as( const std::string& str, bool default_value ) - { - std::string temp = str; - std::transform( temp.begin(), temp.end(), temp.begin(), ::tolower ); - return - temp == "true" || temp == "yes" || temp == "on" ? true : - temp == "false" || temp == "no" || temp == "off" ? false : - default_value; - } - - // template specialization for string - template<> inline std::string - as( const std::string& str, std::string default_value ) - { - return str; - } - - // converts a primitive to a string - // TODO: precision?? - template inline std::string - toString(const T value) - { - std::stringstream out; - out << std::setprecision(20) << std::fixed << value; - std::string outStr; - outStr = out.str(); - return outStr; - } - - // template speciallization for a bool to print out "true" or "false" - template<> inline std::string - toString(const bool value) - { - return value ? "true" : "false"; - } - + /** * A template for defining "optional" class members. An optional member has a default value * and a flag indicating whether the member is "set". @@ -123,6 +69,9 @@ const T& value() const { return _value; } const T& defaultValue() const { return _defaultValue; } + // gets a mutable reference, automatically setting + T& mutable_value() { _set = true; return _value; } + void init(T defValue) { _value=defValue; _defaultValue=defValue; unset(); } operator const T*() const { return &_value; } @@ -139,7 +88,6 @@ public: operator unspecified_bool_type() const { return 0; } }; - } // backwards-compat stuff: @@ -154,6 +102,16 @@ #endif // OSG_VERSION_LESS_THAN( 2, 9, 5 ) +#if OSG_MIN_VERSION_REQUIRED(2,9,8) +# include + namespace osgGA { + typedef CameraManipulator MatrixManipulator; + }; +# include +# define USE_OBSERVER_NODE_PATH 1 +#else +# include +#endif #endif // OSGEARTH_COMMON_H diff -Nru osgearth-2.0+dfsg/src/osgEarth/CompositeTileSource osgearth-2.1.1+dfsg/src/osgEarth/CompositeTileSource --- osgearth-2.0+dfsg/src/osgEarth/CompositeTileSource 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/CompositeTileSource 2011-11-04 19:44:43.000000000 +0000 @@ -19,6 +19,7 @@ #ifndef OSGEARTH_COMPOSITE_TILE_SOURCE_H #define OSGEARTH_COMPOSITE_TILE_SOURCE_H 1 +#include #include #include @@ -27,7 +28,7 @@ /** * Use this class to configure a CompositeTileSource. */ - class CompositeTileSourceOptions : public TileSourceOptions + class OSGEARTH_EXPORT CompositeTileSourceOptions : public TileSourceOptions { public: CompositeTileSourceOptions( const TileSourceOptions& options =TileSourceOptions() ); @@ -92,8 +93,7 @@ CompositeTileSourceOptions _options; bool _initialized; bool _dynamic; - - osg::ref_ptr _preCacheOp; + CompositeTileSourceOptions::ComponentVector _components; }; diff -Nru osgearth-2.0+dfsg/src/osgEarth/CompositeTileSource.cpp osgearth-2.1.1+dfsg/src/osgEarth/CompositeTileSource.cpp --- osgearth-2.0+dfsg/src/osgEarth/CompositeTileSource.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/CompositeTileSource.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -63,6 +63,7 @@ Component c; c._tileSourceInstance = source; c._imageLayerOptions = options; + _components.push_back( c ); } Config @@ -139,10 +140,6 @@ { if ( i->_imageLayerOptions->driver().isSet() ) i->_tileSourceOptions = i->_imageLayerOptions->driver().value(); - - ImageLayerPreCacheOperation* op = new ImageLayerPreCacheOperation(); - op->_processor.init( i->_imageLayerOptions.value(), true ); - _preCacheOp = op; } if ( i->_tileSourceOptions.isSet() ) @@ -176,23 +173,64 @@ i != _options._components.end(); ++i ) { - // check that this source is within the level bounds: - if (i->_imageLayerOptions->minLevel().value() > key.getLevelOfDetail() || - i->_imageLayerOptions->maxLevel().value() < key.getLevelOfDetail() ) - { - continue; - } + if ( progress && progress->isCanceled() ) + return 0L; TileSource* source = i->_tileSourceInstance->get(); if ( source ) { + + //TODO: This duplicates code in ImageLayer::isKeyValid. Maybe should move that to TileSource::isKeyValid instead + int minLevel = 0; + int maxLevel = INT_MAX; + if (i->_imageLayerOptions->minLevel().isSet()) + { + minLevel = i->_imageLayerOptions->minLevel().value(); + } + else if (i->_imageLayerOptions->minLevelResolution().isSet()) + { + minLevel = source->getProfile()->getLevelOfDetailForHorizResolution( i->_imageLayerOptions->minLevelResolution().value(), source->getPixelsPerTile()); + } + + if (i->_imageLayerOptions->maxLevel().isSet()) + { + maxLevel = i->_imageLayerOptions->maxLevel().value(); + } + else if (i->_imageLayerOptions->maxLevelResolution().isSet()) + { + maxLevel = source->getProfile()->getLevelOfDetailForHorizResolution( i->_imageLayerOptions->maxLevelResolution().value(), source->getPixelsPerTile()); + } + + + + + // check that this source is within the level bounds: + if (minLevel > key.getLevelOfDetail() || + maxLevel < key.getLevelOfDetail() ) + { + continue; + } + + + + + if ( !source->getBlacklist()->contains( key.getTileId() ) ) { //Only try to get data if the source actually has data if ( source->hasData( key ) ) { + osg::ref_ptr< ImageLayerPreCacheOperation > preCacheOp; + if ( i->_imageLayerOptions.isSet() ) + { + preCacheOp = new ImageLayerPreCacheOperation(); + preCacheOp->_processor.init( i->_imageLayerOptions.value(), true ); + } + + + ImageOpacityPair imagePair( - source->createImage( key, _preCacheOp.get(), progress ), + source->createImage( key, preCacheOp.get(), progress ), 1.0f ); //If the image is not valid and the progress was not cancelled, blacklist @@ -223,7 +261,11 @@ } } - if ( images.size() == 0 ) + if ( progress && progress->isCanceled() ) + { + return 0L; + } + else if ( images.size() == 0 ) { return 0L; } @@ -255,7 +297,7 @@ i != _options._components.end(); ++i) { - TileSource* source = i->_tileSourceInstance.get(); + TileSource* source = i->_tileSourceInstance->get(); //.get(); if ( source ) { osg::ref_ptr localOverrideProfile = overrideProfile; @@ -280,6 +322,13 @@ } _dynamic = _dynamic || source->isDynamic(); + + // gather extents + const DataExtentList& extents = source->getDataExtents(); + for( DataExtentList::const_iterator j = extents.begin(); j != extents.end(); ++j ) + { + getDataExtents().push_back( *j ); + } } } diff -Nru osgearth-2.0+dfsg/src/osgEarth/Config osgearth-2.1.1+dfsg/src/osgEarth/Config --- osgearth-2.0+dfsg/src/osgEarth/Config 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/Config 2011-11-04 19:44:43.000000000 +0000 @@ -21,27 +21,20 @@ #include #include +#include #include #include #if OSG_MIN_VERSION_REQUIRED(2,9,5) #include #endif #include +#include #include namespace osgEarth { typedef std::list ConfigSet; - /** - * Probably to be deprecated - */ - class Configurable : public osg::Referenced // marker interface class - { - public: - virtual class Config getConfig() const =0; - virtual void mergeConfig( const Config& conf ) =0; - }; // general-purpose name/value pair set. struct Properties : public std::map { @@ -61,10 +54,16 @@ { public: Config() { } + Config( const std::string& key ) : _key(key) { } + Config( const std::string& key, const std::string& value ) : _key( key ), _defaultValue( value ) { } - Config( const Config& rhs ) : _key(rhs._key), _defaultValue(rhs._defaultValue), _attrs(rhs._attrs), _children(rhs._children) { } + Config( const Config& rhs ) : _key(rhs._key), _defaultValue(rhs._defaultValue), _attrs(rhs._attrs), _children(rhs._children), _refMap(rhs._refMap), _uriContext(rhs._uriContext) { } + + /** Context for resolving relative URIs that occur in this Config */ + void setURIContext( const URIContext& value ); + const URIContext& uriContext() const { return _uriContext; } bool loadXML( std::istream& in ); @@ -88,7 +87,7 @@ std::string& attr( const std::string& name ) { return _attrs[name]; } - ConfigSet& children() { return _children; } + //ConfigSet& children() { return _children; } const ConfigSet& children() const { return _children; } const ConfigSet children( const std::string& key ) const { @@ -158,6 +157,7 @@ void add( const std::string& key, const std::string& value ) { _children.push_back( Config( key, value ) ); + _children.back().setURIContext( _uriContext ); } void addChild( const Config& conf ) { @@ -166,6 +166,7 @@ void add( const Config& conf ) { _children.push_back( conf ); + _children.back().setURIContext( _uriContext ); } void add( const std::string& key, const Config& conf ) { @@ -176,7 +177,7 @@ void add( const ConfigSet& set ) { for( ConfigSet::const_iterator i = set.begin(); i != set.end(); i++ ) - _children.push_back( *i ); + add( *i ); } template @@ -221,7 +222,8 @@ void update( const std::string& key, const std::string& value ) { remove(key); - _children.push_back( Config( key, value ) ); + add( Config(key, value) ); + //_children.push_back( Config( key, value ) ); } void updateChild( const Config& conf ) { @@ -230,7 +232,8 @@ void update( const Config& conf ) { remove(conf.key()); - _children.push_back( conf ); + add( conf ); + //_children.push_back( conf ); } void update( const std::string& key, const Config& conf ) { @@ -261,6 +264,10 @@ return osgEarth::as( r, fallback ); } + bool boolValue( bool fallback ) const { + return osgEarth::as( _defaultValue, fallback ); + } + // populates the output value iff the Config exists. template bool getIfSet( const std::string& key, optional& output ) const { @@ -311,11 +318,32 @@ std::string toHashString() const; + /** support for conveying non-serializable objects in a Config (in memory only) */ + + typedef std::map > RefMap; + + void addNonSerializable( const std::string& key, osg::Referenced* obj ) { + _refMap[key] = obj; + } + + void updateNonSerializable( const std::string& key, osg::Referenced* obj ) { + _refMap[key] = obj; + } + + template + X* getNonSerializable( const std::string& key ) const { + RefMap::const_iterator i = _refMap.find(key); + return i == _refMap.end() ? 0 : dynamic_cast( i->second.get() ); + } + protected: std::string _key; std::string _defaultValue; Properties _attrs; - ConfigSet _children; + ConfigSet _children; + URIContext _uriContext; + + RefMap _refMap; }; // specialization for Config @@ -328,7 +356,6 @@ } } - // specialization for Config template<> inline void Config::updateIfSet( const std::string& key, const optional& opt ) { if ( opt.isSet() ) { @@ -339,7 +366,6 @@ } } - // specialization for Config. template<> inline bool Config::getIfSet( const std::string& key, optional& output ) const { if ( hasChild( key ) ) { @@ -350,6 +376,32 @@ return false; } + // specializations for URI: + template <> inline + void Config::addIfSet( const std::string& key, const optional& opt ) { + if ( opt.isSet() ) { + add( Config(key, opt->base()) ); + } + } + + template<> inline + void Config::updateIfSet( const std::string& key, const optional& opt ) { + if ( opt.isSet() ) { + remove(key); + add( Config(key, opt->base()) ); + } + } + + template<> inline + bool Config::getIfSet( const std::string& key, optional& output ) const { + if ( hasValue( key ) ) { + output = URI( value(key), _uriContext ); + return true; + } + else + return false; + } + /** * Base class for all serializable options classes. */ diff -Nru osgearth-2.0+dfsg/src/osgEarth/Config.cpp osgearth-2.1.1+dfsg/src/osgEarth/Config.cpp --- osgearth-2.0+dfsg/src/osgEarth/Config.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/Config.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -29,6 +29,18 @@ return _emptyConfig; } +void +Config::setURIContext( const URIContext& context ) +{ + _uriContext = context; + for( ConfigSet::iterator i = _children.begin(); i != _children.end(); i++ ) + { + i->setURIContext( context.add(i->_uriContext) ); + //URI newURI( i->uriContext(), context ); + //i->setURIContext( *newURI ); + } +} + bool Config::loadXML( std::istream& in ) { diff -Nru osgearth-2.0+dfsg/src/osgEarth/Cube osgearth-2.1.1+dfsg/src/osgEarth/Cube --- osgearth-2.0+dfsg/src/osgEarth/Cube 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/Cube 2011-11-04 19:44:43.000000000 +0000 @@ -125,8 +125,8 @@ // This SRS uses a WGS84 lat/long SRS under the hood for reprojection. So we need the // pre/post transforms to move from cube to latlong and back. - virtual bool preTransform(double& x, double& y, void* context) const; - virtual bool postTransform(double& x, double& y, void* context) const; + virtual bool preTransform(double& x, double& y, double& z, void* context) const; + virtual bool postTransform(double& x, double& y, double& z, void* context) const; virtual bool transformExtent( const SpatialReference* to_srs, diff -Nru osgearth-2.0+dfsg/src/osgEarth/Cube.cpp osgearth-2.1.1+dfsg/src/osgEarth/Cube.cpp --- osgearth-2.0+dfsg/src/osgEarth/Cube.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/Cube.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -426,7 +426,7 @@ } bool -CubeSpatialReference::preTransform(double& x, double& y, void* context) const +CubeSpatialReference::preTransform(double& x, double& y, double& z, void* context) const { // Convert the incoming points from cube => face => lat/long. int face; @@ -449,7 +449,7 @@ } bool -CubeSpatialReference::postTransform(double& x, double& y, void* context) const +CubeSpatialReference::postTransform(double& x, double& y, double& z, void* context) const { //Convert the incoming points from lat/lon back to face coordinates int face; @@ -522,8 +522,8 @@ if ( crosses_pole ) // full x extent. { bool north = face == 4; // else south - to_srs->getGeographicSRS()->transform( -180.0, north? 45.0 : -90.0, to_srs, in_out_xmin, in_out_ymin ); - to_srs->getGeographicSRS()->transform( 180.0, north? 90.0 : -45.0, to_srs, in_out_xmax, in_out_ymax ); + to_srs->getGeographicSRS()->transform2D( -180.0, north? 45.0 : -90.0, to_srs, in_out_xmin, in_out_ymin ); + to_srs->getGeographicSRS()->transform2D( 180.0, north? 90.0 : -45.0, to_srs, in_out_xmax, in_out_ymax ); } else @@ -563,8 +563,8 @@ } else { - bool ok1 = transform( lonmin, latmin, to_srs, in_out_xmin, in_out_ymin, context ); - bool ok2 = transform( lonmax, latmax, to_srs, in_out_xmax, in_out_ymax, context ); + bool ok1 = transform2D( lonmin, latmin, to_srs, in_out_xmin, in_out_ymin, context ); + bool ok2 = transform2D( lonmax, latmax, to_srs, in_out_xmax, in_out_ymax, context ); ok = ok1 && ok2; } } @@ -683,7 +683,7 @@ // and south polar tile regions. for( int face=0; face<6; ++face ) { - GeoExtent partExtent_gcs = _faceExtent_gcs[face].intersectionSameSRS( remoteExtent_gcs ); + GeoExtent partExtent_gcs = _faceExtent_gcs[face].intersectionSameSRS( remoteExtent_gcs.bounds() ); if ( partExtent_gcs.isValid() ) { GeoExtent partExtent = transformGcsExtentOnFace( partExtent_gcs, face ); diff -Nru osgearth-2.0+dfsg/src/osgEarth/DrapeableNode osgearth-2.1.1+dfsg/src/osgEarth/DrapeableNode --- osgearth-2.0+dfsg/src/osgEarth/DrapeableNode 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/DrapeableNode 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,71 @@ +/* -*-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 + */ + +#ifndef OSGEARTH_DRAPEABLE_NODE_H +#define OSGEARTH_DRAPEABLE_NODE_H 1 + +#include +#include + +namespace osgEarth +{ + /** + * Base class for a node that can be "draped" on the MapNode terrain + * using the overlay decorator. + */ + class OSGEARTH_EXPORT DrapeableNode : public osg::Group + { + public: + DrapeableNode( MapNode* mapNode =0L, bool draped =true ); + + /** + * The node to drape (or not) on the MapNode terrain. + */ + void setNode( osg::Node* node ); + osg::Node* getNode() const { return _node.get(); } + + /** + * Whether to drape the node content on the mapnode terrain. + */ + void setDraped( bool value ); + bool getDraped() const { return _draped; } + + public: + virtual void traverse( osg::NodeVisitor& nv ); + + protected: + osg::observer_ptr _mapNode; + + private: + bool _draped; + osg::ref_ptr _node; + osg::ref_ptr _nodeContainer; + + int _dirty; + bool _newDraped; + osg::ref_ptr _newNode; + + void applyChanges(); + void setNodeImpl( osg::Node* ); + void setDrapedImpl( bool ); + }; + +} // namespace osgEarth + +#endif // OSGEARTH_DRAPEABLE_NODE_H diff -Nru osgearth-2.0+dfsg/src/osgEarth/DrapeableNode.cpp osgearth-2.1.1+dfsg/src/osgEarth/DrapeableNode.cpp --- osgearth-2.0+dfsg/src/osgEarth/DrapeableNode.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/DrapeableNode.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,155 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ + +#include +#include +#include + +using namespace osgEarth; + +DrapeableNode::DrapeableNode( MapNode* mapNode, bool draped ) : +_mapNode ( mapNode ), +_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. + _nodeContainer = new osg::Group(); + _nodeContainer->setCullCallback( new CullNodeByFrameNumber() ); + _nodeContainer->setStateSet( this->getOrCreateStateSet() ); // share the stateset +} + +void +DrapeableNode::applyChanges() +{ + if ( _newDraped != _draped ) + { + setDrapedImpl( _newDraped ); + } + + if ( _newNode.valid() ) + { + setNodeImpl( _newNode.get() ); + _newNode = 0L; + } +} + +void +DrapeableNode::setNode( osg::Node* node ) +{ + _newNode = node; + if ( !_dirty ) + { + _dirty = true; + ADJUST_UPDATE_TRAV_COUNT( this, 1 ); + } +} + +void +DrapeableNode::setDraped( bool draped ) +{ + _newDraped = draped; + if ( !_dirty ) + { + _dirty = true; + ADJUST_UPDATE_TRAV_COUNT( this, 1 ); + } +} + +void +DrapeableNode::setNodeImpl( osg::Node* node ) +{ + if ( _node.valid() ) + { + if ( _draped && _mapNode.valid() ) + { + _mapNode->getOverlayGroup()->removeChild( _nodeContainer.get() ); + _mapNode->updateOverlayGraph(); + } + else + { + this->removeChild( _node.get() ); + } + } + + _node = node; + _nodeContainer->removeChildren( 0, _nodeContainer->getNumChildren() ); + + if ( _node.valid() ) + { + if ( _draped && _mapNode.valid() ) + { + _nodeContainer->addChild( _node.get() ); + _mapNode->getOverlayGroup()->addChild( _nodeContainer.get() ); + _mapNode->updateOverlayGraph(); + } + else + { + this->addChild( _node.get() ); + } + } +} + +void +DrapeableNode::setDrapedImpl( bool value ) +{ + if ( _draped != value ) + { + osg::ref_ptr save = _node.get(); + if ( save.valid() ) + setNode( 0L ); + + _draped = value; + + if ( save.valid() ) + setNode( save.get() ); + } +} + + +void +DrapeableNode::traverse( osg::NodeVisitor& nv ) +{ + if ( _draped && nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR && _node.valid() && _mapNode.valid() ) + { + CullNodeByFrameNumber* cb = static_cast(_nodeContainer->getCullCallback()); + cb->_frame = nv.getFrameStamp()->getFrameNumber(); + } + + if ( nv.getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR ) + { + if ( _dirty ) + { + applyChanges(); + + _dirty = false; + ADJUST_UPDATE_TRAV_COUNT( this, -1 ); + } + + // traverse the subgraph + if ( _nodeContainer.valid() && this->getNumChildrenRequiringUpdateTraversal() > 0 ) + { + _nodeContainer->accept( nv ); + } + } + + osg::Group::traverse( nv ); +} diff -Nru osgearth-2.0+dfsg/src/osgEarth/ECEF osgearth-2.1.1+dfsg/src/osgEarth/ECEF --- osgearth-2.0+dfsg/src/osgEarth/ECEF 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/ECEF 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,71 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ + +#ifndef OSGEARTH_ECEF_H +#define OSGEARTH_ECEF_H 1 + +#include +#include +#include + +namespace osgEarth +{ + struct OSGEARTH_EXPORT ECEF + { + /** + * Creates a "localization" matrix for double-precision geocentric + * coordinates. The matrix is ceneterd at the specified ECEF reference point. + */ + static osg::Matrixd createInverseRefFrame( + const osg::Vec3d& ecefRefPoint ); + + /** + * Transforms a point into ECEF coordinates, localizes it with + * the provided world2local matrix, and puts the result in "output". + */ + static void transformAndLocalize( + const osg::Vec3d& input, + osg::Vec3d& output, + const SpatialReference* srs, + const osg::Matrixd& world2local =osg::Matrixd() ); + + /** + * Transforms the points in "input" to ECEF coordinates, localizes them with + * the provided world2local matrix, and puts the result in "output". + */ + static void transformAndLocalize( + const std::vector& input, + osg::Vec3Array* output, + const SpatialReference* srs, + const osg::Matrixd& world2local =osg::Matrixd() ); + + /** + * Transforms a point to ECEF, and at the same time returns a quaternion that + * rotates the point into the local tangent place at that point. + */ + static void transformAndGetRotationMatrix( + const SpatialReference* input_srs, + const osg::Vec3d& input, + osg::Vec3d& out_ecef_point, + osg::Matrixd& out_rotation ); + + }; +} + +#endif // OSGEARTH_ECEF_H diff -Nru osgearth-2.0+dfsg/src/osgEarth/ECEF.cpp osgearth-2.1.1+dfsg/src/osgEarth/ECEF.cpp --- osgearth-2.0+dfsg/src/osgEarth/ECEF.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/ECEF.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,121 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ + +#include + +using namespace osgEarth; + +#define LC "[ECEF] " + +// -------------------------------------------------------------------------- + + +osg::Matrixd +ECEF::createInverseRefFrame( const osg::Vec3d& input ) +{ + // convert to geocentric first: + double X = input.x(), Y = input.y(), Z = input.z(); + + osg::Matrixd localToWorld; + localToWorld.makeTranslate(X,Y,Z); + + // normalize X,Y,Z + double inverse_length = 1.0/sqrt(X*X + Y*Y + Z*Z); + + X *= inverse_length; + Y *= inverse_length; + Z *= inverse_length; + + double length_XY = sqrt(X*X + Y*Y); + double inverse_length_XY = 1.0/length_XY; + + // Vx = |(-Y,X,0)| + localToWorld(0,0) = -Y*inverse_length_XY; + localToWorld(0,1) = X*inverse_length_XY; + localToWorld(0,2) = 0.0; + + // Vy = /(-Z*X/(sqrt(X*X+Y*Y), -Z*Y/(sqrt(X*X+Y*Y),sqrt(X*X+Y*Y))| + double Vy_x = -Z*X*inverse_length_XY; + double Vy_y = -Z*Y*inverse_length_XY; + double Vy_z = length_XY; + inverse_length = 1.0/sqrt(Vy_x*Vy_x + Vy_y*Vy_y + Vy_z*Vy_z); + localToWorld(1,0) = Vy_x*inverse_length; + localToWorld(1,1) = Vy_y*inverse_length; + localToWorld(1,2) = Vy_z*inverse_length; + + // Vz = (X,Y,Z) + localToWorld(2,0) = X; + localToWorld(2,1) = Y; + localToWorld(2,2) = Z; + + return localToWorld; +} + +void +ECEF::transformAndLocalize(const osg::Vec3d& input, + osg::Vec3d& output, + const SpatialReference* srs, + const osg::Matrixd& world2local) +{ + osg::Vec3d ecef; + srs->transformToECEF( input, ecef ); + output = ecef * world2local; +} + + +void +ECEF::transformAndLocalize(const std::vector& input, + osg::Vec3Array* output, + const SpatialReference* srs, + const osg::Matrixd& world2local ) +{ + output->reserve( output->size() + input.size() ); + for( std::vector::const_iterator i = input.begin(); i != input.end(); ++i ) + { + osg::Vec3d ecef; + srs->transformToECEF( *i, ecef ); + output->push_back( ecef * world2local ); + } +} + +void +ECEF::transformAndGetRotationMatrix(const SpatialReference* srs, + const osg::Vec3d& input, + osg::Vec3d& out_point, + osg::Matrixd& out_rotation ) +{ + osg::Vec3d geod_point; + if ( !srs->isGeographic() ) + srs->transform( input, srs->getGeographicSRS(), geod_point ); + else + geod_point = input; + + const osg::EllipsoidModel* em = srs->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() ), + out_rotation ); +} diff -Nru osgearth-2.0+dfsg/src/osgEarth/ElevationLayer osgearth-2.1.1+dfsg/src/osgEarth/ElevationLayer --- osgearth-2.0+dfsg/src/osgEarth/ElevationLayer 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/ElevationLayer 2011-11-04 19:44:43.000000000 +0000 @@ -92,8 +92,8 @@ ElevationLayer( const ElevationLayerOptions& options, TileSource* tileSource ); /** Gets the initialization options with which the layer was created. */ - const ElevationLayerOptions& getElevationLayerOptions() const { return _options; } - virtual const TerrainLayerOptions& getTerrainLayerOptions() const { return _options; } + const ElevationLayerOptions& getElevationLayerOptions() const { return _runtimeOptions; } + virtual const TerrainLayerOptions& getTerrainLayerOptions() const { return _runtimeOptions; } /** Adds a property notification callback to this layer */ void addCallback( ElevationLayerCallback* cb ); @@ -109,7 +109,7 @@ * extents of that TileKey. */ osg::HeightField* createHeightField( - const TileKey& key, + const TileKey& key, ProgressCallback* progress =0L ); protected: @@ -121,7 +121,7 @@ virtual void initTileSource(); private: - ElevationLayerOptions _options; + ElevationLayerOptions _runtimeOptions; ElevationLayerCallbackList _callbacks; virtual void fireCallback( TerrainLayerCallbackMethodPtr method ); diff -Nru osgearth-2.0+dfsg/src/osgEarth/ElevationLayer.cpp osgearth-2.1.1+dfsg/src/osgEarth/ElevationLayer.cpp --- osgearth-2.0+dfsg/src/osgEarth/ElevationLayer.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/ElevationLayer.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -97,22 +97,22 @@ //------------------------------------------------------------------------ ElevationLayer::ElevationLayer( const ElevationLayerOptions& options ) : -TerrainLayer(), -_options( options ) +TerrainLayer ( &_runtimeOptions ), +_runtimeOptions( options ) { init(); } ElevationLayer::ElevationLayer( const std::string& name, const TileSourceOptions& driverOptions ) : -TerrainLayer(), -_options( ElevationLayerOptions(name, driverOptions) ) +TerrainLayer ( &_runtimeOptions ), +_runtimeOptions( ElevationLayerOptions(name, driverOptions) ) { init(); } ElevationLayer::ElevationLayer( const ElevationLayerOptions& options, TileSource* tileSource ) : -TerrainLayer( tileSource ), -_options( options ) +TerrainLayer ( &_runtimeOptions, tileSource ), +_runtimeOptions( options ) { init(); } @@ -120,8 +120,7 @@ void ElevationLayer::init() { - //TODO: probably should graduate this to the superclass. - _actualEnabled = _options.enabled().value(); + _tileSize = 32; } std::string @@ -176,8 +175,8 @@ // call superclass first. TerrainLayer::initTileSource(); - if ( getTileSource() ) - _preCacheOp = new ElevationLayerPreCacheOperation( getTileSource() ); + if ( _tileSource.valid() ) + _preCacheOp = new ElevationLayerPreCacheOperation( _tileSource.get() ); } GeoHeightField @@ -232,24 +231,24 @@ return 0L; } - if ( !_actualCacheOnly && !getTileSource() ) + if ( !isCacheOnly() && !getTileSource() ) { OE_WARN << LC << "Error: ElevationLayer does not have a valid TileSource, cannot create heightfield " << std::endl; return 0L; } //Write the layer properties if they haven't been written yet. Heightfields are always stored in the map profile. - if (!_cacheProfile.valid() && _cache.valid() && _options.cacheEnabled() == true && _tileSource.valid()) + if (!_cacheProfile.valid() && _cache.valid() && _runtimeOptions.cacheEnabled() == true && _tileSource.valid()) { _cacheProfile = mapProfile; if ( _tileSource->isOK() ) { - _cache->storeProperties( _cacheSpec, _cacheProfile, _tileSource->getPixelsPerTile() ); + _cache->storeProperties( _cacheSpec, _cacheProfile.get(), _tileSource->getPixelsPerTile() ); } } //See if we can get it from the cache. - if (_cache.valid() && _options.cacheEnabled() == true ) + if (_cache.valid() && _runtimeOptions.cacheEnabled() == true ) { osg::ref_ptr cachedHF; if ( _cache->getHeightField( key, _cacheSpec, cachedHF ) ) @@ -262,7 +261,7 @@ } //in cache-only mode, if the cache fetch failed, bail out. - if ( result == 0L && _actualCacheOnly ) + if ( result == 0L && isCacheOnly() ) { return 0L; } @@ -355,7 +354,7 @@ } //Write the result to the cache. - if (result && _cache.valid() && _options.cacheEnabled() == true ) + if (result && _cache.valid() && _runtimeOptions.cacheEnabled() == true ) { _cache->setHeightField( key, _cacheSpec, result ); } diff -Nru osgearth-2.0+dfsg/src/osgEarth/ElevationQuery osgearth-2.1.1+dfsg/src/osgEarth/ElevationQuery --- osgearth-2.0+dfsg/src/osgEarth/ElevationQuery 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/ElevationQuery 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,194 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#ifndef OSGEARTH_ELEVATION_QUERY_H +#define OSGEARTH_ELEVATION_QUERY_H 1 + +#include +#include +#include + +namespace osgEarth +{ + /** + * ElevationQuery (EQ) lets you query the elevation at any point on a map. + * + * Rather than intersecting with a loaded scene graph, EQ 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. + * + * 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. + */ + class OSGEARTH_EXPORT ElevationQuery + { + 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. + */ + ElevationQuery( const Map* map ); + ElevationQuery( const MapFrame& mapFrame ); + + /** + * Gets the terrain elevation at a point, given a terrain resolution. + * + * @param point + * Map coordinates for which to query elevation. + * @param pointSRS + * Spatial reference of "point" and "desiredResolution" If this is NULL, assume that + * the input values are expressed in terms of the Map's SRS. + * @param out_elevation + * Stores the elevation result in this variable upon success. + * @param desiredResolution + * Optimal resolution of elevation data to use for the query (if available). + * Pass in 0 (zero) to use the best available resolution. + * @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( + const osg::Vec3d& point, + const SpatialReference* pointSRS, + double& out_elevation, + double desiredResolution =0.0, + double* out_actualResolution =0L ); + + /** + * Gets elevations for a whole array of points, storing the result in the + * "z" element. If "ignoreZ" is false, the new Z value will be offset by + * the original Z value. + */ + bool getElevations( + std::vector& points, + const SpatialReference* pointsSRS, + bool ignoreZ = true, + double desiredResolution =0.0 ); + + /** + * Gets elevations for a whole array of points, storing the results in the + * "out_elevations" vector. + */ + bool getElevations( + const std::vector& points, + const SpatialReference* pointsSRS, + std::vector& out_elevations, + double desiredResolution = 0.0 ); + + /** + * 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; + unsigned _maxCacheSize; + int _tileSize; + unsigned int _maxDataLevel; + int _maxLevelOverride; + Technique _technique; + ElevationInterpolation _interpolation; + + typedef LRUCache< TileKey, osg::ref_ptr > TileCache; + TileCache _tileCache; + + + private: + void postCTOR(); + void sync(); + + bool getElevationImpl( + const osg::Vec3d& point, + const SpatialReference* pointSRS, + double& out_elevation, + double desiredResolution, + double* out_actualResolution =0L ); + }; + +} // namespace osgEarth + +#endif // OSGEARTH_ELEVATION_QUERY_H diff -Nru osgearth-2.0+dfsg/src/osgEarth/ElevationQuery.cpp osgearth-2.1.1+dfsg/src/osgEarth/ElevationQuery.cpp --- osgearth-2.0+dfsg/src/osgEarth/ElevationQuery.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/ElevationQuery.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,302 @@ +#include +#include +#include +#include +#include +#include + +#define LC "[ElevationQuery] " + +using namespace osgEarth; +using namespace OpenThreads; + +ElevationQuery::ElevationQuery( const Map* map ) : +_mapf( map, Map::ELEVATION_LAYERS ) +{ + postCTOR(); +} + +ElevationQuery::ElevationQuery( const MapFrame& mapFrame ) : +_mapf( mapFrame ) +{ + postCTOR(); +} + +void +ElevationQuery::postCTOR() +{ + _tileSize = 0; + _maxDataLevel = 0; + _technique = TECHNIQUE_PARAMETRIC; + _interpolation = INTERP_BILINEAR; + _maxLevelOverride = -1; + + // Limit the size of the cache we'll use to cache heightfields. This is an + // LRU cache. + _tileCache.setMaxSize( 50 ); +} + +void +ElevationQuery::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; + } + } +} + +ElevationQuery::Technique +ElevationQuery::getTechnique() const +{ + return _technique; +} + +void +ElevationQuery::setTechnique( ElevationQuery::Technique technique ) +{ + _technique = technique; +} + +void +ElevationQuery::setMaxTilesToCache( int value ) +{ + _tileCache.setMaxSize( value ); +} + +int +ElevationQuery::getMaxTilesToCache() const +{ + return _tileCache.getMaxSize(); +} + +void +ElevationQuery::setInterpolation( ElevationInterpolation interp) +{ + _interpolation = interp; +} + +ElevationInterpolation +ElevationQuery::getElevationInterpolation() const +{ + return _interpolation; +} + +bool +ElevationQuery::getElevation(const osg::Vec3d& point, + const SpatialReference* pointSRS, + double& out_elevation, + double desiredResolution, + double* out_actualResolution) +{ + sync(); + return getElevationImpl( point, pointSRS, out_elevation, desiredResolution, out_actualResolution ); +} + +bool +ElevationQuery::getElevations(std::vector& points, + const SpatialReference* pointsSRS, + bool ignoreZ, + double desiredResolution ) +{ + sync(); + for( osg::Vec3dArray::iterator i = points.begin(); i != points.end(); ++i ) + { + double elevation; + double z = (*i).z(); + if ( getElevationImpl( *i, pointsSRS, elevation, desiredResolution ) ) + { + (*i).z() = ignoreZ ? elevation : elevation + z; + } + } + return true; +} + +bool +ElevationQuery::getElevations(const std::vector& points, + const SpatialReference* pointsSRS, + std::vector& out_elevations, + double desiredResolution ) +{ + sync(); + for( osg::Vec3dArray::const_iterator i = points.begin(); i != points.end(); ++i ) + { + double elevation; + if ( getElevationImpl( *i, pointsSRS, elevation, desiredResolution ) ) + { + out_elevations.push_back( elevation ); + } + } + return true; +} + +bool +ElevationQuery::getElevationImpl(const osg::Vec3d& point, + const SpatialReference* pointSRS, + double& out_elevation, + double desiredResolution, + double* out_actualResolution) +{ + 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 = desiredResolution > 0.0 + ? _mapf.getProfile()->getLevelOfDetailForHorizResolution( desiredResolution, _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: + osg::Vec3d mapPoint = point; + if ( pointSRS && !pointSRS->isEquivalentTo( _mapf.getProfile()->getSRS() ) ) + { + if ( !pointSRS->transform2D( point.x(), point.y(), _mapf.getProfile()->getSRS(), mapPoint.x(), mapPoint.y() ) ) + { + OE_WARN << LC << "Fail: coord transform failed" << std::endl; + return false; + } + } + + osg::ref_ptr hf; + osg::ref_ptr tile; + + // get the tilekey corresponding to the tile we need: + TileKey key = _mapf.getProfile()->createTileKey( mapPoint.x(), mapPoint.y(), bestAvailLevel ); + if ( !key.valid() ) + { + OE_WARN << LC << "Fail: coords fall outside map" << std::endl; + return false; + } + + // Check the tile cache. Note that the TileSource already likely has a MemCache + // attached to it. We employ a secondary cache here for a couple reasons. One, this + // cache will store not only the heightfield, but also the tesselated tile in the event + // that we're using GEOMETRIC mode. Second, since the call the getHeightField can + // fallback on a lower resolution, this cache will hold the final resolution heightfield + // instead of trying to fetch the higher resolution one each tiem. + + TileCache::Record record = _tileCache.get( key ); + if ( record.valid() ) + tile = record.value().get(); + + // if we found it, make sure it has a heightfield in it: + if ( tile.valid() ) + { + osgTerrain::HeightFieldLayer* layer = dynamic_cast(tile->getElevationLayer()); + if ( layer ) + hf = layer->getHeightField(); + + if ( !hf.valid() ) + tile = 0L; + } + + // if we didn't find it (or it didn't have heightfield data), build it. + if ( !tile.valid() ) + { + // generate the heightfield corresponding to the tile key, automatically falling back + // on lower resolution if necessary: + _mapf.getHeightField( key, true, hf, 0L, _interpolation ); + + // bail out if we could not make a heightfield a all. + if ( !hf.valid() ) + { + OE_WARN << LC << "Unable to create heightfield for key " << key.str() << std::endl; + return false; + } + + // All this stuff is requires for GEOMETRIC mode. An optimization would be to + // defer this so that PARAMETRIC mode doesn't waste time + 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. + _tileCache.insert( key, tile.get() ); + } + + OE_DEBUG << LC << "LRU Cache, hit ratio = " << _tileCache.getStats()._hitRatio << std::endl; + + // see what the actual resolution of the heightfield is. + if ( out_actualResolution ) + *out_actualResolution = (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(), mapPoint.x(), mapPoint.y(), extent.xMin(), extent.yMin(), xInterval, yInterval ); + return true; + } + else // ( _technique == TECHNIQUE_GEOMETRIC ) + { + osg::Vec3d start, end, zero; + + if ( _mapf.getMapInfo().isGeocentric() ) + { + const SpatialReference* mapSRS = _mapf.getProfile()->getSRS(); + + mapSRS->transformToECEF( osg::Vec3d(mapPoint.y(), mapPoint.x(), 50000.0), start ); + mapSRS->transformToECEF( osg::Vec3d(mapPoint.y(), mapPoint.x(), -50000.0), end ); + mapSRS->transformToECEF( osg::Vec3d(mapPoint.y(), mapPoint.x(), 0.0), zero ); + } + else // PROJECTED + { + start.set( mapPoint.x(), mapPoint.y(), 50000.0 ); + end.set ( mapPoint.x(), mapPoint.y(), -50000.0 ); + zero.set ( mapPoint.x(), mapPoint.y(), 0.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_DEBUG << LC << "No intersection" << std::endl; + return false; + } +} diff -Nru osgearth-2.0+dfsg/src/osgEarth/FileUtils osgearth-2.1.1+dfsg/src/osgEarth/FileUtils --- osgearth-2.0+dfsg/src/osgEarth/FileUtils 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/FileUtils 2011-11-04 19:44:43.000000000 +0000 @@ -27,7 +27,7 @@ /** * Determines whether a given filename is a relative path */ - extern OSGEARTH_EXPORT bool isRelativePath(const std::string &fileName); + extern OSGEARTH_EXPORT bool isRelativePath(const std::string& fileName); /** * Gets the full path of a file relative to a file @@ -41,12 +41,14 @@ * @returns * The full path */ - extern OSGEARTH_EXPORT std::string getFullPath(const std::string &relativeTo, const std::string &relativePath); + extern OSGEARTH_EXPORT std::string getFullPath( + const std::string& relativeTo, + const std::string& relativePath ); /** * Gets whether or not the given path contains a zip file within the path */ - extern OSGEARTH_EXPORT bool isZipPath(const std::string &path); + extern OSGEARTH_EXPORT bool isZipPath(const std::string& path); } #endif diff -Nru osgearth-2.0+dfsg/src/osgEarth/FileUtils.cpp osgearth-2.1.1+dfsg/src/osgEarth/FileUtils.cpp --- osgearth-2.0+dfsg/src/osgEarth/FileUtils.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/FileUtils.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -53,6 +53,10 @@ return relativePath; } + //If they didn't specify a relative path, just return the relativeTo + if (relativePath.empty()) return relativeTo; + + //Note: Modified from VPB //Concatinate the paths together @@ -105,4 +109,4 @@ bool osgEarth::isZipPath(const std::string &path) { return (path.find(".zip") != std::string::npos); -} \ No newline at end of file +} diff -Nru osgearth-2.0+dfsg/src/osgEarth/FindNode osgearth-2.1.1+dfsg/src/osgEarth/FindNode --- osgearth-2.0+dfsg/src/osgEarth/FindNode 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/FindNode 2011-11-04 19:44:43.000000000 +0000 @@ -23,7 +23,7 @@ namespace osgEarth { /** - * Visitor that located a node by its type + * Visitor that locates a node by its type */ template class FindTopMostNodeOfTypeVisitor : public osg::NodeVisitor diff -Nru osgearth-2.0+dfsg/src/osgEarth/GeoData osgearth-2.1.1+dfsg/src/osgEarth/GeoData --- osgearth-2.0+dfsg/src/osgEarth/GeoData 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/GeoData 2011-11-04 19:44:43.000000000 +0000 @@ -44,11 +44,18 @@ double height() const; double depth() const; bool contains(double x, double y ) const; + bool contains(const Bounds& rhs) const; Bounds unionWith(const Bounds& rhs) const; + Bounds intersectionWith(const Bounds& rhs) const; void expandBy( double x, double y ); void expandBy( double x, double y, double z ); void expandBy( const Bounds& rhs ); osg::Vec2d center2d() const; + double radius2d() const; + std::string toString() const; + bool isValid() const; + bool isEmpty() const { return !isValid(); } + void transform( const SpatialReference* fromSRS, const SpatialReference* toSRS ); }; /** @@ -119,7 +126,13 @@ bool splitAcrossDateLine( GeoExtent& first, GeoExtent& second ) const; /** - * Returns this extent transformed into another spatial reference. + * Returns this extent transformed into another spatial reference. + * + * NOTE! It is possible that the target SRS will not be able to accomadate the + * extents of the source SRS. (For example, transforming a full WGS84 extent + * to Mercator will resultin an error since Mercator does not cover the entire + * globe.) Consider using Profile:clampAndTransformExtent() instead of using + * this method directly. */ GeoExtent transform( const SpatialReference* to_srs ) const; @@ -135,6 +148,11 @@ bool contains(double x, double y, const SpatialReference* xy_srs =0L) const; /** + * Returns true if this extent fully contains the target bounds. + */ + bool contains( const Bounds& rhs ) const; + + /** * Returns TRUE if this extent intersects another extent. */ bool intersects( const GeoExtent& rhs ) const; @@ -152,13 +170,13 @@ * Grow this extent to include the specified GeoExtent (which is assumed to be * in the extent's SRS. */ - void expandToInclude( const GeoExtent& rhs ); + void expandToInclude( const Bounds& rhs ); /** * Intersect this extent with another extent in the same SRS and return the * result. */ - GeoExtent intersectionSameSRS( const GeoExtent& rhs ) const; + GeoExtent intersectionSameSRS( const Bounds& rhs ) const; /** * Returns a human-readable string containing the extent data (without the SRS) diff -Nru osgearth-2.0+dfsg/src/osgEarth/GeoData.cpp osgearth-2.1.1+dfsg/src/osgEarth/GeoData.cpp --- osgearth-2.0+dfsg/src/osgEarth/GeoData.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/GeoData.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -50,28 +50,50 @@ //nop } +bool +Bounds::isValid() const +{ + return xMin() <= xMax() && yMin() <= yMax(); +} + bool -Bounds::contains(double x, double y ) const { - return x >= xMin() && x <= xMax() && y >= yMin() && y <= yMax(); +Bounds::contains(double x, double y ) const +{ + return + isValid() && + x >= xMin() && x <= xMax() && y >= yMin() && y <= yMax(); +} + +bool +Bounds::contains(const Bounds& rhs) const +{ + return + isValid() && rhs.isValid() && + xMin() <= rhs.xMin() && xMax() >= rhs.xMax() && + yMin() <= rhs.yMin() && yMax() >= rhs.yMax(); } void -Bounds::expandBy( double x, double y ) { +Bounds::expandBy( double x, double y ) +{ osg::BoundingBoxImpl::expandBy( x, y, 0 ); } void -Bounds::expandBy( double x, double y, double z ) { +Bounds::expandBy( double x, double y, double z ) +{ osg::BoundingBoxImpl::expandBy( x, y, z ); } void -Bounds::expandBy( const Bounds& rhs ) { +Bounds::expandBy( const Bounds& rhs ) +{ osg::BoundingBoxImpl::expandBy( rhs ); } Bounds -Bounds::unionWith(const Bounds& rhs) const { +Bounds::unionWith(const Bounds& rhs) const +{ if ( valid() && !rhs.valid() ) return *this; if ( !valid() && rhs.valid() ) return rhs; @@ -87,6 +109,27 @@ return u; } +Bounds +Bounds::intersectionWith(const Bounds& rhs) const +{ + if ( valid() && !rhs.valid() ) return *this; + if ( !valid() && rhs.valid() ) return rhs; + + if ( this->contains(rhs) ) return rhs; + if ( rhs.contains(*this) ) return *this; + + if ( !intersects(rhs) ) return Bounds(); + + double xmin, xmax, ymin, ymax; + + xmin = ( xMin() > rhs.xMin() && xMin() < rhs.xMax() ) ? xMin() : rhs.xMin(); + xmax = ( xMax() > rhs.xMin() && xMax() < rhs.xMax() ) ? xMax() : rhs.xMax(); + ymin = ( yMin() > rhs.yMin() && yMin() < rhs.yMax() ) ? yMin() : rhs.yMin(); + ymax = ( yMax() > rhs.yMin() && yMax() < rhs.yMax() ) ? yMax() : rhs.yMax(); + + return Bounds(xmin, ymin, xmax, ymax); +} + double Bounds::width() const { return xMax()-xMin(); @@ -108,6 +151,24 @@ return osg::Vec2d( c.x(), c.y() ); } +double +Bounds::radius2d() const { + return (center2d() - osg::Vec2d(xMin(),yMin())).length(); +} + +std::string +Bounds::toString() const { + std::stringstream buf; + buf << "(" << xMin() << "," << yMin() << " => " << xMax() << "," << yMax() << ")"; + std::string result = buf.str(); + return result; +} + +void +Bounds::transform( const SpatialReference* from, const SpatialReference* to ) +{ + from->transformExtent( to, _min.x(), _min.y(), _max.x(), _max.y() ); +} /*************************************************************/ @@ -241,7 +302,7 @@ GeoExtent GeoExtent::transform( const SpatialReference* to_srs ) const { - if ( isValid() && to_srs ) + if ( _srs.valid() && to_srs ) { double xmin = _xmin, ymin = _ymin; double xmax = _xmax, ymax = _ymax; @@ -252,7 +313,7 @@ } } - return GeoExtent(); // invalid + return GeoExtent::INVALID; } void @@ -277,7 +338,7 @@ double local_x = x, local_y = y; if (srs && !srs->isEquivalentTo( _srs.get() ) && - !srs->transform(x, y, _srs.get(), local_x, local_y) ) + !srs->transform2D(x, y, _srs.get(), local_x, local_y) ) { return false; } @@ -293,6 +354,16 @@ } bool +GeoExtent::contains( const Bounds& rhs ) const +{ + return + rhs.xMin() >= _xmin && + rhs.yMin() >= _ymin && + rhs.xMax() <= _xmax && + rhs.yMax() <= _ymax; +} + +bool GeoExtent::intersects( const GeoExtent& rhs ) const { bool valid = isValid(); @@ -314,14 +385,14 @@ if ( y > _ymax ) _ymax = y; } -void GeoExtent::expandToInclude(const osgEarth::GeoExtent &rhs) +void GeoExtent::expandToInclude(const Bounds& rhs) { expandToInclude( rhs.xMin(), rhs.yMin() ); expandToInclude( rhs.xMax(), rhs.yMax() ); } GeoExtent -GeoExtent::intersectionSameSRS( const GeoExtent& rhs ) const +GeoExtent::intersectionSameSRS( const Bounds& rhs ) const { Bounds b( osg::maximum( xMin(), rhs.xMin() ), @@ -373,6 +444,9 @@ buf << "INVALID"; else buf << "MIN=" << _xmin << "," << _ymin << " MAX=" << _xmax << "," << _ymax; + + buf << ", SRS=" << _srs->getName(); + std::string bufStr; bufStr = buf.str(); return bufStr; @@ -691,6 +765,9 @@ osg::Image *result = new osg::Image(); result->allocateImage(width, height, 1, GL_RGBA, GL_UNSIGNED_BYTE); + //Initialize the image to be completely transparent + memset(result->data(), 0, result->getImageSizeInBytes()); + ImageUtils::PixelReader ra(result); const double dx = dest_extent.width() / (double)width; const double dy = dest_extent.height() / (double)height; @@ -725,10 +802,13 @@ double src_x = srcPointsX[pixel]; double src_y = srcPointsY[pixel]; - //if ( src_x < src_extent.xMin() || src_x > src_extent.xMax() || src_y < src_extent.yMin() || src_y > src_extent.yMax() ) - //{ - // OE_WARN << LC << "ERROR: sample point out of bounds: " << src_x << ", " << src_y << std::endl; - //} + if ( src_x < src_extent.xMin() || src_x > src_extent.xMax() || src_y < src_extent.yMin() || src_y > src_extent.yMax() ) + { + //If the sample point is outside of the bound of the source extent, increment the pixel and keep looping through. + //OE_WARN << LC << "ERROR: sample point out of bounds: " << src_x << ", " << src_y << std::endl; + pixel++; + continue; + } float px = (src_x - src_extent.xMin()) * xfac; float py = (src_y - src_extent.yMin()) * yfac; @@ -840,6 +920,7 @@ } delete[] srcPointsX; + return result; } @@ -929,7 +1010,7 @@ { double local_x = x, local_y = y; - if ( inputSRS && !inputSRS->transform(x, y, _extent.getSRS(), local_x, local_y) ) + if ( inputSRS && !inputSRS->transform2D(x, y, _extent.getSRS(), local_x, local_y) ) return false; if ( _extent.contains(local_x, local_y) ) @@ -960,7 +1041,7 @@ lon_deg = local_x; } else { - _extent.getSRS()->transform( x, y, inputSRS->getGeographicSRS(), lon_deg, lat_deg ); + _extent.getSRS()->transform2D( x, y, inputSRS->getGeographicSRS(), lon_deg, lat_deg ); } if ( _vsrs->transform( outputVSRS, lat_deg, lon_deg, elevation, newElevation ) ) diff -Nru osgearth-2.0+dfsg/src/osgEarth/GeoMath osgearth-2.1.1+dfsg/src/osgEarth/GeoMath --- osgearth-2.0+dfsg/src/osgEarth/GeoMath 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/GeoMath 2011-11-04 19:44:43.000000000 +0000 @@ -17,12 +17,25 @@ * along with this program. If not, see */ +#ifndef OSGEARTH_GEOMATH_H +#define OSGEARTH_GEOMATH_H 1 + #include +#include #include 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 */ @@ -49,6 +62,18 @@ double radius = osg::WGS_84_RADIUS_EQUATOR); /** + *Computes the distance covered by the given path in meters using the Haversine formula + *Assumes the points in Lon, Lat in degrees + */ + static double distance(const std::vector< osg::Vec3d > &points, double radius = osg::WGS_84_RADIUS_EQUATOR); + + /** + * Computes the distance between two points, in meters. + */ + static double distance(const osg::Vec3d& p1, const osg::Vec3d& p2, const SpatialReference* srs); + + + /** * Computes the initial bearing from one point to the next in radians * @param lat1Rad * The start latitude in radians @@ -124,6 +149,14 @@ double lat2Rad, double lon2Rad, double radius = osg::WGS_84_RADIUS_EQUATOR); + + /** + * Computes the distance between two points in meters following a rhumb line + * Assumes the points are in Lon, Lat in degrees + */ + static double rhumbDistance(const std::vector< osg::Vec3d > &points, double radius = osg::WGS_84_RADIUS_EQUATOR); + + /** *Computes the bearing of the rhumb line between two points in radians * @param lat1Rad @@ -159,4 +192,6 @@ double &out_latRad, double &out_lonRad, double radius = osg::WGS_84_RADIUS_EQUATOR); }; -}; \ No newline at end of file +}; + +#endif diff -Nru osgearth-2.0+dfsg/src/osgEarth/GeoMath.cpp osgearth-2.1.1+dfsg/src/osgEarth/GeoMath.cpp --- osgearth-2.0+dfsg/src/osgEarth/GeoMath.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/GeoMath.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -35,6 +35,40 @@ } double +GeoMath::distance(const std::vector< osg::Vec3d > &points, double radius) +{ + double length = 0; + + if (points.size() > 1) + { + for (unsigned int i = 0; i < points.size()-1; ++i) + { + const osg::Vec3d& current = points[i]; + const osg::Vec3d& next = points[i+1]; + length += GeoMath::distance(osg::DegreesToRadians(current.y()), osg::DegreesToRadians(current.x()), + osg::DegreesToRadians(next.y()), osg::DegreesToRadians(next.x()), radius); + } + } + return length; +} + +double +GeoMath::distance(const osg::Vec3d& p1, const osg::Vec3d& p2, const SpatialReference* srs ) +{ + if ( srs == 0L || srs->isProjected() ) + { + return (p2-p1).length(); + } + else + { + return distance( + osg::DegreesToRadians( p1.y() ), osg::DegreesToRadians( p1.x() ), + osg::DegreesToRadians( p2.y() ), osg::DegreesToRadians( p2.x() ), + srs->getEllipsoid()->getRadiusEquator() ); + } +} + +double GeoMath::bearing(double lat1Rad, double lon1Rad, double lat2Rad, double lon2Rad) { @@ -96,6 +130,23 @@ } double +GeoMath::rhumbDistance(const std::vector< osg::Vec3d > &points, double radius) +{ + double length = 0; + if (points.size() > 1) + { + for (unsigned int i = 0; i < points.size()-1; ++i) + { + const osg::Vec3d& current = points[i]; + const osg::Vec3d& next = points[i+1]; + length += GeoMath::rhumbDistance(osg::DegreesToRadians(current.y()), osg::DegreesToRadians(current.x()), + osg::DegreesToRadians(next.y()), osg::DegreesToRadians(next.x()), radius); + } + } + return length; +} + +double GeoMath::rhumbBearing(double lat1Rad, double lon1Rad, double lat2Rad, double lon2Rad) { diff -Nru osgearth-2.0+dfsg/src/osgEarth/HeightFieldUtils osgearth-2.1.1+dfsg/src/osgEarth/HeightFieldUtils --- osgearth-2.0+dfsg/src/osgEarth/HeightFieldUtils 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/HeightFieldUtils 2011-11-04 19:44:43.000000000 +0000 @@ -21,6 +21,8 @@ #include #include +#include +#include #include namespace osgEarth @@ -104,6 +106,16 @@ int newX, int newY, ElevationInterpolation interp = INTERP_BILINEAR ); + + /** + * Creates a new cluster culler based on a heightfield. + * Cluster cullers are for geocentric maps only, and therefore requires + * the ellipsoid model. + */ + static osg::ClusterCullingCallback* createClusterCullingCallback( + osg::HeightField* grid, + osg::EllipsoidModel* em, + float verticalScale =1.0f ); }; /** diff -Nru osgearth-2.0+dfsg/src/osgEarth/HeightFieldUtils.cpp osgearth-2.1.1+dfsg/src/osgEarth/HeightFieldUtils.cpp --- osgearth-2.0+dfsg/src/osgEarth/HeightFieldUtils.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/HeightFieldUtils.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -304,6 +304,93 @@ return output; } +osg::ClusterCullingCallback* +HeightFieldUtils::createClusterCullingCallback( osg::HeightField* grid, osg::EllipsoidModel* et, float verticalScale ) +{ + //This code is a very slightly modified version of the DestinationTile::createClusterCullingCallback in VirtualPlanetBuilder. + if ( !grid || !et ) + return 0L; + + double globe_radius = et->getRadiusPolar(); + unsigned int numColumns = grid->getNumColumns(); + unsigned int numRows = grid->getNumRows(); + + double midLong = grid->getOrigin().x()+grid->getXInterval()*((double)(numColumns-1))*0.5; + double midLat = grid->getOrigin().y()+grid->getYInterval()*((double)(numRows-1))*0.5; + double midZ = grid->getOrigin().z(); + + double midX,midY; + et->convertLatLongHeightToXYZ(osg::DegreesToRadians(midLat),osg::DegreesToRadians(midLong),midZ, midX,midY,midZ); + + osg::Vec3 center_position(midX,midY,midZ); + osg::Vec3 center_normal(midX,midY,midZ); + center_normal.normalize(); + + osg::Vec3 transformed_center_normal = center_normal; + + unsigned int r,c; + + // populate the vertex/normal/texcoord arrays from the grid. + double orig_X = grid->getOrigin().x(); + double delta_X = grid->getXInterval(); + double orig_Y = grid->getOrigin().y(); + double delta_Y = grid->getYInterval(); + double orig_Z = grid->getOrigin().z(); + + + float min_dot_product = 1.0f; + float max_cluster_culling_height = 0.0f; + float max_cluster_culling_radius = 0.0f; + + for(r=0;rgetHeight(c,r) * verticalScale; + double height = Z; + + et->convertLatLongHeightToXYZ( + osg::DegreesToRadians(Y), osg::DegreesToRadians(X), Z, + X, Y, Z); + + osg::Vec3d v(X,Y,Z); + osg::Vec3 dv = v - center_position; + double d = sqrt(dv.x()*dv.x() + dv.y()*dv.y() + dv.z()*dv.z()); + double theta = acos( globe_radius/ (globe_radius + fabs(height)) ); + double phi = 2.0 * asin (d*0.5/globe_radius); // d/globe_radius; + double beta = theta+phi; + double cutoff = osg::PI_2 - 0.1; + + //log(osg::INFO,"theta="<(globe_radius * tan(beta)); // beta*globe_radius; + min_dot_product = osg::minimum(min_dot_product, local_dot_product); + max_cluster_culling_height = osg::maximum(max_cluster_culling_height,local_m); + max_cluster_culling_radius = osg::maximum(max_cluster_culling_radius,local_radius); + } + else + { + //log(osg::INFO,"Turning off cluster culling for wrap around tile."); + return 0; + } + } + } + + osg::ClusterCullingCallback* ccc = new osg::ClusterCullingCallback; + + ccc->set(center_position + transformed_center_normal*max_cluster_culling_height , + transformed_center_normal, + min_dot_product, + max_cluster_culling_radius); + + return ccc; +} + /******************************************************************************************/ ReplaceInvalidDataOperator::ReplaceInvalidDataOperator(): diff -Nru osgearth-2.0+dfsg/src/osgEarth/HTTPClient osgearth-2.1.1+dfsg/src/osgEarth/HTTPClient --- osgearth-2.0+dfsg/src/osgEarth/HTTPClient 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/HTTPClient 2011-11-04 19:44:43.000000000 +0000 @@ -245,6 +245,16 @@ ProgressCallback* callback = 0 ); /** + * Reads an object. Based on the structure of the URI, it will either try to fetch the + * data using HTTP or simply read the file from disk. + */ + static ResultCode readObjectFile( + const std::string& url, + osg::ref_ptr& output, + const osgDB::ReaderWriter::Options* options = 0, + ProgressCallback* callback = 0); + + /** * Reads a string. Based on the structure of the URI, it will either try to fetch the * data using HTTP or simply read the file from disk. */ @@ -253,6 +263,13 @@ std::string& output, ProgressCallback* callback =0); + /** + * Downloads a file directly to disk. + */ + static bool download( + const std::string& uri, + const std::string& localPath ); + public: /** @@ -280,6 +297,12 @@ const osgDB::ReaderWriter::Options* options = 0, ProgressCallback* callback = 0 ) const; + ResultCode doReadObjectFile( + const std::string& url, + osg::ref_ptr& output, + const osgDB::ReaderWriter::Options* options = 0, + ProgressCallback* callback = 0); + ResultCode doReadImageFile( const std::string& filename, osg::ref_ptr& output, @@ -301,10 +324,10 @@ /** * Convenience method for downloading a URL directly to a file */ - bool downloadFile(const std::string& url, const std::string& filename); + bool doDownload(const std::string& url, const std::string& filename); private: - void *_curl_handle; + void* _curl_handle; std::string _previousPassword; long _previousHttpAuthentication; diff -Nru osgearth-2.0+dfsg/src/osgEarth/HTTPClient.cpp osgearth-2.1.1+dfsg/src/osgEarth/HTTPClient.cpp --- osgearth-2.0+dfsg/src/osgEarth/HTTPClient.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/HTTPClient.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -18,7 +18,7 @@ */ #include -#include +//#include #include #include #include @@ -252,24 +252,48 @@ #define QUOTE(X) QUOTE_(X) #define USER_AGENT "osgearth" QUOTE(OSGEARTH_MAJOR_VERSION) "." QUOTE(OSGEARTH_MINOR_VERSION) -typedef std::map< OpenThreads::Thread*, osg::ref_ptr > ThreadClientMap; -static OpenThreads::Mutex _threadClientMapMutex; -static ThreadClientMap _threadClientMap; + static optional _proxySettings; static std::string _userAgent = USER_AGENT; -HTTPClient& HTTPClient::getClient() + +HTTPClient& +HTTPClient::getClient() { - OpenThreads::ScopedLock lock(_threadClientMapMutex); - static unsigned int numClients = 0; - osg::ref_ptr& client = _threadClientMap[OpenThreads::Thread::CurrentThread()]; - if (!client) - { +#if 1 + static Threading::PerThread< osg::ref_ptr > s_clientPerThread; + + osg::ref_ptr& client = s_clientPerThread.get(); + if ( !client.valid() ) client = new HTTPClient(); - numClients++; + + return *client.get(); +#else + typedef std::map< OpenThreads::Thread*, osg::ref_ptr > ThreadClientMap; + static Threading::ReadWriteMutex _threadClientMapMutex; + static ThreadClientMap _threadClientMap; + + OpenThreads::Thread* current = OpenThreads::Thread::CurrentThread(); + + // first try the map: + { + Threading::ScopedReadLock sharedLock(_threadClientMapMutex); + ThreadClientMap::iterator i = _threadClientMap.find(current); + if ( i != _threadClientMap.end() ) + return *i->second.get(); } - return *client; + // not there; add it. + { + Threading::ScopedWriteLock exclusiveLock(_threadClientMapMutex); + + // normally, we'd double check b/c of the race condition, but since the map is being + // indexed by the actual thread pointer, there's no chance of a race. + HTTPClient* client = new HTTPClient(); + _threadClientMap[current] = client; + return *client; + } +#endif } HTTPClient::HTTPClient() @@ -489,6 +513,15 @@ } HTTPClient::ResultCode +HTTPClient::readObjectFile(const std::string& url, + osg::ref_ptr& output, + const osgDB::ReaderWriter::Options* options, + ProgressCallback* callback ) +{ + return getClient().doReadObjectFile( url, output, options, callback ); +} + +HTTPClient::ResultCode HTTPClient::readString(const std::string& filename, std::string& output, osgEarth::ProgressCallback* callback) @@ -496,6 +529,13 @@ return getClient().doReadString( filename, output, callback ); } +bool +HTTPClient::download(const std::string& uri, + const std::string& localPath) +{ + return getClient().doDownload( uri, localPath ); +} + HTTPResponse HTTPClient::doGet( const HTTPRequest& request, const osgDB::ReaderWriter::Options* options, ProgressCallback* callback) const { @@ -718,7 +758,7 @@ } bool -HTTPClient::downloadFile(const std::string &url, const std::string &filename) +HTTPClient::doDownload(const std::string &url, const std::string &filename) { // download the data HTTPResponse response = this->doGet( HTTPRequest(url) ); @@ -749,6 +789,31 @@ } } +namespace +{ + osgDB::ReaderWriter* + getReader( const std::string& url, const HTTPResponse& response ) + { + osgDB::ReaderWriter* reader = 0L; + + // try to look up a reader by mime-type first: + std::string mimeType = response.getMimeType(); + if ( !mimeType.empty() ) + { + reader = osgEarth::Registry::instance()->getReaderWriterForMimeType(mimeType); + } + + if ( !reader ) + { + // Try to find a reader by file extension. + std::string ext = osgDB::getFileExtension( url ); + reader = osgDB::Registry::instance()->getReaderWriterForExtension( ext ); + } + + return reader; + } +} + HTTPClient::ResultCode HTTPClient::doReadImageFile(const std::string& filename, osg::ref_ptr& output, @@ -763,25 +828,7 @@ if (response.isOK()) { - osgDB::ReaderWriter* reader = 0L; - - // try to look up a reader by mime-type first: - std::string mimeType = response.getMimeType(); - OE_DEBUG << LC << "Looking up extension for mime-type " << mimeType << std::endl; - if ( !mimeType.empty() ) - { - reader = osgEarth::Registry::instance()->getReaderWriterForMimeType(mimeType); - } - - if ( !reader ) - { - // Try to find a reader by file extension. If this fails, we will fetch the file - // anyway and try to get a reader via mime-type. - std::string ext = osgDB::getFileExtension( filename ); - reader = osgDB::Registry::instance()->getReaderWriterForExtension( ext ); - //OE_NOTICE << "Reading " << filename << " with mime " << response.getMimeType() << std::endl; - } - + osgDB::ReaderWriter* reader = getReader(filename, response); if (!reader) { OE_WARN << LC << "Can't find an OSG plugin to read "<doGet(filename, options, callback); if (response.isOK()) { - // Try to find a reader by file extension. If this fails, we will fetch the file - // anyway and try to get a reader via mime-type. - std::string ext = osgDB::getFileExtension( filename ); - osgDB::ReaderWriter *reader = - osgDB::Registry::instance()->getReaderWriterForExtension( ext ); - - //If we didn't get a reader by extension, try to get it via mime type - if (!reader) - { - std::string mimeType = response.getMimeType(); - OE_DEBUG << LC << "Looking up extension for mime-type " << mimeType << std::endl; - if ( mimeType.length() > 0 ) - { - reader = osgEarth::Registry::instance()->getReaderWriterForMimeType(mimeType); - } - } - - // if we still didn't get it, bad news + osgDB::ReaderWriter* reader = getReader(filename, response); if (!reader) { OE_NOTICE<& output, + const osgDB::ReaderWriter::Options* options, + osgEarth::ProgressCallback* callback) +{ + ResultCode result = RESULT_OK; + + if ( osgDB::containsServerAddress( url ) ) + { + HTTPResponse response = this->doGet(url, options, callback); + if ( response.isOK() ) + { + osgDB::ReaderWriter* reader = getReader( url, response ); + if ( !reader ) + { + OE_WARN << LC << "Error: No ReaderWriter for file " << url << std::endl; + result = RESULT_NO_READER; + } + else + { + osgDB::ReaderWriter::ReadResult rr = reader->readObject( response.getPartStream(0), options ); + if ( rr.validNode() ) + { + output = rr.takeObject(); + } + else + { + if ( rr.error() ) + { + OE_WARN << LC << "HTTP Reader Error: " << rr.message() << std::endl; + } + result = RESULT_READER_ERROR; + } + } + } + else + { + result = + response.isCancelled() ? RESULT_CANCELED : + response.getCode() == HTTPResponse::NOT_FOUND ? RESULT_NOT_FOUND : + response.getCode() == HTTPResponse::SERVER_ERROR ? RESULT_SERVER_ERROR : + RESULT_UNKNOWN_ERROR; + + //If we have an error but it's recoverable, like a server error or timeout then set the callback to retry. + if (HTTPClient::isRecoverable( result ) ) + { + if (callback) + { + OE_DEBUG << "Error in HTTPClient for " << url << " but it's recoverable" << std::endl; + callback->setNeedsRetry( true ); + } + } + } + } + else + { + output = osgDB::readObjectFile( url, options ); + if ( !output.valid() ) + result = RESULT_NOT_FOUND; + } + + return result; +} + HTTPClient::ResultCode HTTPClient::doReadString(const std::string& filename, diff -Nru osgearth-2.0+dfsg/src/osgEarth/ImageLayer osgearth-2.1.1+dfsg/src/osgEarth/ImageLayer --- osgearth-2.0+dfsg/src/osgEarth/ImageLayer 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/ImageLayer 2011-11-04 19:44:43.000000000 +0000 @@ -55,12 +55,6 @@ const optional& opacity() const { return _opacity; } /** - * The initial gamma value for this layer's images - */ - optional& gamma() { return _gamma; } - const optional& gamma() const { return _gamma; } - - /** * The initial minimum camera range at which this layer is visible. */ optional& minVisibleRange() { return _minRange; } @@ -96,6 +90,12 @@ optional& minFilter(void) {return _minFilter;} const optional& minFilter(void) const {return _minFilter;} + /** + * Whether LOD blending is enabled for this layer + */ + optional& lodBlending() { return _lodBlending; } + const optional& lodBlending() const { return _lodBlending; } + public: virtual Config getConfig() const; virtual void mergeConfig( const Config& conf ); @@ -105,13 +105,13 @@ void setDefaults(); optional _opacity; - optional _gamma; optional _minRange; optional _maxRange; optional _transparentColor; optional _noDataImageFilename; optional _magFilter; optional _minFilter; + optional _lodBlending; }; //-------------------------------------------------------------------- @@ -121,8 +121,7 @@ */ struct ImageLayerCallback : public TerrainLayerCallback { - virtual void onOpacityChanged( class ImageLayer* layer ) { } - virtual void onGammaChanged( class ImageLayer* layer ) { } + virtual void onOpacityChanged( class ImageLayer* layer ) { } }; typedef void (ImageLayerCallback::*ImageLayerCallbackMethodPtr)(ImageLayer* layer); @@ -178,8 +177,8 @@ /** * Access to the initialization options used to create this image layer */ - const ImageLayerOptions& getImageLayerOptions() const { return _options; } - virtual const TerrainLayerOptions& getTerrainLayerOptions() const { return _options; } + const ImageLayerOptions& getImageLayerOptions() const { return _runtimeOptions; } + virtual const TerrainLayerOptions& getTerrainLayerOptions() const { return _runtimeOptions; } /** Adds a property notification callback to this layer */ void addCallback( ImageLayerCallback* cb ); @@ -193,10 +192,10 @@ public: // runtime properties void setOpacity( float opacity ); - float getOpacity() const { return _actualOpacity; } - - void setGamma( float gamma ); - float getGamma() const { return _actualGamma; } + float getOpacity() const { return *_runtimeOptions.opacity(); } + + void disableLODBlending(); + bool isLODBlendingEnabled() const { return *_runtimeOptions.lodBlending(); } public: // methods @@ -214,12 +213,11 @@ virtual void initTileSource(); private: - ImageLayerOptions _options; - float _actualOpacity; - float _actualGamma; - //float _prevGamma; - //unsigned char _gammaLUT[256]; - //osg::ref_ptr _noDataImage; + //const ImageLayerOptions _options; + ImageLayerOptions _runtimeOptions; + + //float _actualOpacity; + //bool _actualLODBlending; osg::ref_ptr _preCacheOp; diff -Nru osgearth-2.0+dfsg/src/osgEarth/ImageLayer.cpp osgearth-2.1.1+dfsg/src/osgEarth/ImageLayer.cpp --- osgearth-2.0+dfsg/src/osgEarth/ImageLayer.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/ImageLayer.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -51,10 +51,10 @@ ImageLayerOptions::setDefaults() { _opacity.init( 1.0f ); - _gamma.init( 1.0f ); _transparentColor.init( osg::Vec4ub(0,0,0,0) ); _minRange.init( -FLT_MAX ); _maxRange.init( FLT_MAX ); + _lodBlending.init( false ); } void @@ -69,9 +69,9 @@ { conf.getIfSet( "nodata_image", _noDataImageFilename ); conf.getIfSet( "opacity", _opacity ); - conf.getIfSet( "gamma", _gamma ); conf.getIfSet( "min_range", _minRange ); conf.getIfSet( "max_range", _maxRange ); + conf.getIfSet( "lod_blending", _lodBlending ); if ( conf.hasValue( "transparent_color" ) ) _transparentColor = stringToColor( conf.value( "transparent_color" ), osg::Vec4ub(0,0,0,0)); @@ -88,7 +88,7 @@ conf.getIfSet("min_filter","LINEAR_MIPMAP_NEAREST", _minFilter,osg::Texture::LINEAR_MIPMAP_NEAREST); conf.getIfSet("min_filter","NEAREST", _minFilter,osg::Texture::NEAREST); conf.getIfSet("min_filter","NEAREST_MIPMAP_LINEAR", _minFilter,osg::Texture::NEAREST_MIPMAP_LINEAR); - conf.getIfSet("min_filter","NEAREST_MIPMAP_NEAREST",_minFilter,osg::Texture::NEAREST_MIPMAP_NEAREST); + conf.getIfSet("min_filter","NEAREST_MIPMAP_NEAREST",_minFilter,osg::Texture::NEAREST_MIPMAP_NEAREST); } Config @@ -97,9 +97,9 @@ Config conf = TerrainLayerOptions::getConfig(); conf.updateIfSet( "nodata_image", _noDataImageFilename ); conf.updateIfSet( "opacity", _opacity ); - conf.updateIfSet( "gamma", _gamma ); conf.updateIfSet( "min_range", _minRange ); conf.updateIfSet( "max_range", _maxRange ); + conf.updateIfSet( "lod_blending", _lodBlending ); if (_transparentColor.isSet()) conf.update("transparent_color", colorToString( _transparentColor.value())); @@ -117,7 +117,7 @@ conf.updateIfSet("min_filter","NEAREST", _minFilter,osg::Texture::NEAREST); conf.updateIfSet("min_filter","NEAREST_MIPMAP_LINEAR", _minFilter,osg::Texture::NEAREST_MIPMAP_LINEAR); conf.updateIfSet("min_filter","NEAREST_MIPMAP_NEAREST",_minFilter,osg::Texture::NEAREST_MIPMAP_NEAREST); - + return conf; } @@ -134,6 +134,16 @@ ImageLayerTileProcessor _processor; }; + + struct ApplyChromaKey + { + osg::Vec4f _chromaKey; + bool operator()( osg::Vec4f& pixel ) { + bool equiv = ImageUtils::areRGBEquivalent( pixel, _chromaKey ); + if ( equiv ) pixel.a() = 0.0f; + return equiv; + } + }; } //------------------------------------------------------------------------ @@ -157,8 +167,7 @@ if ( _options.noDataImageFilename().isSet() && !_options.noDataImageFilename()->empty() ) { - //OE_INFO << "Setting nodata image to \"" << _options.noDataImageFilename().value() << "\"" << std::endl; - _noDataImage = osgDB::readImageFile( _options.noDataImageFilename().value() ); + _noDataImage = URI(*_options.noDataImageFilename()).readImage(); if ( !_noDataImage.valid() ) { OE_WARN << "Warning: Could not read nodata image from \"" << _options.noDataImageFilename().value() << "\"" << std::endl; @@ -166,16 +175,6 @@ } } -struct ApplyChromaKey -{ - osg::Vec4f _chromaKey; - bool operator()( osg::Vec4f& pixel ) { - bool equiv = ImageUtils::areRGBEquivalent( pixel, _chromaKey ); - if ( equiv ) pixel.a() = 0.0f; - return equiv; - } -}; - void ImageLayerTileProcessor::process( osg::ref_ptr& image ) const { @@ -227,22 +226,22 @@ //------------------------------------------------------------------------ ImageLayer::ImageLayer( const ImageLayerOptions& options ) : -TerrainLayer(), -_options( options ) +TerrainLayer( &_runtimeOptions ), +_runtimeOptions( options ) { init(); } ImageLayer::ImageLayer( const std::string& name, const TileSourceOptions& driverOptions ) : -TerrainLayer(), -_options( ImageLayerOptions(name, driverOptions) ) +TerrainLayer ( &_runtimeOptions ), +_runtimeOptions( ImageLayerOptions(name, driverOptions) ) { init(); } ImageLayer::ImageLayer( const ImageLayerOptions& options, TileSource* tileSource ) : -TerrainLayer( tileSource ), -_options( options ) +TerrainLayer ( &_runtimeOptions, tileSource ), +_runtimeOptions( options ) { init(); } @@ -250,14 +249,7 @@ void ImageLayer::init() { -// _prevGamma = 1.0f; - - // intialize the runtime actuals from the initialization options: - _actualOpacity = _options.opacity().value(); - //_actualGamma = _options.gamma().value(); - - //TODO: probably should graduate this to the superclass. - _actualEnabled = _options.enabled().value(); + //nop } void @@ -297,15 +289,14 @@ void ImageLayer::setOpacity( float value ) { - _actualOpacity = osg::clampBetween( value, 0.0f, 1.0f ); + _runtimeOptions.opacity() = osg::clampBetween( value, 0.0f, 1.0f ); fireCallback( &ImageLayerCallback::onOpacityChanged ); } -void -ImageLayer::setGamma( float value ) +void +ImageLayer::disableLODBlending() { - _actualGamma = value; - fireCallback( &ImageLayerCallback::onGammaChanged ); + _runtimeOptions.lodBlending() = false; } void @@ -337,9 +328,10 @@ _targetProfileHint->isEquivalentTo( getProfile() ); ImageLayerPreCacheOperation* op = new ImageLayerPreCacheOperation(); - op->_processor.init( _options, layerInTargetProfile ); + op->_processor.init( _runtimeOptions, layerInTargetProfile ); _preCacheOp = op; + } GeoImage @@ -348,7 +340,7 @@ GeoImage result; //OE_NOTICE << "[osgEarth::MapLayer::createImage] " << key.str() << std::endl; - if ( !_actualCacheOnly && !getTileSource() ) + if ( !isCacheOnly() && !getTileSource() ) { OE_WARN << LC << "Error: MapLayer does not have a valid TileSource, cannot create image " << std::endl; return GeoImage::INVALID; @@ -370,7 +362,7 @@ OE_DEBUG << LC << "Layer \"" << getName() << "\": Map and Layer profiles are equivalent " << std::endl; } //If the map profile and layer profile are in the same SRS but with different tiling scemes and exact cropping is not required, cache in the layer profile. - else if (mapProfile->getSRS()->isEquivalentTo( layerProfile->getSRS()) && _options.exactCropping() == false ) + else if (mapProfile->getSRS()->isEquivalentTo( layerProfile->getSRS()) && _runtimeOptions.exactCropping() == false ) { OE_DEBUG << LC << "Layer \"" << getName() << "\": Map and Layer profiles are in the same SRS and non-exact cropping is allowed, caching in layer profile." << std::endl; cacheInMapProfile = false; @@ -379,10 +371,10 @@ bool cacheInLayerProfile = !cacheInMapProfile; //Write the cache TMS file if it hasn't been written yet. - if (!_cacheProfile.valid() && _cache.valid() && _options.cacheEnabled() == true && _tileSource.valid()) + if (!_cacheProfile.valid() && _cache.valid() && _runtimeOptions.cacheEnabled() == true && _tileSource.valid()) { _cacheProfile = cacheInMapProfile ? mapProfile : _profile.get(); - _cache->storeProperties( _cacheSpec, _cacheProfile, _tileSource->getPixelsPerTile() ); + _cache->storeProperties( _cacheSpec, _cacheProfile.get(), _tileSource->getPixelsPerTile() ); } if (cacheInMapProfile) @@ -391,7 +383,7 @@ } //If we are caching in the map profile, try to get the image immediately. - if (cacheInMapProfile && _cache.valid() && _options.cacheEnabled() == true ) + if (cacheInMapProfile && _cache.valid() && _runtimeOptions.cacheEnabled() == true ) { osg::ref_ptr cachedImage; if ( _cache->getImage( key, _cacheSpec, cachedImage ) ) @@ -427,9 +419,9 @@ //Scale the extent if necessary GeoExtent ext = key.getExtent(); - if ( _options.edgeBufferRatio().isSet() ) + if ( _runtimeOptions.edgeBufferRatio().isSet() ) { - double ratio = _options.edgeBufferRatio().get(); + double ratio = _runtimeOptions.edgeBufferRatio().get(); ext.scale(ratio, ratio); } @@ -575,7 +567,7 @@ result = mosaic.reproject( key.getProfile()->getSRS(), &key.getExtent(), - _options.reprojectedTileSize().value(), _options.reprojectedTileSize().value() ); + _runtimeOptions.reprojectedTileSize().value(), _runtimeOptions.reprojectedTileSize().value() ); } else { @@ -584,8 +576,8 @@ GeoExtent clampedMapExt = layerProfile->clampAndTransformExtent( key.getExtent() ); if ( clampedMapExt.isValid() ) { - int size = _options.exactCropping() == true ? _options.reprojectedTileSize().value() : 0; - result = mosaic.crop(clampedMapExt, _options.exactCropping().value(), size, size); + int size = _runtimeOptions.exactCropping() == true ? _runtimeOptions.reprojectedTileSize().value() : 0; + result = mosaic.crop(clampedMapExt, _runtimeOptions.exactCropping().value(), size, size); } else result = GeoImage::INVALID; @@ -606,7 +598,7 @@ } //If we got a result, the cache is valid and we are caching in the map profile, write to the map cache. - if (result.valid() && _cache.valid() && _options.cacheEnabled() == true && cacheInMapProfile) + if (result.valid() && _cache.valid() && _runtimeOptions.cacheEnabled() == true && cacheInMapProfile) { OE_DEBUG << LC << "Layer \"" << getName() << "\" writing tile " << key.str() << " to cache " << std::endl; _cache->setImage( key, _cacheSpec, result.getImage()); @@ -619,52 +611,74 @@ bool cacheInLayerProfile, ProgressCallback* progress ) { + // Results: + // + // * return NULL to indicate that the key exceeds the maximum LOD of the source data, + // and that the engine may need to generate a "fallback" tile if necessary. + // + // * return an "empty image" if the LOD is valid BUT the key does not intersect the + // source's data extents. + osg::Image* result = 0L; - if (_cache.valid() && cacheInLayerProfile && _options.cacheEnabled() == true ) + // first check the cache. + // TODO: find a way to avoid caching/checking when the LOD falls + if (_cache.valid() && cacheInLayerProfile && _runtimeOptions.cacheEnabled() == true ) { osg::ref_ptr cachedImage; if ( _cache->getImage( key, _cacheSpec, cachedImage ) ) { - OE_DEBUG << LC << " Layer \"" << getName() << "\" got " << key.str() << " from cache " << std::endl; + OE_INFO << LC << " Layer \"" << getName() << "\" got " << key.str() << " from cache " << std::endl; return ImageUtils::cloneImage(cachedImage.get()); } } - if ( !_actualCacheOnly ) + if ( !isCacheOnly() ) { TileSource* source = getTileSource(); if ( !source ) - return false; + return 0L; - //Only try to get the image if it's not in the blacklist - if (!source->getBlacklist()->contains( key.getTileId() )) + // Only try to get the image if it's not in the blacklist + if ( !source->getBlacklist()->contains(key.getTileId()) ) { - //Only try to get data if the source actually has data - if (source->hasData( key ) ) + // if the tile source cannot service this key's LOD, return NULL. + if ( source->hasDataAtLOD( key.getLevelOfDetail() ) ) { - result = source->createImage( key, _preCacheOp.get(), progress ); + // if the key's extent intersects the source's extent, ask the + // source for an image. + if ( source->hasDataInExtent( key.getExtent() ) ) + { + //Take a reference to the preCacheOp, there is a potential for it to be + //overwritten and deleted if this ImageLayer is added to another Map + //while createImage is going on. + osg::ref_ptr< TileSource::ImageOperation > op = _preCacheOp; + result = source->createImage( key, op.get(), progress ); + + // if no result was created, add this key to the blacklist. + if ( result == 0L && (!progress || !progress->isCanceled()) ) + { + //Add the tile to the blacklist + source->getBlacklist()->add(key.getTileId()); + } + } - //If the image is not valid and the progress was not cancelled, blacklist - if ( result == 0L && (!progress || !progress->isCanceled())) + // otherwise, generate an empty image. + else { - //Add the tile to the blacklist - OE_DEBUG << LC << "Adding tile " << key.str() << " to the blacklist" << std::endl; - source->getBlacklist()->add(key.getTileId()); + result = ImageUtils::createEmptyImage(); } } + else { - OE_DEBUG << LC << "Source has no data at " << key.str() << std::endl; - } - } - else - { - OE_DEBUG << LC << "Tile " << key.str() << " is blacklisted, not checking" << std::endl; + // in this case, the source cannot service the LOD + result = NULL; + } } // Cache is necessary: - if ( result && _cache.valid() && cacheInLayerProfile && _options.cacheEnabled() == true ) + if ( result && _cache.valid() && cacheInLayerProfile && _runtimeOptions.cacheEnabled() == true ) { _cache->setImage( key, _cacheSpec, result ); } diff -Nru osgearth-2.0+dfsg/src/osgEarth/ImageMosaic.cpp osgearth-2.1.1+dfsg/src/osgEarth/ImageMosaic.cpp --- osgearth-2.0+dfsg/src/osgEarth/ImageMosaic.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/ImageMosaic.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -114,4 +114,4 @@ return image.release(); } -/***************************************************************************/ \ No newline at end of file +/***************************************************************************/ diff -Nru osgearth-2.0+dfsg/src/osgEarth/ImageToHeightFieldConverter osgearth-2.1.1+dfsg/src/osgEarth/ImageToHeightFieldConverter --- osgearth-2.0+dfsg/src/osgEarth/ImageToHeightFieldConverter 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/ImageToHeightFieldConverter 2011-11-04 19:44:43.000000000 +0000 @@ -66,4 +66,4 @@ }; } -#endif //OSGEARTH_IMAGE_TO_HEIGHTFIELD_CONVERTER \ No newline at end of file +#endif //OSGEARTH_IMAGE_TO_HEIGHTFIELD_CONVERTER diff -Nru osgearth-2.0+dfsg/src/osgEarth/ImageUtils.cpp osgearth-2.1.1+dfsg/src/osgEarth/ImageUtils.cpp --- osgearth-2.0+dfsg/src/osgEarth/ImageUtils.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/ImageUtils.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -39,31 +39,10 @@ // Calling clone->dirty() might work, but we are not sure. if ( !input ) return 0L; - - if ( input->className() == "Image" ) - { -#if 0 - osg::Image* clone = new osg::Image( *input ); - clone->dirty(); - return clone; -#else - osg::Image* clone = new osg::Image(); - clone->allocateImage( input->s(), input->t(), input->r(), input->getPixelFormat(), input->getDataType(), input->getPacking() ); - clone->setInternalTextureFormat( input->getInternalTextureFormat() ); - if ( input->isMipmap() ) - clone->setMipmapLevels( input->getMipmapLevels() ); - memcpy( clone->data(), input->data(), input->getTotalSizeInBytesIncludingMipmaps() ); - return clone; -#endif - } - - else - { - // handles Image subclasses. - osg::Image* clone = osg::clone( input, osg::CopyOp::DEEP_COPY_ALL ); - clone->dirty(); - return clone; - } + + osg::Image* clone = osg::clone( input, osg::CopyOp::DEEP_COPY_ALL ); + clone->dirty(); + return clone; } void @@ -277,7 +256,7 @@ dest.r()*(1.0f-sa) + src.r()*sa, dest.g()*(1.0f-sa) + src.g()*sa, dest.b()*(1.0f-sa) + src.b()*sa, - dest.a() ); + osg::maximum(sa, da) ); return true; } }; @@ -292,9 +271,9 @@ PixelVisitor mixer; mixer._a = osg::clampBetween( a, 0.0f, 1.0f ); mixer._srcHasAlpha = src->getPixelSizeInBits() == 32; - mixer._destHasAlpha = src->getPixelSizeInBits() == 32; + mixer._destHasAlpha = src->getPixelSizeInBits() == 32; - mixer.accept( src, dest ); + mixer.accept( src, dest ); return true; } @@ -397,8 +376,11 @@ osg::Image* ImageUtils::createEmptyImage() { + //TODO: Make this a static or store it in the registry to avoid creating it + // each time. osg::Image* image = new osg::Image; image->allocateImage(1,1,1, GL_RGBA, GL_UNSIGNED_BYTE); + image->setInternalTextureFormat( GL_RGBA8 ); unsigned char *data = image->data(0,0); memset(data, 0, 4); return image; @@ -416,10 +398,15 @@ { if ( !image ) return 0L; - - if ( image->getPixelFormat() == pixelFormat && image->getDataType() == dataType ) - return cloneImage(image); + if ( image->getPixelFormat() == pixelFormat && image->getDataType() == dataType) + { + GLenum texFormat = image->getInternalTextureFormat(); + if (dataType != GL_UNSIGNED_BYTE + || (pixelFormat == GL_RGB && texFormat == GL_RGB8) + || (pixelFormat == GL_RGBA && texFormat == GL_RGBA8)) + return cloneImage(image); + } if ( !canConvert(image, pixelFormat, dataType) ) return 0L; diff -Nru osgearth-2.0+dfsg/src/osgEarth/LocalTangentPlane osgearth-2.1.1+dfsg/src/osgEarth/LocalTangentPlane --- osgearth-2.0+dfsg/src/osgEarth/LocalTangentPlane 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/LocalTangentPlane 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,63 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ + +#ifndef OSGEARTH_LOCAL_TANGENT_PLANE_H +#define OSGEARTH_LOCAL_TANGENT_PLANE_H 1 + +#include +#include +#include +#include + +namespace osgEarth +{ + /** + * Local Tangent Plane SRS. + * Please call SpatialReference::createLTP() to construct one of these. + */ + class LTPSpatialReference : public SpatialReference + { + public: + LTPSpatialReference(void* handle, const osg::Vec3d& worldPointLLA); + + // CUBE is a projected coordinate system. + virtual bool isGeographic() const { return false; } + virtual bool isProjected() const { return true; } + + // This SRS uses a WGS84 lat/long SRS under the hood for reprojection. So we need the + // pre/post transforms to move from LTP to Geodetic and back. + virtual bool preTransform(double& x, double& y, double& z, void* context) const; + virtual bool postTransform(double& x, double& y, double& z, void* context) const; + + protected: // SpatialReference overrides + + void _init(); + + bool _isEquivalentTo( const SpatialReference* srs ) const; + + private: + + osg::Vec3d _worldPointLLA; + osg::Matrixd _local2world, _world2local; + + }; + +} + +#endif // OSGEARTH_LOCAL_TANGENT_PLANE_H diff -Nru osgearth-2.0+dfsg/src/osgEarth/LocalTangentPlane.cpp osgearth-2.1.1+dfsg/src/osgEarth/LocalTangentPlane.cpp --- osgearth-2.0+dfsg/src/osgEarth/LocalTangentPlane.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/LocalTangentPlane.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,92 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ + +#include +#include +#include +#include +#include + +using namespace osgEarth; + +#define LC "[LTP] " + +// -------------------------------------------------------------------------- + +LTPSpatialReference::LTPSpatialReference( void* handle, const osg::Vec3d& worldPointLLA ) : +SpatialReference( handle, false ), +_worldPointLLA ( worldPointLLA ) +{ + //todo, set proper init string +} + +void +LTPSpatialReference::_init() +{ + SpatialReference::_init(); + + _is_user_defined = true; + _is_contiguous = true; + _is_ltp = true; + _is_geographic = false; + _name = "ENU Local Tangent Plane"; + + // set up the LTP matrixes. + + getEllipsoid()->computeLocalToWorldTransformFromLatLongHeight( + osg::DegreesToRadians(_worldPointLLA.y()), + osg::DegreesToRadians(_worldPointLLA.x()), + _worldPointLLA.z(), + _local2world); + + _world2local.invert( _local2world ); +} + +bool +LTPSpatialReference::preTransform(double& x, double& y, double& z, void* context) const +{ + osg::Vec3d world = osg::Vec3d(x,y,z) * _local2world; + double lat, lon, height; + getEllipsoid()->convertXYZToLatLongHeight(world.x(), world.y(), world.z(), lat, lon, height); + x = osg::RadiansToDegrees(lon); + y = osg::RadiansToDegrees(lat); + z = height; + return true; +} + +bool +LTPSpatialReference::postTransform(double& x, double& y, double& z, void* context) const +{ + osg::Vec3d world; + getEllipsoid()->convertLatLongHeightToXYZ( + osg::DegreesToRadians(y), osg::DegreesToRadians(x), z, + world.x(), world.y(), world.z() ); + osg::Vec3d local = world * _world2local; + x = local.x(), y = local.y(), z = local.z(); + return true; +} + +bool +LTPSpatialReference::_isEquivalentTo( const SpatialReference* srs ) const +{ + return + srs->isLTP() && + _worldPointLLA == static_cast(srs)->_worldPointLLA ; + // todo: check the reference ellipsoids +} diff -Nru osgearth-2.0+dfsg/src/osgEarth/Locators osgearth-2.1.1+dfsg/src/osgEarth/Locators --- osgearth-2.0+dfsg/src/osgEarth/Locators 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/Locators 2011-11-04 19:44:43.000000000 +0000 @@ -44,6 +44,7 @@ GeoLocator( const osgTerrain::Locator& prototype, const GeoExtent& dataExtent, const GeoExtent& displayExtent ); static GeoLocator* createForKey( const class TileKey& key, const class MapInfo& mapInfo ); + static GeoLocator* createForExtent( const GeoExtent& extent, const class MapInfo& mapInfo); void setDataExtent( const GeoExtent& extent ); const GeoExtent& getDataExtent() const; diff -Nru osgearth-2.0+dfsg/src/osgEarth/Locators.cpp osgearth-2.1.1+dfsg/src/osgEarth/Locators.cpp --- osgearth-2.0+dfsg/src/osgEarth/Locators.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/Locators.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -74,16 +74,20 @@ GeoLocator* GeoLocator::createForKey( const TileKey& key, const MapInfo& map ) -{ - //bool isPlateCarre = !map.isGeocentric() && map.isGeographicSRS(); //map->getProfile()->getSRS()->isGeographic(); - +{ const GeoExtent& ex = key.getExtent(); + return createForExtent( ex, map ); +} + +GeoLocator* +GeoLocator::createForExtent( const GeoExtent& extent, const class MapInfo& map) +{ double xmin, ymin, xmax, ymax; - key.getExtent().getBounds( xmin, ymin, xmax, ymax ); + extent.getBounds( xmin, ymin, xmax, ymax ); // A locator will place the tile on the globe: - GeoLocator* locator = key.getProfile()->getSRS()->createLocator( - ex.xMin(), ex.yMin(), ex.xMax(), ex.yMax(), + GeoLocator* locator = extent.getSRS()->createLocator( + extent.xMin(), extent.yMin(), extent.xMax(), extent.yMax(), map.isPlateCarre() ); if ( map.isGeocentric() ) diff -Nru osgearth-2.0+dfsg/src/osgEarth/Map osgearth-2.1.1+dfsg/src/osgEarth/Map --- osgearth-2.0+dfsg/src/osgEarth/Map 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/Map 2011-11-04 19:44:43.000000000 +0000 @@ -64,7 +64,7 @@ const Revision& getRevision() const { return _modelRevision; } int getFirstIndex() const { return _firstIndex; } int getSecondIndex() const { return _secondIndex; } - Layer* getLayer() const { return _layer; } + Layer* getLayer() const { return _layer.get(); } ImageLayer* getImageLayer() const { return static_cast(_layer.get()); } ElevationLayer* getElevationLayer() const { return static_cast(_layer.get()); } ModelLayer* getModelLayer() const { return static_cast(_layer.get()); } @@ -211,9 +211,11 @@ ModelLayer* getModelLayerAt( int index ) const; /** - * Gets the model layer to draw as a terrain mask. + * Copies references of the mask layers into the output list. + * This method is thread safe. It returns the map revision that was + * in effect when the data was copied. */ - MaskLayer* getTerrainMaskLayer() const; + int getTerrainMaskLayers( MaskLayerVector& out_list ) const; /** * Adds a map layer callback to this map. This will be notified whenever layers are @@ -221,6 +223,8 @@ */ void addMapCallback( MapCallback* callback ) const; + void removeMapCallback( MapCallback* callback ); + /** * Adds an image layer to the map. */ @@ -277,15 +281,14 @@ void moveModelLayer( ModelLayer* layer, unsigned int newIndex ); /** - * Set a layer to use as a terrain mask. The map engine will draw - * this layer using the stencil buffer. + * Adds a new layer to use as a terrain mask. */ - void setTerrainMaskLayer( MaskLayer* layer ); + void addTerrainMaskLayer( MaskLayer* layer ); /** * Removed a terrain mask layer that was set with setTerrainMaskLayer(). */ - void removeTerrainMaskLayer(); + void removeTerrainMaskLayer( MaskLayer* layer ); public: /** @@ -318,6 +321,7 @@ const TileKey& key, bool fallback, osg::ref_ptr& out_hf, + bool* out_isFallback =0L, ElevationInterpolation interpolation =INTERP_AVERAGE, ElevationSamplePolicy samplePolicy =SAMPLE_FIRST_VALID, ProgressCallback* progress = 0) const; @@ -346,6 +350,21 @@ bool isGeocentric() const; /** + * Convenience function to convert an arbitrary point to map coordinates. + */ + bool toMapPoint( const osg::Vec3d& input, const SpatialReference* input_srs, osg::Vec3d& output ) const; + + /** + * Convenience function to convert a point (in map coordinates) to world-space (XYZ) coordinates. + */ + bool mapPointToWorldPoint( const osg::Vec3d& input, osg::Vec3d& output ) const; + + /** + * Convenience function to convert a world-space (XYZ) point to map coordinates. + */ + bool worldPointToMapPoint( const osg::Vec3d& input, osg::Vec3d& output ) const; + + /** * Synronizes a map frame to the current revision of the map's data model. * Returns true if new Map model data was available and a sync occurred; * returns false if nothing changed. @@ -355,8 +374,10 @@ enum ModelParts { IMAGE_LAYERS = 1 << 0, ELEVATION_LAYERS = 1 << 1, - MODEL_LAYERS = 1 << 2, TERRAIN_LAYERS = IMAGE_LAYERS | ELEVATION_LAYERS, + MODEL_LAYERS = 1 << 2, + MASK_LAYERS = 1 << 3, + MASKED_TERRAIN_LAYERS = TERRAIN_LAYERS | MASK_LAYERS, ENTIRE_MODEL = 0xff }; @@ -371,7 +392,7 @@ ImageLayerVector _imageLayers; ElevationLayerVector _elevationLayers; ModelLayerVector _modelLayers; - osg::ref_ptr _terrainMaskLayer; + MaskLayerVector _terrainMaskLayers; MapCallbackList _mapCallbacks; osg::ref_ptr _globalOptions; Threading::ReadWriteMutex _mapDataMutex; @@ -388,22 +409,31 @@ * A convenience class that combines a general geospatial profile and additional * information about the map itself. */ - class MapInfo + class OSGEARTH_EXPORT MapInfo { public: MapInfo( const Map* map ) : _profile( map->getProfile() ), _isGeocentric( map->isGeocentric() ), _isCube( map->getMapOptions().coordSysType() == MapOptions::CSTYPE_GEOCENTRIC_CUBE ) { } + + MapInfo( const MapInfo& rhs ) + : _profile( rhs._profile ), + _isGeocentric( rhs._isGeocentric ), + _isCube( rhs._isCube ) { } - const Profile* getProfile() const { return _profile; } + const Profile* getProfile() const { return _profile.get(); } bool isGeocentric() const { return _isGeocentric; } bool isCube() const { return _isCube; } bool isPlateCarre() const { return !_isGeocentric && isGeographicSRS(); } - bool isProjectedSRS() const { return !isProjectedSRS(); } + bool isProjectedSRS() const { return !isGeographicSRS(); } bool isGeographicSRS() const { return _profile->getSRS()->isGeographic(); } + + bool toMapPoint( const osg::Vec3d& input, const SpatialReference* input_srs, osg::Vec3d& output ) const; + bool mapPointToWorldPoint( const osg::Vec3d& input, osg::Vec3d& output ) const; + bool worldPointToMapPoint( const osg::Vec3d& input, osg::Vec3d& output ) const; private: osg::ref_ptr _profile; @@ -464,6 +494,9 @@ const ModelLayerVector& modelLayers() const { return _modelLayers; } ModelLayer* getModelLayerAt(int index) const { return _modelLayers[index].get(); } + /** The mask layer set snapshot */ + const MaskLayerVector& terrainMaskLayers() const { return _maskLayers; } + /** Gets the index of the layer in the layer stack snapshot. */ int indexOf( ImageLayer* layer ) const; int indexOf( ElevationLayer* layer ) const; @@ -472,6 +505,9 @@ /** Gets the map data model revision with which this frame is currently sync'd */ Revision getRevision() const { return _mapDataModelRevision; } + /** Checks whether all the data for the specified key is cached. */ + bool isCached( const TileKey& key ) const; + /** * Equivalent to the Map::createHeightField() method, but operates on the elevation stack * snapshot in this MapFrame. @@ -480,6 +516,7 @@ const TileKey& key, bool fallback, osg::ref_ptr& out_hf, + bool* out_isFallback =0L, ElevationInterpolation interpolation =INTERP_AVERAGE, ElevationSamplePolicy samplePolicy =SAMPLE_FIRST_VALID, ProgressCallback* progress = 0) const; @@ -495,6 +532,7 @@ ImageLayerVector _imageLayers; ElevationLayerVector _elevationLayers; ModelLayerVector _modelLayers; + MaskLayerVector _maskLayers; friend class Map; }; diff -Nru osgearth-2.0+dfsg/src/osgEarth/Map.cpp osgearth-2.1.1+dfsg/src/osgEarth/Map.cpp --- osgearth-2.0+dfsg/src/osgEarth/Map.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/Map.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -200,6 +200,37 @@ return _dataModelRevision; } +ModelLayer* +Map::getModelLayerByName( const std::string& name ) const +{ + Threading::ScopedReadLock( const_cast(this)->_mapDataMutex ); + for( ModelLayerVector::const_iterator i = _modelLayers.begin(); i != _modelLayers.end(); ++i ) + if ( i->get()->getName() == name ) + return i->get(); + return 0L; +} + +ModelLayer* +Map::getModelLayerByUID( UID layerUID ) const +{ + Threading::ScopedReadLock( const_cast(this)->_mapDataMutex ); + for( ModelLayerVector::const_iterator i = _modelLayers.begin(); i != _modelLayers.end(); ++i ) + if ( i->get()->getUID() == layerUID ) + return i->get(); + return 0L; +} + + +ModelLayer* +Map::getModelLayerAt( int index ) const +{ + Threading::ScopedReadLock( const_cast(this)->_mapDataMutex ); + if ( index >= 0 && index < (int)_modelLayers.size() ) + return _modelLayers[index].get(); + else + return 0L; +} + int Map::getNumModelLayers() const { @@ -207,9 +238,16 @@ return _modelLayers.size(); } -MaskLayer* -Map::getTerrainMaskLayer() const { - return _terrainMaskLayer.get(); +int +Map::getTerrainMaskLayers( MaskLayerVector& out_list ) const +{ + out_list.reserve( _terrainMaskLayers.size() ); + + Threading::ScopedReadLock lock( const_cast(this)->_mapDataMutex ); + for( MaskLayerVector::const_iterator i = _terrainMaskLayers.begin(); i != _terrainMaskLayers.end(); ++i ) + out_list.push_back( i->get() ); + + return _dataModelRevision; } void @@ -235,18 +273,17 @@ Cache* Map::getCache() const { - if ( !_cache.valid() && _mapOptions.cache().isSet() ) + if ( !_cache.valid() ) { Cache* cache = 0L; // if there's a cache override in the registry, install it now. - if ( osgEarth::Registry::instance()->getCacheOverride() ) - { - OE_INFO << LC << "Overriding map cache with global cache override" << std::endl; - cache = osgEarth::Registry::instance()->getCacheOverride(); - } + if ( osgEarth::Registry::instance()->getCacheOverride() ) + { + cache = osgEarth::Registry::instance()->getCacheOverride(); + } - if ( !cache ) + else if ( _mapOptions.cache().isSet() ) { cache = CacheFactory::create( _mapOptions.cache().get() ); } @@ -287,9 +324,20 @@ const_cast(this)->_mapCallbacks.push_back( cb ); } +void +Map::removeMapCallback( MapCallback* cb ) +{ + MapCallbackList::iterator i = std::find( _mapCallbacks.begin(), _mapCallbacks.end(), cb); + if (i != _mapCallbacks.end()) + { + _mapCallbacks.erase( i ); + } +} + void Map::addImageLayer( ImageLayer* layer ) { + osgEarth::Registry::instance()->clearBlacklist(); unsigned int index = -1; if ( layer ) { @@ -334,6 +382,7 @@ void Map::insertImageLayer( ImageLayer* layer, unsigned int index ) { + osgEarth::Registry::instance()->clearBlacklist(); if ( layer ) { //Set options for the map from the layer @@ -378,6 +427,7 @@ void Map::addElevationLayer( ElevationLayer* layer ) { + osgEarth::Registry::instance()->clearBlacklist(); unsigned int index = -1; if ( layer ) { @@ -420,6 +470,7 @@ void Map::removeImageLayer( ImageLayer* layer ) { + osgEarth::Registry::instance()->clearBlacklist(); unsigned int index = -1; osg::ref_ptr layerToRemove = layer; @@ -455,6 +506,7 @@ void Map::removeElevationLayer( ElevationLayer* layer ) { + osgEarth::Registry::instance()->clearBlacklist(); unsigned int index = -1; osg::ref_ptr layerToRemove = layer; @@ -634,6 +686,9 @@ { if ( layer ) { + //Take a reference to the layer since we will be deleting it + osg::ref_ptr< ModelLayer > layerRef = layer; + Revision newRevision; { Threading::ScopedWriteLock lock( _mapDataMutex ); @@ -651,7 +706,7 @@ for( MapCallbackList::iterator i = _mapCallbacks.begin(); i != _mapCallbacks.end(); ++i ) { i->get()->onMapModelChanged( MapModelChange( - MapModelChange::REMOVE_MODEL_LAYER, newRevision, layer) ); + MapModelChange::REMOVE_MODEL_LAYER, newRevision, layerRef.get()) ); } } } @@ -704,51 +759,54 @@ } void -Map::setTerrainMaskLayer( MaskLayer* layer ) +Map::addTerrainMaskLayer( MaskLayer* layer ) { if ( layer ) { Revision newRevision; { Threading::ScopedWriteLock lock( _mapDataMutex ); - _terrainMaskLayer = layer; + _terrainMaskLayers.push_back(layer); newRevision = ++_dataModelRevision; } - layer->initialize( _mapOptions.referenceURI().value(), this ); //getReferenceURI(), this ); + layer->initialize( _mapOptions.referenceURI().value(), this ); // a separate block b/c we don't need the mutex for( MapCallbackList::iterator i = _mapCallbacks.begin(); i != _mapCallbacks.end(); i++ ) { i->get()->onMapModelChanged( MapModelChange( MapModelChange::ADD_MASK_LAYER, newRevision, layer) ); - } - } - else - { - removeTerrainMaskLayer(); + } } } void -Map::removeTerrainMaskLayer() +Map::removeTerrainMaskLayer( MaskLayer* layer ) { - if ( _terrainMaskLayer.valid() ) + if ( layer ) { + //Take a reference to the layer since we will be deleting it + osg::ref_ptr< MaskLayer > layerRef = layer; Revision newRevision; - - osg::ref_ptr layer = _terrainMaskLayer.get(); { Threading::ScopedWriteLock lock( _mapDataMutex ); - _terrainMaskLayer = 0L; - newRevision = ++_dataModelRevision; + for( MaskLayerVector::iterator i = _terrainMaskLayers.begin(); i != _terrainMaskLayers.end(); ++i ) + { + if ( i->get() == layer ) + { + _terrainMaskLayers.erase( i ); + newRevision = ++_dataModelRevision; + break; + } + } } // a separate block b/c we don't need the mutex for( MapCallbackList::iterator i = _mapCallbacks.begin(); i != _mapCallbacks.end(); i++ ) { i->get()->onMapModelChanged( MapModelChange( - MapModelChange::REMOVE_MASK_LAYER, newRevision, layer.get()) ); + MapModelChange::REMOVE_MASK_LAYER, newRevision, layerRef.get()) ); } } } @@ -874,6 +932,7 @@ ElevationInterpolation interpolation, ElevationSamplePolicy samplePolicy, osg::ref_ptr& out_result, + bool* out_isFallback, ProgressCallback* progress) { unsigned int lowestLOD = key.getLevelOfDetail(); @@ -887,6 +946,10 @@ unsigned int numValidHeightFields = 0; + if ( out_isFallback ) + { + *out_isFallback = false; + } //First pass: Try to get the exact LOD requested for each enabled heightfield for( ElevationLayerVector::const_iterator i = elevLayers.begin(); i != elevLayers.end(); i++ ) @@ -895,12 +958,8 @@ if (layer->getProfile() && layer->getEnabled() ) { osg::HeightField* hf = layer->createHeightField( key, progress ); - //osg::ref_ptr< osg::HeightField > hf; - //layer->getHeightField( key, hf, progress ); - layerValidMap[ layer ] = (hf != 0L); //hf.valid(); - if ( hf ) - //if (hf.valid()) - { + layerValidMap[ layer ] = (hf != 0L); + if ( hf ) { numValidHeightFields++; GeoHeightField ghf( hf, key.getExtent(), layer->getProfile()->getVerticalSRS() ); heightFields.push_back( ghf ); @@ -914,8 +973,8 @@ return false; } - //Second pass: We were either asked to fallback or we might have some heightfields at the requested LOD and some that are NULL - // Fall back on parent tiles to fill in the missing data if possible. + //Second pass: We were either asked to fallback or we might have some heightfields at the requested + // LOD and some that are NULL. Fall back on parent tiles to fill in the missing data if possible. for( ElevationLayerVector::const_iterator i = elevLayers.begin(); i != elevLayers.end(); i++ ) { ElevationLayer* layer = i->get(); @@ -942,6 +1001,10 @@ heightFields.push_back( GeoHeightField( hf.get(), hf_key.getExtent(), layer->getProfile()->getVerticalSRS() ) ); + + if ( out_isFallback ) + *out_isFallback = true; + } } } @@ -1087,12 +1150,18 @@ Map::getHeightField(const TileKey& key, bool fallback, osg::ref_ptr& out_result, + bool* out_isFallback, ElevationInterpolation interpolation, ElevationSamplePolicy samplePolicy, ProgressCallback* progress) const { Threading::ScopedReadLock lock( const_cast(this)->_mapDataMutex ); - return s_getHeightField( key, _elevationLayers, getProfile(), fallback, interpolation, samplePolicy, out_result, progress ); + + return s_getHeightField( + key, _elevationLayers, getProfile(), fallback, + interpolation, samplePolicy, + out_result, out_isFallback, + progress ); } bool @@ -1143,6 +1212,14 @@ std::copy( _modelLayers.begin(), _modelLayers.end(), std::back_inserter(frame._modelLayers) ); } + if ( frame._parts & MASK_LAYERS ) + { + if ( !frame._initialized ) + frame._maskLayers.reserve( _terrainMaskLayers.size() ); + frame._maskLayers.clear(); + std::copy( _terrainMaskLayers.begin(), _terrainMaskLayers.end(), std::back_inserter(frame._maskLayers) ); + } + // sync the revision numbers. frame._initialized = true; frame._mapDataModelRevision = _dataModelRevision; @@ -1152,6 +1229,81 @@ return result; } +bool +Map::toMapPoint( const osg::Vec3d& input, const SpatialReference* inputSRS, osg::Vec3d& output ) const +{ + return MapInfo(this).toMapPoint(input, inputSRS, output); +} + +bool +Map::mapPointToWorldPoint( const osg::Vec3d& input, osg::Vec3d& output ) const +{ + return MapInfo(this).mapPointToWorldPoint(input, output); +} + +bool +Map::worldPointToMapPoint( const osg::Vec3d& input, osg::Vec3d& output ) const +{ + return MapInfo(this).worldPointToMapPoint(input, output); +} + +//------------------------------------------------------------------------ + +bool +MapInfo::toMapPoint( const osg::Vec3d& input, const SpatialReference* inputSRS, osg::Vec3d& output ) const +{ + if ( !inputSRS ) + return false; + + const SpatialReference* mapSRS = _profile->getSRS(); + + if ( inputSRS->isEquivalentTo( mapSRS ) ) + { + output = input; + return true; + } + + return inputSRS->transform( + input.x(), input.y(), input.z(), + mapSRS, + output.x(), output.y(), output.z() ); +} + +bool +MapInfo::mapPointToWorldPoint( const osg::Vec3d& input, osg::Vec3d& output ) const +{ + if ( _isGeocentric ) + { + _profile->getSRS()->getEllipsoid()->convertLatLongHeightToXYZ( + osg::DegreesToRadians( input.y() ), osg::DegreesToRadians( input.x() ), input.z(), + output.x(), output.y(), output.z() ); + } + else + { + output = input; + } + return true; +} + +bool +MapInfo::worldPointToMapPoint( const osg::Vec3d& input, osg::Vec3d& output ) const +{ + if ( _isGeocentric ) + { + _profile->getSRS()->getEllipsoid()->convertXYZToLatLongHeight( + input.x(), input.y(), input.z(), + output.y(), output.x(), output.z() ); + + output.y() = osg::RadiansToDegrees(output.y()); + output.x() = osg::RadiansToDegrees(output.x()); + } + else + { + output = input; + } + return true; +} + //------------------------------------------------------------------------ MapFrame::MapFrame( const Map* map, Map::ModelParts parts, const std::string& name ) : @@ -1186,7 +1338,8 @@ _mapDataModelRevision( src._mapDataModelRevision ), _imageLayers( src._imageLayers ), _elevationLayers( src._elevationLayers ), -_modelLayers( src._modelLayers ) +_modelLayers( src._modelLayers ), +_maskLayers( src._maskLayers ) { //no sync required here; we copied the arrays etc } @@ -1201,11 +1354,12 @@ MapFrame::getHeightField(const TileKey& key, bool fallback, osg::ref_ptr& out_hf, + bool* out_isFallback, ElevationInterpolation interpolation, ElevationSamplePolicy samplePolicy, ProgressCallback* progress) const { - return s_getHeightField( key, _elevationLayers, _mapInfo.getProfile(), fallback, interpolation, samplePolicy, out_hf, progress ); + return s_getHeightField( key, _elevationLayers, _mapInfo.getProfile(), fallback, interpolation, samplePolicy, out_hf, out_isFallback, progress ); } int @@ -1246,3 +1400,73 @@ return i->get(); return 0L; } + +bool +MapFrame::isCached( const osgEarth::TileKey& key ) const +{ + const Profile* mapProfile = getProfile(); + + //Check the imagery layers + for( ImageLayerVector::const_iterator i = imageLayers().begin(); i != imageLayers().end(); i++ ) + { + ImageLayer* layer = i->get(); + osg::ref_ptr< Cache > cache = layer->getCache(); + + if ( !cache.valid() || !layer->getProfile() ) + return false; + + std::vector< TileKey > keys; + + if ( mapProfile->isEquivalentTo( layer->getProfile() ) ) + { + keys.push_back( key ); + } + else + { + layer->getProfile()->getIntersectingTiles( key, keys ); + } + + for (unsigned int j = 0; j < keys.size(); ++j) + { + if ( layer->isKeyValid( keys[j] ) ) + { + if ( !cache->isCached( keys[j], layer->getCacheSpec() ) ) + { + return false; + } + } + } + } + + for( ElevationLayerVector::const_iterator i = elevationLayers().begin(); i != elevationLayers().end(); ++i ) + { + ElevationLayer* layer = i->get(); + osg::ref_ptr< Cache > cache = layer->getCache(); + + if ( !cache.valid() || !layer->getProfile() ) + return false; + + std::vector keys; + + if ( mapProfile->isEquivalentTo( layer->getProfile() ) ) + { + keys.push_back( key ); + } + else + { + layer->getProfile()->getIntersectingTiles( key, keys ); + } + + for (unsigned int j = 0; j < keys.size(); ++j) + { + if ( layer->isKeyValid( keys[j] ) ) + { + if ( !cache->isCached( keys[j], layer->getCacheSpec() ) ) + { + return false; + } + } + } + } + return true; +} diff -Nru osgearth-2.0+dfsg/src/osgEarth/MapNode osgearth-2.1.1+dfsg/src/osgEarth/MapNode --- osgearth-2.0+dfsg/src/osgEarth/MapNode 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/MapNode 2011-11-04 19:44:43.000000000 +0000 @@ -37,6 +37,20 @@ class OSGEARTH_EXPORT MapNode : public osg::Group { public: + + class TileRangeData : public osg::Referenced + { + public: + TileRangeData(double minRange, double maxRange): + _minRange( minRange ), + _maxRange( maxRange ) + { + } + + double _minRange; + double _maxRange; + }; + /** * Creates an empty map node. */ @@ -68,6 +82,11 @@ */ MapNode( Map* map, const MapNodeOptions& options ); + /** + * Attempts to load a MapNOde from a ".earth" file in the arguments list + */ + static MapNode* load( class osg::ArgumentParser& arguments ); + public: virtual const char* libraryName() const { return "osgEarth"; } @@ -109,14 +128,16 @@ void removeTerrainDecorator( osg::Group* decorator ); /** - * Installs an OverlayNode atop the terrain engine. + * Accesses the group containing anything that will be "draped" on the terrain + * via the OverlayDecorator. */ - void installOverlayNode( osgSim::OverlayNode* overlay ); - + osg::Group* getOverlayGroup() { return _overlayModels.get(); } + /** - * Removes an OverlayNode installed with installOverlayNode + * If you add or remove children to/fro the overlay group above, call this + * method to refresh the overlay graph. */ - void uninstallOverlayNode( osgSim::OverlayNode* overlay ); + void updateOverlayGraph(); /** * Gets the engine properties associated with this node. The engine @@ -129,6 +150,14 @@ */ TerrainEngineNode* getTerrainEngine() const; + /** + * Gets the Config object serializing external data. External data is information + * that osgEarth itself does not control, but that an app can include in the + * MapNode for the purposes of including it in a .earth file. + */ + Config& externalConfig() { return _externalConf; } + const Config& externalConfig() const { return _externalConf; } + public: //override osg::Node virtual osg::BoundingSphere computeBound() const; @@ -147,6 +176,7 @@ osg::ref_ptr< osg::Group > _overlayModels; osg::ref_ptr< OverlayDecorator > _overlayDecorator; MapNodeOptions _mapNodeOptions; + Config _externalConf; // keep track of nodes created by model layers typedef std::map ModelLayerNodeMap; @@ -168,8 +198,10 @@ void onModelLayerAdded( ModelLayer*, unsigned int ); void onModelLayerRemoved( ModelLayer* ); void onModelLayerMoved( ModelLayer* layer, unsigned int oldIndex, unsigned int newIndex ); +#if 0 void onMaskLayerAdded( MaskLayer* ); void onMaskLayerRemoved( MaskLayer* ); +#endif public: void onModelLayerOverlayChanged( ModelLayer* layer ); @@ -177,12 +209,10 @@ private: osg::ref_ptr< ModelLayerCallback > _modelLayerCallback; + osg::ref_ptr< MapCallback > _mapCallback; void init(); - //void adjustUpdateTraversalCount( int delta ); void adjustEventTraversalCount( int delta ); - - void updateOverlayGraph(); }; } // namespace osgEarth diff -Nru osgearth-2.0+dfsg/src/osgEarth/MapNode.cpp osgearth-2.1.1+dfsg/src/osgEarth/MapNode.cpp --- osgearth-2.0+dfsg/src/osgEarth/MapNode.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/MapNode.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -22,6 +22,7 @@ #include #include #include +#include #include using namespace osgEarth; @@ -46,12 +47,14 @@ void onModelLayerMoved( ModelLayer* layer, unsigned int oldIndex, unsigned int newIndex ) { _node->onModelLayerMoved( layer, oldIndex, newIndex); } +#if 0 void onMaskLayerAdded( MaskLayer* layer ) { _node->onMaskLayerAdded( layer ); } void onMaskLayerRemoved( MaskLayer* layer ) { _node->onMaskLayerRemoved( layer ); } +#endif osg::observer_ptr _node; }; @@ -83,27 +86,34 @@ } virtual void apply(osg::PagedLOD& node) - { - for (unsigned int i = 0; i < node.getNumFileNames();) + { + //The PagedLOD node will contain two filenames, the first is empty and is the actual geometry of the + //tile and the second is the filename of the next tile. + if (node.getNumFileNames() > 1) { - const std::string &filename = node.getFileName(i); + //Get the child filename + const std::string &filename = node.getFileName(1); if (osgEarth::Registry::instance()->isBlacklisted(filename)) { - OE_DEBUG << "Removing blacklisted child" << i << " " << filename << std::endl; - //Adjust the previous LOD's range so that it becomes the last child - if (i > 0) - { - node.setRange(i-1, 0, FLT_MAX); - } - node.removeChildren(i, 1); - _numRemoved++; + //If the tile is blacklisted, we set the actual geometry, child 0, to always display + //and the second child to never display + node.setRange(0, 0, FLT_MAX); + node.setRange(1, FLT_MAX, FLT_MAX); } else { - i++; + //If the child is not blacklisted, it is possible that it could have been blacklisted previously so reset the + //ranges of both the first and second children. This gives the second child another + //chance to be traversed in case a layer was added that might have data. + 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); + } } + } - traverse(node); } @@ -112,6 +122,25 @@ //--------------------------------------------------------------------------- +MapNode* +MapNode::load(osg::ArgumentParser& args) +{ + for( unsigned i=1; i output; + if ( HTTPClient::readNodeFile( args[i], output ) == HTTPClient::RESULT_OK ) + { + return dynamic_cast( output.release() ); + } + } + } + return 0L; +} + +//--------------------------------------------------------------------------- + MapNode::MapNode() : _map( new Map() ) { @@ -229,7 +258,12 @@ // a decorator for overlay models: _overlayDecorator = new OverlayDecorator(); - //_overlayDecorator->setOverlayGraph( _overlayModels.get() ); + if ( _mapNodeOptions.overlayVertexWarping().isSet() ) + _overlayDecorator->setVertexWarping( *_mapNodeOptions.overlayVertexWarping() ); + if ( _mapNodeOptions.overlayBlending().isSet() ) + _overlayDecorator->setOverlayBlending( *_mapNodeOptions.overlayBlending() ); + if ( _mapNodeOptions.overlayTextureSize().isSet() ) + _overlayDecorator->setTextureSize( *_mapNodeOptions.overlayTextureSize() ); addTerrainDecorator( _overlayDecorator.get() ); // install any pre-existing model layers: @@ -241,14 +275,17 @@ onModelLayerAdded( k->get(), modelLayerIndex ); } +#if 0 // install any pre-existing mask layer: if ( _map->getTerrainMaskLayer() ) { onMaskLayerAdded( _map->getTerrainMaskLayer() ); } +#endif + _mapCallback = new MapNodeMapCallbackProxy(this); // install a layer callback for processing further map actions: - _map->addMapCallback( new MapNodeMapCallbackProxy(this) ); + _map->addMapCallback( _mapCallback.get() ); osg::StateSet* ss = getOrCreateStateSet(); //ss->setAttributeAndModes( new osg::CullFace() ); //, osg::StateAttribute::ON); @@ -263,18 +300,21 @@ dirtyBound(); - // set the node up to initialize the terrain engine on the first update traversal. - // GW: no longer needed. terrain engine is post-initialized on demand now - // TODO: remove this comment after a while - //ADJUST_UPDATE_TRAV_COUNT( this, 1 ); - // register for event traversals so we can deal with blacklisted filenames adjustEventTraversalCount( 1 ); } MapNode::~MapNode() { - removeChildren( 0, getNumChildren() ); + _map->removeMapCallback( _mapCallback.get() ); + + ModelLayerVector modelLayers; + _map->getModelLayers( modelLayers ); + //Remove our model callback from any of the model layers in the map + for (osgEarth::ModelLayerVector::iterator itr = modelLayers.begin(); itr != modelLayers.end(); ++itr) + { + this->onModelLayerRemoved( itr->get() ); + } } osg::BoundingSphere @@ -358,7 +398,7 @@ } else { - if ( layer->getModelLayerOptions().overlay() == true ) + if ( layer->getOverlay() ) { _overlayModels->addChild( node ); // todo: index? updateOverlayGraph(); @@ -458,43 +498,6 @@ }; void -MapNode::onMaskLayerAdded( MaskLayer* layer ) -{ - osg::Node* node = layer->getOrCreateNode(); - - if ( node && node->asGroup() ) - { - int count = 0; - MaskNodeFinder f; - node->accept( f ); - for( std::list::iterator i = f._groups.begin(); i != f._groups.end(); ++i ) - { - (*i)->addChild( _terrainEngine ); - count++; - } - this->replaceChild( _terrainEngine, node ); - - OE_NOTICE<<"Installed terrain mask (" - <asGroup(); - dirtyBound(); - } -} - -void -MapNode::onMaskLayerRemoved( MaskLayer* layer ) -{ - if ( layer && _maskLayerNode ) - { - osg::ref_ptr child = _maskLayerNode->getChild( 0 ); - this->replaceChild( _maskLayerNode, child.get() ); - _maskLayerNode = 0L; - dirtyBound(); - } -} - -void MapNode::addTerrainDecorator(osg::Group* decorator) { if ( _terrainEngine.valid() ) @@ -535,26 +538,6 @@ } void -MapNode::installOverlayNode( osgSim::OverlayNode* overlay ) -{ - if ( _terrainEngine.valid() ) - { - overlay->addChild( _terrainEngine.get() ); - this->replaceChild( _terrainEngine.get(), overlay ); - } -} - -void -MapNode::uninstallOverlayNode( osgSim::OverlayNode* overlay ) -{ - if ( _terrainEngine.valid() ) - { - osg::ref_ptr overlayChild = overlay->getChild( 0 ); - this->replaceChild( overlay, overlayChild.get() ); - } -} - -void MapNode::adjustEventTraversalCount( int delta ) { int oldCount = this->getNumChildrenRequiringEventTraversal(); @@ -573,7 +556,8 @@ //Only remove the blacklisted filenames if new filenames have been added since last time. _lastNumBlacklistedFilenames = numBlacklist; RemoveBlacklistedFilenamesVisitor v; - accept( v ); + //accept( v ); + _terrainEngine->accept( v ); } } @@ -583,7 +567,7 @@ void MapNode::onModelLayerOverlayChanged( ModelLayer* layer ) { - OE_INFO << "Overlay changed to " << layer->getOverlay() << std::endl; + 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(); @@ -591,8 +575,8 @@ if (node.valid()) { //Remove it from the original parent and add it to the new parent - origParent->removeChild( node ); - newParent->addChild( node ); + origParent->removeChild( node.get() ); + newParent->addChild( node.get() ); } updateOverlayGraph(); diff -Nru osgearth-2.0+dfsg/src/osgEarth/MapNodeOptions osgearth-2.1.1+dfsg/src/osgEarth/MapNodeOptions --- osgearth-2.0+dfsg/src/osgEarth/MapNodeOptions 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/MapNodeOptions 2011-11-04 19:44:43.000000000 +0000 @@ -61,6 +61,24 @@ const optional& enableLighting() const { return _enableLighting; } /** + * Whether to enable overlay vertex warping. See OverlayDecorator + */ + optional& overlayVertexWarping() { return _overlayVertexWarping; } + const optional& overlayVertexWarping() const { return _overlayVertexWarping; } + + /** + * Whether to enable blending on the overlay decorator subgraph. See OverlayDecorator + */ + optional& overlayBlending() { return _overlayBlending; } + const optional& overlayBlending() const { return _overlayBlending; } + + /** + * Texture size to use for the overlay RTT camera. + */ + optional& overlayTextureSize() { return _overlayTextureSize; } + const optional& overlayTextureSize() const { return _overlayTextureSize; } + + /** * Options to conigure the terrain engine (the component that renders the * terrain surface). */ @@ -77,6 +95,9 @@ optional _proxySettings; optional _cacheOnly; optional _enableLighting; + optional _overlayVertexWarping; + optional _overlayBlending; + optional _overlayTextureSize; optional _terrainOptionsConf; TerrainOptions* _terrainOptions; diff -Nru osgearth-2.0+dfsg/src/osgEarth/MapNodeOptions.cpp osgearth-2.1.1+dfsg/src/osgEarth/MapNodeOptions.cpp --- osgearth-2.0+dfsg/src/osgEarth/MapNodeOptions.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/MapNodeOptions.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -31,20 +31,26 @@ //---------------------------------------------------------------------------- MapNodeOptions::MapNodeOptions( const Config& conf ) : -ConfigOptions( conf ), -_proxySettings( ProxySettings() ), -_cacheOnly( false ), -_enableLighting( true ), -_terrainOptions( 0L ) +ConfigOptions ( conf ), +_proxySettings ( ProxySettings() ), +_cacheOnly ( false ), +_enableLighting ( true ), +_overlayVertexWarping( false ), +_overlayBlending ( true ), +_overlayTextureSize ( 4096 ), +_terrainOptions ( 0L ) { mergeConfig( conf ); } MapNodeOptions::MapNodeOptions( const TerrainOptions& to ) : -_proxySettings( ProxySettings() ), -_cacheOnly( false ), -_enableLighting( true ), -_terrainOptions( 0L ) +_proxySettings ( ProxySettings() ), +_cacheOnly ( false ), +_enableLighting ( true ), +_overlayVertexWarping( false ), +_overlayBlending ( true ), +_overlayTextureSize ( 4096 ), +_terrainOptions ( 0L ) { setTerrainOptions( to ); } @@ -65,10 +71,13 @@ Config conf; // start with a fresh one since this is a FINAL object // = ConfigOptions::getConfig(); conf.key() = "options"; - conf.updateObjIfSet( "proxy", _proxySettings ); - conf.updateIfSet( "cache_only", _cacheOnly ); - conf.updateIfSet( "lighting", _enableLighting ); - conf.updateIfSet( "terrain", _terrainOptionsConf ); + conf.updateObjIfSet( "proxy", _proxySettings ); + conf.updateIfSet ( "cache_only", _cacheOnly ); + conf.updateIfSet ( "lighting", _enableLighting ); + conf.updateIfSet ( "terrain", _terrainOptionsConf ); + conf.updateIfSet ( "overlay_warping", _overlayVertexWarping ); + conf.updateIfSet ( "overlay_blending", _overlayBlending ); + conf.updateIfSet ( "overlay_texture_size", _overlayTextureSize ); return conf; } @@ -78,9 +87,12 @@ { ConfigOptions::mergeConfig( conf ); - conf.getObjIfSet( "proxy", _proxySettings ); - conf.getIfSet( "cache_only", _cacheOnly ); - conf.getIfSet( "lighting", _enableLighting ); + conf.getObjIfSet( "proxy", _proxySettings ); + conf.getIfSet ( "cache_only", _cacheOnly ); + conf.getIfSet ( "lighting", _enableLighting ); + conf.getIfSet ( "overlay_warping", _overlayVertexWarping ); + conf.getIfSet ( "overlay_blending", _overlayBlending ); + conf.getIfSet ( "overlay_texture_size", _overlayTextureSize ); if ( conf.hasChild( "terrain" ) ) { diff -Nru osgearth-2.0+dfsg/src/osgEarth/MaskLayer osgearth-2.1.1+dfsg/src/osgEarth/MaskLayer --- osgearth-2.0+dfsg/src/osgEarth/MaskLayer 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/MaskLayer 2011-11-04 19:44:43.000000000 +0000 @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include namespace osgEarth @@ -31,10 +31,42 @@ class Map; /** + * Configuration options for a MaskLayer. + */ + class OSGEARTH_EXPORT MaskLayerOptions : public ConfigOptions + { + public: + MaskLayerOptions( const ConfigOptions& options =ConfigOptions() ); + + MaskLayerOptions( const std::string& name, const MaskSourceOptions& driverOptions =MaskSourceOptions() ); + + /** + * The readable name of the layer. + */ + optional& name() { return _name; } + const optional& name() const { return _name; } + + /** + * Options for the underlying model source driver. + */ + optional& driver() { return _driver; } + const optional& driver() const { return _driver; } + + public: + virtual Config getConfig() const; + virtual void mergeConfig( const Config& conf ); + + private: + void fromConfig( const Config& conf ); + void setDefaults(); + + optional _name; + optional _driver; + }; + + /** * A MaskLayer is a specialized layer used to mask out a part of the terrain. * Typically you would use this if you had a pre-built 3D terrain model for an inset area. - * - * TODO: move this to osgEarth::Util and give it a reasonable API. */ class OSGEARTH_EXPORT MaskLayer : public Layer { @@ -42,34 +74,45 @@ /** * Constructs a new mask layer based on a configuration setup. */ - MaskLayer( const ConfigOptions& options =ConfigOptions() ); + MaskLayer( const MaskLayerOptions& options =MaskLayerOptions() ); /** - * Gets the reference URI (for resolving relative paths) + * Constructs a new mask layer with a user-provided driver options. + */ + MaskLayer( const std::string& name, const MaskSourceOptions& options ); + + /** + * Constructs a new mask layer with a user-provided mask source. */ - const std::string& getReferenceURI() const { return _referenceURI; } + MaskLayer(const MaskLayerOptions& options, MaskSource* source ); /** - * Access the underlying model source. + * Access the underlying mask source. */ - ModelSource* getModelSource() const { return _modelSource.get(); } + MaskSource* getMaskSource() const { return _maskSource.get(); } public: - - osg::Node* getOrCreateNode( ProgressCallback* progress =0L ); + /** + * Gets the geometric boundary polygon representing the area of the + * terrain to mask out. + */ + osg::Vec3dArray* getOrCreateBoundary( float heightScale = 1.0, const SpatialReference* srs = NULL, ProgressCallback* progress =0L ); public: void initialize( const std::string& referenceURI, const Map* map ); - virtual Config getConfig() const; - private: std::string _referenceURI; - osg::ref_ptr _modelSource; - DriverConfigOptions _driverOptions; - osg::ref_ptr _node; + MaskLayerOptions _initOptions, _runtimeOptions; + osg::ref_ptr _maskSource; + Revision _maskSourceRev; + osg::ref_ptr _boundary; + + void copyOptions(); }; + + typedef std::vector< osg::ref_ptr > MaskLayerVector; } #endif // OSGEARTH_MASK_LAYER_H diff -Nru osgearth-2.0+dfsg/src/osgEarth/MaskLayer.cpp osgearth-2.1.1+dfsg/src/osgEarth/MaskLayer.cpp --- osgearth-2.0+dfsg/src/osgEarth/MaskLayer.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/MaskLayer.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -19,22 +19,81 @@ #include #include +#define LC "[MaskLayer] " + using namespace osgEarth; +//------------------------------------------------------------------------ -#define MASK_MODEL_DRIVER "feature_stencil" +MaskLayerOptions::MaskLayerOptions( const ConfigOptions& options ) : +ConfigOptions( options ) +{ + setDefaults(); + fromConfig( _conf ); +} -MaskLayer::MaskLayer( const ConfigOptions& options ) +MaskLayerOptions::MaskLayerOptions( const std::string& name, const MaskSourceOptions& driverOptions ) : +ConfigOptions() { - // just in case the caller did not ref the parameter: - //osg::ref_ptr tempHold = options; + setDefaults(); + fromConfig( _conf ); + _name = name; + _driver = driverOptions; +} - // copy the input options and set some special settings: - Config conf = options.getConfig(); - conf.update( "mask", "true" ); - conf.update( "inverted", "true" ); - conf.update( "extrusion_distance", "100000" ); - _driverOptions = DriverConfigOptions( conf ); - _driverOptions.setDriver( MASK_MODEL_DRIVER ); +void +MaskLayerOptions::setDefaults() +{ + //nop +} + +Config +MaskLayerOptions::getConfig() const +{ + Config conf = ConfigOptions::getConfig(); + + conf.updateIfSet( "name", _name ); + + return conf; +} + +void +MaskLayerOptions::fromConfig( const Config& conf ) +{ + conf.getIfSet( "name", _name ); +} + +void +MaskLayerOptions::mergeConfig( const Config& conf ) +{ + ConfigOptions::mergeConfig( conf ); + fromConfig( conf ); +} + +//------------------------------------------------------------------------ + +MaskLayer::MaskLayer( const MaskLayerOptions& options ) : +_initOptions( options ) +{ + copyOptions(); +} + +MaskLayer::MaskLayer( const std::string& name, const MaskSourceOptions& options ) : +_initOptions( MaskLayerOptions( name, options ) ) +{ + copyOptions(); +} + +MaskLayer::MaskLayer( const MaskLayerOptions& options, MaskSource* source ) : +_maskSource( source ), +_initOptions( options ) +{ + copyOptions(); +} + +void +MaskLayer::copyOptions() +{ + _runtimeOptions = _initOptions; } void @@ -42,43 +101,38 @@ { _referenceURI = referenceURI; - if ( !_modelSource.valid() ) + if ( !_maskSource.valid() && _initOptions.driver().isSet() ) { - _modelSource = ModelSourceFactory::create( _driverOptions ); + _maskSource = MaskSourceFactory::create( *_initOptions.driver() ); } - if ( _modelSource.valid() ) + if ( _maskSource.valid() ) { - _modelSource->initialize( _referenceURI, map ); + _maskSource->initialize( _referenceURI, map ); } } -osg::Node* -MaskLayer::getOrCreateNode( ProgressCallback* progress ) +osg::Vec3dArray* +MaskLayer::getOrCreateBoundary( float heightScale, const SpatialReference *srs, ProgressCallback* progress ) { - osg::Node* result = 0L; - - if ( _modelSource.valid() ) + if ( _maskSource.valid() ) { - result = _modelSource->createNode( progress ); + // if the model source has changed, regenerate the node. + if ( _boundary.valid() && !_maskSource->inSyncWith(_maskSourceRev) ) + { + _boundary = 0L; + } - // a hack to immediately update-traverse this node so it can generate its - // MaskNodes. Otherwise MapNode will not be able to apply it. - if ( result ) + if ( !_boundary.valid() ) { - osg::NodeVisitor nv( osg::NodeVisitor::UPDATE_VISITOR, osg::NodeVisitor::TRAVERSE_ALL_CHILDREN ); - result->accept( nv ); + _boundary = _maskSource->createBoundary( srs, progress ); + + for (osg::Vec3dArray::iterator vIt = _boundary->begin(); vIt != _boundary->end(); ++vIt) + vIt->z() = vIt->z() * heightScale; + + _maskSource->sync( _maskSourceRev ); } } - return result; -} - -Config -MaskLayer::getConfig() const -{ - Config conf = _driverOptions.getConfig(); - conf.key() = "mask"; - conf.remove( "driver" ); - return conf; + return _boundary.get(); } diff -Nru osgearth-2.0+dfsg/src/osgEarth/MaskSource osgearth-2.1.1+dfsg/src/osgEarth/MaskSource --- osgearth-2.0+dfsg/src/osgEarth/MaskSource 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/MaskSource 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,105 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ + +#ifndef OSGEARTH_MASK_SOURCE_H +#define OSGEARTH_MASK_SOURCE_H 1 + +#include +#include +#include +#include +#include +#include + +namespace osgEarth +{ + class Map; + + /** + * Configuration options for a masking source. + */ + class OSGEARTH_EXPORT MaskSourceOptions : public DriverConfigOptions + { + public: + MaskSourceOptions( const ConfigOptions& options =ConfigOptions() ) : + DriverConfigOptions( options ) { fromConfig(_conf); } + + public: // properties + + public: + virtual Config getConfig() const; + + protected: + virtual void mergeConfig( const Config& conf ); + + private: + void fromConfig( const Config& conf ); + }; + + /** + * MaskSource is a plugin object that generates a masking goemetry + */ + class OSGEARTH_EXPORT MaskSource : public virtual Revisioned + { + public: + MaskSource( const MaskSourceOptions& options =MaskSourceOptions() ); + + /** + * Subclass implements this method to create the boundary geometry. + */ + virtual osg::Vec3dArray* createBoundary( const SpatialReference* srs, ProgressCallback* progress =0L ) =0; + + public: + virtual void initialize( const std::string& referenceURI, const Map* map ) { } + + const MaskSourceOptions& getOptions() const { return _options; } + public: + + // META_Object specialization: + virtual osg::Object* cloneType() const { return 0; } // cloneType() not appropriate + virtual osg::Object* clone(const osg::CopyOp&) const { return 0; } // clone() not appropriate + virtual bool isSameKindAs(const osg::Object* obj) const { return dynamic_cast(obj)!=NULL; } + virtual const char* className() const { return "MaskSource"; } + virtual const char* libraryName() const { return "osgEarth"; } + + private: + const MaskSourceOptions _options; + + friend class Map; + friend class MaskSourceFactory; + }; + + //-------------------------------------------------------------------- + + class OSGEARTH_EXPORT MaskSourceDriver : public osgDB::ReaderWriter + { + protected: + const MaskSourceOptions& getMaskSourceOptions( const osgDB::ReaderWriter::Options* rwOpt ) const; + }; + + //-------------------------------------------------------------------- + + class OSGEARTH_EXPORT MaskSourceFactory + { + public: + static MaskSource* create( const MaskSourceOptions& options ); + }; +} + +#endif // OSGEARTH_MASK_SOURCE_H diff -Nru osgearth-2.0+dfsg/src/osgEarth/MaskSource.cpp osgearth-2.1.1+dfsg/src/osgEarth/MaskSource.cpp --- osgearth-2.0+dfsg/src/osgEarth/MaskSource.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/MaskSource.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,99 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#include +#include +#include + +using namespace osgEarth; +using namespace OpenThreads; + +/****************************************************************/ + +void +MaskSourceOptions::fromConfig( const Config& conf ) +{ + //nop +} + +void +MaskSourceOptions::mergeConfig( const Config& conf ) +{ + DriverConfigOptions::mergeConfig( conf ); + fromConfig( conf ); +} + +Config +MaskSourceOptions::getConfig() const +{ + Config conf = DriverConfigOptions::getConfig(); + return conf; +} + +//------------------------------------------------------------------------ + +MaskSource::MaskSource( const MaskSourceOptions& options ) : +_options( options ) +{ + //TODO: is this really necessary? + this->setThreadSafeRefUnref( true ); +} + +//------------------------------------------------------------------------ + +#undef LC +#define LC "[MaskSourceFactory] " +#define MASK_SOURCE_OPTIONS_TAG "__osgEarth::MaskSourceOptions" + +MaskSource* +MaskSourceFactory::create( const MaskSourceOptions& options ) +{ + MaskSource* source = 0L; + + if ( !options.getDriver().empty() ) + { + std::string driverExt = std::string(".osgearth_mask_") + options.getDriver(); + + osg::ref_ptr rwopts = new osgDB::ReaderWriter::Options(); + rwopts->setPluginData( MASK_SOURCE_OPTIONS_TAG, (void*)&options ); + + source = dynamic_cast( osgDB::readObjectFile( driverExt, rwopts.get() ) ); + if ( source ) + { + OE_INFO << "Loaded MaskSource driver \"" << options.getDriver() << "\" OK" << std::endl; + } + else + { + OE_WARN << "FAIL, unable to load MaskSource driver for \"" << options.getDriver() << "\"" << std::endl; + } + } + else + { + OE_WARN << LC << "FAIL, illegal null driver specification" << std::endl; + } + + return source; +} + +//------------------------------------------------------------------------ + +const MaskSourceOptions& +MaskSourceDriver::getMaskSourceOptions( const osgDB::ReaderWriter::Options* options ) const +{ + return *static_cast( options->getPluginData( MASK_SOURCE_OPTIONS_TAG ) ); +} diff -Nru osgearth-2.0+dfsg/src/osgEarth/MimeTypes.cpp osgearth-2.1.1+dfsg/src/osgEarth/MimeTypes.cpp --- osgearth-2.0+dfsg/src/osgEarth/MimeTypes.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/MimeTypes.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -36,6 +36,8 @@ "application/x-world","wrl", "application/x-zip-compressed","zip", "application/zip","zip", + "application/vnd.google-earth.kml+xml","kml", + "application/vnd.google-earth.kmz","kmz", "drawing/x-dwf(old)","dwf", "image/bmp","bmp", "image/cmu-raster","ras", diff -Nru osgearth-2.0+dfsg/src/osgEarth/ModelLayer osgearth-2.1.1+dfsg/src/osgEarth/ModelLayer --- osgearth-2.0+dfsg/src/osgEarth/ModelLayer 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/ModelLayer 2011-11-04 19:44:43.000000000 +0000 @@ -127,12 +127,12 @@ /** * Gets the name of this model layer */ - const std::string& getName() const { return _options.name().value(); } + const std::string& getName() const { return *_runtimeOptions.name(); } /** * Gets the initialization options for this layer. */ - const ModelLayerOptions& getModelLayerOptions() const { return _options; } + const ModelLayerOptions& getModelLayerOptions() const { return _initOptions; } /** * Gets the reference URI (for resolving relative paths) @@ -158,9 +158,9 @@ bool getOverlay() const; void setOverlay( bool overlay ); - // whether to apply lighting to the model layer's root node + /** whether to apply lighting to the model layer's root node */ void setLightingEnabled( bool value ); - const optional& lightingEnabled() const { return _enabled; } + bool isLightingEnabled() const; public: @@ -178,19 +178,23 @@ private: std::string _referenceURI; osg::ref_ptr _modelSource; - const ModelLayerOptions _options; + + const ModelLayerOptions _initOptions; + ModelLayerOptions _runtimeOptions; osg::ref_ptr< osg::Node > _node; - optional _enabled; - optional _lighting; - optional _overlay; + //optional _enabled; + //optional _lighting; + //optional _overlay; Revision _modelSourceRev; ModelLayerCallbackList _callbacks; virtual void fireCallback( ModelLayerCallbackMethodPtr method ); + void copyOptions(); + }; typedef std::vector< osg::ref_ptr > ModelLayerVector; diff -Nru osgearth-2.0+dfsg/src/osgEarth/ModelLayer.cpp osgearth-2.1.1+dfsg/src/osgEarth/ModelLayer.cpp --- osgearth-2.0+dfsg/src/osgEarth/ModelLayer.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/ModelLayer.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -81,30 +81,35 @@ //------------------------------------------------------------------------ ModelLayer::ModelLayer( const ModelLayerOptions& options ) : -_options( options ), -_enabled( true ) +_initOptions( options ) { - // NOP + copyOptions(); } ModelLayer::ModelLayer( const std::string& name, const ModelSourceOptions& options ) : -_options( ModelLayerOptions( name, options ) ), -_enabled( true ) +_initOptions( ModelLayerOptions( name, options ) ) { - //NOP + copyOptions(); } ModelLayer::ModelLayer( const ModelLayerOptions& options, ModelSource* source ) : _modelSource( source ), -_options( options ) +_initOptions( options ) { - //NOP + copyOptions(); } ModelLayer::ModelLayer(const std::string& name, osg::Node* node): -_options(ModelLayerOptions( name )), +_initOptions(ModelLayerOptions( name )), _node(node) { + copyOptions(); +} + +void +ModelLayer::copyOptions() +{ + _runtimeOptions = _initOptions; } void @@ -112,9 +117,9 @@ { _referenceURI = referenceURI; - if ( !_modelSource.valid() && _options.driver().isSet() ) + if ( !_modelSource.valid() && _initOptions.driver().isSet() ) { - _modelSource = ModelSourceFactory::create( *_options.driver() ); + _modelSource = ModelSourceFactory::create( *_initOptions.driver() ); } if ( _modelSource.valid() ) @@ -138,11 +143,11 @@ { _node = _modelSource->createNode( progress ); - if ( _options.enabled().isSet() ) - setEnabled( *_options.enabled() ); + if ( _runtimeOptions.enabled().isSet() ) + setEnabled( *_runtimeOptions.enabled() ); - if ( _options.lightingEnabled().isSet() ) - setLightingEnabled( *_options.lightingEnabled() ); + if ( _runtimeOptions.lightingEnabled().isSet() ) + setLightingEnabled( *_runtimeOptions.lightingEnabled() ); if ( _modelSource->getOptions().depthTestEnabled() == false ) { @@ -164,43 +169,48 @@ bool ModelLayer::getEnabled() const { - return _enabled.get(); + return *_runtimeOptions.enabled(); } void ModelLayer::setEnabled(bool enabled) { - _enabled = enabled; + _runtimeOptions.enabled() = enabled; if ( _node.valid() ) - _node->setNodeMask( _enabled.get() ? ~0 : 0 ); + _node->setNodeMask( enabled ? ~0 : 0 ); } void ModelLayer::setLightingEnabled( bool value ) { - _lighting = value; + _runtimeOptions.lightingEnabled() = value; if ( _node.valid() ) { _node->getOrCreateStateSet()->setMode( GL_LIGHTING, value ? osg::StateAttribute::ON : (osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED) ); - OE_INFO << LC << "Set lighting to " << value << std::endl; } } bool +ModelLayer::isLightingEnabled() const +{ + return *_runtimeOptions.lightingEnabled(); +} + +bool ModelLayer::getOverlay() const { - return _overlay.get(); + return *_runtimeOptions.overlay(); } void ModelLayer::setOverlay(bool overlay) { - if (_overlay != overlay) + if ( _runtimeOptions.overlay() != overlay ) { - _overlay = overlay; + _runtimeOptions.overlay() = overlay; fireCallback( &ModelLayerCallback::onOverlayChanged ); } } diff -Nru osgearth-2.0+dfsg/src/osgEarth/NodeUtils osgearth-2.1.1+dfsg/src/osgEarth/NodeUtils --- osgearth-2.0+dfsg/src/osgEarth/NodeUtils 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/NodeUtils 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,59 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph +* Copyright 2008-2010 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 +*/ +#ifndef OSGEARTH_NODE_UTILS_H +#define OSGEARTH_NODE_UTILS_H 1 + +#include +#include + +namespace osgEarth +{ + /** + * Given a geocentric subgraph and a center point, generate a suitable + * cluster culling callback. + */ + struct OSGEARTH_EXPORT ClusterCullerFactory + { + static osg::ClusterCullingCallback* create( + osg::Node* node, + const osg::Vec3d& centerGeocentric ); + }; + + /** + * Visitor that looks for empty group nodes and removes them from the + * scene graph. (Note, if the entry node is an empty group, it will + * not be affected.) + */ + class OSGEARTH_EXPORT RemoveEmptyGroupsVisitor : public osg::NodeVisitor + { + public: + static void run( osg::Node* entry ) { + if ( entry ) { + RemoveEmptyGroupsVisitor v; + entry->accept( v ); + } + } + + public: + RemoveEmptyGroupsVisitor(); + void apply( osg::Group& group ); //override + }; +} + +#endif // OSGEARTH_CACHING_H diff -Nru osgearth-2.0+dfsg/src/osgEarth/NodeUtils.cpp osgearth-2.1.1+dfsg/src/osgEarth/NodeUtils.cpp --- osgearth-2.0+dfsg/src/osgEarth/NodeUtils.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/NodeUtils.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,302 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ + +#include +#include +#include +#include +#include + +using namespace osgEarth; + +//------------------------------------------------------------------------ + +namespace +{ + struct ComputeMaxNormalLength + { + void set( const osg::Vec3& normalECEF, const osg::Matrixd& local2world, float* maxNormalLen ) + { + _normal = normalECEF; + _local2world = local2world; + _maxNormalLen = maxNormalLen; + } + + void operator()( const osg::Vec3 &v1, bool) + { + compute( v1 ); + } + + void operator()( const osg::Vec3 &v1, const osg::Vec3 &v2, bool) + { + compute( v1 ); + compute( v2 ); + } + + void operator()( const osg::Vec3 &v1, const osg::Vec3 &v2, const osg::Vec3& v3, bool) + { + compute( v1 ); + compute( v2 ); + compute( v3 ); + } + + void operator()( const osg::Vec3 &v1, const osg::Vec3 &v2, const osg::Vec3& v3, const osg::Vec3& v4, bool) + { + compute( v1 ); + compute( v2 ); + compute( v3 ); + compute( v4 ); + } + + void compute( const osg::Vec3& v ) + { + osg::Vec3d vworld = v * _local2world; + double vlen = vworld.length(); + vworld.normalize(); + + // the dot product of the 2 vecs is the cos of the angle between them; + // mult that be the vector length to get the new normal length. + float normalLen = fabs(_normal * vworld) * vlen; + + if ( normalLen < *_maxNormalLen ) + *_maxNormalLen = normalLen; + } + + osg::Vec3 _normal; + osg::Matrixd _local2world; + float* _maxNormalLen; + }; + + struct ComputeMaxRadius2 + { + void set( const osg::Vec3& center, float* maxRadius2 ) + { + _center = center; + _maxRadius2 = maxRadius2; + } + + void operator()( const osg::Vec3 &v1, bool ) + { + compute( v1 ); + } + + void operator()( const osg::Vec3 &v1, const osg::Vec3 &v2, bool ) + { + compute( v1 ); + compute( v2 ); + } + + void operator()( const osg::Vec3 &v1, const osg::Vec3 &v2, const osg::Vec3 &v3, bool ) + { + compute( v1 ); + compute( v2 ); + compute( v3 ); + } + + void operator()( const osg::Vec3 &v1, const osg::Vec3 &v2, const osg::Vec3 &v3, const osg::Vec3& v4, bool ) + { + compute( v1 ); + compute( v2 ); + compute( v3 ); + compute( v4 ); + } + + void compute( const osg::Vec3& v ) + { + float dist = (v - _center).length2(); + if ( dist > *_maxRadius2 ) + *_maxRadius2 = dist; + } + + + + osg::Vec3 _center; + float* _maxRadius2; + }; + + struct ComputeVisitor : public osg::NodeVisitor + { + ComputeVisitor() + : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), + _maxRadius2(0.0f) { } + + void run( osg::Node* node, const osg::Vec3d& centerECEF ) + { + _centerECEF = centerECEF; + _normalECEF = _centerECEF; + _normalECEF.normalize(); + _maxNormalLen = _centerECEF.length(); + + _pass = 1; + node->accept( *this ); + + _centerECEF = _normalECEF * _maxNormalLen; + + _pass = 2; + node->accept( *this ); + } + + void apply( osg::Geode& geode ) + { + if ( _pass == 1 ) + { + osg::Matrixd local2world; + if ( !_matrixStack.empty() ) + local2world = _matrixStack.back(); + + osg::TemplatePrimitiveFunctor pass1; + pass1.set( _normalECEF, local2world, &_maxNormalLen ); + + for( unsigned i=0; iaccept( pass1 ); + } + + else // if ( _pass == 2 ) + { + osg::Vec3d center = _matrixStack.empty() ? _centerECEF : _centerECEF * osg::Matrixd::inverse(_matrixStack.back()); + + osg::TemplatePrimitiveFunctor pass2; + pass2.set( center, &_maxRadius2 ); + for( unsigned i=0; iaccept( pass2 ); + } + } + + void apply( osg::Transform& xform ) + { + osg::Matrixd matrix; + if (!_matrixStack.empty()) matrix = _matrixStack.back(); + xform.computeLocalToWorldMatrix( matrix, this ); + _matrixStack.push_back( matrix ); + traverse(xform); + _matrixStack.pop_back(); + } + + unsigned _pass; + osg::Vec3d _centerECEF; + osg::Vec3f _normalECEF; + float _maxNormalLen; + float _maxRadius2; + std::vector _matrixStack; + }; + + /** + * A customized CCC that works correctly under an RTT camera. The built-in one + * called getEyePoint() instead of getViewPoint() and therefore isn't compatible + * with osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT mode. + */ + struct MyClusterCullingCallback : public osg::ClusterCullingCallback + { + bool cull(osg::NodeVisitor* nv, osg::Drawable* , osg::State*) const + { + osg::CullSettings* cs = dynamic_cast(nv); + if (cs && !(cs->getCullingMode() & osg::CullSettings::CLUSTER_CULLING)) + { + return false; + } + + if (_deviation<=-1.0f) + { + return false; + } + + osg::Vec3 eye_cp = nv->getViewPoint() - _controlPoint; + float radius = eye_cp.length(); + + if (radius<_radius) + { + return false; + } + + float deviation = (eye_cp * _normal)/radius; + + return deviation < _deviation; + } + }; +} + +//------------------------------------------------------------------------ + +osg::ClusterCullingCallback* +ClusterCullerFactory::create( osg::Node* node, const osg::Vec3d& centerECEF ) +{ + // Cluster culling computer. This works in two passes. + // + // It starts with a control point provided by the caller. I the first pass it computes + // a new control point that is along the same geocentric normal but at a lower Z. This + // corresponds to the lowest Z of a vertex with respect to that normal. This means we + // can always use a "deviation" of 0 -- and it gets around the problem of the control + // point being higher than a vertex and corrupting the deviation. + // + // In the second pass, we compute the radius based on the new control point. + + osg::ClusterCullingCallback* ccc = 0L; + if ( node ) + { + ComputeVisitor cv; + cv.run( node, centerECEF ); + + ccc = new MyClusterCullingCallback(); //osg::ClusterCullingCallback(); + ccc->set( cv._centerECEF, cv._normalECEF, 0.0f, sqrt(cv._maxRadius2) ); + } + return ccc; +} + +//------------------------------------------------------------------------ + +RemoveEmptyGroupsVisitor::RemoveEmptyGroupsVisitor() : +osg::NodeVisitor( osg::NodeVisitor::TRAVERSE_ALL_CHILDREN ) +{ + //nop +} + +void +RemoveEmptyGroupsVisitor::apply( osg::Group& group ) +{ + bool removed = true; + while( removed ) + { + removed = false; + for( unsigned i = 0; i < group.getNumChildren(); ++i ) + { + osg::Group* child = group.getChild(i)->asGroup(); + if ( child ) + { + if (child->className() == "Group" && + child->getStateSet() == 0L && + child->getCullCallback() == 0L && + child->getUpdateCallback() == 0L && + child->getUserData() == 0L && + child->getName().empty() && + child->getDescriptions().size() == 0 ) + { + for( unsigned j = 0; j < child->getNumChildren(); ++j ) + { + group.addChild( child->getChild( j ) ); + } + + group.removeChild( i-- ); + removed = true; + } + } + } + } + + traverse(group); +} diff -Nru osgearth-2.0+dfsg/src/osgEarth/OverlayDecorator osgearth-2.1.1+dfsg/src/osgEarth/OverlayDecorator --- osgearth-2.0+dfsg/src/osgEarth/OverlayDecorator 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/OverlayDecorator 2011-11-04 19:44:43.000000000 +0000 @@ -75,6 +75,22 @@ void setMipmapping( bool value ); bool getMipmapping() const { return _mipmapping; } + /** + * Whether to employ overlay warping. Warping can help increase the apparent + * resolution of the overlay image. But it can cause artifacts when you zoom + * in very close. Default = true + */ + void setVertexWarping( bool value ); + bool getVertexWarping() const { return _warping; } + + /** + * 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; } public: // TerrainDecorator virtual void onInstall( TerrainEngineNode* engine ); @@ -82,16 +98,6 @@ public: // osg::Node void traverse( osg::NodeVisitor& nv ); - - public: //osg::Group overrides -#if 0 - virtual bool addChild( Node *child ); - virtual bool insertChild( unsigned int index, Node *child ); - virtual bool removeChildren(unsigned int pos,unsigned int numChildrenToRemove); - virtual bool replaceChild( Node *origChild, Node* newChild ); - virtual bool setChild( unsigned int i, Node* node ); - virtual osg::BoundingSphere computeBound() const; -#endif private: void updateRTTCamera( osg::NodeVisitor& nv ); @@ -101,7 +107,6 @@ void initRTTShaders( osg::StateSet* set ); private: - //osg::ref_ptr _subgraphContainer; osg::ref_ptr _overlayGraph; osg::ref_ptr _rttCamera; osg::ref_ptr _projTexture; @@ -112,20 +117,23 @@ optional _explicitTextureUnit; optional _textureUnit; optional _textureSize; - bool _reservedTextureUnit; bool _useShaders; bool _useWarping; float _warp; + bool _warping; bool _visualizeWarp; bool _isGeocentric; double _maxProjectedMapExtent; bool _mipmapping; + bool _rttBlending; + osg::Matrixd _rttViewMatrix; + osg::Matrixd _rttProjMatrix; + osg::Matrixd _projectorViewMatrix; + osg::Matrixd _projectorProjMatrix; - osg::Matrixd _rttViewMatrix, _rttProjMatrix; - osg::Matrixd _projectorViewMatrix, _projectorProjMatrix; osg::ref_ptr _ellipsoid; - osg::ref_ptr _vp; - osg::ref_ptr _engine; + osg::ref_ptr _vp; + osg::ref_ptr _engine; }; } // namespace osgEarth diff -Nru osgearth-2.0+dfsg/src/osgEarth/OverlayDecorator.cpp osgearth-2.1.1+dfsg/src/osgEarth/OverlayDecorator.cpp --- osgearth-2.0+dfsg/src/osgEarth/OverlayDecorator.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/OverlayDecorator.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -42,8 +43,7 @@ class MyConvexPolyhedron : public osgShadow::ConvexPolyhedron { public: - bool - contains(const osg::BoundingSphere& bs) const + bool intersects(const osg::BoundingSphere& bs) const { for( Faces::const_iterator i = _faces.begin(); i != _faces.end(); ++i ) { @@ -54,19 +54,36 @@ } return true; } + + bool intersects(const osg::BoundingBox& box) const + { + for( Faces::const_iterator i = _faces.begin(); i != _faces.end(); ++i ) + { + osg::Plane up = i->plane; + up.makeUnitLength(); + + if ( up.intersect(box) < 0 ) + return false; + } + return true; + } }; /** * Visits a scene graph (in our case, the overlay graph) and calculates a * geometry bounding box that intersects the provided polytope (which in out case is the * view frustum). + * + * It's called "Coarse" because it does not traverse to the Drawable level, just to + * the Geode bounding sphere level. */ struct CoarsePolytopeIntersector : public osg::NodeVisitor { CoarsePolytopeIntersector(const MyConvexPolyhedron& polytope, osg::BoundingBox& out_bbox) : osg::NodeVisitor( osg::NodeVisitor::TRAVERSE_ALL_CHILDREN ), _bbox(out_bbox), - _original( polytope ) + _original( polytope ), + _coarse( true ) { _polytopeStack.push( polytope ); _matrixStack.push( osg::Matrix::identity() ); @@ -75,7 +92,7 @@ void apply( osg::Node& node ) { const osg::BoundingSphere& bs = node.getBound(); - if ( _polytopeStack.top().contains( bs ) ) + if ( _polytopeStack.top().intersects( bs ) ) { traverse( node ); } @@ -85,15 +102,33 @@ { const osg::BoundingSphere& bs = node.getBound(); - if ( _polytopeStack.top().contains( bs ) ) + if ( _polytopeStack.top().intersects( bs ) ) { - _bbox.expandBy( - osg::BoundingSphere( bs.center() * _matrixStack.top(), bs.radius() ) ); + if ( _coarse ) + { + _bbox.expandBy( + osg::BoundingSphere( bs.center() * _matrixStack.top(), bs.radius() ) ); + } + else + { + for( unsigned i=0; i < node.getNumDrawables(); ++i ) + { + applyDrawable( node.getDrawable(i) ); + } + } + } + } + + void applyDrawable( osg::Drawable* drawable ) + { + const osg::BoundingBox& box = drawable->getBound(); - //for( int i=0; i < node.getNumDrawables(); ++i ) - //{ - // applyDrawable( node.getDrawable(i) ); - //} + if ( _polytopeStack.top().intersects( box ) ) + { + osg::Vec3d bmin = osg::Vec3(box.xMin(), box.yMin(), box.zMin()) * _matrixStack.top(); + osg::Vec3d bmax = osg::Vec3(box.xMax(), box.yMax(), box.zMax()) * _matrixStack.top(); + + _bbox.expandBy( osg::BoundingBox(bmin, bmax) ); } } @@ -116,12 +151,17 @@ MyConvexPolyhedron _original; std::stack _polytopeStack; std::stack _matrixStack; + bool _coarse; }; /** * 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. + * 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, @@ -176,22 +216,16 @@ //--------------------------------------------------------------------------- OverlayDecorator::OverlayDecorator() : -_textureUnit( 1 ), -_textureSize( 1024 ), -_reservedTextureUnit( false ), -_useShaders( false ), -_useWarping( true ), -_warp( 1.0f ), +_textureUnit ( 1 ), +_textureSize ( 1024 ), +_useShaders ( false ), +_useWarping ( false ), +_warp ( 1.0f ), _visualizeWarp( false ), -_mipmapping( true ) +_mipmapping ( true ), +_rttBlending ( true ) { - // force an update traversal: - //ADJUST_UPDATE_TRAV_COUNT( this, 1 ); - - // points to children of this group. We will override the traverse to route through - // this container. That way we can assign a stateset to the children without - // actually modifying them - //_subgraphContainer = new osg::Group(); + // nop } void @@ -201,7 +235,7 @@ if ( _overlayGraph.valid() ) { - // apply the user-request texture unit, it applicable: + // apply the user-request texture unit, if applicable: if ( _explicitTextureUnit.isSet() ) { if ( !_textureUnit.isSet() || *_textureUnit != *_explicitTextureUnit ) @@ -227,13 +261,11 @@ if ( _textureUnit.isSet() ) { - // need to pre-allocate the image here, otherwise the RTT images won't have an alpha channel: - osg::Image* image = new osg::Image(); - image->allocateImage( *_textureSize, *_textureSize, 1, GL_RGBA, GL_UNSIGNED_BYTE ); - image->setInternalTextureFormat( GL_RGBA8 ); - - _projTexture = new osg::Texture2D( image ); + _projTexture = new osg::Texture2D(); _projTexture->setTextureSize( *_textureSize, *_textureSize ); + _projTexture->setInternalFormat( GL_RGBA8 ); + _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 ); @@ -244,7 +276,9 @@ // set up the RTT camera: _rttCamera = new osg::Camera(); _rttCamera->setClearColor( osg::Vec4f(0,0,0,0) ); - _rttCamera->setReferenceFrame( osg::Camera::ABSOLUTE_RF ); + // 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) + _rttCamera->setReferenceFrame( osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT ); _rttCamera->setViewport( 0, 0, *_textureSize, *_textureSize ); _rttCamera->setComputeNearFarMode( osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR ); _rttCamera->setRenderOrder( osg::Camera::PRE_RENDER ); @@ -252,9 +286,23 @@ _rttCamera->attach( osg::Camera::COLOR_BUFFER, _projTexture.get(), 0, 0, _mipmapping ); _rttCamera->getOrCreateStateSet()->setMode( GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED ); + if ( _rttBlending ) + { + osg::BlendFunc* blendFunc = new osg::BlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + _rttCamera->getOrCreateStateSet()->setAttributeAndModes(blendFunc, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); + } + else + { + _rttCamera->getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE); + } + //Enable blending on the RTT camera with pre-multiplied alpha. + //osg::BlendFunc* blendFunc = new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE); + + // texture coordinate generator: _texGenNode = new osg::TexGenNode(); _texGenNode->setTextureUnit( *_textureUnit ); + _texGenNode->getTexGen()->setMode( osg::TexGen::EYE_LINEAR ); // attach the overlay graph to the RTT camera. if ( _overlayGraph.valid() && ( _overlayGraph->getNumParents() == 0 || _overlayGraph->getParent(0) != _rttCamera.get() )) @@ -269,7 +317,6 @@ // assemble the subgraph stateset: _subgraphStateSet = new osg::StateSet(); - //_subgraphContainer->setStateSet( _subgraphStateSet.get() ); if ( _overlayGraph.valid() && _textureUnit.isSet() ) { @@ -280,10 +327,10 @@ _subgraphStateSet->setTextureMode( *_textureUnit, GL_TEXTURE_GEN_Q, osg::StateAttribute::ON ); _subgraphStateSet->setTextureAttributeAndModes( *_textureUnit, _projTexture.get(), osg::StateAttribute::ON ); - // decalling: - osg::TexEnv* env = new osg::TexEnv(); - env->setMode( osg::TexEnv::DECAL ); - _subgraphStateSet->setTextureAttributeAndModes( *_textureUnit, env, osg::StateAttribute::ON ); + // decalling (note: this has no effect when using shaders.. remove? -gw) + //osg::TexEnv* env = new osg::TexEnv(); + //env->setMode( osg::TexEnv::DECAL ); + //_subgraphStateSet->setTextureAttributeAndModes( *_textureUnit, env, osg::StateAttribute::ON ); // set up the shaders if ( _useShaders ) @@ -306,37 +353,68 @@ set->setAttributeAndModes( program, osg::StateAttribute::ON ); std::stringstream buf; - buf << "#version 110 \n" - << "uniform float warp; \n" + buf << "#version 110 \n"; - // because the built-in pow() is busted - << "float mypow( in float x, in float y ) \n" - << "{ \n" - << " return x/(x+y-y*x); \n" - << "} \n" - - << "vec4 warpVertex( in vec4 src ) \n" - << "{ \n" - // normalize to [-1..1], then take the absolute values since we - // want to apply the warping in [0..1] on each side of zero: - << " vec2 srct = vec2( abs(src.x)/src.w, abs(src.y)/src.w ); \n" - << " vec2 sign = vec2( src.x > 0.0 ? 1.0 : -1.0, src.y > 0.0 ? 1.0 : -1.0 ); \n" - - // apply the deformation using a "deceleration" curve: - << " vec2 srcp = vec2( 1.0-mypow(1.0-srct.x,warp), 1.0-mypow(1.0-srct.y,warp) ); \n" - - // re-apply the sign. no need to un-normalize, just use w=1 instead - << " return vec4( sign.x*srcp.x, sign.y*srcp.y, src.z/src.w, 1.0 ); \n" - << "} \n" + if ( _useWarping ) + { + buf << "uniform float warp; \n" - << "void main() \n" - << "{ \n" - << " gl_Position = warpVertex( gl_ModelViewProjectionMatrix * gl_Vertex ); \n" - << " gl_FrontColor = gl_Color; \n" - << "} \n"; + // because the built-in pow() is busted + << "float mypow( in float x, in float y ) \n" + << "{ \n" + << " return x/(x+y-y*x); \n" + << "} \n" + + << "vec4 warpVertex( in vec4 src ) \n" + << "{ \n" + // normalize to [-1..1], then take the absolute values since we + // want to apply the warping in [0..1] on each side of zero: + << " vec2 srct = vec2( abs(src.x)/src.w, abs(src.y)/src.w ); \n" + << " vec2 sign = vec2( src.x > 0.0 ? 1.0 : -1.0, src.y > 0.0 ? 1.0 : -1.0 ); \n" + + // apply the deformation using a "deceleration" curve: + << " vec2 srcp = vec2( 1.0-mypow(1.0-srct.x,warp), 1.0-mypow(1.0-srct.y,warp) ); \n" + + // re-apply the sign. no need to un-normalize, just use w=1 instead + << " return vec4( sign.x*srcp.x, sign.y*srcp.y, src.z/src.w, 1.0 ); \n" + << "} \n" + + << "void main() \n" + << "{ \n" + << " gl_Position = warpVertex( gl_ModelViewProjectionMatrix * gl_Vertex ); \n" + << " gl_FrontColor = gl_Color; \n" + << " gl_TexCoord[0] = gl_MultiTexCoord0;\n" + << "} \n"; + } + + else // no vertex warping + { + buf << "void main() \n" + << "{ \n" + << " gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; \n" + << " gl_FrontColor = gl_Color; \n" + << " gl_TexCoord[0] = gl_MultiTexCoord0;\n" + << "} \n"; + } std::string vertSource = buf.str(); program->addShader( new osg::Shader( osg::Shader::VERTEX, vertSource ) ); + + std::stringstream fragBuf; + fragBuf << "#version 110 \n" + << "uniform sampler2D texture_0; \n" + << "void main() \n" + << "{\n" + << " vec4 tex = texture2D(texture_0, gl_TexCoord[0].xy);\n" + << " vec3 mixed_color = mix(gl_Color.rgb, tex.rgb, tex.a);\n" + //<< " gl_FragColor = vec4(mixed_color, gl_Color.a); \n" + << " gl_FragColor = vec4(mixed_color, gl_Color.a); \n" + << "}\n"; + + std::string fragSource = fragBuf.str(); + + program->addShader( new osg::Shader( osg::Shader::FRAGMENT, fragSource ) ); + set->addUniform(new osg::Uniform("texture_0",0)); } void @@ -370,41 +448,45 @@ // fragment shader - subgraph buf.str(""); buf << "#version 110 \n" - << "uniform sampler2D osgearth_overlay_ProjTex; \n" - << "uniform float warp; \n" + << "uniform sampler2D osgearth_overlay_ProjTex; \n"; - // because the built-in pow() is busted - << "float mypow( in float x, in float y ) \n" - << "{ \n" - << " return x/(x+y-y*x); \n" - << "} \n" + if ( _useWarping ) + { + buf << "uniform float warp; \n" - << "vec2 warpTexCoord( in vec2 src ) \n" - << "{ \n" - // incoming tex coord is [0..1], so we scale to [-1..1] - << " vec2 srcn = vec2( src.x*2.0 - 1.0, src.y*2.0 - 1.0 ); \n" - - // we want to work in the [0..1] space on each side of 0, so can the abs - // and store the signs for later: - << " vec2 srct = vec2( abs(srcn.x), abs(srcn.y) ); \n" - << " vec2 sign = vec2( srcn.x > 0.0 ? 1.0 : -1.0, srcn.y > 0.0 ? 1.0 : -1.0 ); \n" - - // apply the deformation using a deceleration curve: - << " vec2 srcp = vec2( 1.0-mypow(1.0-srct.x,warp), 1.0-mypow(1.0-srct.y,warp) ); \n" - - // reapply the sign, and scale back to [0..1]: - << " vec2 srcr = vec2( sign.x*srcp.x, sign.y*srcp.y ); \n" - << " return vec2( 0.5*(srcr.x + 1.0), 0.5*(srcr.y + 1.0) ); \n" - << "} \n" + // because the built-in pow() is busted + << "float mypow( in float x, in float y ) \n" + << "{ \n" + << " return x/(x+y-y*x); \n" + << "} \n" + + << "vec2 warpTexCoord( in vec2 src ) \n" + << "{ \n" + // incoming tex coord is [0..1], so we scale to [-1..1] + << " vec2 srcn = vec2( src.x*2.0 - 1.0, src.y*2.0 - 1.0 ); \n" + + // we want to work in the [0..1] space on each side of 0, so can the abs + // and store the signs for later: + << " vec2 srct = vec2( abs(srcn.x), abs(srcn.y) ); \n" + << " vec2 sign = vec2( srcn.x > 0.0 ? 1.0 : -1.0, srcn.y > 0.0 ? 1.0 : -1.0 ); \n" + + // apply the deformation using a deceleration curve: + << " vec2 srcp = vec2( 1.0-mypow(1.0-srct.x,warp), 1.0-mypow(1.0-srct.y,warp) ); \n" + + // reapply the sign, and scale back to [0..1]: + << " vec2 srcr = vec2( sign.x*srcp.x, sign.y*srcp.y ); \n" + << " return vec2( 0.5*(srcr.x + 1.0), 0.5*(srcr.y + 1.0) ); \n" + << "} \n"; + } - << "void osgearth_overlay_fragment( inout vec4 color ) \n" + buf << "void osgearth_overlay_fragment( inout vec4 color ) \n" << "{ \n" << " vec2 texCoord = gl_TexCoord["<< *_textureUnit << "].xy / gl_TexCoord["<< *_textureUnit << "].q; \n"; - if ( !_visualizeWarp ) + if ( _useWarping && !_visualizeWarp ) buf << " texCoord = warpTexCoord( texCoord ); \n"; - buf << " vec4 texel = texture2D(osgearth_overlay_ProjTex, texCoord); \n" + buf << " vec4 texel = texture2D(osgearth_overlay_ProjTex, texCoord); \n" << " color = vec4( mix( color.rgb, texel.rgb, texel.a ), color.a); \n" << "} \n"; @@ -444,7 +526,7 @@ void OverlayDecorator::setTextureUnit( int texUnit ) { - if ( texUnit != _explicitTextureUnit.value() ) + if ( !_explicitTextureUnit.isSet() || texUnit != _explicitTextureUnit.value() ) { _explicitTextureUnit = texUnit; reinit(); @@ -458,6 +540,35 @@ { _mipmapping = value; reinit(); + + if ( _mipmapping ) + OE_INFO << LC << "Overlay mipmapping " << (value?"enabled":"disabled") << std::endl; + } +} + +void +OverlayDecorator::setVertexWarping( bool value ) +{ + if ( value != _useWarping ) + { + _useWarping = value; + reinit(); + + if ( _useWarping ) + OE_INFO << LC << "Vertex warping " << (value?"enabled":"disabled")<< std::endl; + } +} + +void +OverlayDecorator::setOverlayBlending( bool value ) +{ + if ( value != _rttBlending ) + { + _rttBlending = value; + reinit(); + + if ( _rttBlending ) + OE_INFO << LC << "Overlay blending " << (value?"enabled":"disabled")<< std::endl; } } @@ -485,8 +596,8 @@ if ( !_textureSize.isSet() ) { - int maxSize = Registry::instance()->getCapabilities().getMaxTextureSize(); - _textureSize.init( osg::minimum( 4096, maxSize ) ); + unsigned maxSize = Registry::instance()->getCapabilities().getMaxFastTextureSize(); + _textureSize.init( osg::minimum( 4096u, maxSize ) ); OE_INFO << LC << "Using texture size = " << *_textureSize << std::endl; } @@ -498,11 +609,10 @@ void OverlayDecorator::onUninstall( TerrainEngineNode* engine ) { - if ( _reservedTextureUnit ) + if ( !_explicitTextureUnit.isSet() && _textureUnit.isSet() ) { _engine->getTextureCompositor()->releaseTextureImageUnit( *_textureUnit ); _textureUnit.unset(); - _reservedTextureUnit = false; } _engine = 0L; @@ -511,14 +621,16 @@ void OverlayDecorator::updateRTTCamera( osg::NodeVisitor& nv ) { + static osg::Matrix normalizeMatrix = + osg::Matrix::translate(1.0,1.0,1.0) * osg::Matrix::scale(0.5,0.5,0.5); + // configure the RTT camera: _rttCamera->setViewMatrix( _rttViewMatrix ); _rttCamera->setProjectionMatrix( _rttProjMatrix ); - // configure the Projector camera: - osg::Matrix MVP = _projectorViewMatrix * _projectorProjMatrix; - osg::Matrix MVPT = MVP * osg::Matrix::translate(1.0,1.0,1.0) * osg::Matrix::scale(0.5,0.5,0.5); - _texGenNode->getTexGen()->setMode( osg::TexGen::EYE_LINEAR ); + osg::Matrix MVPT = _projectorViewMatrix * _projectorProjMatrix * normalizeMatrix; + + //_texGenNode->getTexGen()->setMode( osg::TexGen::EYE_LINEAR ); // moved to initialization _texGenNode->getTexGen()->setPlanesFromMatrix( MVPT ); // uniform update: @@ -530,11 +642,11 @@ } } -static int s_frame = 1; - void OverlayDecorator::cull( osgUtil::CullVisitor* cv ) { + static int s_frame = 1; + osg::Vec3 eye = cv->getEyePoint(); double eyeLen; @@ -571,8 +683,6 @@ // data beyond the visible horizon. double pitchAngleOfHorizon_rad = acos( horizonDistance/eyeLen ); horizonDistanceInRTTPlane = horizonDistance * sin( pitchAngleOfHorizon_rad ); - - _rttViewMatrix = osg::Matrixd::lookAt( eye, osg::Vec3(0,0,0), osg::Vec3(0,0,1) ); } else // projected map { @@ -606,125 +716,120 @@ 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. + double zSavedNear = cv->getCalculatedNearPlane(); + double zSavedFar = cv->getCalculatedFarPlane(); + + cv->setCalculatedNearPlane( FLT_MAX ); + cv->setCalculatedFarPlane( -FLT_MAX ); + // cull the subgraph here. This doubles as the subgraph's official cull traversal // and a gathering of its clip planes. cv->pushStateSet( _subgraphStateSet.get() ); osg::Group::traverse( *cv ); cv->popStateSet(); - //_subgraphContainer->accept( *cv ); - - cv->computeNearPlane(); - // --- FIRST PASS ------------------------ - - // First, intersect the view frustum with the overlay geometry. This will provide - // a maximum required extent for our ortho RTT camera. Depending on the layout of - // the geometry in the overlay graph, this may or may not be optimal ... we will - // work to refine it in later passes if necessary. - - double znear = cv->getCalculatedNearPlane(); - double zfar = cv->getCalculatedFarPlane(); + // Pull a copy of the projection matrix; we will use this to calculate the optimum + // projected texture extent osg::Matrixd projMatrix = *cv->getProjectionMatrix(); - cv->clampProjectionMatrixImplementation( projMatrix, znear, zfar ); - // collect the bounds of overlay geometry that intersects the view frustum. - MyConvexPolyhedron viewPT; - viewPT.setToUnitFrustum( true, true ); - osg::Matrixd viewMVP = (*cv->getModelViewMatrix()) * projMatrix; - viewPT.transform( osg::Matrix::inverse(viewMVP), viewMVP ); + // Clamp the projection matrix to the newly calculated clip planes. This prevents + // any "leakage" from outside the subraph. + double zNear = cv->getCalculatedNearPlane(); + double zFar = cv->getCalculatedFarPlane(); + cv->clampProjectionMatrix( projMatrix, zNear, zFar ); - osg::BoundingBox viewbbox; - CoarsePolytopeIntersector cpi( viewPT, viewbbox ); - _overlayGraph->accept( cpi ); + //OE_NOTICE << std::fixed << "zNear = " << zNear << ", zFar = " << zFar << std::endl; - //TODO: sometimes this viewbbox goes invalid even though there's clearly goemetry - // in view. Happens when you zoom in really close. Need to investigate -gw - //OE_INFO << LC << "OV radius = " << viewbbox.radius() << std::endl; - if ( viewbbox.valid() ) + if ( _isGeocentric ) { - getMinMaxExtentInSilhouette( from, rttLookVec, viewbbox, eMin, eMax ); - eMax = osg::minimum( eMax, horizonDistanceInRTTPlane ); - } - - if ( !_isGeocentric ) - eyeLen = zfar; + // in geocentric mode, clamp the far clip plane to the horizon. + double maxDistance = (1.0 - haslWeight) * horizonDistance + haslWeight * hasl; + maxDistance *= 1.5; + if (zFar - zNear >= maxDistance) + zFar = zNear + maxDistance; -#if 0 - if ( s_frame++ % 100 == 0 ) - { - osgShadow::ConvexPolyhedron tempPH; - tempPH.setToUnitFrustum(true, true); - tempPH.transform(osg::Matrix::inverse(viewMVP), viewMVP); - tempPH.dumpGeometry(); + cv->clampProjectionMatrix( projMatrix, zNear, zFar ); } -#endif - // simple test...TODO - bool needSecondPass = true; - - // --- SECOND PASS: -------------------------- - - // If the calculated eMax isn't quite good enough, go on to calculate a better one - if ( needSecondPass ) - { - // Remake the projection matrix with better hueristic far clipping plane. - // (Jason's method) - //osg::Matrixd projMatrix = *cv->getProjectionMatrix(); - double fovy, aspectRatio, zfar, znear; - cv->getProjectionMatrix()->getPerspective( fovy, aspectRatio, znear, zfar ); - double maxDistance = (1.0 - haslWeight) * horizonDistance + haslWeight * hasl; - maxDistance *= 1.5; - if (zfar - znear >= maxDistance) - zfar = znear + maxDistance; - projMatrix.makePerspective( fovy, aspectRatio, znear, zfar ); + // restore the clip planes in the cull visitor, now that we have our subgraph + // projection matrix. + cv->setCalculatedNearPlane( osg::minimum(zSavedNear, zNear) ); + cv->setCalculatedFarPlane( osg::maximum(zSavedFar, zFar) ); - // contruct the polyhedron representing the viewing frustum. - osgShadow::ConvexPolyhedron frustumPH; - frustumPH.setToUnitFrustum( true, true ); - osg::Matrixd MVP = *cv->getModelViewMatrix() * projMatrix; - osg::Matrixd inverseMVP; - inverseMVP.invert(MVP); - frustumPH.transform( inverseMVP, MVP ); + // contruct the polyhedron representing the viewing frustum. + //osgShadow::ConvexPolyhedron frustumPH; + MyConvexPolyhedron frustumPH; + frustumPH.setToUnitFrustum( true, true ); + osg::Matrixd MVP = *cv->getModelViewMatrix() * projMatrix; + osg::Matrixd inverseMVP; + inverseMVP.invert(MVP); + frustumPH.transform( inverseMVP, MVP ); + + // make a polyhedron representing the viewing frustum of the overlay, and cut it to + // intersect the viewing frustum: + osgShadow::ConvexPolyhedron visiblePH; + + // get the bounds of the overlay graph model. + osg::BoundingBox visibleOverlayBBox; + CoarsePolytopeIntersector cpi( frustumPH, visibleOverlayBBox ); + _overlayGraph->accept( cpi ); + visiblePH.setToBoundingBox( visibleOverlayBBox ); - // make a polyhedron representing the viewing frustum of the overlay, and cut it to - // intersect the viewing frustum: - osgShadow::ConvexPolyhedron visiblePH; - - //const osg::BoundingSphere& bs = _subgraphContainer->getBound(); - const osg::BoundingSphere& bs = osg::Group::getBound(); - - // get the bounds of the model. - osg::ComputeBoundsVisitor cbbv(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN); - //_subgraphContainer->accept(cbbv); - this->accept(cbbv); - visiblePH.setToBoundingBox(cbbv.getBoundingBox()); - - // 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.) - visiblePH.cut( frustumPH ); + // 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.) + visiblePH.cut( frustumPH ); + + // calculate the extents for our orthographic RTT camera (clamping it to the + // visible horizon) + std::vector verts; + visiblePH.getPoints( verts ); -#if 0 - // dumps a copy of the PH to disk...handy - if ( s_frame++ % 100 == 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 ) { - visiblePH.dumpGeometry(); - OE_INFO << "DUMP" << std::endl; + eye2bc.normalize(); + bc = eye + eye2bc * 0.5*horizonDistance; } -#endif - - // calculate the extents for our orthographic RTT camera (clamping it to the - // visible horizon) - std::vector verts; - visiblePH.getPoints( verts ); + + rttLookVec = -bc; + rttLookVec.normalize(); double new_eMax; - getMinMaxExtentInSilhouette( from, rttLookVec, verts, eMin, new_eMax ); - eMax = osg::minimum( eMax, new_eMax ); - //eMax = osg::minimum( eMax, horizonDistanceInRTTPlane ); // already done in first pass -gw + getMinMaxExtentInSilhouette( bc, rttLookVec, verts, eMin, new_eMax ); + eMax = std::min( eMax, new_eMax ); + _rttViewMatrix = osg::Matrixd::lookAt( bc, osg::Vec3d(0,0,0), osg::Vec3d(0,0,1) ); + _rttProjMatrix = osg::Matrixd::ortho( -eMax, eMax, -eMax, eMax, -eyeLen, bc.length() ); + + //OE_INFO << std::fixed << std::setprecision(1) + // << "eMax = " << eMax + // << ", bc = " << bc.x() << ", " << bc.y() << ", " << bc.z() + // << ", eye = " << eye.x() << ", " << eye.y() << ", " << eye.z() + // << ", eyeLen = " << eyeLen + // << std::endl; + } + 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 ); + _rttProjMatrix = osg::Matrix::ortho( -eMax, eMax, -eMax, eMax, -eyeLen, eyeLen ); } - _rttProjMatrix = osg::Matrix::ortho( -eMax, eMax, -eMax, eMax, -eyeLen, eyeLen ); + //OE_NOTICE << LC << "EMIN = " << eMin << ", EMAX = " << eMax << std::endl; if ( _useWarping ) { @@ -803,7 +908,6 @@ _rttCamera->accept( nv ); _texGenNode->accept( nv ); - //_subgraphContainer->accept( nv ); osg::Group::traverse( nv ); } } @@ -820,44 +924,6 @@ if ( cv ) cv->popStateSet(); - //_subgraphContainer->accept( nv ); } } - -#if 0 -/** Override all the osg::Group methods: */ - -bool -OverlayDecorator::addChild( Node *child ) { - if ( !child ) return false; - dirtyBound(); - return _subgraphContainer->addChild( child ); -} -bool -OverlayDecorator::insertChild( unsigned int index, Node *child ) { - if ( !child ) return false; - dirtyBound(); - return _subgraphContainer->insertChild( index, child ); -} -bool -OverlayDecorator::removeChildren(unsigned int pos,unsigned int numChildrenToRemove) { - dirtyBound(); - return _subgraphContainer->removeChildren( pos, numChildrenToRemove ); -} -bool -OverlayDecorator::replaceChild( Node *origChild, Node* newChild ) { - dirtyBound(); - return _subgraphContainer->replaceChild( origChild, newChild ); -} -bool -OverlayDecorator::setChild( unsigned int i, Node* node ) { - dirtyBound(); - return _subgraphContainer->setChild( i, node ); -} - -osg::BoundingSphere -OverlayDecorator::computeBound() const { - return _subgraphContainer->computeBound(); -} -#endif \ No newline at end of file diff -Nru osgearth-2.0+dfsg/src/osgEarth/Profile osgearth-2.1.1+dfsg/src/osgEarth/Profile --- osgearth-2.0+dfsg/src/osgEarth/Profile 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/Profile 2011-11-04 19:44:43.000000000 +0000 @@ -227,7 +227,7 @@ * clamped extents to this profile's SRS, and returns the result. Returned GeoExtent::INVALID * if the transformation fails. */ - GeoExtent clampAndTransformExtent( const GeoExtent& input ) const; + GeoExtent clampAndTransformExtent( const GeoExtent& input, bool* out_clamped =0L ) const; /** * Creates a tile key for a tile that contains the input location at the specified LOD. diff -Nru osgearth-2.0+dfsg/src/osgEarth/Profile.cpp osgearth-2.1.1+dfsg/src/osgEarth/Profile.cpp --- osgearth-2.0+dfsg/src/osgEarth/Profile.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/Profile.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -188,7 +188,7 @@ // automatically figure out proper mercator extents: GDAL_SCOPED_LOCK; double e, dummy; - srs->getGeographicSRS()->transform( 180.0, 0.0, srs.get(), e, dummy ); + srs->getGeographicSRS()->transform2D( 180.0, 0.0, srs.get(), e, dummy ); return Profile::create( srs.get(), -e, -e, e, e, vsrs.get(), numTilesWideAtLod0, numTilesHighAtLod0 ); } else @@ -455,8 +455,11 @@ } GeoExtent -Profile::clampAndTransformExtent( const GeoExtent& input ) const +Profile::clampAndTransformExtent( const GeoExtent& input, bool* out_clamped ) const { + if ( out_clamped ) + *out_clamped = false; + // do the clamping in LAT/LONG. const SpatialReference* geo_srs = getSRS()->getGeographicSRS(); @@ -477,13 +480,19 @@ osg::clampBetween( gcs_input.xMax(), _latlong_extent.xMin(), _latlong_extent.xMax() ), osg::clampBetween( gcs_input.yMax(), _latlong_extent.yMin(), _latlong_extent.yMax() ) ); + if ( out_clamped ) + *out_clamped = (clamped_gcs_input != gcs_input); + // finally, transform the clamped extent into this profile's SRS and return it. GeoExtent result = clamped_gcs_input.getSRS()->isEquivalentTo( this->getSRS() )? clamped_gcs_input : clamped_gcs_input.transform( this->getSRS() ); - OE_DEBUG << LC << "clamp&xform: input=" << input.toString() << ", output=" << result.toString() << std::endl; + if (result.isValid()) + { + OE_DEBUG << LC << "clamp&xform: input=" << input.toString() << ", output=" << result.toString() << std::endl; + } return result; } diff -Nru osgearth-2.0+dfsg/src/osgEarth/Progress osgearth-2.1.1+dfsg/src/osgEarth/Progress --- osgearth-2.0+dfsg/src/osgEarth/Progress 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/Progress 2011-11-04 19:44:43.000000000 +0000 @@ -34,6 +34,7 @@ * Creates a new ProgressCallback */ ProgressCallback(); + virtual ~ProgressCallback() { } /** * Callback function that will be called. @@ -41,10 +42,18 @@ * The current amount of work to be done. * @param total * The total amount of work to be done. + * @param msg + * Description of what is being done. Useful when total is unknown. * @param returns * Returns true if the current task should be cancelled, false otherwise. */ - virtual bool reportProgress(double current, double total); + virtual bool reportProgress(double current, double total, const std::string& msg = std::string()); + + // called when the process starts + virtual void onStarted() { } + + // called when the process completed + virtual void onCompleted() { } void cancel() { _canceled = true; } bool isCanceled() const { return _canceled; } @@ -76,6 +85,7 @@ * Creates a new ConsoleProgressCallback */ ConsoleProgressCallback(); + virtual ~ConsoleProgressCallback() { } /** * Callback function that will be called. @@ -86,7 +96,7 @@ * @param returns * Returns true if the current task should be cancelled, false otherwise. */ - virtual bool reportProgress(double current, double total); + virtual bool reportProgress(double current, double total, const std::string& msg = std::string()); }; } diff -Nru osgearth-2.0+dfsg/src/osgEarth/Progress.cpp osgearth-2.1.1+dfsg/src/osgEarth/Progress.cpp --- osgearth-2.0+dfsg/src/osgEarth/Progress.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/Progress.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -28,10 +28,10 @@ _canceled(false), _needsRetry(false) { + //NOP } -bool -ProgressCallback::reportProgress(double current, double total) +bool ProgressCallback::reportProgress(double /*current*/, double /*total*/, const std::string& /*msg*/) { return false; } @@ -40,12 +40,20 @@ ConsoleProgressCallback::ConsoleProgressCallback() : ProgressCallback() { + //NOP } bool -ConsoleProgressCallback::reportProgress(double current, double total) +ConsoleProgressCallback::reportProgress(double current, double total, const std::string& msg) { - double percentComplete = (current / total) * 100.0; - OE_NOTICE << "Completed " << percentComplete << "% " << current << " of " << total << std::endl; + if (total > 0) + { + double percentComplete = (current / total) * 100.0; + OE_NOTICE << "Completed " << percentComplete << "% " << current << " of " << total << std::endl; + } + else + { + OE_NOTICE << msg << std::endl; + } return false; -} \ No newline at end of file +} diff -Nru osgearth-2.0+dfsg/src/osgEarth/Random osgearth-2.1.1+dfsg/src/osgEarth/Random --- osgearth-2.0+dfsg/src/osgEarth/Random 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/Random 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,82 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph +* Copyright 2008-2010 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 +*/ +#ifndef OSGEARTH_RANDOM_H +#define OSGEARTH_RANDOM_H 1 + +#include + +namespace osgEarth +{ + /** + * Psuedo random number generator. Results are guaranteed to be consistent across + * machines given the same seed value. + */ + class OSGEARTH_EXPORT Random + { + public: + enum Method + { + METHOD_FAST // not great, but super-fast and cheap. + }; + + public: + /** + * Constructs a new random number generator. It is seeded with + * the system clock. + * @param method RNG method to use + */ + Random( Method method =METHOD_FAST ); + + /** + * Constucts a new random number generator with a user-specified seed. + * The results are guaranteed to be globally consistent. + * @param seed RNG seed value + * @param method RNG method to use + */ + Random( unsigned seed, Method method =METHOD_FAST ); + + /** + * Copy constructor. + */ + Random( const Random& rhs ); + + /** + * Resets the PRNG to its initial state (initial seed). + */ + void reset(); + + /** + * Gets the next random number as an unsigned int. + * @param mod Modulus value. The result will fall within the range [0..mod) + */ + unsigned next( unsigned mod ); + + /** + * Gets the next random number as a double in the range [0..1]. + */ + double next(); + + private: + Method _method; + unsigned _seed; + unsigned _next; + }; +} + +#endif // OSGEARTH_RANDOM_H diff -Nru osgearth-2.0+dfsg/src/osgEarth/Random.cpp osgearth-2.1.1+dfsg/src/osgEarth/Random.cpp --- osgearth-2.0+dfsg/src/osgEarth/Random.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/Random.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,83 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#include +#include +#include + +#define LC "[Random] " + +using namespace osgEarth; + +//------------------------------------------------------------------------ + +namespace +{ + void fast_rand( unsigned& next ) + { + // note: this is not a "good" PRNG, but it is good enough for some applications + // and it is wicked fast. + next = next * 1103515245 + 12345; + } +} + +//------------------------------------------------------------------------ + +Random::Random( Random::Method method ) : +_method( method ), +_seed ( (unsigned)::time(0L) ) +{ + _next = _seed; +} + +Random::Random( unsigned seed, Random::Method method ) : +_method( method ), +_seed ( seed == 0 ? (unsigned)::time(0L) : seed ) // seed=0 => time-based +{ + _next = _seed; +} + +Random::Random( const Random& rhs ) : +_method( rhs._method ), +_seed ( rhs._seed ), +_next ( rhs._next ) +{ + //nop +} + +void +Random::reset() +{ + _next = _seed; +} + +unsigned +Random::next( unsigned mod ) +{ + if ( _method == METHOD_FAST ) + { + fast_rand( _next ); + } + return mod == UINT_MAX ? _next : _next % mod; +} + +double +Random::next() +{ + return (double)next(UINT_MAX) / (double)UINT_MAX; +} diff -Nru osgearth-2.0+dfsg/src/osgEarth/Registry osgearth-2.1.1+dfsg/src/osgEarth/Registry --- osgearth-2.0+dfsg/src/osgEarth/Registry 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/Registry 2011-11-04 19:44:43.000000000 +0000 @@ -25,18 +25,18 @@ #include #include #include -#include #include #include #include +#include #define GDAL_SCOPED_LOCK \ OpenThreads::ScopedLock _slock( osgEarth::Registry::instance()->getGDALMutex() )\ namespace osgEarth -{ - typedef int GUID; +{ + class ShaderFactory; /** * Application-wide global repository. @@ -63,8 +63,8 @@ OpenThreads::ReentrantMutex& getGDALMutex(); /** Global override of map caching settings. */ - Cache* getCacheOverride() const; - void setCacheOverride( Cache* cacheOverride ); + Cache* getCacheOverride() const; + void setCacheOverride( Cache* cacheOverride ); /** Registers a mapping of a mime-type to an extension. A process fetching data * over HTTP can use this facility to determine the proper ReaderWriter to use @@ -92,6 +92,11 @@ unsigned int getNumBlacklistedFilenames(); /** + * Clears the blacklist + */ + void clearBlacklist(); + + /** * The system wide default vertical spatial reference system. */ const VerticalSpatialReference* getDefaultVSRS() const; @@ -113,13 +118,31 @@ * Gets a reference to the global task service manager. */ TaskServiceManager* getTaskServiceManager() { - return _taskServiceManager; } + return _taskServiceManager.get(); } /** * Generates an instance-wide global unique ID. */ UID createUID(); + /** + * Gets or sets the local root of the system default cache. + */ + void setCacheDirectory( const std::string& dir ); + + const std::string& getCacheDirectory() const; + + /** + * Gets the default set of osgDB::Options to use. + */ + const osgDB::Options* getDefaultOptions() const { return _defaultOptions.get(); } + + /** + * Clones an options structure (fixing the archive caching), or creates + * a new one. + */ + osgDB::Options* cloneOrCreateOptions( const osgDB::Options* options =0L ) const; + protected: virtual ~Registry(); Registry(); @@ -157,6 +180,8 @@ osg::ref_ptr< Capabilities > _caps; void initCapabilities(); + + osg::ref_ptr _defaultOptions; }; } diff -Nru osgearth-2.0+dfsg/src/osgEarth/Registry.cpp osgearth-2.1.1+dfsg/src/osgEarth/Registry.cpp --- osgearth-2.0+dfsg/src/osgEarth/Registry.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/Registry.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -18,7 +18,10 @@ */ #include #include +#include +#include #include +#include #include #include #include @@ -31,6 +34,8 @@ #define STR_CUBE "cube" #define STR_LOCAL "local" +#define LC "[Registry] " + // from MimeTypes.cpp extern const char* builtinMimeTypeExtMappings[]; @@ -55,6 +60,29 @@ _shaderLib = new ShaderFactory(); _taskServiceManager = new TaskServiceManager(); + + // activate KMZ support + osgDB::Registry::instance()->addFileExtensionAlias( "kmz", "kml" ); + osgDB::Registry::instance()->addArchiveExtension( "kmz" ); +#if OSG_MIN_VERSION_REQUIRED(3,0,0) + osgDB::Registry::instance()->addMimeTypeExtensionMapping( "application/vnd.google-earth.kml+xml", "kml" ); + osgDB::Registry::instance()->addMimeTypeExtensionMapping( "application/vnd.google-earth.kmz", "kmz" ); +#endif + + // set up our default r/w options to NOT cache archives! + _defaultOptions = new osgDB::Options(); + _defaultOptions->setObjectCacheHint( (osgDB::Options::CacheHintOptions) + ((int)_defaultOptions->getObjectCacheHint() & ~osgDB::Options::CACHE_ARCHIVES) ); + + // see if there's a cache in the envvar + const char* cachePath = ::getenv("OSGEARTH_CACHE_PATH"); + if ( cachePath ) + { + TMSCacheOptions tmso; + tmso.setPath( std::string(cachePath) ); + setCacheOverride( new TMSCache(tmso) ); + OE_INFO << LC << "Setting cache (from env.var.) to " << tmso.path() << std::endl; + } } Registry::~Registry() @@ -120,11 +148,12 @@ { // automatically figure out proper mercator extents: const SpatialReference* srs = SpatialReference::create( "spherical-mercator" ); - double e, dummy; - srs->getGeographicSRS()->transform( 180.0, 0.0, srs, e, dummy ); - + //double e, dummy; + //srs->getGeographicSRS()->transform( 180.0, 0.0, srs, e, dummy ); + /*const_cast(this)->_global_mercator_profile = Profile::create( + srs, -e, -e, e, e, 0L, 1, 1 );*/ const_cast(this)->_global_mercator_profile = Profile::create( - srs, -e, -e, e, e, 0L, 1, 1 ); + srs, MERC_MINX, MERC_MINY, MERC_MAXX, MERC_MAXY, 0L, 1, 1 ); } } return _global_mercator_profile.get(); @@ -207,6 +236,14 @@ OE_DEBUG << "Blacklist size = " << _blacklistedFilenames.size() << std::endl; } +void +Registry::clearBlacklist() +{ + OpenThreads::ScopedLock lock(_blacklistMutex); + _blacklistedFilenames.clear(); + OE_DEBUG << "Blacklist size = " << _blacklistedFilenames.size() << std::endl; +} + unsigned int Registry::getNumBlacklistedFilenames() { @@ -253,6 +290,21 @@ return (UID)( _uidGen++ ); } +osgDB::Options* +Registry::cloneOrCreateOptions( const osgDB::Options* input ) const +{ + osgDB::Options* newOptions = input ? static_cast(input->clone(osg::CopyOp::SHALLOW_COPY)) : new osgDB::Options(); + + // clear the CACHE_ARCHIVES flag because it is evil + if ( ((int)newOptions->getObjectCacheHint() & osgDB::Options::CACHE_ARCHIVES) != 0 ) + { + newOptions->setObjectCacheHint( (osgDB::Options::CacheHintOptions) + ((int)newOptions->getObjectCacheHint() & ~osgDB::Options::CACHE_ARCHIVES) ); + } + + return newOptions; +} + //Simple class used to add a file extension alias for the earth_tile to the earth plugin class RegisterEarthTileExtension { diff -Nru osgearth-2.0+dfsg/src/osgEarth/ShaderComposition osgearth-2.1.1+dfsg/src/osgEarth/ShaderComposition --- osgearth-2.0+dfsg/src/osgEarth/ShaderComposition 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/ShaderComposition 2011-11-04 19:44:43.000000000 +0000 @@ -127,7 +127,7 @@ VirtualProgram( const VirtualProgram& VirtualProgram, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY ); - META_StateAttribute( osgCandidate, VirtualProgram, Type( PROGRAM ) ) + META_StateAttribute( osgEarth, VirtualProgram, Type( PROGRAM ) ) /** return -1 if *this < *rhs, 0 if *this==*rhs, 1 if *this>*rhs.*/ virtual int compare(const StateAttribute& sa) const diff -Nru osgearth-2.0+dfsg/src/osgEarth/ShaderComposition.cpp osgearth-2.1.1+dfsg/src/osgEarth/ShaderComposition.cpp --- osgearth-2.0+dfsg/src/osgEarth/ShaderComposition.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/ShaderComposition.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -129,6 +129,9 @@ _shaderMap.erase( ShaderMap::key_type( shaderSemantic, type ) ); } +static unsigned s_applies = 0; +static int s_framenum = 0; + void VirtualProgram::apply( osg::State & state ) const { @@ -235,7 +238,7 @@ } // finally, apply the program attribute. - state.applyAttribute( program ); + program->apply( state ); } else { @@ -310,6 +313,7 @@ buf << "void osgearth_vert_setupTexturing(); \n" << "void osgearth_vert_setupLighting(); \n" << "uniform bool osgearth_LightingEnabled; \n" + << "uniform float osgearth_CameraElevation; \n" << "varying float osgearth_CameraRange; \n"; if ( preTexture ) @@ -331,6 +335,8 @@ << " vec4 position4 = gl_ModelViewMatrix * gl_Vertex; \n" << " osgearth_CameraRange = length( position4.xyz ); \n" +// << " vec3 cameraPos = normalize(vec3( osg_ViewMatrixInverse[3][0], osg_ViewMatrixInverse[3][1], osg_ViewMatrixInverse[3][2] )); + << " vec3 position = position4.xyz / position4.w; \n" << " vec3 normal = normalize( gl_NormalMatrix * gl_Normal ); \n"; @@ -506,9 +512,11 @@ ShaderFactory::createDefaultLightingFragmentShader() const { static char s_PerVertexLighting_FragmentShaderSource[] = - "void osgearth_frag_applyLighting( inout vec4 color ) \n" + "void osgearth_frag_applyLighting( inout vec4 color ) \n" "{ \n" + " float alpha = color.a; \n" " color = color * gl_Color + gl_SecondaryColor; \n" + " color.a = alpha; \n" "} \n"; return new osg::Shader( osg::Shader::FRAGMENT, s_PerVertexLighting_FragmentShaderSource ); diff -Nru osgearth-2.0+dfsg/src/osgEarth/ShaderUtils osgearth-2.1.1+dfsg/src/osgEarth/ShaderUtils --- osgearth-2.0+dfsg/src/osgEarth/ShaderUtils 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/ShaderUtils 2011-11-04 19:44:43.000000000 +0000 @@ -21,7 +21,9 @@ #include #include +#include #include +#include namespace osgEarth { @@ -45,6 +47,7 @@ bool _dirty; bool _applied; bool _useUpdateTrav; + OpenThreads::Mutex _stateSetMutex; osg::ref_ptr _lightingEnabledUniform; osg::ref_ptr _lightEnabledUniform; @@ -55,27 +58,57 @@ * differently on different drivers (ATI vs NVIDIA), so this class helps mitigate * those differences. */ - class OSGEARTH_EXPORT ArrayUniform : public osg::Referenced + class OSGEARTH_EXPORT ArrayUniform // : public osg::Referenced { public: - ArrayUniform( osg::Uniform::Type type, const std::string& name, int size ); + /** Empty array uniform */ + ArrayUniform() { } - /** creates an array uniform helper from an existing stateset */ - ArrayUniform( osg::StateSet* from, const std::string& name ); + /** + * Creates or retrieves a named uniform array. + */ + ArrayUniform( + const std::string& name, + osg::Uniform::Type type, + osg::StateSet* stateSet, + unsigned size =1 ); + + void attach( + const std::string& name, + osg::Uniform::Type type, + osg::StateSet* stateSet, + unsigned size =1 ); + + void detach(); - void setElement( int index, int value ); - void setElement( int index, bool value ); - void setElement( int index, float value ); + // ArrayUniform( osg::Uniform::Type type, const std::string& name, int size ); - void addTo( osg::StateSet* stateSet ); - void removeFrom( osg::StateSet* stateSet ); + /** creates an array uniform helper from an existing stateset */ + //ArrayUniform( osg::StateSet* from, const std::string& name ); - bool isComplete() const { return _uniform.valid() && _uniformAlt.valid(); } - int getNumElements() const { return isComplete() ? _uniform->getNumElements() : -1; } + void setElement( unsigned index, int value ); + void setElement( unsigned index, unsigned value ); + void setElement( unsigned index, bool value ); + void setElement( unsigned index, float value ); + void setElement( unsigned index, const osg::Matrix& value ); + void setElement( unsigned index, const osg::Vec3& value ); + + bool getElement( unsigned index, int& out_value ) const; + bool getElement( unsigned index, unsigned& out_value ) const; + bool getElement( unsigned index, bool& out_value ) const; + bool getElement( unsigned index, float& out_value ) const; + bool getElement( unsigned index, osg::Matrix& out_value ) const; + bool getElement( unsigned index, osg::Vec3& out_value ) const; + + bool isValid() const { return _uniform.valid() && _uniformAlt.valid(); } + int getNumElements() const { return isValid() ? _uniform->getNumElements() : -1; } private: - osg::ref_ptr _uniform; - osg::ref_ptr _uniformAlt; + osg::ref_ptr _uniform; + osg::ref_ptr _uniformAlt; + osg::observer_ptr _stateSet; + + void ensureCapacity( unsigned newSize ); }; } diff -Nru osgearth-2.0+dfsg/src/osgEarth/ShaderUtils.cpp osgearth-2.1.1+dfsg/src/osgEarth/ShaderUtils.cpp --- osgearth-2.0+dfsg/src/osgEarth/ShaderUtils.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/ShaderUtils.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -137,9 +137,13 @@ // apply if necessary: if ( !_applied && !_useUpdateTrav ) { - node->getOrCreateStateSet()->addUniform( _lightingEnabledUniform.get() ); - node->getStateSet()->addUniform( _lightEnabledUniform.get() ); - _applied = true; + OpenThreads::ScopedLock lock( _stateSetMutex ); + if (!_applied) + { + node->getOrCreateStateSet()->addUniform( _lightingEnabledUniform.get() ); + node->getStateSet()->addUniform( _lightEnabledUniform.get() ); + _applied = true; + } } } } @@ -167,55 +171,208 @@ //------------------------------------------------------------------------ -ArrayUniform::ArrayUniform( osg::Uniform::Type type, const std::string& name, int size ) +ArrayUniform::ArrayUniform( const std::string& name, osg::Uniform::Type type, osg::StateSet* stateSet, unsigned size ) { - _uniform = new osg::Uniform( type, name, size ); - _uniformAlt = new osg::Uniform( type, name + "[0]", size ); + attach( name, type, stateSet, size ); } -ArrayUniform::ArrayUniform( osg::StateSet* stateSet, const std::string& name ) +void +ArrayUniform::attach( const std::string& name, osg::Uniform::Type type, osg::StateSet* stateSet, unsigned size ) { - _uniform = stateSet->getUniform( name ); + _uniform = stateSet->getUniform( name ); _uniformAlt = stateSet->getUniform( name + "[0]" ); + + if ( !isValid() ) + { + _uniform = new osg::Uniform( type, name, size ); + _uniformAlt = new osg::Uniform( type, name + "[0]", size ); + stateSet->addUniform( _uniform.get() ); + stateSet->addUniform( _uniformAlt.get() ); + } + + _stateSet = stateSet; } void -ArrayUniform::setElement( int index, int value ) +ArrayUniform::setElement( unsigned index, int value ) { - _uniform->setElement( index, value ); - _uniformAlt->setElement( index, value ); + if ( isValid() ) + { + ensureCapacity( index+1 ); + _uniform->setElement( index, value ); + _uniformAlt->setElement( index, value ); + } } void -ArrayUniform::setElement( int index, bool value ) +ArrayUniform::setElement( unsigned index, unsigned value ) { - _uniform->setElement( index, value ); - _uniformAlt->setElement( index, value ); + if ( isValid() ) + { + ensureCapacity( index+1 ); + _uniform->setElement( index, value ); + _uniformAlt->setElement( index, value ); + } } void -ArrayUniform::setElement( int index, float value ) +ArrayUniform::setElement( unsigned index, bool value ) { - _uniform->setElement( index, value ); - _uniformAlt->setElement( index, value ); + if ( isValid() ) + { + ensureCapacity( index+1 ); + _uniform->setElement( index, value ); + _uniformAlt->setElement( index, value ); + } } void -ArrayUniform::addTo( osg::StateSet* stateSet ) +ArrayUniform::setElement( unsigned index, float value ) { - if ( stateSet ) + if ( isValid() ) { - stateSet->addUniform( _uniform.get() ); - stateSet->addUniform( _uniformAlt.get() ); + ensureCapacity( index+1 ); + _uniform->setElement( index, value ); + _uniformAlt->setElement( index, value ); } } -void -ArrayUniform::removeFrom( osg::StateSet* stateSet ) +void +ArrayUniform::setElement( unsigned index, const osg::Matrix& value ) { - if ( stateSet ) + if ( isValid() ) { - stateSet->removeUniform( _uniform->getName() ); - stateSet->removeUniform( _uniformAlt->getName() ); + ensureCapacity( index+1 ); + _uniform->setElement( index, value ); + _uniformAlt->setElement( index, value ); + } +} + +void +ArrayUniform::setElement( unsigned index, const osg::Vec3& value ) +{ + if ( isValid() ) + { + ensureCapacity( index+1 ); + _uniform->setElement( index, value ); + _uniformAlt->setElement( index, value ); + } +} + +bool +ArrayUniform::getElement( unsigned index, int& out_value ) const +{ + return isValid() ? _uniform->getElement( index, out_value ) : false; +} + +bool +ArrayUniform::getElement( unsigned index, unsigned& out_value ) const +{ + return isValid() ? _uniform->getElement( index, out_value ) : false; +} + +bool +ArrayUniform::getElement( unsigned index, bool& out_value ) const +{ + return isValid() ? _uniform->getElement( index, out_value ) : false; +} + +bool +ArrayUniform::getElement( unsigned index, float& out_value ) const +{ + return isValid() ? _uniform->getElement( index, out_value ) : false; +} + +bool +ArrayUniform::getElement( unsigned index, osg::Matrix& out_value ) const +{ + return isValid() ? _uniform->getElement( index, out_value ) : false; +} + +bool +ArrayUniform::getElement( unsigned index, osg::Vec3& out_value ) const +{ + return isValid() ? _uniform->getElement( index, out_value ) : false; +} + + +void +ArrayUniform::ensureCapacity( unsigned newSize ) +{ + if ( isValid() && _uniform->getNumElements() < newSize ) + { + osg::ref_ptr stateSet_safe = _stateSet.get(); + if ( stateSet_safe.valid() ) + { + osg::ref_ptr _oldUniform = _uniform.get(); + osg::ref_ptr _oldUniformAlt = _oldUniform.get(); + + stateSet_safe->removeUniform( _uniform->getName() ); + stateSet_safe->removeUniform( _uniformAlt->getName() ); + + _uniform = new osg::Uniform( _uniform->getType(), _uniform->getName(), newSize ); + _uniformAlt = new osg::Uniform( _uniform->getType(), _uniform->getName() + "[0]", newSize ); + + switch( _oldUniform->getInternalArrayType(_oldUniform->getType()) ) + { + case GL_FLOAT: + { + for( unsigned i = 0; i < _oldUniform->getNumElements(); ++i ) + { + float value; + _oldUniform->getElement(i, value); + setElement( i, value ); + } + } + break; + + case GL_INT: + { + for( unsigned i = 0; i < _oldUniform->getNumElements(); ++i ) + { + int value; + _oldUniform->getElement(i, value); + setElement( i, value ); + } + } + break; + + case GL_UNSIGNED_INT: + { + for( unsigned i = 0; i < _oldUniform->getNumElements(); ++i ) + { + unsigned value; + _oldUniform->getElement(i, value); + setElement( i, value ); + } + } + break; + } + + stateSet_safe->addUniform( _uniform.get() ); + stateSet_safe->addUniform( _uniformAlt.get() ); + + stateSet_safe.release(); // don't want to unref delete + } + } +} + +void +ArrayUniform::detach() +{ + if ( isValid() ) + { + osg::ref_ptr stateSet_safe = _stateSet.get(); + if ( stateSet_safe.valid() ) + { + stateSet_safe->removeUniform( _uniform->getName() ); + stateSet_safe->removeUniform( _uniformAlt->getName() ); + + _uniform = 0L; + _uniformAlt = 0L; + _stateSet = 0L; + + stateSet_safe.release(); // don't want to unref delete + } } } diff -Nru osgearth-2.0+dfsg/src/osgEarth/SpatialReference osgearth-2.1.1+dfsg/src/osgEarth/SpatialReference --- osgearth-2.0+dfsg/src/osgEarth/SpatialReference 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/SpatialReference 2011-11-04 19:44:43.000000000 +0000 @@ -20,6 +20,7 @@ #define OSGEARTH_SPATIAL_REFERENCE_H 1 #include +#include #include #include #include @@ -27,6 +28,15 @@ namespace osgEarth { + //Definitions for the mercator extent + const double MERC_MINX = -2.00375e+007; + const double MERC_MINY = -2.00375e+007; + const double MERC_MAXX = 2.00375e+007; + const double MERC_MAXY = 2.00375e+007; + const double MERC_WIDTH = MERC_MAXX - MERC_MINX; + const double MERC_HEIGHT = MERC_MAXY - MERC_MINY; + + class OSGEARTH_EXPORT GeoLocator; /** @@ -60,19 +70,40 @@ static SpatialReference* createFromHandle( void* ogrHandle, bool xferOwnership =false ); public: - /** Transform a point to another SRS. */ + /** + * Transform a point to another SRS. + */ virtual bool transform( + double x, double y, double z, + const SpatialReference* to_srs, + double& out_x, double& out_y, double& out_z, + void* context =0L ) const; + + bool transform( + const osg::Vec3d& input, + const SpatialReference* to_srs, + osg::Vec3d& output, + void* context =0L) const + { + return transform(input.x(), input.y(), input.z(), to_srs, output.x(), output.y(), output.z(), context); + } + + bool transform2D( double x, double y, const SpatialReference* to_srs, double& out_x, double& out_y, - void* context =0L ) const; + void* context =0L ) const + { + double dummyZ; + return transform(x, y, dummyZ, to_srs, out_x, out_y, dummyZ, context); + } /** - * Transforms an array of 2D points from this SRS to another SRS. + * Transforms an array of 3D points from this SRS to another SRS. */ virtual bool transformPoints( const SpatialReference* to_srs, - double* x, double *y, + double* x, double* y, double* z, unsigned int numPoints, void* context =0L, bool ignore_errors =false) const; @@ -81,12 +112,43 @@ * Transforms an array of points from this SRS to another SRS. */ virtual bool transformPoints( - const SpatialReference* to_srs, - osg::Vec3dArray* points, + const SpatialReference* to_srs, + std::vector& points, void* context =0L, bool ignore_errors =false) const; /** + * Transforms a point to geocentric/ECEF coordinates. + */ + bool transformToECEF( + const osg::Vec3d& input, + osg::Vec3d& output ) const; + + /** + * Transforms an array of points to geocentric/ECEF coordinates. The points + * are transformed in place. + */ + bool transformToECEF( + std::vector& points, + bool ignore_errors =false) const; + + /** + * Transforms a point from geocentric/ECEF coordinates into this SRS (with a + * height above ellipsoid). + */ + bool transformFromECEF( + const osg::Vec3d& input, + osg::Vec3d& output ) const; + + /** + * Transforms an array of points from geocentric/ECEF coordinates into this SRS + * (with a height-above-ellipoid). The points are transformed in place. + */ + bool transformFromECEF( + std::vector& points, + bool ignoreErrors =false) const; + + /** * Transforms a spatial extent to another SRS. * * TODO: Update this method to work for: @@ -103,7 +165,7 @@ const SpatialReference* to_srs, double in_xmin, double in_ymin, double in_xmax, double in_ymax, - double* x, double *y, + double* x, double* y, unsigned int numx, unsigned int numy, void* context = 0L, bool ignore_errors = false ) const; @@ -132,6 +194,9 @@ /** Tests whether this SRS is a Unified Cube projection (osgEarth-internal) */ virtual bool isCube() const; + /** Tests whether this SRS is a Local Tangent Plane projection (osgEarth-internal) */ + virtual bool isLTP() const { return _is_ltp; } + /** Gets the readable name of this SRS. */ const std::string& getName() const; @@ -147,12 +212,28 @@ /** Gets the string that was used to initialize this SRS */ const std::string& getInitString() const; + /** Gets the datum identifier of this SRS (or empty string if not available) */ + const std::string& getDatumName() const; + /** Tests this SRS for equivalence with another. */ virtual bool isEquivalentTo( const SpatialReference* rhs ) const; /** Gets a reference to this SRS's underlying geographic SRS. */ const SpatialReference* getGeographicSRS() const; + /** Creates and returns a local trangent plane SRS at the given reference location. + The reference location is expressed in this object's SRS, but it tangent to + the globe at getGeographicSRS(). LTP units are in meters. */ + SpatialReference* createTangentPlaneSRS( const osg::Vec3d& refPos ) const; + + /** Creates a transverse mercator projection centered at the specified longitude. */ + SpatialReference* createTransMercFromLongitude( const Angular& lon ) const; + + /** 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. */ + SpatialReference* createUTMFromLongitude( const Angular& lon ) const; + /** Creates a new CSN based on this spatial reference. */ osg::CoordinateSystemNode* createCoordinateSystemNode() const; @@ -192,12 +273,14 @@ bool _is_cube; bool _is_contiguous; bool _is_user_defined; + bool _is_ltp; std::string _name; std::string _wkt; std::string _proj4; std::string _init_type; std::string _init_str; std::string _init_str_lc; + std::string _datum; osg::ref_ptr _ellipsoid; osg::ref_ptr _geo_srs; @@ -209,8 +292,8 @@ virtual void _init(); virtual bool _isEquivalentTo( const SpatialReference* srs ) const; - virtual bool preTransform(double& x, double& y, void* context) const { return true; } - virtual bool postTransform(double& x, double& y, void* context) const { return true;} + virtual bool preTransform(double& x, double& y, double& z, void* context) const { return true; } + virtual bool postTransform(double& x, double& y, double& z, void* context) const { return true;} @@ -226,9 +309,9 @@ static SpatialReference* createCube(); + SpatialReference* validate(); }; - } diff -Nru osgearth-2.0+dfsg/src/osgEarth/SpatialReference.cpp osgearth-2.1.1+dfsg/src/osgEarth/SpatialReference.cpp --- osgearth-2.0+dfsg/src/osgEarth/SpatialReference.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/SpatialReference.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -20,35 +20,58 @@ #include #include #include +#include #include #include #include #include #include +#define LC "[SpatialReference] " + using namespace osgEarth; #define USE_CUSTOM_MERCATOR_TRANSFORM 1 //#undef USE_CUSTOM_MERCATOR_TRANSFORM +//------------------------------------------------------------------------ -static std::string -getOGRAttrValue( void* _handle, const std::string& name, int child_num, bool lowercase =false) +namespace { - GDAL_SCOPED_LOCK; - const char* val = OSRGetAttrValue( _handle, name.c_str(), child_num ); - if ( val ) + std::string + getOGRAttrValue( void* _handle, const std::string& name, int child_num, bool lowercase =false) + { + GDAL_SCOPED_LOCK; + const char* val = OSRGetAttrValue( _handle, name.c_str(), child_num ); + if ( val ) + { + std::string t = val; + if ( lowercase ) + { + std::transform( t.begin(), t.end(), t.begin(), ::tolower ); + } + return t; + } + return ""; + } + + std::string& + replaceIn( std::string& s, const std::string& sub, const std::string& other) { - std::string t = val; - if ( lowercase ) + if ( sub.empty() ) return s; + size_t b=0; + for( ; ; ) { - std::transform( t.begin(), t.end(), t.begin(), ::tolower ); + b = s.find( sub, b ); + if ( b == s.npos ) break; + s.replace( b, sub.size(), other ); + b += other.size(); } - return t; + return s; } - return ""; } +//------------------------------------------------------------------------ SpatialReference::SpatialReferenceCache& SpatialReference::getSpatialReferenceCache() { @@ -71,7 +94,7 @@ } else { - OE_WARN << "[osgEarth::SRS] Unable to create spatial reference from PROJ4: " << init << std::endl; + OE_WARN << LC << "Unable to create spatial reference from PROJ4: " << init << std::endl; OSRDestroySpatialReference( handle ); } return result; @@ -92,12 +115,50 @@ } else { - OE_WARN << "[osgEarth::SRS] Unable to create SRS: " << init << std::endl; + OE_WARN << LC << "Unable to create SRS: " << init << std::endl; OSRDestroySpatialReference( handle ); } return result; } +#if 0 +SpatialReference* +SpatialReference::createLTP( const osg::Vec3d& refPointLLA, const SpatialReference* geoSRS ) +{ + GDAL_SCOPED_LOCK; + + void* handle = OSRNewSpatialReference( NULL ); + bool ok = false; + SpatialReference* result = 0L; + + if ( geoSRS ) + { + std::string init = geoSRS->getGeographicSRS()->getWKT(); + char buf[4096]; + char* buf_ptr = &buf[0]; + strcpy( buf, init.c_str() ); + ok = ( OSRImportFromWkt( handle, &buf_ptr ) == OGRERR_NONE ); + } + else + { + std::string init = "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs"; + ok = ( OSRImportFromProj4( handle, init.c_str() ) == OGRERR_NONE ); + } + + if ( ok ) + { + result = new LTPSpatialReference( handle, refPointLLA ); + } + else + { + OE_WARN << LC << "Unable to create LTP SRS" << std::endl; + if ( handle ) + OSRDestroySpatialReference( handle ); + } + + return result; +} +#endif SpatialReference* SpatialReference::createFromWKT( const std::string& init, const std::string& init_alias, const std::string& name ) @@ -115,7 +176,7 @@ } else { - OE_WARN << "[osgEarth::SRS] Unable to create spatial reference from WKT: " << init << std::endl; + OE_WARN << LC << "Unable to create spatial reference from WKT: " << init << std::endl; OSRDestroySpatialReference( handle ); } return result.release(); @@ -124,6 +185,9 @@ SpatialReference* SpatialReference::create( const std::string& init ) { + static OpenThreads::Mutex s_mutex; + OpenThreads::ScopedLock exclusiveLock(s_mutex); + std::string low = init; std::transform( low.begin(), low.end(), low.begin(), ::tolower ); @@ -213,22 +277,6 @@ return srs; } - -static std::string& -replaceIn( std::string& s, const std::string& sub, const std::string& other) -{ - if ( sub.empty() ) return s; - size_t b=0; - for( ; ; ) - { - b = s.find( sub, b ); - if ( b == s.npos ) break; - s.replace( b, sub.size(), other ); - b += other.size(); - } - return s; -} - SpatialReference* SpatialReference::validate() { @@ -247,7 +295,7 @@ else replaceIn( new_wkt, "Lambert_Conformal_Conic", "Lambert_Conformal_Conic_1SP" ); - OE_NOTICE << "Morphing Lambert_Conformal_Conic to 1SP/2SP" << std::endl; + OE_INFO << LC << "Morphing Lambert_Conformal_Conic to 1SP/2SP" << std::endl; return createFromWKT( new_wkt, _init_str, _name ); } @@ -257,13 +305,13 @@ { std::string new_wkt = getWKT(); replaceIn( new_wkt, "Plate_Carree", "Equirectangular" ); - OE_NOTICE << "[osgEarth::SRS] Morphing Plate_Carree to Equirectangular" << std::endl; + OE_INFO << LC << "Morphing Plate_Carree to Equirectangular" << std::endl; return createFromWKT( new_wkt, _init_str, _name ); //, input->getReferenceFrame() ); } else if ( proj == "Equidistant_Cylindrical" ) { std::string new_wkt = getWKT(); - OE_NOTICE << "[osgEarth::SRS] Morphing Equidistant_Cylindrical to Equirectangular" << std::endl; + OE_INFO << LC << "Morphing Equidistant_Cylindrical to Equirectangular" << std::endl; replaceIn( new_wkt, "Equidistant_Cylindrical", "Equirectangular" ); return createFromWKT( new_wkt, _init_str, _name ); } @@ -286,7 +334,15 @@ _owns_handle( true ), _name( name ), _init_type( init_type ), -_init_str( init_str ) +_init_str( init_str ), +_is_geographic( false ), +_is_mercator( false ), +_is_north_polar( false ), +_is_south_polar( false ), +_is_cube( false ), +_is_contiguous( false ), +_is_user_defined( false ), +_is_ltp( false ) { _init_str_lc = init_str; std::transform( _init_str_lc.begin(), _init_str_lc.end(), _init_str_lc.begin(), ::tolower ); @@ -303,17 +359,22 @@ SpatialReference::~SpatialReference() { - if ( _handle && _owns_handle ) - { - GDAL_SCOPED_LOCK; + if ( _handle ) + { + GDAL_SCOPED_LOCK; - for (TransformHandleCache::iterator itr = _transformHandleCache.begin(); itr != _transformHandleCache.end(); ++itr) - { - OCTDestroyCoordinateTransformation(itr->second); - } - OSRDestroySpatialReference( _handle ); - } - _handle = NULL; + for (TransformHandleCache::iterator itr = _transformHandleCache.begin(); itr != _transformHandleCache.end(); ++itr) + { + OCTDestroyCoordinateTransformation(itr->second); + } + + if ( _owns_handle ) + { + OSRDestroySpatialReference( _handle ); + } + + _handle = NULL; + } } bool @@ -349,6 +410,14 @@ } const std::string& +SpatialReference::getDatumName() const +{ + if ( !_initialized ) + const_cast(this)->init(); + return _datum; +} + +const std::string& SpatialReference::getWKT() const { if ( !_initialized ) @@ -396,7 +465,8 @@ isSouthPolar() != rhs->isSouthPolar() || isContiguous() != rhs->isContiguous() || isUserDefined() != rhs->isUserDefined() || - isCube() != rhs->isCube() ) + isCube() != rhs->isCube() || + isLTP() != rhs->isLTP() ) { return false; } @@ -432,21 +502,68 @@ { GDAL_SCOPED_LOCK; - void* new_handle = OSRNewSpatialReference( NULL ); - int err = OSRCopyGeogCSFrom( new_handle, _handle ); - if ( err == OGRERR_NONE ) + if ( !_geo_srs.valid() ) // double-check pattern { - const_cast(this)->_geo_srs = new SpatialReference( new_handle ); - } - else - { - OSRDestroySpatialReference( new_handle ); + void* new_handle = OSRNewSpatialReference( NULL ); + int err = OSRCopyGeogCSFrom( new_handle, _handle ); + if ( err == OGRERR_NONE ) + { + const_cast(this)->_geo_srs = new SpatialReference( new_handle ); + } + else + { + OSRDestroySpatialReference( new_handle ); + } } } return _geo_srs.get(); } +SpatialReference* +SpatialReference::createTangentPlaneSRS( const osg::Vec3d& pos ) const +{ + SpatialReference* result = 0L; + osg::Vec3d lla; + if ( this->transform(pos, this->getGeographicSRS(), lla) ) + { + result = new LTPSpatialReference( this->getGeographicSRS()->_handle, lla ); + } + else + { + OE_WARN << LC << "Unable to create LTP SRS" << std::endl; + } + return result; +} + +SpatialReference* +SpatialReference::createTransMercFromLongitude( const Angular& lon ) const +{ + // note. using tmerc with +lat_0 <> 0 is sloooooow. + std::string datum = getDatumName(); + std::stringstream buf; + buf << "+proj=tmerc +lat_0=0" + << " +lon_0=" << lon.as(Units::DEGREES) + << " +datum=" << (!datum.empty() ? "wgs84" : datum); + std::string projstr; + projstr = buf.str(); + return create( projstr ); +} + +SpatialReference* +SpatialReference::createUTMFromLongitude( const Angular& lon ) const +{ + // note. UTM is up to 10% faster than TMERC for the same meridian. + unsigned zone = 1 + (unsigned)floor((lon.as(Units::DEGREES)+180.0)/6.0); + std::string datum = getDatumName(); + std::stringstream buf; + buf << "+proj=utm +zone=" << zone + << " +datum=" << (!datum.empty() ? "wgs84" : datum); + std::string projstr; + projstr = buf.str(); + return create( projstr ); +} + bool SpatialReference::isMercator() const { @@ -579,9 +696,9 @@ } bool -SpatialReference::transform(double x, double y, +SpatialReference::transform(double x, double y, double z, const SpatialReference* out_srs, - double& out_x, double& out_y, + double& out_x, double& out_y, double& out_z, void* context ) const { if ( !_initialized ) @@ -592,80 +709,37 @@ { out_x = x; out_y = y; + out_z = z; return true; } - GDAL_SCOPED_LOCK; - - preTransform(x, y, context); - - void* xform_handle = NULL; - TransformHandleCache::const_iterator itr = _transformHandleCache.find(out_srs->getWKT()); - if (itr != _transformHandleCache.end()) - { - OE_DEBUG << "SpatialReference: using cached transform handle" << std::endl; - xform_handle = itr->second; - } - else - { - xform_handle = OCTNewCoordinateTransformation( _handle, out_srs->_handle); - const_cast(this)->_transformHandleCache[out_srs->getWKT()] = xform_handle; - } - - if ( !xform_handle ) - { - OE_WARN - << "SpatialReference: SRS xform not possible" << std::endl - << " From => " << getName() << std::endl - << " To => " << out_srs->getName() << std::endl; - return false; - } - - double temp_x = x; - double temp_y = y; - double temp_z = 0.0; - bool result; - - if ( OCTTransform( xform_handle, 1, &temp_x, &temp_y, &temp_z ) ) - { - result = true; - out_x = temp_x; - out_y = temp_y; + out_x = x; + out_y = y; + out_z = z; + bool result = transformPoints(out_srs, &out_x, &out_y, &out_z, 1, context); - out_srs->postTransform(out_x, out_y, context); - } - else - { - OE_WARN << "SRS: Failed to xform a point from " - << getName() << " to " << out_srs->getName() - << std::endl; - result = false; - } return result; } // http://en.wikipedia.org/wiki/Mercator_projection#Mathematics_of_the_projection static bool -mercatorToGeographic( double* x, double* y, int numPoints ) +mercatorToGeographic( double* x, double* y, double* z, int numPoints ) { - const GeoExtent& merc = osgEarth::Registry::instance()->getGlobalMercatorProfile()->getExtent(); - for( int i=0; igetGlobalMercatorProfile()->getExtent(); - for( int i=0; i(this)->init(); //Check for equivalence and return if the coordinate systems are the same. - if (isEquivalentTo(out_srs)) return true; + if (isEquivalentTo(out_srs)) + return true; - for (unsigned int i = 0; i < numPoints; ++i) + if ( z ) { - preTransform(x[i], y[i], context); + for (unsigned int i = 0; i < numPoints; ++i) + preTransform(x[i], y[i], z[i], context); + } + else + { + double dummyZ = 0.0; + for (unsigned int i = 0; i < numPoints; ++i) + preTransform(x[i], y[i], dummyZ, context); } bool success = false; @@ -705,12 +788,12 @@ if ( isGeographic() && out_srs->isMercator() ) { - success = geographicToMercator( x, y, numPoints ); + success = geographicToMercator( x, y, z, numPoints ); } else if ( isMercator() && out_srs->isGeographic() ) { - success = mercatorToGeographic( x, y, numPoints ); + success = mercatorToGeographic( x, y, z, numPoints ); } else @@ -723,7 +806,7 @@ TransformHandleCache::const_iterator itr = _transformHandleCache.find(out_srs->getWKT()); if (itr != _transformHandleCache.end()) { - OE_DEBUG << "SpatialRefernece: using cached transform handle" << std::endl; + //OE_DEBUG << "SpatialRefernece: using cached transform handle" << std::endl; xform_handle = itr->second; } else @@ -734,41 +817,48 @@ if ( !xform_handle ) { - OE_WARN - << "SpatialReference: SRS xform not possible" << std::endl + OE_WARN << LC + << "SRS xform not possible" << std::endl << " From => " << getName() << std::endl << " To => " << out_srs->getName() << std::endl; return false; } - double* temp_z = new double[numPoints]; - success = OCTTransform( xform_handle, numPoints, x, y, temp_z ) > 0; - delete[] temp_z; + //double* temp_z = new double[numPoints]; + //success = OCTTransform( xform_handle, numPoints, x, y, temp_z ) > 0; + //delete[] temp_z; + + success = OCTTransform( xform_handle, numPoints, x, y, z ) > 0; // END GDAL_SCOPE_LOCK } if ( success || ignore_errors ) { - for (unsigned int i = 0; i < numPoints; ++i) + if ( z ) + { + for (unsigned int i = 0; i < numPoints; ++i) + out_srs->postTransform(x[i], y[i], z[i], context); + } + else { - out_srs->postTransform(x[i], y[i], context); + double dummyZ = 0.0; + for (unsigned int i = 0; i < numPoints; ++i) + out_srs->postTransform(x[i], y[i], dummyZ, context); } } else { - OE_WARN << "SpatialReference: Failed to xform a point from " + OE_WARN << LC << "Failed to xform a point from " << getName() << " to " << out_srs->getName() << std::endl; } return success; } - - bool SpatialReference::transformPoints(const SpatialReference* out_srs, - osg::Vec3dArray* points, + std::vector& points, void* context, bool ignore_errors ) const { @@ -776,67 +866,171 @@ const_cast(this)->init(); //Check for equivalence and return if the coordinate systems are the same. - if (isEquivalentTo(out_srs)) return true; + if (isEquivalentTo(out_srs)) + return true; - int numPoints = points->size(); + int numPoints = points.size(); double* x = new double[numPoints]; double* y = new double[numPoints]; + double* z = new double[numPoints]; for( int i=0; igetEllipsoid()->convertLatLongHeightToXYZ( + osg::DegreesToRadians( lat ), osg::DegreesToRadians( lon ), alt, + output.x(), output.y(), output.z() ); + + return true; +} + +bool +SpatialReference::transformToECEF(std::vector& points, + bool ignoreErrors ) const +{ + if ( points.size() == 0 ) + return false; + + const SpatialReference* geoSRS = getGeographicSRS(); + const osg::EllipsoidModel* ellipsoid = geoSRS->getEllipsoid(); + + for( unsigned i=0; iconvertLatLongHeightToXYZ( + osg::DegreesToRadians( p.y() ), osg::DegreesToRadians( p.x() ), p.z(), + p.x(), p.y(), p.z() ); + } + + return true; +} + +bool +SpatialReference::transformFromECEF(const osg::Vec3d& input, + osg::Vec3d& output ) const +{ + // transform to lat/long: + osg::Vec3d geo; + + getGeographicSRS()->getEllipsoid()->convertXYZToLatLongHeight( + input.x(), input.y(), input.z(), + geo.y(), geo.x(), geo.z() ); + + // then convert to the local SRS. + if ( isGeographic() ) + { + output.set( osg::RadiansToDegrees(geo.x()), osg::RadiansToDegrees(geo.y()), geo.z() ); + } + else + { + getGeographicSRS()->transform( + osg::RadiansToDegrees(geo.x()), osg::RadiansToDegrees(geo.y()), geo.z(), + this, + output.x(), output.y(), output.z() ); + //output.z() = geo.z(); + } + + return true; +} + +bool +SpatialReference::transformFromECEF(std::vector& points, + bool ignoreErrors ) const +{ + bool ok = true; + + // first convert all the points to lat/long (in place): + for( unsigned i=0; igetEllipsoid()->convertXYZToLatLongHeight( + p.x(), p.y(), p.z(), + geo.y(), geo.x(), geo.z() ); + geo.x() = osg::RadiansToDegrees( geo.x() ); + geo.y() = osg::RadiansToDegrees( geo.y() ); + p = geo; + } + + // then convert them all to the local SRS if necessary. + if ( !isGeographic() ) + { + ok = getGeographicSRS()->transformPoints( this, points, 0L, ignoreErrors ); + } + + return ok; +} bool SpatialReference::transformExtent(const SpatialReference* to_srs, - double& in_out_xmin, - double& in_out_ymin, - double& in_out_xmax, - double& in_out_ymax, - void* context ) const + double& in_out_xmin, + double& in_out_ymin, + double& in_out_xmax, + double& in_out_ymax, + void* context ) const { if ( !_initialized ) const_cast(this)->init(); int oks = 0; - //Transform all points and take the maximum bounding rectangle the resulting points double llx, lly; double ulx, uly; double urx, ury; double lrx, lry; + double dummyZ = 0; //Lower Left - oks += transform( in_out_xmin, in_out_ymin, to_srs, llx, lly, context ) == true; + oks += transform( in_out_xmin, in_out_ymin, 0, to_srs, llx, lly, dummyZ, context ) == true; //Upper Left - oks += transform( in_out_xmin, in_out_ymax, to_srs, ulx, uly, context ) == true; + oks += transform( in_out_xmin, in_out_ymax, 0, to_srs, ulx, uly, dummyZ, context ) == true; //Upper Right - oks += transform( in_out_xmax, in_out_ymax, to_srs, urx, ury, context ) == true; + oks += transform( in_out_xmax, in_out_ymax, 0, to_srs, urx, ury, dummyZ, context ) == true; //Lower Right - oks += transform( in_out_xmax, in_out_ymin, to_srs, lrx, lry, context ) == true; + oks += transform( in_out_xmax, in_out_ymin, 0, to_srs, lrx, lry, dummyZ, context ) == true; if (oks == 4) @@ -850,13 +1044,12 @@ return false; } -bool SpatialReference::transformExtentPoints( - const SpatialReference* to_srs, - double in_xmin, double in_ymin, - double in_xmax, double in_ymax, - double* x, double *y, - unsigned int numx, unsigned int numy, - void* context, bool ignore_errors ) const +bool SpatialReference::transformExtentPoints(const SpatialReference* to_srs, + double in_xmin, double in_ymin, + double in_xmax, double in_ymax, + double* x, double* y, + unsigned int numx, unsigned int numy, + void* context, bool ignore_errors ) const { const double dx = (in_xmax - in_xmin) / (numx - 1); const double dy = (in_ymax - in_ymin) / (numy - 1); @@ -876,7 +1069,7 @@ pixel++; } } - return transformPoints(to_srs, x, y, numx * numy, context, ignore_errors); + return transformPoints(to_srs, x, y, 0L, numx * numy, context, ignore_errors); } void @@ -884,9 +1077,13 @@ { GDAL_SCOPED_LOCK; - // calls the internal version, which can be overriden by the developer. - // therefore do not call init() from the constructor! - _init(); + // always double-check the _initialized flag after obtaining the lock. + if ( !_initialized ) + { + // calls the internal version, which can be overriden by the developer. + // therefore do not call init() from the constructor! + _init(); + } } void @@ -904,6 +1101,9 @@ double semi_minor_axis = OSRGetSemiMinor( _handle, &err ); _ellipsoid = new osg::EllipsoidModel( semi_major_axis, semi_minor_axis ); + // try to get an ellipsoid name: + _ellipsoid->setName( getOGRAttrValue(_handle, "SPHEROID", 0, true) ); + // extract the projection: if ( _name.empty() || _name == "unnamed" ) { @@ -961,5 +1161,8 @@ OGRFree( proj4buf ); } + // Try to extract the datum + _datum = getOGRAttrValue( _handle, "DATUM", 0, true ); + _initialized = true; } diff -Nru osgearth-2.0+dfsg/src/osgEarth/StringUtils osgearth-2.1.1+dfsg/src/osgEarth/StringUtils --- osgearth-2.0+dfsg/src/osgEarth/StringUtils 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/StringUtils 2011-11-04 19:44:43.000000000 +0000 @@ -21,11 +21,18 @@ #include #include +#include #include #include +#include +#include +#include +#include namespace osgEarth { + typedef std::vector StringVector; + /** Replaces all the instances of "sub" with "other" in "s". */ static std::string& replaceIn( std::string& s, const std::string& sub, const std::string& other) @@ -72,54 +79,61 @@ } /** - * Tokenizes a string. - * http://stackoverflow.com/questions/53849/how-do-i-tokenize-a-string-in-c + * True is "ref" ends with "pattern" */ - class StringTokenizer + static + bool endsWith( const std::string& ref, const std::string& pattern ) { - public: - StringTokenizer(const std::string& s) - : _string(s), _offset(0), _delimiters(" \t\n\r") { } - StringTokenizer(const std::string& s, const std::string& delimiters) - : _string(s), _offset(0), _delimiters(delimiters) { } - bool nextToken() { - return nextToken( _delimiters ); } - bool nextToken(const std::string& delimiters) { - size_t i = _string.find_first_not_of(delimiters, _offset); - if (std::string::npos == i) { - _offset = _string.length(); - return false; - } - size_t j = _string.find_first_of(delimiters, i); - if (std::string::npos == j) { - _token = _string.substr(i); - _offset = _string.length(); - return true; - } - _token = _string.substr(i, j - i); - _offset = j; - return true; - } - std::string token() const { - return trim( _token ); - } - protected: - const std::string _string; - size_t _offset; - std::string _token; - std::string _delimiters; - }; + return ref.find( pattern ) == ref.length()-pattern.length(); + } /** - * Splits "input" into one or more strings in "output" based on the delimiting character(s) in "delims". + * Splits a string up into a vector of strings based on a set of + * delimiters, quotes, and rules. */ - static - void split( const std::string& input, const std::string& delims, std::vector& output, bool allowEmpties =true ) + class OSGEARTH_EXPORT StringTokenizer + { + public: + StringTokenizer( const std::string& delims =" \t\r\n", const std::string& quotes ="'\"" ); + + StringTokenizer( + const std::string& input, StringVector& output, + const std::string& delims =" \t\r\n", const std::string& quotes ="'\"", + bool keepEmpties =true, bool trimTokens =true); + + void tokenize( const std::string& input, StringVector& output ) const; + + bool& keepEmpties() { return _allowEmpties; } + + bool& trimTokens() { return _trimTokens; } + + void addDelim( char delim, bool keepAsToken =false ); + + void addDelims( const std::string& delims, bool keepAsTokens =false ); + + void addQuote( char delim, bool keepInToken =false ); + + void addQuotes( const std::string& delims, bool keepInTokens =false ); + + private: + typedef std::map TokenMap; + TokenMap _delims; + TokenMap _quotes; + bool _allowEmpties; + bool _trimTokens; + }; + + static std::string + joinStrings( const StringVector& input, char delim ) { - StringTokenizer t(input, delims); - while( t.nextToken() ) - if ( allowEmpties || !t.token().empty() ) - output.push_back( t.token() ); + std::stringstream buf; + for( StringVector::const_iterator i = input.begin(); i != input.end(); ++i ) + { + buf << *i; + if ( (i+1) != input.end() ) buf << delim; + } + std::string result = buf.str(); + return result; } /** Returns a lower-case version of the input string. */ @@ -153,12 +167,42 @@ colorToString( const osg::Vec4ub& c ) { std::stringstream ss; - ss << c.r() << " " << c.g() << " " << c.b() << " " << c.a(); + ss << (int)c.r() << " " << (int)c.g() << " " << (int)c.b() << " " << (int)c.a(); std::string ssStr; ssStr = ss.str(); return ssStr; } + /** Converts a string to a vec3f */ + static osg::Vec3f + stringToVec3f( const std::string& str, const osg::Vec3f& default_value ) + { + std::stringstream buf(str); + osg::Vec3f out = default_value; + buf >> out.x(); + if ( !buf.eof() ) { + buf >> out.y() >> out.z(); + } + else { + out.y() = out.x(); + out.z() = out.x(); + } + return out; + } + + /** Converts a vec3f to a string */ + static std::string + vec3fToString( const osg::Vec3f& v ) + { + std::stringstream buf; + buf << std::setprecision(6) + << v.x() << " " << v.y() << " " << v.z() + << std::endl; + std::string result; + result = buf.str(); + return result; + } + /** Parses an HTML color ("#rrggbb" or "#rrggbbaa") into an OSG color. */ static osg::Vec4f htmlColorToVec4f( const std::string& html ) @@ -233,6 +277,105 @@ return h; } + + //------------------------------------------------------------------------ + // conversion templates + + // converts a string to primitive using serialization + template inline T + as( const std::string& str, const T& default_value ) + { + T temp = default_value; + std::istringstream strin( str ); + if ( !strin.eof() ) strin >> temp; + return temp; + } + + // template specialization for a bool + template<> inline bool + as( const std::string& str, const bool& default_value ) + { + std::string temp = str; + std::transform( temp.begin(), temp.end(), temp.begin(), ::tolower ); + return + temp == "true" || temp == "yes" || temp == "on" ? true : + temp == "false" || temp == "no" || temp == "off" ? false : + default_value; + } + + template<> inline osg::Vec3f + as( const std::string& str, const osg::Vec3f& default_value ) + { + return stringToVec3f(str, default_value); + } + + // template specialization for string + template<> inline std::string + as( const std::string& str, const std::string& default_value ) + { + return str; + } + + // converts a primitive to a string + // TODO: precision?? + template inline std::string + toString(const T& value) + { + std::stringstream out; + out << std::setprecision(20) << std::fixed << value; + std::string outStr; + outStr = out.str(); + return outStr; + } + + // template speciallization for a bool to print out "true" or "false" + template<> inline std::string + toString(const bool& value) + { + return value ? "true" : "false"; + } + + template<> inline std::string + toString(const osg::Vec3f& value) + { + return vec3fToString(value); + } + + struct Stringify + { + operator std::string () const + { + std::string result; + result = buf.str(); + return result; + } + + Stringify& operator << (bool val) { buf << val; return (*this); } + Stringify& operator << (short val) { buf << val; return (*this); } + Stringify& operator << (unsigned short val) { buf << val; return (*this); } + Stringify& operator << (int val) { buf << val; return (*this); } + Stringify& operator << (unsigned int val) { buf << val; return (*this); } + Stringify& operator << (long val) { buf << val; return (*this); } + Stringify& operator << (unsigned long val) { buf << val; return (*this); } + Stringify& operator << (float val) { buf << val; return (*this); } + Stringify& operator << (double val) { buf << val; return (*this); } + Stringify& operator << (long double val) { buf << val; return (*this); } + Stringify& operator << (const void* val) { buf << val; return (*this); } + Stringify& operator << (char val) { buf << val; return (*this); } + Stringify& operator << (signed char val) { buf << val; return (*this); } + Stringify& operator << (unsigned char val) { buf << val; return (*this); } + Stringify& operator << (const char* val) { buf << val; return (*this); } + Stringify& operator << (const signed char* val) { buf << val; return (*this); } + Stringify& operator << (const unsigned char* val) { buf << val; return (*this); } + Stringify& operator << (const std::string& val) { buf << val; return (*this); } + Stringify& operator << (std::streambuf* val) { buf << val; return (*this); } + Stringify& operator << (std::ostream& (*val)(std::ostream&)) { buf << val; return (*this); } + Stringify& operator << (std::ios& (*val)(std::ostream&)) { buf << val; return (*this); } + Stringify& operator << (std::ios_base& (*val)(std::ios_base&)) { buf << val; return (*this); } + + protected: + std::stringstream buf; + }; } #endif // OSGEARTH_STRING_UTILS_H diff -Nru osgearth-2.0+dfsg/src/osgEarth/StringUtils.cpp osgearth-2.1.1+dfsg/src/osgEarth/StringUtils.cpp --- osgearth-2.0+dfsg/src/osgEarth/StringUtils.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/StringUtils.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,135 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ + +#include + +using namespace osgEarth; + +StringTokenizer::StringTokenizer( const std::string& delims, const std::string& quotes ) : +_allowEmpties( true ), +_trimTokens ( true ) +{ + addDelims( delims ); + addQuotes( quotes ); +} + +StringTokenizer::StringTokenizer(const std::string& input, + StringVector& output, + const std::string& delims, + const std::string& quotes, + bool allowEmpties, + bool trimTokens ) : +_allowEmpties( allowEmpties ), +_trimTokens ( trimTokens ) +{ + addDelims( delims ); + addQuotes( quotes ); + tokenize( input, output ); +} + +void +StringTokenizer::addDelim( char delim, bool keep ) +{ + _delims[delim] = keep; +} + +void +StringTokenizer::addDelims( const std::string& delims, bool keep ) +{ + for( unsigned i=0; isecond ) + buf << c; + } + else + { + buf << c; + } + } + else + { + if ( q != _quotes.end() ) + { + quoted = true; + if ( q->second ) + buf << c; + } + else + { + TokenMap::const_iterator d = _delims.find( c ); + if ( d == _delims.end() ) + { + buf << c; + } + else + { + std::string token = _trimTokens ? trim(buf.str()) : buf.str(); + + if ( _allowEmpties || !token.empty() ) + output.push_back( token ); + + if ( d->second == true ) + { + output.push_back( std::string(1, c) ); + } + + buf.str(""); + } + } + } + } + + std::string last = _trimTokens ? trim(buf.str()) : buf.str(); + if ( !last.empty() ) + output.push_back( last ); +} + diff -Nru osgearth-2.0+dfsg/src/osgEarth/TaskService osgearth-2.1.1+dfsg/src/osgEarth/TaskService --- osgearth-2.0+dfsg/src/osgEarth/TaskService 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/TaskService 2011-11-04 19:44:43.000000000 +0000 @@ -21,13 +21,13 @@ #include #include -#include -#include -#include +#include #include +#include #include #include #include +#include namespace osgEarth { @@ -69,6 +69,13 @@ const std::string& getName() const { return _name; } void setName( const std::string& name ) { _name = name; } void reset() { _result = 0L; } + osg::Timer_t startTime() const { return _startTime; } + osg::Timer_t endTime() const { return _endTime; } + double runTime() const { return osg::Timer::instance()->delta_s(_startTime,_endTime); } + + void setCompletedEvent( Threading::Event* value ) { _completedEvent = value; } + Threading::Event* getCompletedEvent() const { return _completedEvent; } + protected: float _priority; volatile State _state; @@ -76,10 +83,42 @@ osg::ref_ptr _result; osg::ref_ptr< ProgressCallback > _progress; std::string _name; + osg::Timer_t _startTime; + osg::Timer_t _endTime; + Threading::Event* _completedEvent; }; typedef std::list< osg::ref_ptr > TaskRequestList; + typedef std::vector< osg::ref_ptr > TaskRequestVector; + + typedef std::multimap< float, osg::ref_ptr > TaskRequestPriorityMap; + + /** + * Convenience template for creating a task that synchronized with an event. + * Initialze multiple ParallelTask's with a common MultiEvent (semaphore) to + * run them in parallel and wait for them all to complete. + */ + template + struct ParallelTask : public TaskRequest, T + { + ParallelTask() : _mev(0L), _sev(0L) { } + ParallelTask( Threading::MultiEvent* ev ) : _mev(ev), _sev(0L) { } + ParallelTask( Threading::Event* ev ) : _sev(ev), _mev(0L) { } + + void operator()( ProgressCallback* pc ) + { + this->execute(); + if ( _mev ) + _mev->notify(); + else if ( _sev ) + _sev->set(); + } + + Threading::MultiEvent* _mev; + Threading::Event* _sev; + }; + class TaskRequestQueue : public osg::Referenced { public: @@ -97,11 +136,10 @@ unsigned int getNumRequests() const; private: - TaskRequestList _requests; + TaskRequestPriorityMap _requests; OpenThreads::Mutex _mutex; OpenThreads::Condition _cond; volatile bool _done; - //osg::ref_ptr< osg::RefBlock > _block; int _stamp; }; @@ -159,7 +197,7 @@ }; /** - * Managed a pool of TaskService objects, automatically allocating + * Manages a pool of TaskService objects, automatically allocating * threads among them based on a weighting metric. */ class OSGEARTH_EXPORT TaskServiceManager : public osg::Referenced diff -Nru osgearth-2.0+dfsg/src/osgEarth/TaskService.cpp osgearth-2.1.1+dfsg/src/osgEarth/TaskService.cpp --- osgearth-2.0+dfsg/src/osgEarth/TaskService.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/TaskService.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -39,8 +39,9 @@ { if ( _state == STATE_IN_PROGRESS ) { + _startTime = osg::Timer::instance()->tick(); (*this)( _progress.get() ); - //OE_INFO << LC << "TR [" << getName() << "] finished" << std::endl; + _endTime = osg::Timer::instance()->tick(); } else { @@ -86,14 +87,18 @@ void TaskRequestQueue::add( TaskRequest* request ) { - ScopedLock lock(_mutex); - request->setState( TaskRequest::STATE_PENDING ); // install a progress callback if one isn't already installed if ( !request->getProgressCallback() ) request->setProgressCallback( new ProgressCallback() ); + ScopedLock lock(_mutex); + + // insert by priority. + _requests.insert( std::pair(request->getPriority(), request) ); + +#if 0 // insert by priority. bool inserted = false; for( TaskRequestList::iterator i = _requests.begin(); i != _requests.end(); i++ ) @@ -109,6 +114,7 @@ if ( !inserted ) _requests.push_back( request ); +#endif // since there is data in the queue, wake up one waiting task thread. _cond.signal(); @@ -130,8 +136,8 @@ return 0L; } - osg::ref_ptr next = _requests.front(); - _requests.pop_front(); + osg::ref_ptr next = _requests.begin()->second.get(); //_requests.front(); + _requests.erase( _requests.begin() ); //_requests.pop_front(); // I'm done, someone else take a turn: // (technically this shouldn't be necessary since add() bumps the semaphore once @@ -186,12 +192,25 @@ else if ( !_request->wasCanceled() ) { + if ( _request->getProgressCallback() ) + _request->getProgressCallback()->onStarted(); + _request->setState( TaskRequest::STATE_IN_PROGRESS ); _request->run(); + + //OE_INFO << LC << "Task \"" << _request->getName() << "\" runtime = " << _request->runTime() << " s." << std::endl; + } + else + { + //OE_INFO << LC << "Task \"" << _request->getName() << "\" was cancelled before it ran." << std::endl; } _request->setState( TaskRequest::STATE_COMPLETED ); + // signal the completion of a request. + if ( _request->getProgressCallback() ) + _request->getProgressCallback()->onCompleted(); + // Release the request _request = 0; } @@ -271,8 +290,8 @@ //Remove finished threads every 60 frames if (stamp - _lastRemoveFinishedThreadsStamp > 60) { - removeFinishedThreads(); - _lastRemoveFinishedThreadsStamp = stamp; + removeFinishedThreads(); + _lastRemoveFinishedThreadsStamp = stamp; } } diff -Nru osgearth-2.0+dfsg/src/osgEarth/TerrainEngineNode osgearth-2.1.1+dfsg/src/osgEarth/TerrainEngineNode --- osgearth-2.0+dfsg/src/osgEarth/TerrainEngineNode 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/TerrainEngineNode 2011-11-04 19:44:43.000000000 +0000 @@ -21,13 +21,14 @@ #include #include -#include #include #include #include namespace osgEarth { + class TextureCompositor; + /** * TerrainEngineNode is the base class and interface for map engine implementations. * @@ -65,6 +66,8 @@ TerrainEngineNode( const TerrainEngineNode& rhs, const osg::CopyOp& op =osg::CopyOp::DEEP_COPY_ALL ); + virtual ~TerrainEngineNode(); + public: // osg::Node overrides virtual osg::BoundingSphere computeBound() const; virtual void traverse( osg::NodeVisitor& ); @@ -110,19 +113,22 @@ void onEnabledChanged( TerrainLayer* layer ); void onOpacityChanged( ImageLayer* layer ); - void onGammaChanged( ImageLayer* layer ); - // a uniform array marking the opacity of each image layer - osg::ref_ptr< ArrayUniform > _layerOpacityUniform; - //osg::ref_ptr< osg::Uniform > _layerOpacityUniform; - - // a uniform array marking the enabled state of each image layer - osg::ref_ptr< ArrayUniform > _layerEnabledUniform; - //osg::ref_ptr< osg::Uniform > _layerEnabledUniform; - - // a uniform array holding the min and max visible range of each image layer - osg::ref_ptr< ArrayUniform > _layerRangeUniform; - //osg::ref_ptr< osg::Uniform > _layerRangeUniform; + ArrayUniform _layerOpacityUniform; + ArrayUniform _layerEnabledUniform; + ArrayUniform _layerRangeUniform; + + //// a uniform array marking the opacity of each image layer + //osg::ref_ptr< ArrayUniform > _layerOpacityUniform; + ////osg::ref_ptr< osg::Uniform > _layerOpacityUniform; + + //// a uniform array marking the enabled state of each image layer + //osg::ref_ptr< ArrayUniform > _layerEnabledUniform; + ////osg::ref_ptr< osg::Uniform > _layerEnabledUniform; + + //// a uniform array holding the min and max visible range of each image layer + //osg::ref_ptr< ArrayUniform > _layerRangeUniform; + ////osg::ref_ptr< osg::Uniform > _layerRangeUniform; private: MapFrame _mapf; @@ -131,6 +137,7 @@ osg::ref_ptr _imageLayerController; osg::ref_ptr _map; osg::ref_ptr _startFrameTimeUniform; + osg::ref_ptr _cameraElevationUniform; enum InitStage { INIT_NONE, diff -Nru osgearth-2.0+dfsg/src/osgEarth/TerrainEngineNode.cpp osgearth-2.1.1+dfsg/src/osgEarth/TerrainEngineNode.cpp --- osgearth-2.0+dfsg/src/osgEarth/TerrainEngineNode.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/TerrainEngineNode.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -71,11 +72,26 @@ _mapf.sync(); int layerNum = _mapf.indexOf( static_cast(layer) ); if ( layerNum >= 0 ) - _layerEnabledUniform->setElement( layerNum, layer->getEnabled() ); + _layerEnabledUniform.setElement( layerNum, layer->getEnabled() ); else OE_WARN << LC << "Odd, updateLayerOpacity did not find layer" << std::endl; } +TerrainEngineNode::~TerrainEngineNode() +{ + //Remove any callbacks added to the image layers + if (_map.valid()) + { + MapFrame mapf( _map.get(), Map::IMAGE_LAYERS, "TerrainEngineNode::~TerrainEngineNode" ); + for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); ++i ) + { + i->get()->removeCallback( _imageLayerController.get() ); + } + } + + +} + // this handler adjusts the uniform set when a terrain layer's "opacity" value changes void TerrainEngineNode::ImageLayerController::onOpacityChanged( ImageLayer* layer ) @@ -86,18 +102,11 @@ _mapf.sync(); int layerNum = _mapf.indexOf( layer ); if ( layerNum >= 0 ) - _layerOpacityUniform->setElement( layerNum, layer->getOpacity() ); + _layerOpacityUniform.setElement( layerNum, layer->getOpacity() ); else OE_WARN << LC << "Odd, onOpacityChanged did not find layer" << std::endl; } -// this handler adjusts the uniform set when an image layer's "gamma" value changes -void -TerrainEngineNode::ImageLayerController::onGammaChanged( ImageLayer* layer ) -{ - //TODO -} - //------------------------------------------------------------------------ TerrainEngineNode::TerrainEngineNode() : @@ -151,6 +160,14 @@ osg::StateSet* set = getOrCreateStateSet(); set->setAttributeAndModes( new osg::CullFace( osg::CullFace::BACK ), osg::StateAttribute::ON ); + // elevation uniform + _cameraElevationUniform = new osg::Uniform( osg::Uniform::FLOAT, "osgearth_CameraElevation" ); + _cameraElevationUniform->set( 0.0f ); + set->addUniform( _cameraElevationUniform.get() ); + + set->getOrCreateUniform( "osgearth_ImageLayerAttenuation", osg::Uniform::FLOAT )->set( + *options.attentuationDistance() ); + _initStage = INIT_PREINIT_COMPLETE; } @@ -266,6 +283,11 @@ // get a copy of the image layer stack: MapFrame mapf( _map.get(), Map::IMAGE_LAYERS ); + _imageLayerController->_layerEnabledUniform.detach(); + _imageLayerController->_layerOpacityUniform.detach(); + _imageLayerController->_layerRangeUniform.detach(); + +#if 0 if ( _imageLayerController->_layerEnabledUniform.valid() ) _imageLayerController->_layerEnabledUniform->removeFrom( stateSet ); @@ -274,50 +296,43 @@ if ( _imageLayerController->_layerRangeUniform.valid() ) _imageLayerController->_layerRangeUniform->removeFrom( stateSet ); +#endif - //stateSet->removeUniform( "osgearth_ImageLayerOpacity" ); - //stateSet->removeUniform( "osgearth_ImageLayerOpacity[0]" ); - //stateSet->removeUniform( "osgearth_ImageLayerEnabled" ); - //stateSet->removeUniform( "osgearth_ImageLayerEnabled[0]" ); - //stateSet->removeUniform( "osgearth_ImageLayerRange" ); - //stateSet->removeUniform( "osgearth_ImageLayerRange[0]" ); - stateSet->removeUniform( "osgearth_ImageLayerAttenuation" ); + //stateSet->removeUniform( "osgearth_ImageLayerAttenuation" ); if ( mapf.imageLayers().size() > 0 ) { // the "enabled" uniform is fixed size. this is handy to account for layers that are in flux...i.e., their source // layer count has changed, but the shader has not yet caught up. In the future we might use this to disable // "ghost" layers that used to exist at a given index, but no longer do. - - _imageLayerController->_layerEnabledUniform = new ArrayUniform( osg::Uniform::BOOL, "osgearth_ImageLayerEnabled", 64 ); //mapf.imageLayers().size() ); - _imageLayerController->_layerOpacityUniform = new ArrayUniform( osg::Uniform::FLOAT, "osgearth_ImageLayerOpacity", mapf.imageLayers().size() ); - _imageLayerController->_layerRangeUniform = new ArrayUniform( osg::Uniform::FLOAT, "osgearth_ImageLayerRange", 2 * mapf.imageLayers().size() ); + + _imageLayerController->_layerEnabledUniform.attach( "osgearth_ImageLayerEnabled", osg::Uniform::BOOL, stateSet, 16 ); + _imageLayerController->_layerOpacityUniform.attach( "osgearth_ImageLayerOpacity", osg::Uniform::FLOAT, stateSet, mapf.imageLayers().size() ); + _imageLayerController->_layerRangeUniform.attach ( "osgearth_ImageLayerRange", osg::Uniform::FLOAT, stateSet, 2 * mapf.imageLayers().size() ); + + //_imageLayerController->_layerEnabledUniform = new ArrayUniform( osg::Uniform::BOOL, "osgearth_ImageLayerEnabled", 64 ); //mapf.imageLayers().size() ); + //_imageLayerController->_layerOpacityUniform = new ArrayUniform( osg::Uniform::FLOAT, "osgearth_ImageLayerOpacity", mapf.imageLayers().size() ); + //_imageLayerController->_layerRangeUniform = new ArrayUniform( osg::Uniform::FLOAT, "osgearth_ImageLayerRange", 2 * mapf.imageLayers().size() ); for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); ++i ) { ImageLayer* layer = i->get(); int index = (int)(i - mapf.imageLayers().begin()); - _imageLayerController->_layerOpacityUniform->setElement( index, layer->getOpacity() ); - _imageLayerController->_layerEnabledUniform->setElement( index, layer->getEnabled() ); - _imageLayerController->_layerRangeUniform->setElement( (2*index), layer->getImageLayerOptions().minVisibleRange().value() ); - _imageLayerController->_layerRangeUniform->setElement( (2*index)+1, layer->getImageLayerOptions().maxVisibleRange().value() ); + _imageLayerController->_layerOpacityUniform.setElement( index, layer->getOpacity() ); + _imageLayerController->_layerEnabledUniform.setElement( index, layer->getEnabled() ); + _imageLayerController->_layerRangeUniform.setElement( (2*index), layer->getImageLayerOptions().minVisibleRange().value() ); + _imageLayerController->_layerRangeUniform.setElement( (2*index)+1, layer->getImageLayerOptions().maxVisibleRange().value() ); } // set the remainder of the layers to disabled for( int j=mapf.imageLayers().size(); j<64; ++j ) - _imageLayerController->_layerEnabledUniform->setElement( j, false ); + _imageLayerController->_layerEnabledUniform.setElement( j, false ); - _imageLayerController->_layerOpacityUniform->addTo( stateSet ); - _imageLayerController->_layerEnabledUniform->addTo( stateSet ); - _imageLayerController->_layerRangeUniform->addTo( stateSet ); - //stateSet->addUniform( _imageLayerController->_layerOpacityUniform.get() ); - //stateSet->addUniform( _imageLayerController->_layerEnabledUniform.get() ); - //stateSet->addUniform( _imageLayerController->_layerRangeUniform.get() ); + //_imageLayerController->_layerOpacityUniform->addTo( stateSet ); + //_imageLayerController->_layerEnabledUniform->addTo( stateSet ); + //_imageLayerController->_layerRangeUniform->addTo( stateSet ); } - - stateSet->getOrCreateUniform( "osgearth_ImageLayerAttenuation", osg::Uniform::FLOAT )->set( - *getTerrainOptions().attentuationDistance() ); } void @@ -342,7 +357,23 @@ if ( nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR ) { if ( Registry::instance()->getCapabilities().supportsGLSL() ) + { _updateLightingUniformsHelper.cullTraverse( this, &nv ); + + osgUtil::CullVisitor* cv = dynamic_cast( &nv ); + if ( cv ) + { + osg::Vec3d eye = cv->getEyePoint(); + + float elevation; + if ( _map->isGeocentric() ) + elevation = eye.length() - osg::WGS_84_RADIUS_EQUATOR; + else + elevation = eye.z(); + + _cameraElevationUniform->set( elevation ); + } + } } //else if ( nv.getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR ) diff -Nru osgearth-2.0+dfsg/src/osgEarth/TerrainLayer osgearth-2.1.1+dfsg/src/osgEarth/TerrainLayer --- osgearth-2.0+dfsg/src/osgEarth/TerrainLayer 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/TerrainLayer 2011-11-04 19:44:43.000000000 +0000 @@ -66,11 +66,32 @@ const optional& minLevel() const { return _minLevel; } /** + * Gets or sets the minimum level resolution for which this layer should generate data. + */ + optional& minLevelResolution() { return _minLevelResolution;} + const optional& minLevelResolution() const { return _minLevelResolution; } + + + /** * The maximum level of detail for which this layer should generate data. */ optional& maxLevel() { return _maxLevel; } const optional& maxLevel() const { return _maxLevel; } + /** + * The maximum level resolution for which this layer should generate data. + */ + optional& maxLevelResolution() { return _maxLevelResolution; } + const optional& maxLevelResolution() const { return _maxLevelResolution; } + + /** + * The maximum level that data should be queried for this layer. + */ + optional& maxDataLevel() { return _maxDataLevel; } + const optional& maxDataLevel() const { return _maxDataLevel; } + + + /** * Whether to render this layer with the map. */ @@ -143,6 +164,8 @@ optional _driver; optional _minLevel; optional _maxLevel; + optional _minLevelResolution; + optional _maxLevelResolution; optional _cacheFormat; optional _cacheEnabled; optional _cacheOnly; @@ -152,6 +175,7 @@ optional _reprojectedTileSize; optional _edgeBufferRatio; optional _cacheId; + optional _maxDataLevel; }; /** @@ -171,16 +195,23 @@ class OSGEARTH_EXPORT TerrainLayer : public Layer { protected: - TerrainLayer(); + TerrainLayer( TerrainLayerOptions* options ); - TerrainLayer( TileSource* tileSource ); + TerrainLayer( TerrainLayerOptions* options, TileSource* tileSource ); public: - virtual const TerrainLayerOptions& getTerrainLayerOptions() const =0; + /** + * The options data connected to this layer. + */ + const TerrainLayerOptions& getTerrainLayerOptions() const { + return *_runtimeOptions; } + /** + * Whether to draw this layer. + */ void setEnabled( bool value ); - bool getEnabled() const { return _actualEnabled; } + bool getEnabled() const { return *_runtimeOptions->enabled(); } /** * Gets the readable name of the map layer. @@ -201,7 +232,7 @@ /** * Gets the tile size of the this MapLayer */ - unsigned int getTileSize() const { return _tileSize; } + unsigned int getTileSize() const; /** * Gets the maximum data level of this MapLayer @@ -213,7 +244,7 @@ */ bool isDynamic() const; - const std::string& getCacheFormat() const { return _actualCacheFormat; } + const std::string& getCacheFormat() const { return *_runtimeOptions->cacheFormat(); } public: // methods @@ -246,6 +277,12 @@ */ virtual void setTargetProfileHint( const Profile* profile ); + /** + * Whether this layer is in "cache only" mode (i.e. data will only be + * read from the cache and not from the tile source + */ + bool isCacheOnly() const { return *_runtimeOptions->cacheOnly(); } + protected: virtual void initTileSource(); @@ -254,31 +291,22 @@ protected: - // these are called "actual" because they override the corresponding - // init settings found in the TerrainLayerOptions. - bool _actualEnabled; - std::string _actualCacheFormat; - bool _actualCacheOnly; - osg::ref_ptr _tileSource; osg::ref_ptr _cache; CacheSpec _cacheSpec; - osg::ref_ptr< const Profile > _profile; - osg::ref_ptr< const Profile > _cacheProfile; - - optional _overrideCacheOnly; - - bool _tileSourceInitialized; - + osg::ref_ptr _profile; + osg::ref_ptr _cacheProfile; osg::ref_ptr _targetProfileHint; + bool _tileSourceInitialized; + unsigned _tileSize; + private: - std::string _name; - bool _cacheOnlyEnv; - unsigned int _tileSize; - std::string _referenceURI; - OpenThreads::Mutex _initTileSourceMutex; + std::string _name; + std::string _referenceURI; + OpenThreads::Mutex _initTileSourceMutex; + TerrainLayerOptions* _runtimeOptions; void init(); virtual void fireCallback( TerrainLayerCallbackMethodPtr method ) =0; diff -Nru osgearth-2.0+dfsg/src/osgEarth/TerrainLayer.cpp osgearth-2.1.1+dfsg/src/osgEarth/TerrainLayer.cpp --- osgearth-2.0+dfsg/src/osgEarth/TerrainLayer.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/TerrainLayer.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -79,6 +79,8 @@ conf.updateIfSet( "cacheid", _cacheId ); conf.updateIfSet( "min_level", _minLevel ); conf.updateIfSet( "max_level", _maxLevel ); + conf.updateIfSet( "min_level_resolution", _minLevelResolution ); + conf.updateIfSet( "max_level_resolution", _maxLevelResolution ); conf.updateIfSet( "cache_enabled", _cacheEnabled ); conf.updateIfSet( "cache_only", _cacheOnly ); conf.updateIfSet( "cache_format", _cacheFormat ); @@ -86,6 +88,11 @@ conf.updateIfSet( "enabled", _enabled ); conf.updateIfSet( "edge_buffer_ratio", _edgeBufferRatio); conf.updateObjIfSet( "profile", _profile ); + conf.updateIfSet( "max_data_level", _maxDataLevel); + conf.updateIfSet( "reprojected_tilesize", _reprojectedTileSize); + + //Merge the TileSource options + if (driver().isSet()) conf.merge( driver()->getConfig() ); return conf; } @@ -96,7 +103,9 @@ _name = conf.value("name"); conf.getIfSet( "cacheid", _cacheId ); conf.getIfSet( "min_level", _minLevel ); - conf.getIfSet( "max_level", _maxLevel ); + conf.getIfSet( "max_level", _maxLevel ); + conf.getIfSet( "min_level_resolution", _minLevelResolution ); + conf.getIfSet( "max_level_resolution", _maxLevelResolution ); conf.getIfSet( "cache_enabled", _cacheEnabled ); conf.getIfSet( "cache_only", _cacheOnly ); conf.getIfSet( "cache_format", _cacheFormat ); @@ -104,6 +113,9 @@ conf.getIfSet( "enabled", _enabled ); conf.getIfSet( "edge_buffer_ratio", _edgeBufferRatio); conf.getObjIfSet( "profile", _profile ); + conf.getIfSet( "max_data_level", _maxDataLevel); + conf.getIfSet( "reprojected_tilesize", _reprojectedTileSize); + if ( conf.hasValue("driver") ) driver() = TileSourceOptions(conf); @@ -118,13 +130,15 @@ //------------------------------------------------------------------------ -TerrainLayer::TerrainLayer() +TerrainLayer::TerrainLayer( TerrainLayerOptions* options ) : +_runtimeOptions( options ) { init(); } -TerrainLayer::TerrainLayer( TileSource* tileSource ) : -_tileSource( tileSource ) +TerrainLayer::TerrainLayer( TerrainLayerOptions* options, TileSource* tileSource ) : +_runtimeOptions( options ), +_tileSource ( tileSource ) { init(); } @@ -132,22 +146,8 @@ void TerrainLayer::init() { - // Warning: don't access getTerrainLayerOptions() here, since it's a virtual function. - _tileSourceInitialized = false; - - _tileSize = 256; - _actualCacheFormat = ""; - _actualCacheOnly = false; - _actualEnabled = true; - - // Parse any environment variables: - if ( ::getenv("OSGEARTH_CACHE_ONLY") != 0 ) - { - _cacheOnlyEnv = true; - _actualCacheOnly = true; - OE_INFO << "CACHE-ONLY mode enabled!!" << std::endl; - } + _tileSize = 256; } void @@ -157,25 +157,30 @@ { _cache = cache; - const TerrainLayerOptions& opt = getTerrainLayerOptions(); - // Read properties from the cache if not already set - if (_cache.valid() && opt.cacheEnabled() == true ) + if ( _cache.valid() && _runtimeOptions->cacheEnabled() == true ) { // create the unique cache ID for the tile configuration. std::string cacheId; - if ( opt.cacheId().isSet() && !opt.cacheId()->empty() ) + if ( _runtimeOptions->cacheId().isSet() && !_runtimeOptions->cacheId()->empty() ) { // user expliticy set a cacheId in the terrain layer options. - cacheId = *opt.cacheId(); + cacheId = *_runtimeOptions->cacheId(); } else { // system will generate a cacheId. + Config hashConf = _runtimeOptions->driver()->getConfig(); + + // remove cache-control properties before hashing. + hashConf.remove( "cache_only" ); + hashConf.remove( "cache_enabled" ); + std::stringstream buf; + //OE_NOTICE << hashConf.toHashString() << std::endl; buf << std::fixed << std::setfill('0') << std::hex - << osgEarth::hashString( opt.driver()->getConfig().toHashString() ); + << osgEarth::hashString( hashConf.toHashString() ); cacheId = buf.str(); } @@ -191,15 +196,12 @@ } // Set the cache format if it hasn't been explicitly set - _actualCacheFormat = opt.cacheFormat().value(); - if ( _actualCacheFormat.empty() ) - _actualCacheFormat = _cacheSpec.format(); - - // check for a cache-only override. - if ( _overrideCacheOnly.isSetTo( true ) ) - _actualCacheOnly = true; + if ( !_runtimeOptions->cacheFormat().isSet() ) + { + _runtimeOptions->cacheFormat() = _cacheSpec.format(); + } - _cacheSpec = CacheSpec( cacheId, _actualCacheFormat, getName() ); + _cacheSpec = CacheSpec( cacheId, *_runtimeOptions->cacheFormat(), getName() ); } } } @@ -213,11 +215,13 @@ TileSource* TerrainLayer::getTileSource() const { - if ( (_tileSource.valid() && !_tileSourceInitialized) || (!_tileSource.valid() && _actualCacheOnly == false) ) + if ((_tileSource.valid() && !_tileSourceInitialized) || + (!_tileSource.valid() && _runtimeOptions->cacheOnly() == false) ) { OpenThreads::ScopedLock< OpenThreads::Mutex > lock(const_cast(this)->_initTileSourceMutex ); // double-check pattern - if ( (_tileSource.valid() && !_tileSourceInitialized) || (!_tileSource.valid() && _actualCacheOnly == false) ) + if ((_tileSource.valid() && !_tileSourceInitialized) || + (!_tileSource.valid() && _runtimeOptions->cacheOnly() == false)) { const_cast(this)->initTileSource(); } @@ -231,9 +235,7 @@ { if ( !_profile.valid() ) { - //const TerrainLayerOptions& opt = getTerrainLayerOptions(); - - if ( _actualCacheOnly == false && !_tileSourceInitialized ) //!_tileSource.valid() ) + if ( _runtimeOptions->cacheOnly() == false && !_tileSourceInitialized ) { // Call getTileSource to make sure the TileSource is initialized getTileSource(); @@ -251,14 +253,31 @@ 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) + if ( ts ) { return ts->getMaxDataLevel(); } + + //Just default return 20; } +unsigned +TerrainLayer::getTileSize() const +{ + TileSource* ts = getTileSource(); + return ts ? ts->getPixelsPerTile() : _tileSize; +} + bool TerrainLayer::isDynamic() const { @@ -279,23 +298,21 @@ { OE_DEBUG << LC << "Initializing tile source ..." << std::endl; - const TerrainLayerOptions& opt = getTerrainLayerOptions(); - // instantiate it from driver options if it has not already been created: if ( !_tileSource.valid() ) { - if ( opt.driver().isSet() ) + if ( _runtimeOptions->driver().isSet() ) { - _tileSource = TileSourceFactory::create( opt.driver().value() ); + _tileSource = TileSourceFactory::create( *_runtimeOptions->driver() ); } } // next check for an override-profile. The profile usually comes from the // TileSource itself, but you have the option of overriding: osg::ref_ptr overrideProfile; - if ( opt.profile().isSet() ) + if ( _runtimeOptions->profile().isSet() ) { - overrideProfile = Profile::create( opt.profile().value() ); + overrideProfile = Profile::create( *_runtimeOptions->profile() ); } // Initialize the profile with the context information: @@ -313,14 +330,12 @@ _tileSource = NULL; } } - //_tileSource = tileSource; // Set the cache format to the native format of the TileSource if it isn't already set. - _actualCacheFormat = opt.cacheFormat().value(); - if ( _actualCacheFormat.empty() ) + if ( _runtimeOptions->cacheFormat()->empty() ) { - _actualCacheFormat = suggestCacheFormat(); - _cacheSpec = CacheSpec( _cacheSpec.cacheId(), _actualCacheFormat, _cacheSpec.name() ); + _runtimeOptions->cacheFormat() = suggestCacheFormat(); + _cacheSpec = CacheSpec( _cacheSpec.cacheId(), *_runtimeOptions->cacheFormat(), _cacheSpec.name() ); } // Set the profile from the TileSource if possible: @@ -334,12 +349,15 @@ else if (_cache.valid()) { OE_NOTICE << "Could not initialize TileSource " << _name << " but cache is valid. Setting layer to cache_only." << std::endl; - _actualCacheOnly = true; + _runtimeOptions->cacheOnly() = true; } - // check for a cache-only override. - if ( _overrideCacheOnly.isSetTo( true ) ) - _actualCacheOnly = true; + // check the environment to see if cache only should be enabled + if ( _runtimeOptions->cacheOnly() == false && ::getenv("OSGEARTH_CACHE_ONLY") != 0 ) + { + _runtimeOptions->cacheOnly() = true; + OE_INFO << "CACHE-ONLY mode enabled!!" << std::endl; + } _tileSourceInitialized = true; } @@ -348,16 +366,37 @@ TerrainLayer::isKeyValid(const TileKey& key) const { if (!key.valid()) return false; - const TerrainLayerOptions& opt = getTerrainLayerOptions(); - if ( opt.minLevel().isSet() && (int)key.getLevelOfDetail() < opt.minLevel().value() ) return false; - if ( opt.maxLevel().isSet() && (int)key.getLevelOfDetail() > opt.maxLevel().value() ) return false; + + // Check to see if explicit levels of detail are set + if ( _runtimeOptions->minLevel().isSet() && (int)key.getLevelOfDetail() < _runtimeOptions->minLevel().value() ) + return false; + if ( _runtimeOptions->maxLevel().isSet() && (int)key.getLevelOfDetail() > _runtimeOptions->maxLevel().value() ) + return false; + + // Check to see if levels of detail based on resolution are set + if ( _runtimeOptions->minLevelResolution().isSet() ) + { + unsigned int minLevel = getProfile()->getLevelOfDetailForHorizResolution( + _runtimeOptions->minLevelResolution().value(), getTileSize() ); + OE_DEBUG << "Computed min level of " << minLevel << std::endl; + if (key.getLevelOfDetail() < minLevel) return false; + } + + if (_runtimeOptions->maxLevelResolution().isSet()) + { + unsigned int maxLevel = getProfile()->getLevelOfDetailForHorizResolution( + _runtimeOptions->maxLevelResolution().value(), getTileSize() ); + OE_DEBUG << "Computed max level of " << maxLevel << std::endl; + if (key.getLevelOfDetail() > maxLevel) return false; + } + return true; } void TerrainLayer::setEnabled( bool value ) { - _actualEnabled = value; + _runtimeOptions->enabled() = value; fireCallback( &TerrainLayerCallback::onEnabledChanged ); } @@ -370,5 +409,5 @@ void TerrainLayer::setCacheOnly( bool value ) { - _overrideCacheOnly = value; + _runtimeOptions->cacheOnly() = value; } diff -Nru osgearth-2.0+dfsg/src/osgEarth/TerrainOptions osgearth-2.1.1+dfsg/src/osgEarth/TerrainOptions --- osgearth-2.0+dfsg/src/osgEarth/TerrainOptions 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/TerrainOptions 2011-11-04 19:44:43.000000000 +0000 @@ -33,8 +33,11 @@ public: /** Tile loading modes. */ enum Mode { - /** Load tiles using the standard OSG database pager mechanism. The default. */ - MODE_STANDARD, + /** Load tiles one LOD at a tile, serially */ + MODE_SERIAL, + + /** Load tiles one LOD at a tile, in parallel */ + MODE_PARALLEL, /** Load tiles using a task service thread pool, enforcing sequential display of tile LODs. */ @@ -42,7 +45,10 @@ /** Load tiles using a task service thread pool, but prioritize loading of the highest visible LOD for imagery (elevation data is always sequential). */ - MODE_PREEMPTIVE + MODE_PREEMPTIVE, + + /** Load tiles using the standard OSG database pager mechanism. The default. */ + MODE_STANDARD = MODE_SERIAL }; public: @@ -165,12 +171,6 @@ const optional& attentuationDistance() const { return _attenuationDistance; } /** - * Level-of-detail blending - */ - optional& lodBlending() { return _lodBlending; } - const optional& lodBlending() const { return _lodBlending; } - - /** * Transition time, in seconds, for tile image fade-in when LOD blending is enabled */ optional& lodTransitionTime() { return _lodTransitionTimeSeconds; } @@ -210,8 +210,13 @@ * The interpolation method to use when sampling heightfields. */ optional& elevationInterpolation(void) { return _elevationInterpolation; } - optional const& elevationInterpolation(void) const { return _elevationInterpolation;} + const optional& elevationInterpolation(void) const { return _elevationInterpolation;} + /** + * Whether to enable mipmaping and mipmap generation on textures + */ + optional& enableMipmapping() { return _enableMipmapping; } + const optional& enableMipmapping() const { return _enableMipmapping; } public: virtual Config getConfig() const; @@ -237,6 +242,7 @@ optional _attenuationDistance; optional _lodBlending; optional _lodTransitionTimeSeconds; + optional _enableMipmapping; #if 0 optional _contourMagFilter; optional _contourMinFilter; diff -Nru osgearth-2.0+dfsg/src/osgEarth/TerrainOptions.cpp osgearth-2.1.1+dfsg/src/osgEarth/TerrainOptions.cpp --- osgearth-2.0+dfsg/src/osgEarth/TerrainOptions.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/TerrainOptions.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -38,7 +38,9 @@ void LoadingPolicy::fromConfig( const Config& conf ) { - conf.getIfSet( "mode", "standard", _mode, MODE_STANDARD ); + conf.getIfSet( "mode", "standard", _mode, MODE_SERIAL ); + conf.getIfSet( "mode", "serial", _mode, MODE_SERIAL ); + conf.getIfSet( "mode", "parallel", _mode, MODE_PARALLEL ); conf.getIfSet( "mode", "sequential", _mode, MODE_SEQUENTIAL ); conf.getIfSet( "mode", "preemptive", _mode, MODE_PREEMPTIVE ); conf.getIfSet( "loading_threads", _numLoadingThreads ); @@ -52,7 +54,8 @@ LoadingPolicy::getConfig() const { Config conf( "loading_policy" ); - conf.addIfSet( "mode", "standard", _mode, MODE_STANDARD ); + conf.addIfSet( "mode", "standard", _mode, MODE_STANDARD ); // aka MODE_SERIAL + conf.addIfSet( "mode", "parallel", _mode, MODE_PARALLEL ); conf.addIfSet( "mode", "sequential", _mode, MODE_SEQUENTIAL ); conf.addIfSet( "mode", "preemptive", _mode, MODE_PREEMPTIVE ); conf.addIfSet( "loading_threads", _numLoadingThreads ); @@ -96,7 +99,8 @@ _attenuationDistance( 1000000 ), _lodBlending( false ), _lodTransitionTimeSeconds( 0.5f ), -_elevationInterpolation( INTERP_BILINEAR ) +_elevationInterpolation( INTERP_BILINEAR ), +_enableMipmapping( true ) { fromConfig( _conf ); } @@ -116,8 +120,8 @@ conf.updateIfSet( "max_lod", _maxLOD ); conf.updateIfSet( "lighting", _enableLighting ); conf.updateIfSet( "attenuation_distance", _attenuationDistance ); - conf.updateIfSet( "lod_blending", _lodBlending ); conf.updateIfSet( "lod_transition_time", _lodTransitionTimeSeconds ); + conf.updateIfSet( "mipmapping", _enableMipmapping ); conf.updateIfSet( "compositor", "auto", _compositingTech, COMPOSITING_AUTO ); conf.updateIfSet( "compositor", "texture_array", _compositingTech, COMPOSITING_TEXTURE_ARRAY ); @@ -145,8 +149,8 @@ conf.getIfSet( "max_lod", _maxLOD ); conf.getIfSet( "lighting", _enableLighting ); conf.getIfSet( "attenuation_distance", _attenuationDistance ); - conf.getIfSet( "lod_blending", _lodBlending ); conf.getIfSet( "lod_transition_time", _lodTransitionTimeSeconds ); + conf.getIfSet( "mipmapping", _enableMipmapping ); conf.getIfSet( "compositor", "auto", _compositingTech, COMPOSITING_AUTO ); conf.getIfSet( "compositor", "texture_array", _compositingTech, COMPOSITING_TEXTURE_ARRAY ); diff -Nru osgearth-2.0+dfsg/src/osgEarth/TextureCompositor osgearth-2.1.1+dfsg/src/osgEarth/TextureCompositor --- osgearth-2.0+dfsg/src/osgEarth/TextureCompositor 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/TextureCompositor 2011-11-04 19:44:43.000000000 +0000 @@ -31,6 +31,16 @@ { /** * Tracks the usage of texture slots and their rendering order for a map. + * + * A "slot" is a home for a texture layer. For example, in a multitexturing setup, a + * slot corresponds to a texture image unit in the hardware. In a texture_array setup, + * a slot is one "slice" of the texture array. + * + * A single layer can occupy more than one slot. (Specifically, this is the case when + * LOD Blending is enabled on that layer -- the layer will require 2 slots in order to + * blend the images together in the shader.) In this case, we refer to the first slot + * as the "primary" slot, and any additional slots belonging to the same layer as + * "secondary" slots. */ class OSGEARTH_EXPORT TextureLayout { @@ -45,8 +55,12 @@ public: TextureLayout(); - /** Gets the texture slot corresponding to the layer UID */ - int getSlot( UID layerUID ) const; + /** + * Gets a texture slot corresponding to the layer UID. There may be more than one, + * the "which" parameter allows you to select one in particular. You can also limit + * the number of slots to search. + */ + int getSlot( UID layerUID, unsigned which =0, unsigned maxSlotsToSearch =~0 ) const; /** Gets the render order index of the layer with the given UID */ int getOrder( UID layerUID ) const; @@ -54,22 +68,37 @@ /** Gets the index of the highest populated slot. */ int getMaxUsedSlot() const; + /** Whether the indicated slot is available for use */ + bool isSlotAvailable( int slot ) const; + /** Accesses a vector that maps layer UIDs to texture slots. */ const TextureSlotVector& getTextureSlots() const { return _slots; } /** Access a vector that specifies the rendering order of texture slots. */ const RenderOrderVector& getRenderOrder() const { return _order; } - public: - void applyMapModelChange( const MapModelChange& change ); + /** Returns "true" if the layout contains at least one secondary slot allocation. */ + bool containsSecondarySlots( unsigned maxSlotsToSearch = ~0) const; + + /** whether LOD blending is enabled on a layer */ + bool isBlendingEnabled( UID layerUID ) const; + + protected: + friend class TextureCompositor; + + void applyMapModelChange( const MapModelChange& change, bool reserveSeconarySlotIfNecessary ); void setReservedSlots( const std::set& reservedSlots ); protected: - TextureSlotVector _slots; - RenderOrderVector _order; - bool _textureImageUnitPerSlot; - std::set _reservedSlots; + TextureSlotVector _slots; + RenderOrderVector _order; + bool _textureImageUnitPerSlot; + std::set _reservedSlots; + std::map _lodBlending; + + void assignPrimarySlot( ImageLayer* layer, int index ); + void assignSecondarySlot( ImageLayer* layer ); }; //----------------------------------------------------------------------- @@ -86,13 +115,17 @@ virtual bool usesShaderComposition() const =0; - virtual void updateMasterStateSet( osg::StateSet* stateSet, const TextureLayout& layout ) const { } + virtual bool blendingRequiresSecondarySlot() const { return false; } virtual bool supportsLayerUpdate() const { return false; } + virtual void updateMasterStateSet( osg::StateSet* stateSet, const TextureLayout& layout ) const { } + virtual GeoImage prepareImage( const GeoImage& image, const GeoExtent& tileExtent ) const { return image; } - virtual void applyLayerUpdate( osg::StateSet* stateSet, UID layerUID, const GeoImage& preparedImage, const GeoExtent& tileExtent, const TextureLayout& layout ) const { } + virtual GeoImage prepareSecondaryImage( const GeoImage& image, const GeoExtent& tileExtent ) const { return image; } + + virtual void applyLayerUpdate( osg::StateSet* stateSet, UID layerUID, const GeoImage& preparedImage, const TileKey& tileKey, const TextureLayout& layout, osg::StateSet* parentStateSet ) const { } virtual void applyLayerRemoval( osg::StateSet* stateSet, UID layerUID ) const { } @@ -146,15 +179,21 @@ GeoImage prepareImage( const GeoImage& image, const GeoExtent& tileExtent ) const; /** + * Like prepareImage, but prepares it for use as a secondary texture (for LOD blending). + */ + GeoImage prepareSecondaryImage( const GeoImage& image, const GeoExtent& tileExtent ) const; + + /** * Updates a stateset's texture composition with an image. Typically this will be the image * returned from prepareImage(), but it doesn't have to be. Note: if the stateset is live in * the scene graph, be sure to only call this method from UPDATE trav. */ void applyLayerUpdate( - osg::StateSet* stateSet, - UID layerUID, + osg::StateSet* stateSet, + UID layerUID, const GeoImage& preparedImage, - const GeoExtent& tileExtent ) const; + const TileKey& tileKey, + osg::StateSet* parentStateSet ) const; /** * Updates a stateset's texture composition based on the information that a layer has been @@ -167,8 +206,8 @@ * texture slot according to the compositor's layout. */ void assignTexCoordArray( - osg::Geometry* geom, - UID layerUID, + osg::Geometry* geom, + UID layerUID, osg::Vec2Array* texCoords ) const; /** diff -Nru osgearth-2.0+dfsg/src/osgEarth/TextureCompositor.cpp osgearth-2.1.1+dfsg/src/osgEarth/TextureCompositor.cpp --- osgearth-2.0+dfsg/src/osgEarth/TextureCompositor.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/TextureCompositor.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -41,16 +41,25 @@ } int -TextureLayout::getSlot( UID layerUID ) const +TextureLayout::getSlot( UID layerUID, unsigned which, unsigned maxSlotsToSearch ) const { - TextureSlotVector::const_iterator i = std::find( _slots.begin(), _slots.end(), layerUID ); - return i != _slots.end() ? (int)(i-_slots.begin()) : -1; + for( unsigned slot = 0; slot < _slots.size() && slot < maxSlotsToSearch; ++slot ) + { + if ( _slots[slot] == layerUID ) + { + if ( which == 0 ) + return slot; + else + --which; + } + } + return -1; } int TextureLayout::getOrder( UID layerUID ) const { - int slot = getSlot( layerUID ); + int slot = getSlot( layerUID, 0 ); RenderOrderVector::const_iterator i = std::find( _order.begin(), _order.end(), slot ); return i != _order.end() ? (int)(i-_order.begin()) : -1; } @@ -65,66 +74,124 @@ } void -TextureLayout::applyMapModelChange( const MapModelChange& change ) +TextureLayout::assignPrimarySlot( ImageLayer* layer, int orderIndex ) { - if ( change.getAction() == MapModelChange::ADD_IMAGE_LAYER ) + int slot = -1; + + bool found = false; + for( TextureSlotVector::iterator i = _slots.begin(); i != _slots.end() && !found; ++i ) { - bool found = false; - for( TextureSlotVector::iterator i = _slots.begin(); i != _slots.end() && !found; ++i ) + slot = (int)(i - _slots.begin()); + + // negative UID means the slot is empty. + bool slotAvailable = (*i < 0) && (_reservedSlots.find(slot) == _reservedSlots.end()); + if ( slotAvailable ) { - int slot = (int)(i - _slots.begin()); + // record this UID in the new slot: + *i = layer->getUID(); - // negative UID means the slot is empty. - bool slotAvailable = (*i < 0) && (_reservedSlots.find(slot) == _reservedSlots.end()); - if ( slotAvailable ) + // record the render order of this slot: + if ( orderIndex >= (int)_order.size() ) { - *i = change.getImageLayer()->getUID(); - - if ( change.getFirstIndex() >= (int)_order.size() ) - { - _order.resize( change.getFirstIndex() + 1, -1 ); - _order[change.getFirstIndex()] = slot; - } + _order.resize( orderIndex + 1, -1 ); + _order[orderIndex] = slot; + } + else + { + if (_order[orderIndex] == -1) + _order[orderIndex] = slot; else - { - if (_order[change.getFirstIndex()] == -1) - _order[change.getFirstIndex()] = slot; - else - _order.insert(_order.begin() + change.getFirstIndex(), slot); - } - - found = true; - break; + _order.insert(_order.begin() + orderIndex, slot); } + + found = true; + break; } + } - if ( !found ) + if ( !found ) + { + // put the UID in the next available slot (that's not reserved). + while( _reservedSlots.find(_slots.size()) != _reservedSlots.end() ) + _slots.push_back( -1 ); + + slot = _slots.size(); + _slots.push_back( layer->getUID() ); + _order.push_back( _slots.size() - 1 ); + } + + OE_INFO << LC << "Allocated SLOT " << slot << "; primary slot for layer \"" << layer->getName() << "\"" << std::endl; +} + +void +TextureLayout::assignSecondarySlot( ImageLayer* layer ) +{ + int slot = -1; + bool found = false; + + for( TextureSlotVector::iterator i = _slots.begin(); i != _slots.end() && !found; ++i ) + { + slot = (int)(i - _slots.begin()); + + // negative UID means the slot is empty. + bool slotAvailable = (*i < 0) && (_reservedSlots.find(slot) == _reservedSlots.end()); + if ( slotAvailable ) { - // put the UID in the next available slot (that's not reserved). - while( _reservedSlots.find(_slots.size()) != _reservedSlots.end() ) - _slots.push_back( -1 ); + // record this UID in the new slot: + *i = layer->getUID(); + found = true; + break; + } + } + + if ( !found ) + { + // put the UID in the next available slot (that's not reserved). + while( _reservedSlots.find(_slots.size()) != _reservedSlots.end() ) + _slots.push_back( -1 ); + + slot = _slots.size(); + _slots.push_back( layer->getUID() ); + } + + OE_INFO << LC << "Allocated SLOT " << slot << "; secondary slot for layer \"" << layer->getName() << "\"" << std::endl; +} + +void +TextureLayout::applyMapModelChange( const MapModelChange& change, bool reserveSeconarySlotIfNecessary ) +{ + if ( change.getAction() == MapModelChange::ADD_IMAGE_LAYER ) + { + assignPrimarySlot( change.getImageLayer(), change.getFirstIndex() ); + + bool blendingOn = change.getImageLayer()->getImageLayerOptions().lodBlending() == true; + _lodBlending[ change.getImageLayer()->getUID() ] = blendingOn; - _slots.push_back( change.getImageLayer()->getUID() ); - _order.push_back( _slots.size() - 1 ); + if ( blendingOn && reserveSeconarySlotIfNecessary ) + { + assignSecondarySlot( change.getImageLayer() ); } } else if ( change.getAction() == MapModelChange::REMOVE_IMAGE_LAYER ) { - for( TextureSlotVector::iterator i = _slots.begin(); i != _slots.end(); ++i ) + for( int which = 0; which <= 1; ++which ) { - if ( *i == change.getLayer()->getUID() ) + int slot = getSlot( change.getLayer()->getUID(), which ); + if ( slot < 0 ) + break; + + _slots[slot] = -1; + + if ( which == 0 ) // primary slot; remove from render order: { - *i = -1; - for( RenderOrderVector::iterator j = _order.begin(); j != _order.end(); ++j ) + for( RenderOrderVector::iterator j = _order.begin(); j != _order.end(); ) { - if ( *j == (int)(i - _slots.begin()) ) - { + if ( *j == slot ) j = _order.erase( j ); - break; - } + else + ++j; } - break; } } } @@ -156,6 +223,38 @@ _reservedSlots = reservedSlots; } +bool +TextureLayout::isSlotAvailable( int i ) const +{ + if ( (i < (int)_slots.size() && _slots[i] < 0) || i >= (int)_slots.size() ) + { + if ( _reservedSlots.find(i) == _reservedSlots.end() ) + { + return true; + } + } + return false; +} + +bool +TextureLayout::containsSecondarySlots( unsigned maxSlotsToSearch ) const +{ + for( int slot = 0; slot < (int)_slots.size() && slot < (int)maxSlotsToSearch; ++slot ) + { + UID uid = _slots[slot]; + if ( getSlot(uid, 0) != slot ) + return true; + } + return false; +} + +bool +TextureLayout::isBlendingEnabled( UID layerUID ) const +{ + std::map::const_iterator i = _lodBlending.find(layerUID); + return i != _lodBlending.end() ? i->second : false; +} + //--------------------------------------------------------------------------- TextureCompositor::TextureCompositor(const TerrainOptions& options) : @@ -187,16 +286,15 @@ out_unit = -1; //TODO: this only supports GPU texturing.... - int maxUnits = osgEarth::Registry::instance()->getCapabilities().getMaxGPUTextureUnits(); + unsigned maxUnits = osgEarth::Registry::instance()->getCapabilities().getMaxGPUTextureUnits(); if ( _tech == TerrainOptions::COMPOSITING_MULTITEXTURE_GPU ) { Threading::ScopedWriteLock exclusiveLock( _layoutMutex ); - const TextureLayout::TextureSlotVector& slots = _layout.getTextureSlots(); - for( int i=0; i= (int)slots.size() ) + if ( _layout.isSlotAvailable(i) ) { out_unit = i; _reservedUnits.insert( i ); @@ -208,10 +306,28 @@ // all taken, return false. return false; } - else // texture array or multipass... they area locked to unit 0 + + else if ( _tech == TerrainOptions::COMPOSITING_TEXTURE_ARRAY ) + { + // texture array reserved slots 0 and 1 (for primary and blending) + for( unsigned i=2; iblendingRequiresSecondarySlot() : false ); } bool @@ -257,16 +376,24 @@ return _impl.valid() ? _impl->prepareImage( image, tileExtent ) : GeoImage::INVALID; } +GeoImage +TextureCompositor::prepareSecondaryImage( const GeoImage& image, const GeoExtent& tileExtent ) const +{ + return _impl.valid() ? _impl->prepareSecondaryImage( image, tileExtent ) : GeoImage::INVALID; +} + void TextureCompositor::applyLayerUpdate(osg::StateSet* stateSet, UID layerUID, const GeoImage& preparedImage, - const GeoExtent& tileExtent ) const + const TileKey& tileKey, + osg::StateSet* parentStateSet) const { if ( _impl.valid() ) { Threading::ScopedReadLock sharedLock( const_cast(this)->_layoutMutex ); - _impl->applyLayerUpdate( stateSet, layerUID, preparedImage, tileExtent, _layout ); + _impl->applyLayerUpdate( stateSet, layerUID, preparedImage, tileKey, + _layout, parentStateSet ); } } diff -Nru osgearth-2.0+dfsg/src/osgEarth/TextureCompositorMulti osgearth-2.1.1+dfsg/src/osgEarth/TextureCompositorMulti --- osgearth-2.0+dfsg/src/osgEarth/TextureCompositorMulti 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/TextureCompositorMulti 2011-11-04 19:44:43.000000000 +0000 @@ -37,14 +37,16 @@ bool usesShaderComposition() const { return _useGPU; } + bool blendingRequiresSecondarySlot() const { return true; } + void updateMasterStateSet( osg::StateSet* stateSet, const TextureLayout& layout ) const; bool supportsLayerUpdate() const { return true; } void applyLayerUpdate( osg::StateSet* stateSet, UID layerUID, - const GeoImage& preparedImage, const GeoExtent& tileExtent, - const TextureLayout& layout ) const; + const GeoImage& preparedImage, const TileKey& tileExtent, + const TextureLayout& layout, osg::StateSet* parentStateSet) const; osg::Shader* createSamplerFunction( UID layerUID, @@ -53,10 +55,10 @@ const TextureLayout& layout ) const; private: - bool _lodBlending; float _lodTransitionTime; bool _useGPU; bool _enableMipmappingOnUpdatedTextures; + bool _enableMipmapping; }; } diff -Nru osgearth-2.0+dfsg/src/osgEarth/TextureCompositorMulti.cpp osgearth-2.1.1+dfsg/src/osgEarth/TextureCompositorMulti.cpp --- osgearth-2.0+dfsg/src/osgEarth/TextureCompositorMulti.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/TextureCompositorMulti.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -28,23 +28,48 @@ using namespace osgEarth; -#define LC "[TextureCompositorMultiTexture] " +#define LC "[TextureCompositorMulti] " //------------------------------------------------------------------------ namespace { static osg::Shader* - s_createTextureVertexShader( int maxUnits ) + s_createTextureVertexShader( const TextureLayout& layout, bool blending ) { std::stringstream buf; + + const TextureLayout::TextureSlotVector& slots = layout.getTextureSlots(); + + if ( blending ) + { + buf << "uniform mat4 osgearth_TexBlendMatrix[" << slots.size() << "];\n"; + } buf << "void osgearth_vert_setupTexturing() \n" << "{ \n"; - for(int i=0; i= 0 ) + { + UID uid = slots[slot]; + int primarySlot = layout.getSlot(uid, 0); + + if ( slot == primarySlot ) + { + // normal unit: + buf << " gl_TexCoord["<< slot <<"] = gl_MultiTexCoord" << slot << ";\n"; + } + else + { + // secondary (blending) unit: + buf << " gl_TexCoord["<< slot <<"] = osgearth_TexBlendMatrix["<< primarySlot << "] * gl_MultiTexCoord" << primarySlot << ";\n"; + } + } } buf << "} \n"; @@ -54,9 +79,8 @@ } static osg::Shader* - s_createTextureFragShaderFunction( const TextureLayout& layout, int maxUnits, bool blending, float blendTime ) + s_createTextureFragShaderFunction( const TextureLayout& layout, int maxSlots, bool blending, float fadeInDuration ) { - //const TextureLayout::TextureSlotVector& slots = layout.getTextureSlots(); const TextureLayout::RenderOrderVector& order = layout.getRenderOrder(); std::stringstream buf; @@ -66,65 +90,90 @@ if ( blending ) { buf << "#extension GL_ARB_shader_texture_lod : enable \n" - << "uniform float osgearth_SlotStamp[" << maxUnits << "]; \n" - << "uniform float osg_FrameTime; \n"; + << "uniform float osgearth_SlotStamp[" << maxSlots << "]; \n" + << "uniform float osg_FrameTime; \n" + << "uniform float osgearth_LODRangeFactor; \n"; } - buf << "uniform float osgearth_ImageLayerOpacity[" << maxUnits << "]; \n" - << "uniform bool osgearth_ImageLayerEnabled[" << maxUnits << "]; \n" - << "uniform float osgearth_ImageLayerRange[" << 2*maxUnits << "]; \n" + buf << "uniform float osgearth_ImageLayerOpacity[" << maxSlots << "]; \n" + //The enabled array is a fixed size. Make sure this corresponds to the size definition in TerrainEngineNode.cpp + << "uniform bool osgearth_ImageLayerEnabled[" << 16 << "]; \n" + << "uniform float osgearth_ImageLayerRange[" << 2 * maxSlots << "]; \n" << "uniform float osgearth_ImageLayerAttenuation; \n" + << "uniform float osgearth_CameraElevation; \n" << "varying float osgearth_CameraRange; \n"; - if ( order.size() > 0 ) + const TextureLayout::TextureSlotVector& slots = layout.getTextureSlots(); + + for( int i = 0; i < maxSlots && i < (int)slots.size(); ++i ) { - buf << "uniform sampler2D "; - for( unsigned int i=0; i= 0 ) + { + buf << "uniform sampler2D tex" << i << ";\n"; + } } buf << "void osgearth_frag_applyTexturing( inout vec4 color ) \n" << "{ \n" << " vec3 color3 = color.rgb; \n" - << " vec4 texel, texel2; \n" - << " float dmin, dmax, atten_min, atten_max, age; \n"; + << " vec4 texel; \n" + << " float maxOpacity = 0.0; \n" + << " float dmin, dmax, atten_min, atten_max, age; \n"; - for( unsigned int i=0; i= 0 && dmax <= 0.0) { \n" << " atten_max = -clamp( dmax, -osgearth_ImageLayerAttenuation, 0 ) / osgearth_ImageLayerAttenuation; \n" << " atten_min = clamp( dmin, 0, osgearth_ImageLayerAttenuation ) / osgearth_ImageLayerAttenuation; \n"; - if ( blending ) + if ( secondarySlot >= 0 ) // LOD blending enabled for this layer { - float invBlendTime = 1.0f/blendTime; + float invFadeInDuration = 1.0f/fadeInDuration; - buf << " age = "<< invBlendTime << " * min( "<< blendTime << ", osg_FrameTime - osgearth_SlotStamp[" << slot << "] ); \n" - << " if ( age < 1.0 ) \n" - << " texel = texture2DLod(tex" << slot << ", gl_TexCoord["<< slot << "].st, 1.0-age); \n" - << " else \n" - << " texel = texture2D(tex" << slot << ", gl_TexCoord["<< slot << "].st ); \n"; + buf << " age = "<< invFadeInDuration << " * min( "<< fadeInDuration << ", osg_FrameTime - osgearth_SlotStamp[" << slot << "] ); \n" + << " age = clamp(age, 0.0, 1.0); \n" + << " vec4 texel0 = texture2D(tex" << slot << ", gl_TexCoord["<< slot << "].st);\n" + << " vec4 texel1 = texture2D(tex" << secondarySlot << ", gl_TexCoord["<< secondarySlot << "].st);\n" + << " float mixval = age * osgearth_LODRangeFactor;\n" + + // pre-multiply alpha before mixing: + << " texel0.rgb *= texel0.a; \n" + << " texel1.rgb *= texel1.a; \n" + << " texel = mix(texel1, texel0, mixval); \n" + + // revert to non-pre-multiplies alpha (assumes openGL state uses non-pre-mult alpha) + << " if (texel.a > 0.0) { \n" + << " texel.rgb /= texel.a; \n" + << " } \n"; } else { buf << " texel = texture2D(tex" << slot << ", gl_TexCoord["<< slot <<"].st); \n"; } - - buf << " color3 = mix(color3, texel.rgb, texel.a * osgearth_ImageLayerOpacity[" << i << "] * atten_max * atten_min); \n" + + buf << " float opacity = texel.a * osgearth_ImageLayerOpacity[" << i << "];\n" + << " color3 = mix(color3, texel.rgb, opacity * atten_max * atten_min); \n" + << " if (opacity > maxOpacity) {\n" + << " maxOpacity = opacity;\n" + << " }\n" << " } \n" << " } \n"; } + + buf << " color = vec4(color3, maxOpacity);\n" + << "} \n"; + - buf << " color = vec4(color3,color.a); \n" - << "} \n"; std::string str = buf.str(); //OE_INFO << std::endl << str; @@ -136,10 +185,17 @@ namespace { + static std::string makeSamplerName(int slot) + { + std::stringstream buf; + buf << "tex" << slot; + return buf.str(); + } + static osg::Texture2D* - s_getTexture( osg::StateSet* stateSet, UID layerUID, const TextureLayout& layout, bool lodBlending ) + s_getTexture( osg::StateSet* stateSet, UID layerUID, const TextureLayout& layout, osg::StateSet* parentStateSet) { - int slot = layout.getSlot( layerUID ); + int slot = layout.getSlot( layerUID, 0 ); if ( slot < 0 ) return 0L; @@ -152,8 +208,7 @@ // configure the mipmapping - // only enable anisotropic filtering if we are NOT using mipmap blending. - tex->setMaxAnisotropy( lodBlending ? 1.0f : 16.0f ); + tex->setMaxAnisotropy( 16.0f ); tex->setResizeNonPowerOfTwoHint(false); tex->setFilter( osg::Texture::MAG_FILTER, osg::Texture::LINEAR ); @@ -166,12 +221,40 @@ stateSet->setTextureAttributeAndModes( slot, tex, osg::StateAttribute::ON ); // install the slot attribute - std::stringstream buf; - buf << "tex" << slot; - std::string name = buf.str(); + std::string name = makeSamplerName(slot); stateSet->getOrCreateUniform( name.c_str(), osg::Uniform::SAMPLER_2D )->set( slot ); } + // see if we need an LOD blending secondary texture: + int secondarySlot = layout.getSlot( layerUID, 1 ); + if ( secondarySlot >= 0 ) + { + osg::Texture2D* parentTex = 0; + + //int parentSlot = slot + layout.getRenderOrder().size(); + std::string parentSampler = makeSamplerName( secondarySlot ); + if (parentStateSet) + { + parentTex = static_cast( + parentStateSet->getTextureAttribute( slot, osg::StateAttribute::TEXTURE ) ); + + if (parentTex) + { + stateSet->setTextureAttributeAndModes(secondarySlot, parentTex, osg::StateAttribute::ON ); + stateSet->getOrCreateUniform(parentSampler.c_str(), + osg::Uniform::SAMPLER_2D )->set( secondarySlot ); + } + } + + if ( !parentTex ) + { + // Bind the main texture as the secondary texture and + // set the scaling factors appropriately. + stateSet->getOrCreateUniform( + parentSampler.c_str(), osg::Uniform::SAMPLER_2D)->set(slot); + } + + } return tex; } } @@ -179,35 +262,33 @@ //------------------------------------------------------------------------ TextureCompositorMultiTexture::TextureCompositorMultiTexture( bool useGPU, const TerrainOptions& options ) : -_lodBlending( *options.lodBlending() ), _lodTransitionTime( *options.lodTransitionTime() ), +_enableMipmapping( *options.enableMipmapping() ), _useGPU( useGPU ) { - // validate - if ( _lodBlending && _lodTransitionTime <= 0.0f ) - { - _lodBlending = false; - OE_WARN << LC << "Disabling LOD blending because transition time <= 0.0" << std::endl; - } - _enableMipmappingOnUpdatedTextures = Registry::instance()->getCapabilities().supportsMipmappedTextureUpdates(); } void -TextureCompositorMultiTexture::applyLayerUpdate(osg::StateSet* stateSet, - UID layerUID, - const GeoImage& preparedImage, - const GeoExtent& tileExtent, - const TextureLayout& layout ) const +TextureCompositorMultiTexture::applyLayerUpdate(osg::StateSet* stateSet, + UID layerUID, + const GeoImage& preparedImage, + const TileKey& tileKey, + const TextureLayout& layout, + osg::StateSet* parentStateSet) const { - osg::Texture2D* tex = s_getTexture( stateSet, layerUID, layout, _lodBlending ); + osg::Texture2D* tex = s_getTexture( stateSet, layerUID, layout, parentStateSet); if ( tex ) { osg::Image* image = preparedImage.getImage(); image->dirty(); // required for ensure the texture recognizes the image as new data tex->setImage( image ); - if (_enableMipmappingOnUpdatedTextures && ImageUtils::isPowerOfTwo( image ) && !(!image->isMipmap() && ImageUtils::isCompressed(image))) + // set up proper mipmapping filters: + if (_enableMipmapping && + _enableMipmappingOnUpdatedTextures && + ImageUtils::isPowerOfTwo( image ) && + !(!image->isMipmap() && ImageUtils::isCompressed(image)) ) { if ( tex->getFilter(osg::Texture::MIN_FILTER) != osg::Texture::LINEAR_MIPMAP_LINEAR ) tex->setFilter( osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR ); @@ -217,27 +298,44 @@ tex->setFilter( osg::Texture::MIN_FILTER, osg::Texture::LINEAR ); } - if ( _enableMipmappingOnUpdatedTextures && _lodBlending ) + bool lodBlending = layout.getSlot(layerUID, 1) >= 0; + + if (_enableMipmapping && + _enableMipmappingOnUpdatedTextures && + lodBlending ) { + int slot = layout.getSlot(layerUID, 0); + // update the timestamp on the image layer to support blending. - osg::ref_ptr stamp = new ArrayUniform( stateSet, "osgearth_SlotStamp" ); - if ( !stamp->isComplete() || stamp->getNumElements() < layout.getMaxUsedSlot() + 1 ) + float now = (float)osg::Timer::instance()->delta_s( osg::Timer::instance()->getStartTick(), osg::Timer::instance()->tick() ); + ArrayUniform stampUniform( "osgearth_SlotStamp", osg::Uniform::FLOAT, stateSet, layout.getMaxUsedSlot() + 1 ); + stampUniform.setElement( slot, now ); + + // set the texture matrix to properly position the blend (parent) texture + osg::Matrix mat; + if ( parentStateSet != 0L ) { - stamp = new ArrayUniform( osg::Uniform::FLOAT, "osgearth_SlotStamp", layout.getMaxUsedSlot()+1 ); - stamp->addTo( stateSet ); + unsigned tileX, tileY; + tileKey.getTileXY(tileX, tileY); + + mat(0,0) = 0.5f; + mat(1,1) = 0.5f; + mat(3,0) = (float)(tileX % 2) * 0.5f; + mat(3,1) = (float)(1 - tileY % 2) * 0.5f; } - float now = (float)osg::Timer::instance()->delta_s( osg::Timer::instance()->getStartTick(), osg::Timer::instance()->tick() ); - stamp->setElement( layout.getSlot(layerUID), now ); + ArrayUniform texMatUniform( "osgearth_TexBlendMatrix", osg::Uniform::FLOAT_MAT4, stateSet, layout.getMaxUsedSlot() + 1 ); + texMatUniform.setElement( slot, mat ); } } } void -TextureCompositorMultiTexture::updateMasterStateSet(osg::StateSet* stateSet, - const TextureLayout& layout ) const +TextureCompositorMultiTexture::updateMasterStateSet(osg::StateSet* stateSet, + const TextureLayout& layout ) const { - int maxUnits = layout.getMaxUsedSlot() + 1; + int numSlots = layout.getMaxUsedSlot() + 1; + int maxUnits = numSlots; if ( _useGPU ) { @@ -245,6 +343,7 @@ if ( maxUnits > Registry::instance()->getCapabilities().getMaxGPUTextureUnits() ) { maxUnits = Registry::instance()->getCapabilities().getMaxGPUTextureUnits(); + OE_WARN << LC << "Warning! You have exceeded the number of texture units available on your GPU (" << maxUnits << "). Consider using another compositing mode." @@ -254,13 +353,16 @@ VirtualProgram* vp = static_cast( stateSet->getAttribute(osg::StateAttribute::PROGRAM) ); if ( maxUnits > 0 ) { - vp->setShader( - "osgearth_frag_applyTexturing", - s_createTextureFragShaderFunction(layout, maxUnits, _lodBlending, _lodTransitionTime ) ); + // see if we have any blended layers: + bool hasBlending = layout.containsSecondarySlots( maxUnits ); vp->setShader( "osgearth_vert_setupTexturing", - s_createTextureVertexShader(maxUnits) ); + s_createTextureVertexShader(layout, hasBlending) ); + + vp->setShader( + "osgearth_frag_applyTexturing", + s_createTextureFragShaderFunction(layout, maxUnits, hasBlending, _lodTransitionTime ) ); } else { diff -Nru osgearth-2.0+dfsg/src/osgEarth/TextureCompositorTexArray osgearth-2.1.1+dfsg/src/osgEarth/TextureCompositorTexArray --- osgearth-2.0+dfsg/src/osgEarth/TextureCompositorTexArray 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/TextureCompositorTexArray 2011-11-04 19:44:43.000000000 +0000 @@ -44,19 +44,31 @@ bool supportsLayerUpdate() const { return true; } - GeoImage prepareImage( const GeoImage& layerImage, const GeoExtent& tileExtent ) const; - + GeoImage prepareImage( const GeoImage& layerImage, const GeoExtent& tileExtent ) const + { + return prepareImage(layerImage, tileExtent, textureSize() ); + } + + GeoImage prepareSecondaryImage( const GeoImage& layerImage, const GeoExtent& tileExtent ) const + { + return prepareImage(layerImage, tileExtent, textureSize() / 2); + } + + protected: + GeoImage prepareImage( const GeoImage& layerImage, const GeoExtent& tileExtent, unsigned size ) const; + public: void applyLayerUpdate( osg::StateSet* stateSet, UID layerUID, const GeoImage& preparedImage, - const GeoExtent& tileExtent, - const TextureLayout& layout ) const; + const TileKey& tileKey, + const TextureLayout& layout, + osg::StateSet* parentStateSet ) const; osg::Shader* createSamplerFunction( UID layerUID, const std::string& functionName, osg::Shader::Type type, const TextureLayout& layout ) const; + static inline unsigned textureSize() { return 256; } private: - bool _lodBlending; float _lodTransitionTime; }; } diff -Nru osgearth-2.0+dfsg/src/osgEarth/TextureCompositorTexArray.cpp osgearth-2.1.1+dfsg/src/osgEarth/TextureCompositorTexArray.cpp --- osgearth-2.0+dfsg/src/osgEarth/TextureCompositorTexArray.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/TextureCompositorTexArray.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -21,10 +21,13 @@ // only in newer OSG versions. #if OSG_VERSION_GREATER_OR_EQUAL( 2, 9, 8 ) +#include + #include #include #include #include +#include using namespace osgEarth; @@ -35,93 +38,118 @@ namespace { - static osg::Shader* - s_createTextureFragShaderFunction( const TextureLayout& layout, bool blending, float blendTime ) - { - int numSlots = layout.getMaxUsedSlot() + 1; +static osg::Shader* +s_createTextureFragShaderFunction( const TextureLayout& layout, bool blending, float blendTime ) +{ + int numSlots = layout.getMaxUsedSlot() + 1; - std::stringstream buf; + std::stringstream buf; - buf << "#version 130 \n" - << "#extension GL_EXT_gpu_shader4 : enable \n"; + buf << "#version 130 \n" + << "#extension GL_EXT_gpu_shader4 : enable \n"; - if ( blending ) - { - buf << "#extension GL_ARB_shader_texture_lod : enable \n" - << "uniform float osgearth_SlotStamp[ " << numSlots << "]; \n" - << "uniform float osg_FrameTime; \n"; - } + if ( blending ) + { + buf << "#extension GL_ARB_shader_texture_lod : enable \n" + << "uniform float osgearth_SlotStamp[ " << numSlots << "]; \n" + << "uniform float osg_FrameTime;\n" + << "uniform float osgearth_LODRangeFactor;\n\n"; + } - buf << "uniform sampler2DArray tex0; \n" - << "uniform float region[ " << 4*numSlots << "]; \n" - << "uniform float osgearth_ImageLayerOpacity[" << numSlots << "]; \n" - << "uniform bool osgearth_ImageLayerEnabled[" << numSlots << "]; \n" - << "uniform float osgearth_ImageLayerRange[" << 2*numSlots << "]; \n" - << "uniform float osgearth_ImageLayerAttenuation; \n" - << "varying float osgearth_CameraRange; \n" + buf << "uniform sampler2DArray tex0; \n"; + + if ( blending ) + buf << "uniform sampler2DArray tex1;\n"; - << "void osgearth_frag_applyTexturing( inout vec4 color ) \n" - << "{ \n" - << " vec3 color3 = color.rgb; \n" - << " float u, v, dmin, dmax, atten_min, atten_max, age; \n" - << " vec4 texel; \n"; + buf << "uniform float region[ " << 8*numSlots << "]; \n" + << "uniform float osgearth_ImageLayerOpacity[" << numSlots << "]; \n" + << "uniform bool osgearth_ImageLayerEnabled[" << numSlots << "]; \n" + << "uniform float osgearth_ImageLayerRange[" << 2*numSlots << "]; \n" + << "uniform float osgearth_ImageLayerAttenuation; \n" + << "varying float osgearth_CameraRange; \n" + + << "void osgearth_frag_applyTexturing( inout vec4 color ) \n" + << "{ \n" + << " vec3 color3 = color.rgb; \n" + << " float u, v, dmin, dmax, atten_min, atten_max, age; \n" + << " vec4 texel; \n"; + + const TextureLayout::TextureSlotVector& slots = layout.getTextureSlots(); + const TextureLayout::RenderOrderVector& order = layout.getRenderOrder(); + + for( unsigned int i = 0; i < order.size(); ++i ) + { + int slot = order[i]; + int q = 2 * i; + int r = 8 * slot; + UID uid = slots[slot]; + + buf << " if (osgearth_ImageLayerEnabled["<< i << "]) \n" + << " { \n" + << " u = region["<< r <<"] + (region["<< r+2 <<"] * gl_TexCoord[0].s); \n" + << " v = region["<< r+1 <<"] + (region["<< r+3 <<"] * gl_TexCoord[0].t); \n" + << " dmin = osgearth_CameraRange - osgearth_ImageLayerRange["<< q << "]; \n" + << " dmax = osgearth_CameraRange - osgearth_ImageLayerRange["<< q+1 <<"]; \n" + << " if (dmin >= 0 && dmax <= 0.0) \n" + << " { \n" + << " atten_max = -clamp( dmax, -osgearth_ImageLayerAttenuation, 0 ) / osgearth_ImageLayerAttenuation; \n" + << " atten_min = clamp( dmin, 0, osgearth_ImageLayerAttenuation ) / osgearth_ImageLayerAttenuation; \n"; - const TextureLayout::RenderOrderVector& order = layout.getRenderOrder(); + if ( layout.isBlendingEnabled(uid) ) + { + float invBlendTime = 1.0f/blendTime; - for( unsigned int i = 0; i < order.size(); ++i ) + buf << " age = "<< invBlendTime << " * min( "<< blendTime << ", osg_FrameTime - osgearth_SlotStamp[" << slot << "] ); \n" + << " age = clamp(age, 0.0, 1.0);\n" + << " float pu, pv;\n" + << " pu = region["<< r+4 <<"] + (region["<< r+6 <<"] * gl_TexCoord[0].s); \n" + << " pv = region["<< r+5 <<"] + (region["<< r+7 <<"] * gl_TexCoord[0].t); \n" + + << " vec3 texCoord = vec3(pu, pv, " << slot <<");\n;\n" + << " vec4 texel0 = texture2DArray( tex0, vec3(u, v, " << slot << ") );\n" + << " vec4 texel1 = texture2DArray( tex1, vec3(pu, pv, " << slot << ") );\n" + << " float mixval = age * osgearth_LODRangeFactor;\n" + + // pre-multiply alpha before mixing: + << " texel0.rgb *= texel0.a; \n" + << " texel1.rgb *= texel1.a; \n" + << " texel = mix(texel1, texel0, mixval); \n" + + // revert to non-pre-multiplies alpha (assumes openGL state uses non-pre-mult alpha) + << " if (texel.a > 0.0) { \n" + << " texel.rgb /= texel.a; \n" + << " } \n"; + } + else { - int slot = order[i]; - int q = 2 * i; - int r = 4 * slot; - - buf << " if (osgearth_ImageLayerEnabled["<< i << "]) { \n" - << " u = region["<< r <<"] + (region["<< r+2 <<"] * gl_TexCoord[0].s); \n" - << " v = region["<< r+1 <<"] + (region["<< r+3 <<"] * gl_TexCoord[0].t); \n" - << " dmin = osgearth_CameraRange - osgearth_ImageLayerRange["<< q << "]; \n" - << " dmax = osgearth_CameraRange - osgearth_ImageLayerRange["<< q+1 <<"]; \n" - << " if (dmin >= 0 && dmax <= 0.0) { \n" - << " atten_max = -clamp( dmax, -osgearth_ImageLayerAttenuation, 0 ) / osgearth_ImageLayerAttenuation; \n" - << " atten_min = clamp( dmin, 0, osgearth_ImageLayerAttenuation ) / osgearth_ImageLayerAttenuation; \n"; - - if ( blending ) - { - float invBlendTime = 1.0f/blendTime; - - buf << " age = "<< invBlendTime << " * min( "<< blendTime << ", osg_FrameTime - osgearth_SlotStamp[" << slot << "] ); \n" - << " if ( age < 1.0 ) \n" - << " texel = texture2DArrayLod( tex0, vec3(u,v,"<< slot <<"), 1.0-age); \n" - << " else \n" - << " texel = texture2DArray( tex0, vec3(u,v,"<< slot <<") ); \n"; - } - else - { - buf << " texel = texture2DArray( tex0, vec3(u,v,"<< slot <<") ); \n"; - } - - buf << " color3 = mix(color3, texel.rgb, texel.a * osgearth_ImageLayerOpacity["<< i <<"] * atten_max * atten_min); \n" - << " } \n" - << " } \n" - ; + buf << " texel = texture2DArray( tex0, vec3(u,v,"<< slot <<") ); \n"; } - buf << " color = vec4(color3.rgb, color.a); \n" - << "} \n"; - - std::string str = buf.str(); - return new osg::Shader( osg::Shader::FRAGMENT, str ); + buf << " color3 = mix(color3, texel.rgb, texel.a * osgearth_ImageLayerOpacity["<< i <<"] * atten_max * atten_min); \n" + << " } \n" + << " } \n" + ; } + + buf << " color = vec4(color3.rgb, color.a); \n" + << "} \n"; + + std::string str = buf.str(); + return new osg::Shader( osg::Shader::FRAGMENT, str ); +} } //------------------------------------------------------------------------ namespace { - static osg::Texture2DArray* - s_getTexture( osg::StateSet* stateSet, const TextureLayout& layout ) + osg::Texture2DArray* + s_getTexture( osg::StateSet* stateSet, const TextureLayout& layout, + int unit, unsigned textureSize ) { osg::Texture2DArray* tex = static_cast( - stateSet->getTextureAttribute( 0, osg::StateAttribute::TEXTURE ) ); + stateSet->getTextureAttribute( unit, osg::StateAttribute::TEXTURE ) ); // if the texture array doesn't exist, create it anew. if ( !tex ) @@ -129,10 +157,10 @@ tex = new SparseTexture2DArray(); tex->setSourceFormat( GL_RGBA ); tex->setInternalFormat( GL_RGBA8 ); - tex->setTextureWidth( 256 ); - tex->setTextureHeight( 256 ); - - // configure the mipmapping + tex->setTextureWidth( textureSize ); + tex->setTextureHeight( textureSize ); + + // configure the mipmapping tex->setMaxAnisotropy(16.0f); tex->setResizeNonPowerOfTwoHint(false); tex->setFilter( osg::Texture::MAG_FILTER, osg::Texture::LINEAR ); @@ -142,8 +170,7 @@ tex->setWrap(osg::Texture::WRAP_S,osg::Texture::CLAMP_TO_EDGE); tex->setWrap(osg::Texture::WRAP_T,osg::Texture::CLAMP_TO_EDGE); - stateSet->setTextureAttribute( 0, tex, osg::StateAttribute::ON ); - stateSet->getOrCreateUniform( "tex0", osg::Uniform::SAMPLER_2D_ARRAY )->set( 0 ); + stateSet->setTextureAttribute( unit, tex, osg::StateAttribute::ON ); } // grow the texture array if necessary. @@ -162,71 +189,86 @@ return tex; } + + osg::Uniform* ensureSampler(osg::StateSet* ss, int unit) + { + std::stringstream sstream; + sstream << "tex" << unit; + std::string str = sstream.str(); + osg::ref_ptr sampler = ss->getUniform(str); + int samplerUnit = -1; + if (sampler.valid() && sampler->getType() == osg::Uniform::SAMPLER_2D_ARRAY) + sampler->get(samplerUnit); + if (samplerUnit == -1 || samplerUnit != unit) + { + sampler = new osg::Uniform(osg::Uniform::SAMPLER_2D_ARRAY, str); + sampler->set(unit); + ss->addUniform(sampler.get()); + } + return sampler.get(); + } - static osg::Uniform* - s_getRegionUniform( osg::StateSet* stateSet, const TextureLayout& layout ) + void assignImage(osg::Texture2DArray* texture, int slot, osg::Image* image) { - osg::Uniform* region = stateSet->getUniform( "region" ); + // We have to dirty() the image because otherwise the texture2d + // array implementation will not recognize it as new data. + image->dirty(); + texture->setImage( slot, image ); - // if the region-uniform doesn't exist, create it now - if ( !region ) + if (ImageUtils::isPowerOfTwo( image ) && !(!image->isMipmap() && ImageUtils::isCompressed(image))) { - region = new osg::Uniform( osg::Uniform::FLOAT, "region", layout.getTextureSlots().size() * 4 ); - stateSet->addUniform( region ); + if ( texture->getFilter(osg::Texture::MIN_FILTER) != osg::Texture::LINEAR_MIPMAP_LINEAR ) + texture->setFilter( osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR ); } - - // if the region exists but is too small, re-allocate it (cannot grow it) and copy over the old values - else if ( region->getNumElements() < layout.getTextureSlots().size() * 4 ) - { - osg::Uniform* newRegion = new osg::Uniform( osg::Uniform::FLOAT, "region", layout.getTextureSlots().size() * 4 ); - for( unsigned int i=0; igetNumElements(); ++i ) - { - float value; - region->getElement( i, value ); - newRegion->setElement( i, value ); - } - - stateSet->removeUniform( region ); - stateSet->addUniform( newRegion ); - region = newRegion; + else if ( texture->getFilter(osg::Texture::MIN_FILTER) != osg::Texture::LINEAR ) + { + texture->setFilter( osg::Texture::MIN_FILTER, osg::Texture::LINEAR ); } + } - return region; + void getImageTransform( + const GeoExtent& tileExtent, + const GeoExtent& imageExtent, + osg::Vec4& transform) + { + if (!tileExtent.isValid() || !imageExtent.isValid()) + return; + + transform[0] = (tileExtent.xMin() - imageExtent.xMin()) / imageExtent.width(); + transform[1] = (tileExtent.yMin() - imageExtent.yMin()) / imageExtent.height(); + transform[2] = tileExtent.width() / imageExtent.width(); + transform[3] = tileExtent.height() / imageExtent.height(); } -}; +} //------------------------------------------------------------------------ TextureCompositorTexArray::TextureCompositorTexArray( const TerrainOptions& options ) : -_lodBlending( *options.lodBlending() ), _lodTransitionTime( *options.lodTransitionTime() ) { - // validate - if ( _lodBlending && _lodTransitionTime <= 0.0f ) - { - _lodBlending = false; - OE_WARN << LC << "Disabling LOD blending because transition time <= 0.0" << std::endl; - } + //nop } GeoImage -TextureCompositorTexArray::prepareImage( const GeoImage& layerImage, const GeoExtent& tileExtent ) const +TextureCompositorTexArray::prepareImage( const GeoImage& layerImage, const GeoExtent& tileExtent, unsigned textureSize ) const { const osg::Image* image = layerImage.getImage(); + if (!image) + return GeoImage::INVALID; if (image->getPixelFormat() != GL_RGBA || image->getInternalTextureFormat() != GL_RGBA8 || - image->s() != 256 || - image->t() != 256 ) + image->s() != textureSize || + image->t() != textureSize ) { // Because all tex2darray layers must be identical in format, let's use RGBA. osg::ref_ptr newImage = ImageUtils::convertToRGBA8( image ); // TODO: revisit. For now let's just settle on 256 (again, all layers must be the same size) - if ( image->s() != 256 || image->t() != 256 ) + if ( image->s() != textureSize || image->t() != textureSize ) { osg::ref_ptr resizedImage; - if ( ImageUtils::resizeImage( newImage.get(), 256, 256, resizedImage ) ) + if ( ImageUtils::resizeImage( newImage.get(), textureSize, textureSize, resizedImage ) ) newImage = resizedImage.get(); } @@ -236,75 +278,93 @@ { return layerImage; } - - // NOTE: moved this into TileSource::getImage. - // Failure to do this with a Texture2DArray will result in texture corruption if we are - // updating layers (like in sequential mode). - //const_cast(image.get())->setDataVariance( osg::Object::DYNAMIC ); } void -TextureCompositorTexArray::applyLayerUpdate(osg::StateSet* stateSet, - UID layerUID, - const GeoImage& preparedImage, - const GeoExtent& tileExtent, - const TextureLayout& layout ) const +TextureCompositorTexArray::applyLayerUpdate(osg::StateSet* stateSet, + UID layerUID, + const GeoImage& preparedImage, + const TileKey& tileKey, + const TextureLayout& layout, + osg::StateSet* parentStateSet) const { + GeoExtent tileExtent(tileKey.getExtent()); int slot = layout.getSlot( layerUID ); if ( slot < 0 ) return; // means the layer no longer exists // access the texture array, creating or growing it if necessary: - osg::Texture2DArray* texture = s_getTexture( stateSet, layout ); - - // assign the new image at the proper position in the texture array. We have to - // dirty() the image because otherwise the texture2d array implementation will not - // recognize it as new data. + osg::Texture2DArray* texture = s_getTexture( stateSet, layout, 0, + textureSize() ); + ensureSampler( stateSet, 0 ); + // assign the new image at the proper position in the texture array. osg::Image* image = preparedImage.getImage(); - image->dirty(); - texture->setImage( slot, image ); - - if (ImageUtils::isPowerOfTwo( image ) && !(!image->isMipmap() && ImageUtils::isCompressed(image))) - { - if ( texture->getFilter(osg::Texture::MIN_FILTER) != osg::Texture::LINEAR_MIPMAP_LINEAR ) - texture->setFilter( osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR ); - } - else if ( texture->getFilter(osg::Texture::MIN_FILTER) != osg::Texture::LINEAR ) - { - texture->setFilter( osg::Texture::MIN_FILTER, osg::Texture::LINEAR ); - } + assignImage(texture, slot, image); // update the region uniform to reflect the geo extent of the image: const GeoExtent& imageExtent = preparedImage.getExtent(); - float xoffset = (tileExtent.xMin() - imageExtent.xMin()) / imageExtent.width(); - float yoffset = (tileExtent.yMin() - imageExtent.yMin()) / imageExtent.height(); - float xscale = tileExtent.width() / imageExtent.width(); - float yscale = tileExtent.height() / imageExtent.height(); + osg::Vec4 tileTransform; + getImageTransform(tileExtent, imageExtent, tileTransform); // access the region uniform, creating or growing it if necessary: - osg::Uniform* region = s_getRegionUniform( stateSet, layout ); - if ( region ) + ArrayUniform regionUni( "region", osg::Uniform::FLOAT, stateSet, layout.getMaxUsedSlot()+1 ); + if ( regionUni.isValid() ) { - int layerOffset = slot * 4; - region->setElement( layerOffset, xoffset ); - region->setElement( layerOffset + 1, yoffset ); - region->setElement( layerOffset + 2, xscale ); - region->setElement( layerOffset + 3, yscale ); - region->dirty(); + int layerOffset = slot * 8; + for (int i = 0; i < 4; ++i) + regionUni.setElement( layerOffset + i, tileTransform[i]); + //region->dirty(); } - if ( _lodBlending ) + if ( layout.isBlendingEnabled( layerUID ) && regionUni.isValid() ) { - // update the timestamp on the image layer to support blending. - osg::Uniform* stamp = stateSet->getUniform( "osgearth_SlotStamp" ); - if ( !stamp || stamp->getNumElements() < (unsigned int)layout.getMaxUsedSlot() + 1 ) + osg::Uniform* secondarySampler = ensureSampler( stateSet, 1 ); + osg::Texture2DArray* parentTexture = 0; + const unsigned parentLayerOffset = slot * 8 + 4; + if ( parentStateSet ) { - stamp = new osg::Uniform( osg::Uniform::FLOAT, "osgearth_SlotStamp", layout.getMaxUsedSlot()+1 ); - stateSet->addUniform( stamp ); + ArrayUniform parentRegion( "region", osg::Uniform::FLOAT, parentStateSet, layout.getMaxUsedSlot()+1 ); + + //osg::Uniform* parentRegion = s_getRegionUniform( parentStateSet, + // layout ); + GeoExtent parentExtent(tileKey.createParentKey().getExtent()); + float widthRatio, heightRatio; + parentRegion.getElement(slot * 8 + 2, widthRatio); + parentRegion.getElement(slot * 8 + 3, heightRatio); + float parentImageWidth = parentExtent.width() / widthRatio; + float parentImageHeight = parentExtent.height() / heightRatio; + float xRatio, yRatio; + parentRegion.getElement(slot * 8, xRatio); + parentRegion.getElement(slot * 8 + 1, yRatio); + float ParentImageXmin = parentExtent.xMin() - xRatio * parentImageWidth; + float ParentImageYmin = parentExtent.yMin() - yRatio * parentImageHeight; + regionUni.setElement(parentLayerOffset, + static_cast((tileExtent.xMin() - ParentImageXmin) / parentImageWidth)); + regionUni.setElement(parentLayerOffset + 1, + static_cast((tileExtent.yMin() - ParentImageYmin) / parentImageHeight)); + regionUni.setElement(parentLayerOffset + 2, + static_cast(tileExtent.width() / parentImageWidth)); + regionUni.setElement(parentLayerOffset + 3, + static_cast(tileExtent.height() / parentImageHeight)); + //regionUni.dirty(); + parentTexture = static_cast(parentStateSet->getTextureAttribute(0, osg::StateAttribute::TEXTURE)); } + else + { + // setting the parent transform values to -1 disabled blending for this layer. #hack -gw + for (int i = 0; i < 4; ++i) + regionUni.setElement(parentLayerOffset + i, tileTransform[i]); + } + + if (parentTexture) + stateSet->setTextureAttribute(1, parentTexture, osg::StateAttribute::ON); + else + secondarySampler->set(0); + // update the timestamp on the image layer to support fade-in blending. float now = (float)osg::Timer::instance()->delta_s( osg::Timer::instance()->getStartTick(), osg::Timer::instance()->tick() ); - stamp->setElement( slot, now ); + ArrayUniform stampUniform( "osgearth_SlotStamp", osg::Uniform::FLOAT, stateSet, layout.getMaxUsedSlot()+1 ); + stampUniform.setElement( slot, now ); } } @@ -315,7 +375,7 @@ vp->setShader( "osgearth_frag_applyTexturing", - s_createTextureFragShaderFunction(layout, _lodBlending, _lodTransitionTime ) ); + s_createTextureFragShaderFunction(layout, true, _lodTransitionTime ) ); } osg::Shader* @@ -329,7 +389,7 @@ int slot = layout.getSlot( layerUID ); if ( slot >= 0 ) { - int r = 4 * slot; + int r = 8 * slot; std::string texCoord = type == osg::Shader::VERTEX ? "gl_MultiTexCoord0" : diff -Nru osgearth-2.0+dfsg/src/osgEarth/ThreadingUtils osgearth-2.1.1+dfsg/src/osgEarth/ThreadingUtils --- osgearth-2.0+dfsg/src/osgEarth/ThreadingUtils 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/ThreadingUtils 2011-11-04 19:44:43.000000000 +0000 @@ -22,6 +22,7 @@ #include #include #include +#include #include #define USE_CUSTOM_READ_WRITE_LOCK 1 @@ -33,6 +34,7 @@ { typedef OpenThreads::Mutex Mutex; typedef OpenThreads::ScopedLock ScopedMutexLock; + typedef OpenThreads::Thread Thread; #ifdef USE_CUSTOM_READ_WRITE_LOCK @@ -55,6 +57,20 @@ return _set ? true : (_cond.wait( &_m ) == 0); } + /** waits on a signal, and then automatically resets it before returning. */ + inline bool waitAndReset() { + OpenThreads::ScopedLock lock( _m ); + if ( _set ) { + _set = false; + return true; + } + else { + bool value = _cond.wait( &_m ) == 0; + _set = false; + return value; + } + } + inline void set() { OpenThreads::ScopedLock lock( _m ); if ( !_set ) { @@ -79,6 +95,56 @@ bool _set; }; + /** Same as an Event, but waits on multiple notifications before releasing its wait. */ + class MultiEvent + { + public: + MultiEvent( int num =1 ) : _set( num ), _num(num) { } + + ~MultiEvent() { + reset(); + for( int i=0; i<255; ++i ) // workaround buggy broadcast + _cond.signal(); + } + + inline bool wait() { + OpenThreads::ScopedLock lock( _m ); + while( _set > 0 ) + if ( _cond.wait( &_m ) != 0 ) + return false; + return true; + } + + /** waits on a signal, and then automatically resets it before returning. */ + inline bool waitAndReset() { + OpenThreads::ScopedLock lock( _m ); + while( _set > 0 ) + if ( _cond.wait( &_m ) != 0 ) + return false; + _set = _num; + return true; + } + + inline void notify() { + OpenThreads::ScopedLock lock( _m ); + if ( _set > 0 ) + --_set; + if ( _set == 0 ) + _cond.broadcast(); // possible deadlock before OSG r10457 on windows + //_cond.signal(); + } + + inline void reset( int num =0 ) { + OpenThreads::ScopedLock lock( _m ); + if ( num > 0 ) _num = num; + _set = _num; + } + + protected: + OpenThreads::Mutex _m; + OpenThreads::Condition _cond; + int _set, _num; + }; /** * Custom read/write lock. The read/write lock in OSG can unlock mutexes from a different @@ -227,6 +293,23 @@ #endif + /** Template for per-thread data storage */ + template + struct PerThread + { + T& get() { + ScopedMutexLock lock(_mutex); + return _data[OpenThreads::Thread::CurrentThread()]; + } + const T& get() const { + ScopedMutexLock lock(_mutex); + return _data[OpenThreads::Thread::CurrentThread()]; + } + private: + std::map _data; + OpenThreads::Mutex _mutex; + }; + } } // namepsace osgEarth::Threading diff -Nru osgearth-2.0+dfsg/src/osgEarth/TileKey osgearth-2.1.1+dfsg/src/osgEarth/TileKey --- osgearth-2.0+dfsg/src/osgEarth/TileKey 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/TileKey 2011-11-04 19:44:43.000000000 +0000 @@ -37,7 +37,12 @@ */ class OSGEARTH_EXPORT TileKey { - public: + public: + /** + * Constructs an invalid TileKey. + */ + TileKey() { } + /** * Creates a new TileKey with the given tile xy at the specified level of detail * @@ -154,16 +159,7 @@ unsigned int getTileX() const { return _x; } unsigned int getTileY() const { return _y; } - - /** Whether or not the profile for this TileKey is Projected */ - bool isProjected() const; - - /** Whether or not the profile for this TileKey is in the Mercator projection */ - bool isMercator() const; - - /** Whether or not the profile for this TileKey is in the Geodetic projection */ - bool isGeodetic() const; - + static inline int getLOD(const osgTerrain::TileID& id) { //The name of the lod changed after OSG 2.6 from layer to level diff -Nru osgearth-2.0+dfsg/src/osgEarth/TileKey.cpp osgearth-2.1.1+dfsg/src/osgEarth/TileKey.cpp --- osgearth-2.0+dfsg/src/osgEarth/TileKey.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/TileKey.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -176,18 +176,3 @@ return TileKey( _lod, x, y, _profile.get() ); } - -bool TileKey::isGeodetic() const -{ - return _profile->getProfileType() == Profile::TYPE_GEODETIC; //GLOBAL_GEODETIC; -} - -bool TileKey::isMercator() const -{ - return _profile->getProfileType() == Profile::TYPE_MERCATOR; //GLOBAL_MERCATOR; -} - -bool TileKey::isProjected() const -{ - return _profile->getProfileType() == Profile::TYPE_LOCAL; //PROJECTED; -} diff -Nru osgearth-2.0+dfsg/src/osgEarth/TileSource osgearth-2.1.1+dfsg/src/osgEarth/TileSource --- osgearth-2.0+dfsg/src/osgEarth/TileSource 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/TileSource 2011-11-04 19:44:43.000000000 +0000 @@ -222,7 +222,13 @@ /** * Gets the number of pixels per tile for this TileSource. */ - virtual int getPixelsPerTile() const; + virtual int getPixelsPerTile() const; + + /** + * Gets the list of areas with data for this TileSource + */ + const DataExtentList& getDataExtents() const { return _dataExtents; } + DataExtentList& getDataExtents() { return _dataExtents; } /** * Creates an image for the given TileKey @@ -238,7 +244,7 @@ virtual osg::HeightField* createHeightField( const TileKey& key, HeightFieldOperation* prepOp =0L, - ProgressCallback* progress = 0L ); + ProgressCallback* progress = 0L ); public: @@ -302,17 +308,22 @@ *Gets the blacklist for this TileSource */ TileBlacklist* getBlacklist(); - const TileBlacklist* getBlacklist() const; + const TileBlacklist* getBlacklist() const; /** - * Gets the list of areas with data for this TileSource + * Whether or not the source has data for the given TileKey */ - const DataExtentList& getDataExtents() const { return _dataExtents; } + virtual bool hasData(const TileKey& key) const; /** - * Whether or not the source has data for the given TileKey + * Whether the tile source can generate data for the specified LOD. */ - virtual bool hasData(const TileKey& key) const; + virtual bool hasDataAtLOD( unsigned lod ) const; + + /** + * Whether the tile source can generate data within the specified extent + */ + virtual bool hasDataInExtent( const GeoExtent& extent ) const; /** * Whether this TileSource produces tiles whose data can change after @@ -337,7 +348,7 @@ protected: - ~TileSource(); + virtual ~TileSource(); /** * Creates an image for the given TileKey. @@ -351,11 +362,6 @@ */ virtual osg::HeightField* createHeightField( const TileKey& key, ProgressCallback* progress ); - /** - * Mutable list of the data areas - */ - DataExtentList& getDataExtents() { return _dataExtents; } - /** * Called by subclasses to initialize their profile */ diff -Nru osgearth-2.0+dfsg/src/osgEarth/TileSource.cpp osgearth-2.1.1+dfsg/src/osgEarth/TileSource.cpp --- osgearth-2.0+dfsg/src/osgEarth/TileSource.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/TileSource.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -191,15 +191,15 @@ osg::Image* TileSource::createImage(const TileKey& key, ImageOperation* prepOp, ProgressCallback* progress) { - // Try to get it from the memcache fist + // Try to get it from the memcache fist if (_memCache.valid()) - { + { osg::ref_ptr cachedImage; - if ( _memCache->getImage( key, CacheSpec(), cachedImage ) ) + if ( _memCache->getImage( key, CacheSpec(), cachedImage ) ) { return ImageUtils::cloneImage(cachedImage.get()); } - } + } osg::ref_ptr newImage = createImage(key, progress); @@ -238,6 +238,7 @@ _memCache->setHeightField( key, CacheSpec(), newHF.get() ); } + //TODO: why not just newHF.release()? -gw return newHF.valid() ? new osg::HeightField( *newHF.get() ) : 0L; } @@ -302,49 +303,63 @@ } bool -TileSource::hasData(const osgEarth::TileKey& key) const +TileSource::hasDataAtLOD( unsigned lod ) const { //If no data extents are provided, just return true - if (_dataExtents.size() == 0) return true; + if ( _dataExtents.size() == 0 ) + return true; - const osgEarth::GeoExtent& keyExtent = key.getExtent(); - bool intersectsData = false; - - //osg::Timer_t loopStart = osg::Timer::instance()->tick(); + bool intersects = false; for (DataExtentList::const_iterator itr = _dataExtents.begin(); itr != _dataExtents.end(); ++itr) { - if (keyExtent.intersects( *itr ) && key.getLevelOfDetail() >= itr->getMinLevel() && key.getLevelOfDetail() <= itr->getMaxLevel()) + if ( itr->getMinLevel() <= lod && lod <= itr->getMaxLevel() ) { - intersectsData = true; + intersects = true; break; } } - //osg::Timer_t loopEnd = osg::Timer::instance()->tick(); + return intersects; +} - /* - osg::Timer_t rtreeStart = osg::Timer::instance()->tick(); +bool +TileSource::hasDataInExtent( const GeoExtent& extent ) const +{ + //If no data extents are provided, just return true + if ( _dataExtents.size() == 0 ) + return true; - std::list validExtents = _dataExtentsIndex->find(keyExtent); - //OE_NOTICE << "Found " << validExtents.size() << " intersecting areas " << std::endl; - for (std::list::const_iterator itr = validExtents.begin(); itr != validExtents.end(); ++itr) - { - unsigned int index = *itr; - //OE_NOTICE << "index " << index << std::endl; - const DataExtent& e = _dataExtents[index]; - if (keyExtent.intersects( e ) && key.getLevelOfDetail() >= e.getMinLevel() && key.getLevelOfDetail() <= e.getMaxLevel()) + bool intersects = false; + + for (DataExtentList::const_iterator itr = _dataExtents.begin(); itr != _dataExtents.end(); ++itr) + { + if ( extent.intersects( *itr ) ) { - intersectsData = true; + intersects = true; break; } } - osg::Timer_t rtreeEnd = osg::Timer::instance()->tick(); + return intersects; +} + - double loopTime = osg::Timer::instance()->delta_m(loopStart, loopEnd); - double rtreeTime = osg::Timer::instance()->delta_m(rtreeStart, rtreeEnd); +bool +TileSource::hasData(const osgEarth::TileKey& key) const +{ + //If no data extents are provided, just return true + if (_dataExtents.size() == 0) return true; + + const osgEarth::GeoExtent& keyExtent = key.getExtent(); + bool intersectsData = false; - OE_NOTICE << "Loop = " << loopTime << "ms rtree=" << rtreeTime << "ms diff=" << rtreeTime - loopTime << std::endl; - */ + for (DataExtentList::const_iterator itr = _dataExtents.begin(); itr != _dataExtents.end(); ++itr) + { + if (keyExtent.intersects( *itr ) && key.getLevelOfDetail() >= itr->getMinLevel() && key.getLevelOfDetail() <= itr->getMaxLevel()) + { + intersectsData = true; + break; + } + } return intersectsData; } diff -Nru osgearth-2.0+dfsg/src/osgEarth/TMS.cpp osgearth-2.1.1+dfsg/src/osgEarth/TMS.cpp --- osgearth-2.0+dfsg/src/osgEarth/TMS.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/TMS.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -199,6 +200,9 @@ } } + + if (_profile_type == Profile::TYPE_GEODETIC) return osgEarth::Registry::instance()->getGlobalGeodeticProfile(); + if (_profile_type == Profile::TYPE_MERCATOR) return osgEarth::Registry::instance()->getGlobalMercatorProfile(); return Profile::create( @@ -278,10 +282,10 @@ bool inter = intersects(_minX, _minY, _maxX, _maxY, keyMinX, keyMinY, keyMaxX, keyMaxY); - if (!inter && tilekey.isMercator()) + if (!inter && tilekey.getProfile()->getSRS()->isMercator()) { - tilekey.getProfile()->getSRS()->transform(keyMinX, keyMinY, tilekey.getProfile()->getSRS()->getGeographicSRS(), keyMinX, keyMinY); - tilekey.getProfile()->getSRS()->transform(keyMaxX, keyMaxY, tilekey.getProfile()->getSRS()->getGeographicSRS(), keyMaxX, keyMaxY); + tilekey.getProfile()->getSRS()->transform2D(keyMinX, keyMinY, tilekey.getProfile()->getSRS()->getGeographicSRS(), keyMinX, keyMinY); + tilekey.getProfile()->getSRS()->transform2D(keyMaxX, keyMaxY, tilekey.getProfile()->getSRS()->getGeographicSRS(), keyMaxX, keyMaxY); inter = intersects(_minX, _minY, _maxX, _maxY, keyMinX, keyMinY, keyMaxX, keyMaxY); } diff -Nru osgearth-2.0+dfsg/src/osgEarth/Units osgearth-2.1.1+dfsg/src/osgEarth/Units --- osgearth-2.0+dfsg/src/osgEarth/Units 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/Units 2011-11-04 19:44:43.000000000 +0000 @@ -23,147 +23,215 @@ namespace osgEarth { - class Units + class OSGEARTH_EXPORT Units { public: // linear - static const Units MILLIMETERS; static const Units CENTIMETERS; - static const Units FEET_INTERNATIONAL; - static const Units FEET_US; + static const Units DATA_MILES; + static const Units FATHOMS; + static const Units FEET; + static const Units FEET_US_SURVEY; // http://www.wsdot.wa.gov/reference/metrics/foottometer.htm + static const Units INCHES; + static const Units KILOFEET; static const Units KILOMETERS; + static const Units KILOYARDS; static const Units METERS; - static const Units MILES; + static const Units MILES; // statute miles + static const Units MILLIMETERS; static const Units NAUTICAL_MILES; + static const Units YARDS; // angular + static const Units BAM; static const Units DEGREES; + static const Units NATO_MILS; // http://www.convertworld.com/en/angle/Mil+(NATO).html static const Units RADIANS; - enum Type { TYPE_LINEAR, TYPE_ANGULAR }; + // temporal + static const Units DAYS; + static const Units HOURS; + static const Units MICROSECONDS; + static const Units MILLISECONDS; + static const Units MINUTES; + static const Units SECONDS; + static const Units WEEKS; + + // speed + static const Units FEET_PER_SECOND; + static const Units YARDS_PER_SECOND; + static const Units METERS_PER_SECOND; + static const Units KILOMETERS_PER_SECOND; + static const Units KILOMETERS_PER_HOUR; + static const Units MILES_PER_HOUR; + static const Units DATA_MILES_PER_HOUR; + static const Units KNOTS; + + enum Type { TYPE_LINEAR, TYPE_ANGULAR, TYPE_TEMPORAL, TYPE_SPEED, TYPE_INVALID }; public: static bool convert( const Units& from, const Units& to, double input, double& output ) { - if ( from._type == to._type ) { - output = input * from._toBase / to._toBase; + if ( canConvert(from, to) ) { + if ( from._type == TYPE_LINEAR || from._type == TYPE_ANGULAR || from._type == TYPE_TEMPORAL ) + convertSimple( from, to, input, output ); + else if ( from._type == TYPE_SPEED ) + convertSpeed( from, to, input, output ); return true; } return false; } + static double convert( const Units& from, const Units& to, double input ) { + double output; + convert( from, to, input, output ); + return output; + } + static bool canConvert( const Units& from, const Units& to ) { return from._type == to._type; } + + bool canConvert( const Units& to ) const { + return _type == to._type; + } + + bool convertTo( const Units& to, double input, double& output ) const { + return convert( *this, to, input, output ); + } + + double convertTo( const Units& to, double input ) const { + return convert( *this, to, input ); + } const std::string& getName() const { return _name; } const std::string& getAbbr() const { return _abbr; } + const Type& getType() const { return _type; } + bool operator == ( const Units& rhs ) const { return _type == rhs._type && _toBase == rhs._toBase; } bool operator != ( const Units& rhs ) const { return _type != rhs._type || _toBase != rhs._toBase; } + bool isLinear() const { return _type == TYPE_LINEAR; } + + bool isAngular() const { return _type == TYPE_ANGULAR; } + + bool isTemporal() const { return _type == TYPE_TEMPORAL; } + + bool isSpeed() const { return _type == TYPE_SPEED; } + public: - // Make a new unit definition. if type == TYPE_LINEAR, toBase should convert to meters. If - // type == TYPE_ANGULAR, toBase should convert to radians. + // Make a new unit definition (LINEAR, ANGULAR, TEMPORAL) Units( const std::string& name, const std::string& abbr, const Type& type, double toBase ); + // Maks a new unit definition (SPEED) + Units( const std::string& name, const std::string& abbr, const Units& distance, const Units& time ); + + Units() : _type(TYPE_INVALID) { } + private: + + static void convertSimple( const Units& from, const Units& to, double input, double& output ) { + output = input * from._toBase / to._toBase; + } + static void convertSpeed( const Units& from, const Units& to, double input, double& output ) { + double t = from._distance->convertTo( *to._distance, input ); + output = to._time->convertTo( *from._time, t ); + } + + std::string _name, _abbr; Type _type; double _toBase; + const Units* _distance; + const Units* _time; }; -#if 0 - class Units + struct Linear; + + template + class qualified_double { public: - static bool convert( const Units& from, const Units& to, double input, double& output ) { - return from.convertValue( to, input, output ); } + qualified_double( double value, const Units& units ) : _value(value), _units(units) { } - const std::string& getName() const { return _name; } - const std::string& getAbbr() const { return _abbr; } + qualified_double( const T& rhs ) : _value(rhs._value), _units(rhs._units) { } - public: - virtual bool isLinear() const =0; - virtual bool isAngular() const =0; - virtual bool operator == ( const Units& rhs ) const =0; - bool operator != ( const Units& rhs ) const { return !(operator==(rhs)); } - - protected: - Units( const std::string& name, const std::string& abbr) - : _name(name), _abbr(abbr) { } + void set( double value, const Units& units ) { + _value = value; + _units = units; + } - virtual bool convertValue(const Units& to, double input, double& output) const =0; + T& operator = ( const T& rhs ) { + if ( _units.canConvert(rhs._units) ) + _value = rhs.as(_units); + return static_cast(*this); + } - std::string _name, _abbr; - }; + T operator + ( const T& rhs ) const { + return _units.canConvert(rhs._units) ? + T(_value + rhs.as(_units), _units) : + T(0, Units()); + } - class OSGEARTH_EXPORT LinearUnits : public Units - { - public: - static const LinearUnits MILLIMETERS; - static const LinearUnits CENTIMETERS; - static const LinearUnits FEET_INTERNATIONAL; - static const LinearUnits FEET_US; - static const LinearUnits KILOMETERS; - static const LinearUnits METERS; - static const LinearUnits MILES; - static const LinearUnits NAUTICAL_MILES; - - bool convertValue( const Units& to, double input, double& output ) const { - if ( to.isLinear() ) { - output = input * _toMeters / static_cast(to)._toMeters; - return true; - } - return false; + T operator - ( const T& rhs ) const { + return _units.canConvert(rhs._units) ? + T(_value - rhs.as(_units), _units) : + T(0, Units()); } - bool operator == ( const Units& rhs ) const { - return rhs.isLinear() && _toMeters == static_cast(rhs)._toMeters; } + bool operator == ( const T& rhs ) const { + return _units.canConvert( rhs._units ) && rhs.as(_units) == _value; + } - bool isLinear() const { return true; } - bool isAngular() const { return false; } + bool operator != ( const T& rhs ) const { + return !_units.canConvert(rhs._units) || rhs.as(_units) != _value; + } - public: - LinearUnits( const std::string& name, const std::string& abbr, double toMeters ); + bool operator < ( const T& rhs ) const { + return _units.canConvert(rhs._units) && _value < rhs.as(_units); + } - protected: - double _toMeters; - }; + bool operator > ( const T& rhs ) const { + return _units.canConvert(rhs._units) && _value > rhs.as(_units); + } - class OSGEARTH_EXPORT AngularUnits : public Units - { - public: - static const AngularUnits DEGREES; - static const AngularUnits RADIANS; + double as( const Units& convertTo ) const { + return _units.convertTo( convertTo, _value ); + } + const Units& getUnits() const { return _units; } - bool convertValue( const Units& to, double input, double& output ) const { - if ( to.isAngular() ) { - output = input * _toMeters / static_cast(to)._toMeters; - return true; - } - return false; - } + private: + double _value; + Units _units; + }; - bool operator == ( const Units& rhs ) const { - return rhs.isAngular() && _toRadians == static_cast(rhs)._toRadians; } + struct Linear : public qualified_double { + Linear() : qualified_double(0, Units::METERS) { } + Linear(double value, const Units& units) : qualified_double(value, units) { } + }; - bool isLinear() const { return false; } - bool isAngular() const { return true; } + struct Angular : public qualified_double { + Angular() : qualified_double(0, Units::DEGREES) { } + Angular(double value) : qualified_double(value, Units::DEGREES) { } + Angular(double value, const Units& units) : qualified_double(value, units) { } + }; - public: - AngularUnits( const std::string& name, const std::string& abbr, double toMeters ); + struct Temporal : public qualified_double { + Temporal() : qualified_double(0, Units::SECONDS) { } + Temporal(double value, const Units& units) : qualified_double(value, units) { } + }; - protected: - double _toRadians; + struct Speed : public qualified_double { + Speed() : qualified_double(0, Units::METERS_PER_SECOND) { } + Speed(double value, const Units& units) : qualified_double(value, units) { } }; -#endif } #endif // OSGEARTH_UNITS_H diff -Nru osgearth-2.0+dfsg/src/osgEarth/Units.cpp osgearth-2.1.1+dfsg/src/osgEarth/Units.cpp --- osgearth-2.0+dfsg/src/osgEarth/Units.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/Units.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -23,23 +23,57 @@ using namespace osgEarth; Units::Units( const std::string& name, const std::string& abbr, const Units::Type& type, double toBase ) : -_name( name ), -_abbr( abbr ), -_type( type ), +_name ( name ), +_abbr ( abbr ), +_type ( type ), _toBase( toBase ) { //nop } -const Units Units::CENTIMETERS ( "centimeters", "cm", Units::TYPE_LINEAR, 0.01 ); -const Units Units::FEET_INTERNATIONAL( "feet (int'l)", "ft", Units::TYPE_LINEAR, 0.3048 ); -const Units Units::FEET_US ( "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 ); -const Units Units::MILLIMETERS ( "millimeters", "mm", Units::TYPE_LINEAR, 0.001 ); -const Units Units::NAUTICAL_MILES ( "nautical miles", "nm", Units::TYPE_LINEAR, 1852.0 ); +Units::Units( const std::string& name, const std::string& abbr, const Units& distance, const Units& time ) : +_name ( name ), +_abbr ( abbr ), +_type ( TYPE_SPEED ), +_distance( &distance ), +_time ( &time ) +{ + //nop +} -const Units Units::DEGREES ( "degrees", "deg", Units::TYPE_ANGULAR, osg::PI/180.0 ); +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 ); +const Units Units::MILLIMETERS ( "millimeters", "mm", Units::TYPE_LINEAR, 0.001 ); +const Units Units::YARDS ( "yards", "yd", Units::TYPE_LINEAR, 0.9144 ); +const Units Units::NAUTICAL_MILES ( "nautical miles", "nm", Units::TYPE_LINEAR, 1852.0 ); +const Units Units::DATA_MILES ( "data miles", "dm", Units::TYPE_LINEAR, 1828.8 ); +const Units Units::INCHES ( "inches", "in", Units::TYPE_LINEAR, 0.0254 ); +const Units Units::FATHOMS ( "fathoms", "fm", Units::TYPE_LINEAR, 1.8288 ); +const Units Units::KILOFEET ( "kilofeet", "kf", Units::TYPE_LINEAR, 304.8 ); +const Units Units::KILOYARDS ( "kiloyards", "ky", Units::TYPE_LINEAR, 914.4 ); + +const Units Units::DEGREES ( "degrees", "\xb0",Units::TYPE_ANGULAR, 0.017453292519943295 ); const Units Units::RADIANS ( "radians", "rad", Units::TYPE_ANGULAR, 1.0 ); +const Units Units::BAM ( "BAM", "bam", Units::TYPE_ANGULAR, 6.283185307179586476925286766559 ); +const Units Units::NATO_MILS ( "mils", "mil", Units::TYPE_ANGULAR, 9.8174770424681038701957605727484e-4 ); + +const Units Units::DAYS ( "days", "d", Units::TYPE_TEMPORAL, 1.0/86400.0 ); +const Units Units::HOURS ( "hours", "hr", Units::TYPE_TEMPORAL, 1.0/3600.0 ); +const Units Units::MICROSECONDS ( "microseconds", "us", Units::TYPE_TEMPORAL, 1000000.0 ); +const Units Units::MILLISECONDS ( "milliseconds", "ms", Units::TYPE_TEMPORAL, 1000.0 ); +const Units Units::MINUTES ( "minutes", "min", Units::TYPE_TEMPORAL, 1.0/60.0 ); +const Units Units::SECONDS ( "seconds", "s", Units::TYPE_TEMPORAL, 1.0 ); +const Units Units::WEEKS ( "weeks", "wk", Units::TYPE_TEMPORAL, 1.0/604800.0 ); +const Units Units::FEET_PER_SECOND ( "feet per second", "ft/s", Units::FEET, Units::SECONDS ); +const Units Units::YARDS_PER_SECOND ( "yards per second", "yd/s", Units::YARDS, Units::SECONDS ); +const Units Units::METERS_PER_SECOND ( "meters per second", "m/s", Units::METERS, Units::SECONDS ); +const Units Units::KILOMETERS_PER_SECOND( "kilometers per second", "km/s", Units::KILOMETERS, Units::SECONDS ); +const Units Units::KILOMETERS_PER_HOUR ( "kilometers per hour", "kmh", Units::KILOMETERS, Units::SECONDS ); +const Units Units::MILES_PER_HOUR ( "miles per hour", "mph", Units::MILES, Units::HOURS ); +const Units Units::DATA_MILES_PER_HOUR ( "data miles per hour", "dm/h", Units::DATA_MILES, Units::HOURS ); +const Units Units::KNOTS ( "nautical miles per hour", "kts", Units::NAUTICAL_MILES, Units::HOURS ); diff -Nru osgearth-2.0+dfsg/src/osgEarth/URI osgearth-2.1.1+dfsg/src/osgEarth/URI --- osgearth-2.0+dfsg/src/osgEarth/URI 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/URI 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,208 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#ifndef OSGEARTH_URI +#define OSGEARTH_URI + +#include +#include +#include +#include +#include +#include +#include + +namespace osgEarth +{ + class Config; + class URI; + + /** + * Context for resolving relative URIs. + * + * This object provides "context" for a relative URI. In other words, it provides + * all of the information the system needs to resolve it to an absolute location from + * which OSG can load data. + * + * The "referrer" is the location of an object that "points" to the object in the + * corresponding URI. The location conveyed by the URI will be relative to the location of + * its referrer. For example, a referrer of "http://server/folder/hello.xml" applied + * to the URI "there.jpg" will resolve to "http://server/folder/there.jpg". NOTE that referrer + * it not itself a location (like a folder); rather it's the object that referred to the URI + * being contextualized. + */ + class OSGEARTH_EXPORT URIContext + { + public: + /** Creates an empty context. */ + URIContext() { } + + /** Creates a context that specifies a referring object. */ + URIContext( const std::string& referrer ) : _referrer(referrer) { } + + /** Copy constructor. */ + URIContext( const URIContext& rhs ) : _referrer(rhs._referrer) { } + + /** Serializes this context to an Options structure. This is useful when passing context information + to an osgDB::ReaderWriter that takes a stream as input -- the stream is anonymous, therefore this + is the preferred way to communicate the context information. */ + void store( osgDB::Options* options ); + + /** Creates a context from the serialized version in an Options structure (see above) */ + URIContext( const osgDB::Options* options ); + + /** Returns the referring object. */ + const std::string& referrer() const { return _referrer; } + + /** True if the context is empty */ + bool empty() const { return _referrer.empty(); } + + /** Parents the input context with the current object, placing the subContext's information + under it. Used to re-parent relative locations with a higher-level referrer object. */ + URIContext add( const URIContext& subContext ) const; + + /** Returns a new context with the sub path appended to the current referrer path. */ + URIContext add( const std::string& subPath ) const; + + /** creates a string suitable for passing to an osgDB::ReaderWriter implementation */ + std::string getOSGPath( const std::string& target ) const; + + private: + friend class URI; + std::string _referrer; + }; + + /** + * Stream container for reading a URI directly from a stream + */ + class OSGEARTH_EXPORT URIStream + { + public: + URIStream( const URI& uri ); + + virtual ~URIStream(); + + public: + // auto-cast to istream + operator std::istream& (); + + protected: + friend class URI; + std::istream* _fileStream; + std::stringstream _bufStream; + }; + + /** + * Represents the location of a resource, providing the raw (original, possibly + * relative) and absolute forms. + */ + class OSGEARTH_EXPORT URI + { + public: + /** + * Constructs an empty (and invalid) URI. + */ + URI(); + + /** + * Constructs a new URI from a location (typically an absolute url) + */ + URI( const std::string& location ); + + /** + * Constructs a new URI from a location and an existing context. + */ + URI( const std::string& location, const URIContext& context ); + + /** + * Constructs a new URI from a string. + */ + URI( const char* location ); + + public: + + /** The base (possibly relative) location string. */ + const std::string& base() const { return _baseURI; } + + /** The fully qualified location string. */ + const std::string& full() const { return _fullURI; } + + /** The dereference operator also returns the fully qualified URI, since it's a common operation. */ + const std::string& operator * () const { return _fullURI; } + + /** Context with which this URI was created. */ + const URIContext& context() const { return _context; } + + /** Whether the URI is empty */ + bool empty() const { return _baseURI.empty(); } + + /** Returns a copy of this URI with the suffix appended */ + URI append( const std::string& suffix ) const; + + public: + + bool operator < ( const URI& rhs ) const { return _fullURI < rhs._fullURI; } + + public: // convenience reader methods + + /** Result codes for the read* methods. Call getLastResultCode() to fetch. */ + enum ResultCode { + RESULT_OK, + RESULT_CANCELED, + RESULT_NOT_FOUND, + RESULT_SERVER_ERROR, + RESULT_TIMEOUT, + RESULT_NO_READER, + RESULT_READER_ERROR, + RESULT_UNKNOWN_ERROR + }; + + /** Reads an object from the URI. */ + osg::Object* readObject( + const osgDB::Options* options =0L, + ResultCode* out_code =0L ) const; + + /** Reads an image from the URI. */ + osg::Image* readImage( + const osgDB::Options* options =0L, + ResultCode* out_code =0L ) const; + + /** Reads a node from the URI. */ + osg::Node* readNode( + const osgDB::Options* options =0L, + ResultCode* out_code =0L ) const; + + /** Reads a string from the URI. */ + std::string readString( + ResultCode* out_code =0L ) const; + + public: + /** Copier */ + URI( const URI& rhs ) : _baseURI(rhs._baseURI), _fullURI(rhs._fullURI), _context(rhs._context) { } + + public: + //TODO: methods to load data from the URI. + + protected: + std::string _baseURI; + std::string _fullURI; + URIContext _context; + }; +} + +#endif // OSGEARTH_URI diff -Nru osgearth-2.0+dfsg/src/osgEarth/URI.cpp osgearth-2.1.1+dfsg/src/osgEarth/URI.cpp --- osgearth-2.0+dfsg/src/osgEarth/URI.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/URI.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,229 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define LC "[URI] " + +using namespace osgEarth; + +//------------------------------------------------------------------------ + +namespace +{ + /** + * "Fixes" the osgDB options by disabling the automatic archive caching. Archive caching + * screws up our URI resolution because with it on, osgDB remembers every archive file + * you open and effectively puts it in all future search paths :( + */ + const osgDB::Options* + fixOptions( const osgDB::Options* input ) + { + if ( input && input->getObjectCacheHint() == osgDB::Options::CACHE_NONE ) + { + return input; + } + else + { + return Registry::instance()->cloneOrCreateOptions( input ); + } + } +} + +//------------------------------------------------------------------------ + +URIStream::URIStream( const URI& uri ) : +_fileStream( 0L ) +{ + if ( osgDB::containsServerAddress(uri.full()) ) + { + HTTPResponse res = HTTPClient::get( uri.full() ); + if ( res.isOK() ) + { + std::string buf = res.getPartAsString(0); + _bufStream.str(buf); + } + } + else + { + _fileStream = new std::ifstream( uri.full().c_str() ); + } +} + +URIStream::~URIStream() +{ + if ( _fileStream ) + delete _fileStream; +} + +URIStream::operator std::istream& () +{ + static std::istringstream s_nullStream; + + if ( _fileStream ) + return *_fileStream; + else + return _bufStream; +} + +//------------------------------------------------------------------------ + +std::string +URIContext::getOSGPath( const std::string& target ) const +{ + return osgEarth::getFullPath( _referrer, target ); +} + +URIContext +URIContext::add( const URIContext& sub ) const +{ + return URIContext( osgEarth::getFullPath(_referrer, sub._referrer) ); +} + +URIContext +URIContext::add( const std::string& sub ) const +{ + return URIContext(osgDB::concatPaths( _referrer, sub )); +} + +void +URIContext::store( osgDB::Options* options ) +{ + if ( options ) + { + options->setDatabasePath( _referrer ); + options->setPluginStringData( "osgEarth::URIContext::referrer", _referrer ); + } +} + +URIContext::URIContext( const osgDB::Options* options ) +{ + if ( options ) + { + _referrer = options->getPluginStringData( "osgEarth::URIContext::referrer" ); + } +} + +//------------------------------------------------------------------------ + +URI::URI() +{ + //nop +} + +URI::URI( const std::string& location ) +{ + _baseURI = location; + _fullURI = location; +} + +URI::URI( const std::string& location, const URIContext& context ) +{ + _context = context; + _baseURI = location; + _fullURI = context.getOSGPath( _baseURI ); +} + +URI::URI( const char* location ) +{ + _baseURI = std::string(location); + _fullURI = _baseURI; +} + +URI +URI::append( const std::string& suffix ) const +{ + URI result; + result._baseURI = _baseURI + suffix; + result._fullURI = _fullURI + suffix; + result._context = _context; + return result; +} + +osg::Object* +URI::readObject( const osgDB::Options* options, ResultCode* out_code ) const +{ + if ( empty() ) { + if ( out_code ) *out_code = RESULT_NOT_FOUND; + return 0L; + } + + osg::ref_ptr myOptions = fixOptions(options); + + osg::ref_ptr object; + ResultCode result = (ResultCode)HTTPClient::readObjectFile( _fullURI, object, myOptions.get() ); + if ( out_code ) *out_code = result; + + return object.release(); +} + +osg::Image* +URI::readImage( const osgDB::Options* options, ResultCode* out_code ) const +{ + if ( empty() ) { + if ( out_code ) *out_code = RESULT_NOT_FOUND; + return 0L; + } + + osg::ref_ptr myOptions = fixOptions(options); + + //OE_INFO << LC << "readImage: " << _fullURI << std::endl; + + osg::ref_ptr image; + ResultCode result = (ResultCode)HTTPClient::readImageFile( _fullURI, image, myOptions.get() ); + if ( out_code ) *out_code = result; + + return image.release(); +} + +osg::Node* +URI::readNode( const osgDB::Options* options, ResultCode* out_code ) const +{ + if ( empty() ) { + if ( out_code ) *out_code = RESULT_NOT_FOUND; + return 0L; + } + + osg::ref_ptr myOptions = fixOptions(options); + + osg::ref_ptr node; + ResultCode result = (ResultCode)HTTPClient::readNodeFile( _fullURI, node, myOptions.get() ); + if ( out_code ) *out_code = result; + return node.release(); +} + +std::string +URI::readString( ResultCode* out_code ) const +{ + if ( empty() ) { + if ( out_code ) *out_code = RESULT_NOT_FOUND; + return 0L; + } + + std::string str; + ResultCode result = (ResultCode)HTTPClient::readString( _fullURI, str ); + if ( out_code ) *out_code = result; + return str; +} diff -Nru osgearth-2.0+dfsg/src/osgEarth/Utils osgearth-2.1.1+dfsg/src/osgEarth/Utils --- osgearth-2.0+dfsg/src/osgEarth/Utils 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/Utils 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,398 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph +* Copyright 2008-2010 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 +*/ +#ifndef OSGEARTH_UTILS_H +#define OSGEARTH_UTILS_H 1 + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace osg +{ + class EllipsoidModel; +} + +namespace osgEarth +{ + //------------------------------------------------------------------------ + + class CacheStats + { + public: + CacheStats( unsigned entries, unsigned maxEntries, unsigned queries, float hitRatio ) + : _entries(entries), _maxEntries(maxEntries), _queries(queries), _hitRatio(hitRatio) { } + + unsigned _entries; + unsigned _maxEntries; + unsigned _queries; + float _hitRatio; + }; + + //------------------------------------------------------------------------ + + /** + * Least-recently-used cache class. + * K = key type, T = value type + * + * usage: + * LRUCache cache; + * cache.put( key, value ); + * LRUCache.Record rec = cache.get( key ); + * if ( rec.valid() ) + * const T& value = rec.value(); + */ + template + class LRUCache + { + public: + struct Record { + 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; + }; + + protected: + typedef typename std::list::iterator lru_iter; + typedef typename std::list lru_type; + typedef typename std::pair map_value_type; + typedef typename std::map map_type; + typedef typename map_type::iterator map_iter; + + map_type _map; + lru_type _lru; + unsigned _max; + unsigned _buf; + unsigned _queries; + unsigned _hits; + + public: + LRUCache( unsigned max =100 ) : _max(max) { + _buf = _max/10; + _queries = 0; + _hits = 0; + } + + void insert( const K& key, const T& value ) { + map_iter mi = _map.find( key ); + if ( mi != _map.end() ) { + _lru.erase( mi->second.second ); + mi->second.first = value; + _lru.push_back( key ); + mi->second.second = _lru.end(); + mi->second.second--; + } + else { + _lru.push_back( key ); + lru_iter last = _lru.end(); last--; + _map[key] = std::make_pair(value, last); + } + + if ( _lru.size() > _max ) { + for( unsigned i=0; i < _buf; ++i ) { + const K& key = _lru.front(); + _map.erase( key ); + _lru.pop_front(); + } + } + } + + Record get( const K& key ) { + _queries++; + map_iter mi = _map.find( key ); + if ( mi != _map.end() ) { + _lru.erase( mi->second.second ); + _lru.push_back( key ); + lru_iter new_iter = _lru.end(); new_iter--; + mi->second.second = new_iter; + _hits++; + return Record( &(mi->second.first) ); + } + else { + return Record( 0L ); + } + } + + void erase( const K& key ) { + map_iter mi = _map.find( key ); + if ( mi != _map.end() ) { + _lru.erase( mi->second.second ); + _map.erase( mi ); + } + } + + void setMaxSize( unsigned max ) { + _max = max; + _buf = max/10; + while( _lru.size() > _max ) { + const K& key = _lru.front(); + _map.erase( key ); + _lru.pop_front(); + } + } + + unsigned getMaxSize() const { + return _max; + } + + CacheStats getStats() const { + return CacheStats( + _lru.size(), _max, _queries, _queries > 0 ? (float)_hits/(float)_queries : 0.0f ); + } + }; + + + /** + * Removes the given event handler from the view. + * This is the equivalent of osgViewer::View::removeEventHandler which is not available + * in older versions of OSG + */ + extern OSGEARTH_EXPORT void removeEventHandler(osgViewer::View* view, osgGA::GUIEventHandler* handler); + + + /** + * A simple culling-plane callback (a simpler version of ClusterCullingCallback) + */ + struct OSGEARTH_EXPORT CullNodeByNormal : public osg::NodeCallback { + osg::Vec3d _normal; + CullNodeByNormal( const osg::Vec3d& normal ); + void operator()(osg::Node* node, osg::NodeVisitor* nv); + }; + + struct CullDrawableByNormal : public osg::Drawable::CullCallback { + osg::Vec3d _normal; + CullDrawableByNormal( const osg::Vec3d& normal ) : _normal(normal) { } + bool cull(osg::NodeVisitor* nv, osg::Drawable* drawable, osg::State* state) const { + return nv && nv->getEyePoint() * _normal <= 0; + } + }; + + struct OSGEARTH_EXPORT CullNodeByHorizon : public osg::NodeCallback { + osg::Vec3d _world; + double _r2; + CullNodeByHorizon( const osg::Vec3d& world, const osg::EllipsoidModel* model ); + void operator()(osg::Node* node, osg::NodeVisitor* nv ); + }; + + struct OSGEARTH_EXPORT CullNodeByFrameNumber : public osg::NodeCallback { + unsigned _frame; + CullNodeByFrameNumber() : _frame(0) { } + void operator()( osg::Node* node, osg::NodeVisitor* nv ) { + if ( nv->getFrameStamp()->getFrameNumber() - _frame <= 1 ) + traverse(node, nv); + } + }; + + /** + * A pixel-based AutoTransform variant. + */ + class OSGEARTH_EXPORT PixelAutoTransform : public osg::AutoTransform + { + public: + PixelAutoTransform(); + + /** + * Sets the minimim width of the object, in pixels, when the scale + * factor is 1.0. + */ + void setMinPixelWidthAtScaleOne( double pixels ) { _minPixels = pixels; } + + /** + * Sets the node to use to calculate the screen size. If this is NULL, + * it will use the size of the first child node. + */ + void setSizingNode( osg::Node* node ) { _sizingNode = node; dirty(); } + + /** + * Forces a recalculation of the autoscale on the next traversal + * (this usually doesn't happen unless the camera moves) + */ + void dirty(); + + public: // override + void accept( osg::NodeVisitor& nv ); + + protected: + double _minPixels; + bool _dirty; + osg::observer_ptr _sizingNode; + }; + + /** + * Same of osg::MixinVector, but with a superclass template parameter. + */ + template + class MixinVector : public SuperClass + { + typedef typename std::vector vector_type; + public: + typedef typename vector_type::allocator_type allocator_type; + typedef typename vector_type::value_type value_type; + typedef typename vector_type::const_pointer const_pointer; + typedef typename vector_type::pointer pointer; + typedef typename vector_type::const_reference const_reference; + typedef typename vector_type::reference reference; + typedef typename vector_type::const_iterator const_iterator; + typedef typename vector_type::iterator iterator; + typedef typename vector_type::const_reverse_iterator const_reverse_iterator; + typedef typename vector_type::reverse_iterator reverse_iterator; + typedef typename vector_type::size_type size_type; + typedef typename vector_type::difference_type difference_type; + + explicit MixinVector() : _impl() + { + } + + explicit MixinVector(size_type initial_size, const value_type& fill_value = value_type()) + : _impl(initial_size, fill_value) + { + } + + template + MixinVector(InputIterator first, InputIterator last) + : _impl(first, last) + { + } + + MixinVector(const vector_type& other) + : _impl(other) + { + } + + MixinVector(const MixinVector& other) + : _impl(other._impl) + { + } + + MixinVector& operator=(const vector_type& other) + { + _impl = other; + return *this; + } + + MixinVector& operator=(const MixinVector& other) + { + _impl = other._impl; + return *this; + } + + virtual ~MixinVector() {} + + void clear() { _impl.clear(); } + void resize(size_type new_size, const value_type& fill_value = value_type()) { _impl.resize(new_size, fill_value); } + void reserve(size_type new_capacity) { _impl.reserve(new_capacity); } + + void swap(vector_type& other) { _impl.swap(other); } + void swap(MixinVector& other) { _impl.swap(other._impl); } + + bool empty() const { return _impl.empty(); } + size_type size() const { return _impl.size(); } + size_type capacity() const { return _impl.capacity(); } + size_type max_size() const { return _impl.max_size(); } + allocator_type get_allocator() const { return _impl.get_allocator(); } + + const_iterator begin() const { return _impl.begin(); } + iterator begin() { return _impl.begin(); } + const_iterator end() const { return _impl.end(); } + iterator end() { return _impl.end(); } + + const_reverse_iterator rbegin() const { return _impl.rbegin(); } + reverse_iterator rbegin() { return _impl.rbegin(); } + const_reverse_iterator rend() const { return _impl.rend(); } + reverse_iterator rend() { return _impl.rend(); } + + const_reference operator[](size_type index) const { return _impl[index]; } + reference operator[](size_type index) { return _impl[index]; } + + const_reference at(size_type index) const { return _impl.at(index); } + reference at(size_type index) { return _impl.at(index); } + + void assign(size_type count, const value_type& value) { _impl.assign(count, value); } + template + void assign(Iter first, Iter last) { _impl.assign(first, last); } + + void push_back(const value_type& value) { _impl.push_back(value); } + void pop_back() { _impl.pop_back(); } + + iterator erase(iterator where) { return _impl.erase(where); } + iterator erase(iterator first, iterator last) { return _impl.erase(first, last); } + + iterator insert(iterator where, const value_type& value) { return _impl.insert(where, value); } + + template + void insert(iterator where, InputIterator first, InputIterator last) + { + _impl.insert(where, first, last); + } + + void insert(iterator where, size_type count, const value_type& value) + { + _impl.insert(where, count, value); + } + + const_reference back() const { return _impl.back(); } + reference back() { return _impl.back(); } + const_reference front() const { return _impl.front(); } + reference front() { return _impl.front(); } + + vector_type& asVector() { return _impl; } + const vector_type& asVector() const { return _impl; } + + friend inline bool operator==(const MixinVector& left, const MixinVector& right) { return left._impl == right._impl; } + friend inline bool operator==(const MixinVector& left, const std::vector& right) { return left._impl == right; } + friend inline bool operator==(const std::vector& left, const MixinVector& right) { return left == right._impl; } + + friend inline bool operator!=(const MixinVector& left, const MixinVector& right) { return left._impl != right._impl; } + friend inline bool operator!=(const MixinVector& left, const std::vector& right) { return left._impl != right; } + friend inline bool operator!=(const std::vector& left, const MixinVector& right) { return left != right._impl; } + + friend inline bool operator<(const MixinVector& left, const MixinVector& right) { return left._impl < right._impl; } + friend inline bool operator<(const MixinVector& left, const std::vector& right) { return left._impl < right; } + friend inline bool operator<(const std::vector& left, const MixinVector& right) { return left < right._impl; } + + friend inline bool operator>(const MixinVector& left, const MixinVector& right) { return left._impl > right._impl; } + friend inline bool operator>(const MixinVector& left, const std::vector& right) { return left._impl > right; } + friend inline bool operator>(const std::vector& left, const MixinVector& right) { return left > right._impl; } + + friend inline bool operator<=(const MixinVector& left, const MixinVector& right) { return left._impl <= right._impl; } + friend inline bool operator<=(const MixinVector& left, const std::vector& right) { return left._impl <= right; } + friend inline bool operator<=(const std::vector& left, const MixinVector& right) { return left <= right._impl; } + + friend inline bool operator>=(const MixinVector& left, const MixinVector& right) { return left._impl >= right._impl; } + friend inline bool operator>=(const MixinVector& left, const std::vector& right) { return left._impl >= right; } + friend inline bool operator>=(const std::vector& left, const MixinVector& right) { return left >= right._impl; } + + private: + vector_type _impl; + }; + +} + +#endif // OSGEARTH_UTILS_H diff -Nru osgearth-2.0+dfsg/src/osgEarth/Utils.cpp osgearth-2.1.1+dfsg/src/osgEarth/Utils.cpp --- osgearth-2.0+dfsg/src/osgEarth/Utils.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/Utils.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,299 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#include +#include +#include + +using namespace osgEarth; + +//------------------------------------------------------------------------ + +void osgEarth::removeEventHandler(osgViewer::View* view, osgGA::GUIEventHandler* handler) +{ + osgViewer::View::EventHandlers::iterator itr = std::find(view->getEventHandlers().begin(), view->getEventHandlers().end(), handler); + if (itr != view->getEventHandlers().end()) + { + view->getEventHandlers().erase(itr); + } +} + +//------------------------------------------------------------------------ + +CullNodeByHorizon::CullNodeByHorizon( const osg::Vec3d& world, const osg::EllipsoidModel* model ) : +_world(world), +_r2(model->getRadiusPolar() * model->getRadiusPolar()) +{ + //nop +} + +void +CullNodeByHorizon::operator()(osg::Node* node, osg::NodeVisitor* nv) +{ + if ( nv ) + { + osg::Vec3d eye, center, up; + osgUtil::CullVisitor* cv = static_cast( nv ); + cv->getCurrentCamera()->getViewMatrixAsLookAt(eye,center,up); + + double d2 = eye.length2(); + double horiz2 = d2 - _r2; + + double dist2 = (_world-eye).length2(); + + if ( dist2 < horiz2 ) + { + traverse(node, nv); + } + } +} + +//------------------------------------------------------------------------ + +CullNodeByNormal::CullNodeByNormal( const osg::Vec3d& normal ) +{ + _normal = normal; + //_normal.normalize(); +} + +void +CullNodeByNormal::operator()(osg::Node* node, osg::NodeVisitor* nv) +{ + osg::Vec3d eye, center, up; + osgUtil::CullVisitor* cv = static_cast( nv ); + + cv->getCurrentCamera()->getViewMatrixAsLookAt(eye,center,up); + + eye.normalize(); + osg::Vec3d normal = _normal; + normal.normalize(); + + double dotProduct = eye * normal; + if ( dotProduct > 0.0 ) + { + traverse(node, nv); + } +} + +//------------------------------------------------------------------------ + +#undef LC +#define LC "[PixelAutoTransform] " + +PixelAutoTransform::PixelAutoTransform() : +osg::AutoTransform() +{ + // deactivate culling for the first traversal. We will reactivate it later. + setCullingActive( false ); +} + +void +PixelAutoTransform::accept( osg::NodeVisitor& nv ) +{ + if ( nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR ) + { + // re-activate culling now that the first cull traversal has taken place. + this->setCullingActive( true ); + osg::CullStack* cs = dynamic_cast(&nv); + if ( cs ) + { + osg::Viewport::value_type width = _previousWidth; + osg::Viewport::value_type height = _previousHeight; + + osg::Viewport* viewport = cs->getViewport(); + if (viewport) + { + width = viewport->width(); + height = viewport->height(); + } + + osg::Vec3d eyePoint = cs->getEyeLocal(); + osg::Vec3d localUp = cs->getUpLocal(); + osg::Vec3d position = getPosition(); + + const osg::Matrix& projection = *(cs->getProjectionMatrix()); + + bool doUpdate = _firstTimeToInitEyePoint || _dirty; + if ( !_firstTimeToInitEyePoint ) + { + osg::Vec3d dv = _previousEyePoint - eyePoint; + + if (dv.length2() > getAutoUpdateEyeMovementTolerance() * (eyePoint-getPosition()).length2()) + { + doUpdate = true; + } + else + { + osg::Vec3d dupv = _previousLocalUp - localUp; + + // rotating the camera only affects ROTATE_TO_* + if ((_autoRotateMode && dupv.length2() > getAutoUpdateEyeMovementTolerance()) || + (width != _previousWidth || height != _previousHeight) || + (projection != _previousProjection) || + (position != _previousPosition) ) + { + doUpdate = true; + } + } + } + _firstTimeToInitEyePoint = false; + + if ( doUpdate ) + { + if ( getAutoScaleToScreen() ) + { + double radius = + _sizingNode.valid() ? _sizingNode->getBound().radius() : + getNumChildren() > 0 ? getChild(0)->getBound().radius() : + 0.48; + + double pixels = cs->pixelSize( getPosition(), radius ); + + double scaledMinPixels = _minPixels * _minimumScale; + double scale = pixels < scaledMinPixels ? scaledMinPixels / pixels : 1.0; + + OE_DEBUG << LC + << "Pixels = " << pixels << ", minPix = " << _minPixels << ", scale = " << scale + << std::endl; + + setScale( scale ); + } + + _previousEyePoint = eyePoint; + _previousLocalUp = localUp; + _previousWidth = width; + _previousHeight = height; + _previousProjection = projection; + _previousPosition = position; + + _matrixDirty = true; + } + + if (_autoRotateMode==ROTATE_TO_SCREEN) + { + osg::Vec3d translation; + osg::Quat rotation; + osg::Vec3d scale; + osg::Quat so; + + cs->getModelViewMatrix()->decompose( translation, rotation, scale, so ); + + setRotation(rotation.inverse()); + } + else if (_autoRotateMode==ROTATE_TO_CAMERA) + { + osg::Vec3d PosToEye = _position - eyePoint; + osg::Matrix lookto = osg::Matrix::lookAt( + osg::Vec3d(0,0,0), PosToEye, localUp); + osg::Quat q; + q.set(osg::Matrix::inverse(lookto)); + setRotation(q); + } + +#if OSG_MIN_VERSION_REQUIRED(3,0,0) + else if (_autoRotateMode==ROTATE_TO_AXIS) + { + osg::Matrix matrix; + osg::Vec3 ev(eyePoint - _position); + + switch(_cachedMode) + { + case(AXIAL_ROT_Z_AXIS): + { + ev.z() = 0.0f; + float ev_length = ev.length(); + if (ev_length>0.0f) + { + //float rotation_zrotation_z = atan2f(ev.x(),ev.y()); + //mat.makeRotate(inRadians(rotation_z),0.0f,0.0f,1.0f); + float inv = 1.0f/ev_length; + float s = ev.x()*inv; + float c = -ev.y()*inv; + matrix(0,0) = c; + matrix(1,0) = -s; + matrix(0,1) = s; + matrix(1,1) = c; + } + break; + } + case(AXIAL_ROT_Y_AXIS): + { + ev.y() = 0.0f; + float ev_length = ev.length(); + if (ev_length>0.0f) + { + //float rotation_zrotation_z = atan2f(ev.x(),ev.y()); + //mat.makeRotate(inRadians(rotation_z),0.0f,0.0f,1.0f); + float inv = 1.0f/ev_length; + float s = -ev.z()*inv; + float c = ev.x()*inv; + matrix(0,0) = c; + matrix(2,0) = s; + matrix(0,2) = -s; + matrix(2,2) = c; + } + break; + } + case(AXIAL_ROT_X_AXIS): + { + ev.x() = 0.0f; + float ev_length = ev.length(); + if (ev_length>0.0f) + { + //float rotation_zrotation_z = atan2f(ev.x(),ev.y()); + //mat.makeRotate(inRadians(rotation_z),0.0f,0.0f,1.0f); + float inv = 1.0f/ev_length; + float s = -ev.z()*inv; + float c = -ev.y()*inv; + matrix(1,1) = c; + matrix(2,1) = -s; + matrix(1,2) = s; + matrix(2,2) = c; + } + break; + } + case(ROTATE_TO_AXIS): // need to implement + { + float ev_side = ev*_side; + float ev_normal = ev*_normal; + float rotation = atan2f(ev_side,ev_normal); + matrix.makeRotate(rotation,_axis); + break; + } + } + osg::Quat q; + q.set(matrix); + setRotation(q); + } +#endif + + _dirty = false; + } + } + + // finally, skip AT's accept and do Transform. + Transform::accept(nv); + +} + +void +PixelAutoTransform::dirty() +{ + _dirty = true; + setCullingActive( false ); +} diff -Nru osgearth-2.0+dfsg/src/osgEarth/Version osgearth-2.1.1+dfsg/src/osgEarth/Version --- osgearth-2.0+dfsg/src/osgEarth/Version 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/Version 2011-11-04 19:44:43.000000000 +0000 @@ -25,8 +25,8 @@ extern "C" { #define OSGEARTH_MAJOR_VERSION 2 -#define OSGEARTH_MINOR_VERSION 0 -#define OSGEARTH_PATCH_VERSION 0 +#define OSGEARTH_MINOR_VERSION 1 +#define OSGEARTH_PATCH_VERSION 1 #define OSGEARTH_SOVERSION 0 /* Convenience macro that can be used to decide whether a feature is present or not i.e. diff -Nru osgearth-2.0+dfsg/src/osgEarth/VerticalSpatialReference.cpp osgearth-2.1.1+dfsg/src/osgEarth/VerticalSpatialReference.cpp --- osgearth-2.0+dfsg/src/osgEarth/VerticalSpatialReference.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/VerticalSpatialReference.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -52,7 +52,7 @@ else if ( s == "meters" || s == "metres" || s == "meter" || s == "metre" ) return new VerticalSpatialReference( Units::METERS ); else if ( startsWith( s, "feet" ) || startsWith( s, "foot" ) ) - return new VerticalSpatialReference( Units::FEET_US ); + return new VerticalSpatialReference( Units::FEET ); return 0L; } diff -Nru osgearth-2.0+dfsg/src/osgEarth/XmlUtils osgearth-2.1.1+dfsg/src/osgEarth/XmlUtils --- osgearth-2.0+dfsg/src/osgEarth/XmlUtils 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/XmlUtils 2011-11-04 19:44:43.000000000 +0000 @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -88,7 +89,7 @@ void addSubElement(const std::string& tag, const Properties& attrs, const std::string& text); - Config getConfig() const; + virtual Config getConfig() const; public: // XmlNode virtual bool isElement() const { return true; } @@ -128,22 +129,28 @@ class OSGEARTH_EXPORT XmlDocument : public XmlElement { public: - XmlDocument( const std::string& source_uri ="" ); + XmlDocument(); XmlDocument( const Config& conf ); virtual ~XmlDocument(); + + static XmlDocument* load( const std::string& location ); + + static XmlDocument* load( const URI& uri ); - static XmlDocument* load( std::istream& in ); - + static XmlDocument* load( std::istream& in, const URIContext& context =URIContext() ); + void store( std::ostream& out ) const; const std::string& getName() const; + virtual Config getConfig() const; + protected: - std::string source_uri; - std::string name; - osg::ref_ptr root; + URI _sourceURI; + std::string _name; + osg::ref_ptr _root; }; } diff -Nru osgearth-2.0+dfsg/src/osgEarth/XmlUtils.cpp osgearth-2.1.1+dfsg/src/osgEarth/XmlUtils.cpp --- osgearth-2.0+dfsg/src/osgEarth/XmlUtils.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarth/XmlUtils.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -19,6 +19,7 @@ #include #include +#include #include #include "tinyxml.h" #include @@ -226,7 +227,7 @@ { XmlNode* n = c->get(); if ( n->isElement() ) - conf.children().push_back( static_cast(n)->getConfig() ); + conf.add( static_cast(n)->getConfig() ); } conf.value() = getText(); @@ -247,12 +248,20 @@ } +#if 0 XmlDocument::XmlDocument( const std::string& _source_uri ) : XmlElement( "Document" ), source_uri( _source_uri ) { //NOP } +#endif + +XmlDocument::XmlDocument() : +XmlElement( "Document" ) +{ + //nop +} XmlDocument::XmlDocument( const Config& conf ) : XmlElement( conf ) @@ -265,106 +274,132 @@ //NOP } - -static XmlAttributes -getAttributes( const char** attrs ) +namespace { - XmlAttributes map; - const char** ptr = attrs; - while( *ptr != NULL ) + XmlAttributes + getAttributes( const char** attrs ) { - std::string name = *ptr++; - std::string value = *ptr++; - std::transform( name.begin(), name.end(), name.begin(), tolower ); - map[name] = value; + XmlAttributes map; + const char** ptr = attrs; + while( *ptr != NULL ) + { + std::string name = *ptr++; + std::string value = *ptr++; + std::transform( name.begin(), name.end(), name.begin(), tolower ); + map[name] = value; + } + return map; } - return map; -} -void processNode(XmlElement* parent, TiXmlNode* node) -{ - XmlElement* new_element = 0; - switch (node->Type()) + void processNode(XmlElement* parent, TiXmlNode* node) { - case TiXmlNode::TINYXML_ELEMENT: + XmlElement* new_element = 0; + switch (node->Type()) { - TiXmlElement* element = node->ToElement(); - std::string tag = element->Value(); - std::transform( tag.begin(), tag.end(), tag.begin(), tolower); - - //Get all the attributes - XmlAttributes attrs; - TiXmlAttribute* attr = element->FirstAttribute(); - while (attr) + case TiXmlNode::TINYXML_ELEMENT: + { + TiXmlElement* element = node->ToElement(); + std::string tag = element->Value(); + std::transform( tag.begin(), tag.end(), tag.begin(), tolower); + + //Get all the attributes + XmlAttributes attrs; + TiXmlAttribute* attr = element->FirstAttribute(); + while (attr) + { + std::string name = attr->Name(); + std::string value = attr->Value(); + std::transform( name.begin(), name.end(), name.begin(), tolower); + attrs[name] = value; + attr = attr->Next(); + } + + //All the element to the stack + new_element = new XmlElement( tag, attrs ); + parent->getChildren().push_back( new_element ); + } + break; + case TiXmlNode::TINYXML_TEXT: { - std::string name = attr->Name(); - std::string value = attr->Value(); - std::transform( name.begin(), name.end(), name.begin(), tolower); - attrs[name] = value; - attr = attr->Next(); + TiXmlText* text = node->ToText(); + std::string data( text->Value()); + parent->getChildren().push_back( new XmlText( data ) ); } + break; + } - //All the element to the stack - new_element = new XmlElement( tag, attrs ); - parent->getChildren().push_back( new_element ); - } - break; - case TiXmlNode::TINYXML_TEXT: - { - TiXmlText* text = node->ToText(); - std::string data( text->Value()); - parent->getChildren().push_back( new XmlText( data ) ); + XmlElement* new_parent = new_element ? new_element : parent; + TiXmlNode* child; + for (child = node->FirstChild(); child != 0; child = child->NextSibling()) + { + processNode( new_parent, child ); } - break; - } - - XmlElement* new_parent = new_element ? new_element : parent; - TiXmlNode* child; - for (child = node->FirstChild(); child != 0; child = child->NextSibling()) - { - processNode( new_parent, child ); } -} -void -removeDocType(std::string &xmlStr) -{ - //TinyXML has an issue with parsing DTDs. See http://www.grinninglizard.com/tinyxmldocs/index.html - //We need to remove any !DOCTYPE block that appears in the XML before parsing to avoid errors. - std::string::size_type startIndex = xmlStr.find(" - while (endIndex < xmlStr.size()) + void + removeDocType(std::string &xmlStr) { - endIndex+=1; - if (xmlStr[endIndex] == '<') - { - numChildElements++; - } - else if (xmlStr[endIndex] == '>') + //TinyXML has an issue with parsing DTDs. See http://www.grinninglizard.com/tinyxmldocs/index.html + //We need to remove any !DOCTYPE block that appears in the XML before parsing to avoid errors. + std::string::size_type startIndex = xmlStr.find(" + while (endIndex < xmlStr.size()) { - if (numChildElements == 0) + endIndex+=1; + if (xmlStr[endIndex] == '<') { - break; + numChildElements++; } - else + else if (xmlStr[endIndex] == '>') { - numChildElements--; + if (numChildElements == 0) + { + break; + } + else + { + numChildElements--; + } } } + + //Now, replace the element with whitespace + xmlStr.erase(startIndex, endIndex - startIndex + 1); } +} + - //Now, replace the element with whitespace - xmlStr.erase(startIndex, endIndex - startIndex + 1); +XmlDocument* +XmlDocument::load( const std::string& location ) +{ + return load( URI(location) ); } +XmlDocument* +XmlDocument::load( const URI& uri ) +{ + std::string buffer; + if ( HTTPClient::readString( uri.full(), buffer ) != HTTPClient::RESULT_OK ) + { + return 0L; + } + + std::stringstream buf(buffer); + XmlDocument* result = load(buf); + + if ( result ) + result->_sourceURI = uri; + + return result; +} XmlDocument* -XmlDocument::load( std::istream& in ) +XmlDocument::load( std::istream& in, const URIContext& uriContext ) { TiXmlDocument xmlDoc; @@ -385,16 +420,27 @@ buf << xmlDoc.ErrorDesc() << " (row " << xmlDoc.ErrorRow() << ", col " << xmlDoc.ErrorCol() << ")"; std::string str = buf.str(); OE_WARN << "Error in XML document: " << str << std::endl; + if ( !uriContext.referrer().empty() ) + OE_WARN << uriContext.referrer() << std::endl; } if ( !xmlDoc.Error() && xmlDoc.RootElement() ) { doc = new XmlDocument(); processNode( doc, xmlDoc.RootElement() ); + doc->_sourceURI = URI("", uriContext); } return doc; } +Config +XmlDocument::getConfig() const +{ + Config conf = XmlElement::getConfig(); + conf.setURIContext( _sourceURI.full() ); + return conf; +} + #define INDENT 4 static void @@ -440,4 +486,4 @@ { storeNode( i->get(), 0, out ); }*/ -} \ No newline at end of file +} diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/agglite/AGGLiteRasterizerTileSource.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/agglite/AGGLiteRasterizerTileSource.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/agglite/AGGLiteRasterizerTileSource.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/agglite/AGGLiteRasterizerTileSource.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -22,7 +22,6 @@ #include #include #include -#include //TODO: replace this with ImageRasterizer #include #include @@ -84,11 +83,11 @@ //override bool renderFeaturesForStyle( - const Symbology::Style* style, + const Style& style, const FeatureList& inFeatures, - osg::Referenced* buildData, - const GeoExtent& imageExtent, - osg::Image* image ) + osg::Referenced* buildData, + const GeoExtent& imageExtent, + osg::Image* image ) { // local copy of the features that we can process FeatureList features = inFeatures; @@ -99,8 +98,8 @@ FilterContext context; context.profile() = getFeatureSource()->getFeatureProfile(); - const LineSymbol* masterLine = style->getSymbol(); - const PolygonSymbol* masterPoly = style->getSymbol(); + const LineSymbol* masterLine = style.getSymbol(); + const PolygonSymbol* masterPoly = style.getSymbol(); //bool embeddedStyles = getFeatureSource()->hasEmbeddedStyles(); @@ -133,9 +132,10 @@ { // check for an embedded style: const LineSymbol* line = feature->style().isSet() ? - feature->style()->get()->getSymbol() : masterLine; + feature->style()->getSymbol() : masterLine; + const PolygonSymbol* poly = - feature->style().isSet() ? feature->style()->get()->getSymbol() : masterPoly; + feature->style().isSet() ? feature->style()->getSymbol() : masterPoly; // if we have polygons but only a LineSymbol, draw the poly as a line. if ( geom->getComponentType() == Geometry::TYPE_POLYGON ) @@ -259,8 +259,8 @@ // set up a default color: osg::Vec4 c = color; - unsigned int a = 127+(c.a()*255)/2; // scale alpha up - agg::rgba8 fgColor( c.r()*255, c.g()*255, c.b()*255, a ); + unsigned int a = (unsigned int)(127+(c.a()*255)/2); // scale alpha up + agg::rgba8 fgColor( (unsigned int)(c.r()*255), (unsigned int)(c.g()*255), (unsigned int)(c.b()*255), a ); GeometryIterator gi( croppedGeometry.get() ); while( gi.hasMore() ) @@ -269,10 +269,10 @@ Geometry* g = gi.next(); const LineSymbol* line = feature->style().isSet() ? - feature->style()->get()->getSymbol() : masterLine; + feature->style()->getSymbol() : masterLine; const PolygonSymbol* poly = - feature->style().isSet() ? feature->style()->get()->getSymbol() : masterPoly; + feature->style().isSet() ? feature->style()->getSymbol() : masterPoly; if (g->getType() == Geometry::TYPE_RING || g->getType() == Geometry::TYPE_LINESTRING) { @@ -291,7 +291,7 @@ } a = 127+(c.a()*255)/2; // scale alpha up - fgColor = agg::rgba8( c.r()*255, c.g()*255, c.b()*255, a ); + fgColor = agg::rgba8( (unsigned int)(c.r()*255), (unsigned int)(c.g()*255), (unsigned int)(c.b()*255), a ); ras.filling_rule( agg::fill_even_odd ); for( Geometry::iterator p = g->begin(); p != g->end(); p++ ) diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/arcgis/ArcGISOptions osgearth-2.1.1+dfsg/src/osgEarthDrivers/arcgis/ArcGISOptions --- osgearth-2.0+dfsg/src/osgEarthDrivers/arcgis/ArcGISOptions 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/arcgis/ArcGISOptions 2011-11-04 19:44:43.000000000 +0000 @@ -29,8 +29,8 @@ class ArcGISOptions : public TileSourceOptions // NO EXPORT; header only { public: - optional& url() { return _url; } - const optional& url() const { return _url; } + optional& url() { return _url; } + const optional& url() const { return _url; } optional& token() { return _token; } const optional& token() const { return _token; } @@ -45,7 +45,7 @@ public: Config getConfig() const { Config conf = TileSourceOptions::getConfig(); - conf.updateIfSet("url", _url); + conf.updateIfSet("url", _url ); conf.updateIfSet("token", _token ); return conf; } @@ -61,7 +61,7 @@ } private: - optional _url; + optional _url; optional _token; }; diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/arcgis/MapService.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/arcgis/MapService.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/arcgis/MapService.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/arcgis/MapService.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -146,7 +146,7 @@ double ymin = doc["fullExtent"].get("ymin", 0).asDouble(); double xmax = doc["fullExtent"].get("xmax", 0).asDouble(); double ymax = doc["fullExtent"].get("ymax", 0).asDouble(); - int srs = doc["fullExtent"].get("spatialReference", "").get("wkid", 0).asInt(); + int srs = doc["fullExtent"].get("spatialReference", osgEarth::Json::Value::null).get("wkid", 0).asInt(); //Assumes the SRS is going to be an EPSG code std::stringstream ss; diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/arcgis/MapService.h osgearth-2.1.1+dfsg/src/osgEarthDrivers/arcgis/MapService.h --- osgearth-2.0+dfsg/src/osgEarthDrivers/arcgis/MapService.h 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/arcgis/MapService.h 2011-11-04 19:44:43.000000000 +0000 @@ -128,4 +128,4 @@ bool setError( const std::string& ); }; -#endif // OSGEARTH_ARCGIS_MAP_SERVICE_H \ No newline at end of file +#endif // OSGEARTH_ARCGIS_MAP_SERVICE_H diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/arcgis/ReaderWriterArcGIS.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/arcgis/ReaderWriterArcGIS.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/arcgis/ReaderWriterArcGIS.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/arcgis/ReaderWriterArcGIS.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -69,20 +69,20 @@ if ( _format.empty() ) _format = "png"; - std::string url = _options.url().value(); + URI url = _options.url().value(); //Add the token if necessary if (_options.token().isSet()) { std::string token = _options.token().value(); if (!token.empty()) { - std::string sep = url.find( "?" ) == std::string::npos ? "?" : "&"; - url = url + sep + "token=" + token; + std::string sep = url.full().find( "?" ) == std::string::npos ? "?" : "&"; + url = url.append( sep + "token=" + token ); } } // read metadata from the server - if ( !_map_service.init( url ) ) //, getOptions()) ) + if ( !_map_service.init( url.full() ) ) //, getOptions()) ) { OE_WARN << "[osgearth] [ArcGIS] map service initialization failed: " << _map_service.getError() << std::endl; @@ -169,7 +169,7 @@ if ( _map_service.isTiled() ) { - buf << _options.url().value() << "/tile" + buf << _options.url()->full() << "/tile" << "/" << level << "/" << tile_y << "/" << tile_x << "." << f; @@ -179,7 +179,7 @@ const GeoExtent& ex = key.getExtent(); buf << std::setprecision(16) - << _options.url().value() << "/export" + << _options.url()->full() << "/export" << "?bbox=" << ex.xMin() << "," << ex.yMin() << "," << ex.xMax() << "," << ex.yMax() << "&format=" << f << "&size=256,256" diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/cache_sqlite3/Sqlite3Cache.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/cache_sqlite3/Sqlite3Cache.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/cache_sqlite3/Sqlite3Cache.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/cache_sqlite3/Sqlite3Cache.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -66,7 +66,7 @@ sqlite3* openDatabase( const std::string& path, bool serialized ) { //Try to create the path if it doesn't exist - std::string dirPath = osgDB::getFilePath(path); + std::string dirPath = osgDB::getFilePath(path); //If the path doesn't currently exist or we can't create the path, don't cache the file if (!osgDB::fileExists(dirPath) && !osgDB::makeDirectory(dirPath)) @@ -1011,7 +1011,15 @@ public: Sqlite3Cache( const CacheOptions& options ) : AsyncCache(options), _options(options), _db(0L) - { + { + if ( _options.path().get().empty() || options.getReferenceURI().empty() ) + _databasePath = _options.path().get(); + else + { + _databasePath = osgEarth::getFullPath( options.getReferenceURI(), _options.path().get() ); + } + + setName( "sqlite3" ); _nbRequest = 0; @@ -1036,8 +1044,8 @@ _L2cache->setMaxNumTilesInCache( 64 ); OE_INFO << LC << "Using L2 memory cache" << std::endl; #endif - - _db = openDatabase( _options.path().value(), _options.serialized().value() ); + + _db = openDatabase( _databasePath, _options.serialized().value() ); if ( _db ) { @@ -1570,7 +1578,7 @@ std::map::const_iterator k = _dbPerThread.find(thread); if ( k == _dbPerThread.end() ) { - db = openDatabase( _options.path().value(), _options.serialized().value() ); + db = openDatabase( _databasePath, _options.serialized().value() ); if ( db ) { _dbPerThread[thread] = db; @@ -1662,6 +1670,7 @@ int _nbRequest; std::vector _layersList; + std::string _databasePath; }; diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/CMakeLists.txt osgearth-2.1.1+dfsg/src/osgEarthDrivers/CMakeLists.txt --- osgearth-2.0+dfsg/src/osgEarthDrivers/CMakeLists.txt 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/CMakeLists.txt 2011-11-04 19:44:43.000000000 +0000 @@ -23,11 +23,15 @@ osgEarth ) +# Folder name for plugins +SET(OSGEARTH_PLUGINS_FOLDER Plugins) + ############################################################ # # NodeKit/Psudo loader plugins # ADD_SUBDIRECTORY(earth) +ADD_SUBDIRECTORY(kml) ADD_SUBDIRECTORY(wcs) ADD_SUBDIRECTORY(wms) @@ -41,6 +45,7 @@ ADD_SUBDIRECTORY(osg) ADD_SUBDIRECTORY(agglite) ADD_SUBDIRECTORY(model_simple) +ADD_SUBDIRECTORY(debug) IF(LIBZIP_FOUND) ADD_SUBDIRECTORY(zipfs) @@ -50,18 +55,16 @@ IF(GDAL_FOUND) ADD_SUBDIRECTORY(gdal) ADD_SUBDIRECTORY(feature_ogr) -# ADD_SUBDIRECTORY(model_feature_overlay) + ADD_SUBDIRECTORY(feature_wfs) ADD_SUBDIRECTORY(model_feature_stencil) ADD_SUBDIRECTORY(model_feature_geom) - ADD_SUBDIRECTORY(model_feature_label) + ADD_SUBDIRECTORY(mask_feature) + ADD_SUBDIRECTORY(label_overlay) ENDIF(GDAL_FOUND) IF(SQLITE3_FOUND) ADD_SUBDIRECTORY(cache_sqlite3) + ADD_SUBDIRECTORY(mbtiles) ENDIF(SQLITE3_FOUND) -ADD_SUBDIRECTORY(engine_osgterrain) -#ADD_SUBDIRECTORY(engine_droam) -IF(NOT (${OPENSCENEGRAPH_VERSION} VERSION_LESS "2.9.10")) -ADD_SUBDIRECTORY(engine_seamless) -ENDIF(NOT (${OPENSCENEGRAPH_VERSION} VERSION_LESS "2.9.10")) +ADD_SUBDIRECTORY(engine_osgterrain) \ No newline at end of file diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/debug/CMakeLists.txt osgearth-2.1.1+dfsg/src/osgEarthDrivers/debug/CMakeLists.txt --- osgearth-2.0+dfsg/src/osgEarthDrivers/debug/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/debug/CMakeLists.txt 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,10 @@ +SET(TARGET_SRC DebugTileSource.cpp) +SET(TARGET_H DebugOptions) +SET(TARGET_COMMON_LIBRARIES ${TARGET_COMMON_LIBRARIES} osgEarthSymbology) +SETUP_PLUGIN(osgearth_debug) + +# to install public driver includes: +SET(LIB_NAME debug) +SET(LIB_PUBLIC_HEADERS DebugOptions) +INCLUDE(ModuleInstallOsgEarthDriverIncludes OPTIONAL) + diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/debug/DebugOptions osgearth-2.1.1+dfsg/src/osgEarthDrivers/debug/DebugOptions --- osgearth-2.0+dfsg/src/osgEarthDrivers/debug/DebugOptions 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/debug/DebugOptions 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,74 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#ifndef OSGEARTH_DRIVER_DEBUG_DRIVEROPTIONS +#define OSGEARTH_DRIVER_DEBUG_DRIVEROPTIONS 1 + +#include +#include + +namespace osgEarth { namespace Drivers +{ + using namespace osgEarth; + + class DebugOptions : public TileSourceOptions // NO EXPORT; header only + { + public: + optional& colorCode() { return _colorCode; } + const optional& colorCode() const { return _colorCode; } + + optional& tms() { return _tms; } + const optional& tms() const { return _tms; } + + public: + DebugOptions( const TileSourceOptions& opt =TileSourceOptions() ) : TileSourceOptions( opt ), + _colorCode( "#000000" ), + _tms( false ) + { + setDriver( "debug" ); + fromConfig( _conf ); + } + + public: + Config getConfig() const { + Config conf = TileSourceOptions::getConfig(); + conf.updateIfSet( "color", _colorCode ); + conf.updateIfSet( "tms", _tms ); + return conf; + } + + protected: + void mergeConfig( const Config& conf ) { + TileSourceOptions::mergeConfig( conf ); + fromConfig( conf ); + } + + private: + void fromConfig( const Config& conf ) { + conf.getIfSet( "color", _colorCode ); + conf.getIfSet( "tms", _tms); + } + + optional _colorCode; + optional _tms; + }; + +} } // namespace osgEarth::Drivers + +#endif // OSGEARTH_DRIVER_DEBUG_DRIVEROPTIONS + diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/debug/DebugTileSource.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/debug/DebugTileSource.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/debug/DebugTileSource.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/debug/DebugTileSource.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,173 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#if !((OPENSCENEGRAPH_MAJOR_VERSION <= 2) && (OPENSCENEGRAPH_MINOR_VERSION <= 8)) +# include +#endif +#include +#include +#include + +#include "DebugOptions" + +using namespace osgEarth; +using namespace osgEarth::Drivers; +using namespace osgEarth::Symbology; + +namespace +{ + static osg::Vec4 colors[4] = { + osg::Vec4(1,0,0,1), + osg::Vec4(0,1,0,1), + osg::Vec4(0,0,1,1), + osg::Vec4(1,0,1,1) + }; + + void copySubImageAndColorize( const osg::Image* src, osg::Image* dst, unsigned dx, unsigned dy, osg::Vec4& newColor) + { + ImageUtils::PixelReader read(src); + ImageUtils::PixelWriter write(dst); + + for( int src_t=0, dst_t=dy; src_t < src->t(); src_t++, dst_t++ ) + { + for( int src_s=0, dst_s=dx; src_s < src->s(); src_s++, dst_s++ ) + { + osg::Vec4 color = read(src_s, src_t); + if ( color.a() > 0.5f ) + color = newColor; + write( color, dst_s, dst_t ); + } + } + } +} + +class DebugTileSource : public TileSource +{ +public: + DebugTileSource( const DebugOptions& options ) : TileSource( options ), _options(options) + { + _geom = new Ring(); + _geom->push_back( osg::Vec3(5, 5, 0) ); + _geom->push_back( osg::Vec3(250, 5, 0) ); + _geom->push_back( osg::Vec3(250, 250, 0) ); + _geom->push_back( osg::Vec3(5, 250, 0) ); + _font = osgText::readFontFile( "arial.ttf" ); + + _color = osgEarth::htmlColorToVec4f( *_options.colorCode() ); + } + + // Yahoo! uses spherical mercator, but the top LOD is a 2x2 tile set. + void initialize( const std::string& referenceURI, const Profile* overrideProfile) + { + if ( overrideProfile ) + setProfile( overrideProfile ); + else + setProfile( Profile::create("global-geodetic") ); + } + + osg::Image* createImage( const TileKey& key, ProgressCallback* progress ) + { + // first draw the colored outline: + GeometryRasterizer rasterizer( 256, 256 ); + rasterizer.draw( _geom.get(), colors[key.getLevelOfDetail() % 4] ); + osg::Image* image = rasterizer.finalize(); + + // next render the tile key text: + std::stringstream buf; + if (*_options.tms()) + { + //Print out a TMS key for the TileKey + unsigned int tileX, tileY; + key.getTileXY(tileX, tileY); + unsigned int numRows, numCols; + key.getProfile()->getNumTiles(key.getLevelOfDetail(), numCols, numRows); + tileY = numRows - tileY - 1; + buf << key.getLevelOfDetail() << "/" << tileX << "/" << tileY; + } + else + { + buf << key.str(); + } + + std::string text = buf.str(); + + unsigned x = 10, y = 10; + + osgText::FontResolution resolution(32, 32); + for( unsigned i=0; igetGlyph( resolution, text.at(i) ); + copySubImageAndColorize( glyph, image, x, y, _color ); + x += glyph->s() + 1; +#endif + } + + return image; + } + + virtual std::string getExtension() const + { + return "png"; + } + + virtual bool supportsPersistentCaching() const + { + return false; + } + +private: + const DebugOptions _options; + osg::ref_ptr _geom; + osg::ref_ptr _font; + osg::Vec4 _color; +}; + + +class DebugTileSourceDriver : public TileSourceDriver +{ + public: + DebugTileSourceDriver() + { + supportsExtension( "osgearth_debug", "Debugging driver" ); + } + + virtual const char* className() + { + return "Debugging Driver"; + } + + virtual ReadResult readObject(const std::string& file_name, const Options* options) const + { + if ( !acceptsExtension(osgDB::getLowerCaseFileExtension( file_name ))) + return ReadResult::FILE_NOT_HANDLED; + + return new DebugTileSource( getTileSourceOptions(options) ); + } +}; + +REGISTER_OSGPLUGIN(osgearth_debug, DebugTileSourceDriver) diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/earth/CMakeLists.txt osgearth-2.1.1+dfsg/src/osgEarthDrivers/earth/CMakeLists.txt --- osgearth-2.0+dfsg/src/osgEarthDrivers/earth/CMakeLists.txt 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/earth/CMakeLists.txt 2011-11-04 19:44:43.000000000 +0000 @@ -17,5 +17,5 @@ EarthFileSerializer1.cpp EarthFileSerializer2.cpp) -#### end var setup ### SETUP_PLUGIN(earth) + diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/earth/EarthFileSerializer1.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/earth/EarthFileSerializer1.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/earth/EarthFileSerializer1.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/earth/EarthFileSerializer1.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -81,6 +81,12 @@ MapNodeOptions mapNodeOptions( mapNodeOptionsConf ); mapNodeOptions.setTerrainOptions( TerrainOptions(terrainOptionsConf) ); + //Set the reference URI of the cache config. + if (mapOptions.cache().isSet()) + { + mapOptions.cache()->setReferenceURI(referenceURI); + } + // the reference URI allows osgEarth to resolve relative paths within the configuration mapOptions.referenceURI() = referenceURI; @@ -136,10 +142,16 @@ } // Mask layer: - Config maskLayerConf = conf.child( "mask" ); - if ( !maskLayerConf.empty() ) + ConfigSet masks = conf.children( "mask" ); + for( ConfigSet::const_iterator i = masks.begin(); i != masks.end(); i++ ) { - map->setTerrainMaskLayer( new MaskLayer(maskLayerConf) ); + Config maskLayerConf = *i; + + MaskLayerOptions options(maskLayerConf); + options.name() = maskLayerConf.value( "name" ); + options.driver() = MaskSourceOptions(options); + + map->addTerrainMaskLayer( new MaskLayer(options) ); } return new MapNode( map, mapNodeOptions ); diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/earth/EarthFileSerializer2.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/earth/EarthFileSerializer2.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/earth/EarthFileSerializer2.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/earth/EarthFileSerializer2.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -25,6 +25,12 @@ { MapOptions mapOptions( conf.child( "options" ) ); + //Set the reference URI of the cache config. + if (mapOptions.cache().isSet()) + { + mapOptions.cache()->setReferenceURI(referenceURI); + } + // the reference URI allows osgEarth to resolve relative paths within the configuration mapOptions.referenceURI() = referenceURI; @@ -102,7 +108,10 @@ ConfigSet overlays = conf.children( "overlay" ); for( ConfigSet::const_iterator i = overlays.begin(); i != overlays.end(); i++ ) { - const Config& layerDriverConf = *i; + Config layerDriverConf = *i; + if ( !layerDriverConf.hasValue("driver") ) + layerDriverConf.attr("driver") = "feature_geom"; + //const Config& layerDriverConf = *i; ModelLayerOptions layerOpt( layerDriverConf ); layerOpt.name() = layerDriverConf.value( "name" ); @@ -113,13 +122,42 @@ } // Mask layer: - Config maskLayerConf = conf.child( "mask" ); - if ( !maskLayerConf.empty() ) + ConfigSet masks = conf.children( "mask" ); + for( ConfigSet::const_iterator i = masks.begin(); i != masks.end(); i++ ) + { + Config maskLayerConf = *i; + + MaskLayerOptions options(maskLayerConf); + options.name() = maskLayerConf.value( "name" ); + options.driver() = MaskSourceOptions(options); + + map->addTerrainMaskLayer( new MaskLayer(options) ); + } + + + //Add any addition paths specified in the options/osg_file_paths element to the file path. Useful for pointing osgEarth at resource folders. + Config osg_file_paths = conf.child( "options" ).child("osg_file_paths"); + ConfigSet urls = osg_file_paths.children("url"); + for (ConfigSet::const_iterator i = urls.begin(); i != urls.end(); i++) { - map->setTerrainMaskLayer( new MaskLayer(maskLayerConf) ); + std::string path = osgEarth::getFullPath( referenceURI, (*i).value()); + OE_DEBUG << "Adding OSG file path " << path << std::endl; + osgDB::Registry::instance()->getDataFilePathList().push_back( path ); } - return new MapNode( map, mapNodeOptions ); + + + + MapNode* mapNode = new MapNode( map, mapNodeOptions ); + + // External configs: + Config ext = conf.child( "external" ); + if ( !ext.empty() ) + { + mapNode->externalConfig() = ext; + } + + return mapNode; } @@ -169,5 +207,12 @@ mapConf.add( "model", layerConf ); } + Config ext = input->externalConfig(); + if ( !ext.empty() ) + { + ext.key() = "external"; + mapConf.addChild( ext ); + } + return mapConf; } diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/earth/ReaderWriterOsgEarth.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/earth/ReaderWriterOsgEarth.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/earth/ReaderWriterOsgEarth.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/earth/ReaderWriterOsgEarth.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -114,13 +114,14 @@ if ( HTTPClient::readString( fileName, buf ) != HTTPClient::RESULT_OK ) return ReadResult::ERROR_IN_READING_FILE; - // since we're now passing off control to the stream, we have to pass along the - // reference URI as well. TODO: later it will probably be a better idea to have - // a search path for data referenced in the mapfile. + // Since we're now passing off control to the stream, we have to pass along the + // reference URI as well.. osg::ref_ptr myOptions = options ? static_cast(options->clone(osg::CopyOp::DEEP_COPY_ALL)) : new Options(); - myOptions->setPluginData( "__ReaderWriterOsgEarth::ref_uri", (void*)&fileName ); + + URIContext( fileName ).store( myOptions.get() ); + //myOptions->setPluginData( "__ReaderWriterOsgEarth::ref_uri", (void*)&fileName ); std::stringstream in( buf ); return readNode( in, myOptions.get() ); @@ -129,7 +130,11 @@ virtual ReadResult readNode(std::istream& in, const Options* options ) const { - osg::ref_ptr doc = XmlDocument::load( in ); + // pull the URI context from the options structure (since we're reading + // from an "anonymous" stream here) + URIContext uriContext( options ); + + osg::ref_ptr doc = XmlDocument::load( in, uriContext ); if ( !doc.valid() ) return ReadResult::ERROR_IN_READING_FILE; @@ -146,14 +151,7 @@ if ( !conf.empty() ) { // see if we were given a reference URI to use: - std::string refURI; - if ( options ) - { - const std::string* value = static_cast( - options->getPluginData( "__ReaderWriterOsgEarth::ref_uri") ); - if ( value ) - refURI = *value; - } + std::string refURI = uriContext.referrer(); if ( conf.value("version") == "2" ) { diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_droam/CMakeLists.txt osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_droam/CMakeLists.txt --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_droam/CMakeLists.txt 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_droam/CMakeLists.txt 2011-11-04 19:44:43.000000000 +0000 @@ -5,6 +5,7 @@ CubeManifold.cpp Diamond.cpp DRoamNode.cpp + GeodeticManifold.cpp Manifold.cpp MeshManager.cpp Plugin.cpp @@ -16,6 +17,7 @@ CubeManifold Diamond DRoamNode + GeodeticManifold Manifold MeshManager AMRGeometry diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_droam/CubeManifold.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_droam/CubeManifold.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_droam/CubeManifold.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_droam/CubeManifold.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -46,35 +46,35 @@ // Construct the eight root "vertex diamonds". These are used only for their // vertex positions (as grandparents of the root edge diamonds). They have // no children or ancestors of their own. - _vd[0] = new Diamond(mesh, 0L, 0, "vd0"); // X, -Y, Z + _vd[0] = new Diamond(mesh, TileKey(), 0, "vd0"); // X, -Y, Z _vd[0]->setCoord( p(1, -1, 1) ); _vd[0]->_childValence = 3; - _vd[1] = new Diamond(mesh, 0L, 0, "vd1"); // -X, Y, Z + _vd[1] = new Diamond(mesh, TileKey(), 0, "vd1"); // -X, Y, Z _vd[1]->setCoord( p(-1, 1, 1) ); _vd[0]->_childValence = 3; - _vd[2] = new Diamond(mesh, 0L, 0, "vd2"); // -X, -Y, -Z + _vd[2] = new Diamond(mesh, TileKey(), 0, "vd2"); // -X, -Y, -Z _vd[2]->setCoord( p(-1, -1, -1) ); _vd[0]->_childValence = 3; - _vd[3] = new Diamond(mesh, 0L, 0, "vd3"); // X, Y, -Z + _vd[3] = new Diamond(mesh, TileKey(), 0, "vd3"); // X, Y, -Z _vd[3]->setCoord( p(1, 1, -1) ); _vd[0]->_childValence = 3; - _vd[4] = new Diamond(mesh, 0L, 0, "vd4"); // X, Y, Z + _vd[4] = new Diamond(mesh, TileKey(), 0, "vd4"); // X, Y, Z _vd[4]->setCoord( p(1, 1, 1) ); _vd[0]->_childValence = 3; - _vd[5] = new Diamond(mesh, 0L, 0, "vd5"); // -X, -Y, Z + _vd[5] = new Diamond(mesh, TileKey(), 0, "vd5"); // -X, -Y, Z _vd[5]->setCoord( p(-1, -1, 1) ); _vd[0]->_childValence = 3; - _vd[6] = new Diamond(mesh, 0L, 0, "vd6"); // X, -Y, -Z + _vd[6] = new Diamond(mesh, TileKey(), 0, "vd6"); // X, -Y, -Z _vd[6]->setCoord( p(1, -1, -1) ); _vd[0]->_childValence = 3; - _vd[7] = new Diamond(mesh, 0L, 0, "vd7"); // -X, Y, -Z + _vd[7] = new Diamond(mesh, TileKey(), 0, "vd7"); // -X, Y, -Z _vd[7]->setCoord( p(-1, 1, -1) ); _vd[0]->_childValence = 3; @@ -86,7 +86,7 @@ // The first 3 share vd0 as a common PARENT_L ancestor: - _fd[NEG_Y] = new Diamond(mesh, new TileKey(0,1,0,_profile.get()), 1, "fd -y"); // -Y face (-90=>0 long) + _fd[NEG_Y] = new Diamond(mesh, TileKey(0,1,0,_profile.get()), 1, "fd -y"); // -Y face (-90=>0 long) _fd[NEG_Y]->setCoord( p(0, -1, 0) ); _fd[NEG_Y]->_a[PARENT_R] = _vd[2]; _fd[NEG_Y]->_a[PARENT_L] = _vd[0]; @@ -94,7 +94,7 @@ _fd[NEG_Y]->_a[GDPARENT] = _vd[6]; _fd[NEG_Y]->_orientation = 6; - _fd[POS_X] = new Diamond(mesh, new TileKey(0,2,0,_profile.get()), 1, "fd +x"); // +X face (0=>90 long) + _fd[POS_X] = new Diamond(mesh, TileKey(0,2,0,_profile.get()), 1, "fd +x"); // +X face (0=>90 long) _fd[POS_X]->setCoord( p(1, 0, 0) ); _fd[POS_X]->_a[PARENT_R] = _vd[3]; _fd[POS_X]->_a[PARENT_L] = _vd[0]; @@ -102,7 +102,7 @@ _fd[POS_X]->_a[GDPARENT] = _vd[4]; _fd[POS_X]->_orientation = 0; - _fd[POS_Z] = new Diamond(mesh, new TileKey(0,4,0,_profile.get()), 1, "fd +z"); // +Z face (north polar) + _fd[POS_Z] = new Diamond(mesh, TileKey(0,4,0,_profile.get()), 1, "fd +z"); // +Z face (north polar) _fd[POS_Z]->setCoord( p(0, 0, 1) ); _fd[POS_Z]->_a[PARENT_R] = _vd[1]; _fd[POS_Z]->_a[PARENT_L] = _vd[0]; @@ -112,7 +112,7 @@ // The next 3 share vd7 as a common QUADTREE ancestor: - _fd[POS_Y] = new Diamond(mesh, new TileKey(0,3,0,_profile.get()), 1, "fd +y"); // +Y face (90=>180 long) + _fd[POS_Y] = new Diamond(mesh, TileKey(0,3,0,_profile.get()), 1, "fd +y"); // +Y face (90=>180 long) _fd[POS_Y]->setCoord( p(0, 1, 0) ); _fd[POS_Y]->_a[PARENT_R] = _vd[1]; _fd[POS_Y]->_a[PARENT_L] = _vd[3]; @@ -120,7 +120,7 @@ _fd[POS_Y]->_a[GDPARENT] = _vd[4]; _fd[POS_Y]->_orientation = 2; - _fd[NEG_X] = new Diamond(mesh, new TileKey(0,0,0,_profile.get()), 1, "fd -x"); // -X face (-180=>-90 long) + _fd[NEG_X] = new Diamond(mesh, TileKey(0,0,0,_profile.get()), 1, "fd -x"); // -X face (-180=>-90 long) _fd[NEG_X]->setCoord( p(-1, 0, 0) ); _fd[NEG_X]->_a[PARENT_R] = _vd[2]; _fd[NEG_X]->_a[PARENT_L] = _vd[1]; @@ -128,7 +128,7 @@ _fd[NEG_X]->_a[GDPARENT] = _vd[5]; _fd[NEG_X]->_orientation = 0; - _fd[NEG_Z] = new Diamond(mesh, new TileKey(0,5,0,_profile.get()), 1, "fd -z"); // -Z face (south polar) + _fd[NEG_Z] = new Diamond(mesh, TileKey(0,5,0,_profile.get()), 1, "fd -z"); // -Z face (south polar) _fd[NEG_Z]->setCoord( p(0, 0, -1) ); _fd[NEG_Z]->_a[PARENT_R] = _vd[3]; _fd[NEG_Z]->_a[PARENT_L] = _vd[2]; @@ -143,7 +143,7 @@ // as its "quadtree" ancestor. That adds up to the 4 verts of the diamond. // GROUP OF THREE under the vd[0] "quadtree": - _ed[0] = new Diamond(mesh, 0L, 2, "ed0"); + _ed[0] = new Diamond(mesh, TileKey(), 2, "ed0"); _ed[0]->setCoord( p(0, -1, 1) ); _ed[0]->_a[PARENT_R] = _fd[POS_Z]; _ed[0]->_a[PARENT_L] = _fd[NEG_Y]; @@ -151,7 +151,7 @@ _ed[0]->_a[GDPARENT] = _vd[5]; // _ed[0]->_color = RED; - _ed[1] = new Diamond(mesh, 0L, 2, "ed1"); + _ed[1] = new Diamond(mesh, TileKey(), 2, "ed1"); _ed[1]->setCoord( p(1, -1, 0 ) ); _ed[1]->_a[PARENT_R] = _fd[NEG_Y]; _ed[1]->_a[PARENT_L] = _fd[POS_X]; @@ -159,7 +159,7 @@ _ed[1]->_a[GDPARENT] = _vd[6]; //_ed[1]->_color = RED; - _ed[2] = new Diamond(mesh, 0L, 2, "ed2"); + _ed[2] = new Diamond(mesh, TileKey(), 2, "ed2"); _ed[2]->setCoord( p(1, 0, 1) ); _ed[2]->_a[PARENT_R] = _fd[POS_X]; _ed[2]->_a[PARENT_L] = _fd[POS_Z]; @@ -168,7 +168,7 @@ //_ed[2]->_color = RED; // GROUP OF THREE under the vd[1] "quadtree": - _ed[3] = new Diamond(mesh, 0L, 2, "ed3"); + _ed[3] = new Diamond(mesh, TileKey(), 2, "ed3"); _ed[3]->setCoord( p(0, 1, 1) ); _ed[3]->_a[PARENT_R] = _fd[POS_Z]; _ed[3]->_a[PARENT_L] = _fd[POS_Y]; @@ -176,7 +176,7 @@ _ed[3]->_a[GDPARENT] = _vd[4]; //_ed[3]->_color = GREEN; - _ed[4] = new Diamond(mesh, 0L, 2, "ed4"); + _ed[4] = new Diamond(mesh, TileKey(), 2, "ed4"); _ed[4]->setCoord( p(-1, 1, 0) ); _ed[4]->_a[PARENT_R] = _fd[POS_Y]; _ed[4]->_a[PARENT_L] = _fd[NEG_X]; @@ -184,7 +184,7 @@ _ed[4]->_a[GDPARENT] = _vd[7]; //_ed[4]->_color = GREEN; - _ed[5] = new Diamond(mesh, 0L, 2, "ed5"); + _ed[5] = new Diamond(mesh, TileKey(), 2, "ed5"); _ed[5]->setCoord( p(-1, 0, 1) ); _ed[5]->_a[PARENT_R] = _fd[NEG_X]; _ed[5]->_a[PARENT_L] = _fd[POS_Z]; @@ -193,7 +193,7 @@ //_ed[5]->_color = GREEN; // GROUP OF THREE under the vd[2] "quadtree": - _ed[6] = new Diamond(mesh, 0L, 2, "ed6"); + _ed[6] = new Diamond(mesh, TileKey(), 2, "ed6"); _ed[6]->setCoord( p(-1, -1, 0) ); _ed[6]->_a[PARENT_R] = _fd[NEG_Y]; _ed[6]->_a[PARENT_L] = _fd[NEG_X]; @@ -201,7 +201,7 @@ _ed[6]->_a[GDPARENT] = _vd[5]; //_ed[6]->_color = BLUE; - _ed[7] = new Diamond(mesh, 0L, 2, "ed7"); + _ed[7] = new Diamond(mesh, TileKey(), 2, "ed7"); _ed[7]->setCoord( p(-1, 0, -1) ); _ed[7]->_a[PARENT_R] = _fd[NEG_X]; _ed[7]->_a[PARENT_L] = _fd[NEG_Z]; @@ -209,7 +209,7 @@ _ed[7]->_a[GDPARENT] = _vd[7]; //_ed[7]->_color = BLUE; - _ed[8] = new Diamond(mesh, 0L, 2, "ed8"); + _ed[8] = new Diamond(mesh, TileKey(), 2, "ed8"); _ed[8]->setCoord( p(0, -1, -1) ); _ed[8]->_a[PARENT_R] = _fd[NEG_Z]; _ed[8]->_a[PARENT_L] = _fd[NEG_Y]; @@ -218,7 +218,7 @@ //_ed[8]->_color = BLUE; // GROUP OF THREE under the vd[3] "quadtree": - _ed[9] = new Diamond(mesh, 0L, 2, "ed9"); + _ed[9] = new Diamond(mesh, TileKey(), 2, "ed9"); _ed[9]->setCoord( p(1, 1, 0) ); _ed[9]->_a[PARENT_R] = _fd[POS_Y]; _ed[9]->_a[PARENT_L] = _fd[POS_X]; @@ -226,7 +226,7 @@ _ed[9]->_a[GDPARENT] = _vd[4]; //_ed[9]->_color = YELLOW; - _ed[10] = new Diamond(mesh, 0L, 2, "ed10"); + _ed[10] = new Diamond(mesh, TileKey(), 2, "ed10"); _ed[10]->setCoord( p(1, 0, -1) ); _ed[10]->_a[PARENT_R] = _fd[POS_X]; _ed[10]->_a[PARENT_L] = _fd[NEG_Z]; @@ -234,7 +234,7 @@ _ed[10]->_a[GDPARENT] = _vd[6]; //_ed[10]->_color = YELLOW; - _ed[11] = new Diamond(mesh, 0L, 2, "ed11"); + _ed[11] = new Diamond(mesh, TileKey(), 2, "ed11"); _ed[11]->setCoord( p(0, 1, -1) ); _ed[11]->_a[PARENT_R] = _fd[NEG_Z]; _ed[11]->_a[PARENT_L] = _fd[POS_Y]; diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_droam/Diamond osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_droam/Diamond --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_droam/Diamond 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_droam/Diamond 2011-11-04 19:44:43.000000000 +0000 @@ -56,12 +56,12 @@ */ struct Diamond : osg::Referenced { - Diamond( MeshManager* mesh, osgEarth::TileKey* key, Level level, const std::string& name ="" ); + Diamond( MeshManager* mesh, const osgEarth::TileKey& key, Level level, const std::string& name ="" ); ~Diamond(); void activate(); // call after the diamond is entirely initialized. - osg::ref_ptr _key; // tile key corresponding to this diamond (geom diamonds only) + osgEarth::TileKey _key; // tile key corresponding to this diamond (geom diamonds only) std::string _name; // name of this diamond (for debugging only-- remove later) Level _level; // subdivision level Orientation _orientation; // orientation of the diamond diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_droam/Diamond.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_droam/Diamond.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_droam/Diamond.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_droam/Diamond.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -38,7 +38,7 @@ static int s_numDiamonds = 0; -Diamond::Diamond( MeshManager* mesh, osgEarth::TileKey* key, Level level, const std::string& name ) : +Diamond::Diamond( MeshManager* mesh, const osgEarth::TileKey& key, Level level, const std::string& name ) : osg::Referenced(true), _mesh( mesh ), _key( key ), @@ -513,12 +513,12 @@ osg::Vec2f offset( 0.0, 0.0 ); double span = 1.0; - const TileKey* ssaKey = _currentStateSetOwner->_key.get(); + const TileKey& ssaKey = _currentStateSetOwner->_key; if ( _level > _currentStateSetOwner->_level ) //_currentStateSetOwner != _targetStateSetOwner ) { span = 1.0/(double)(1 << ((_level-_currentStateSetOwner->_level)/2)); - offset.x() = (_key->getGeoExtent().xMin()-ssaKey->getGeoExtent().xMin())/ssaKey->getGeoExtent().width(); - offset.y() = (_key->getGeoExtent().yMin()-ssaKey->getGeoExtent().yMin())/ssaKey->getGeoExtent().height(); + offset.x() = (_key.getExtent().xMin()-ssaKey.getExtent().xMin())/ssaKey.getExtent().width(); + offset.y() = (_key.getExtent().yMin()-ssaKey.getExtent().yMin())/ssaKey.getExtent().height(); } int o = _orientation; @@ -736,7 +736,7 @@ unsigned short whichParent = c <= 1 ? PARENT_R : PARENT_L; osg::ref_ptr qa = d->_a[whichParent].get(); // the common parent - Diamond* child = new Diamond( _mesh, 0L, d->_level+1 ); + Diamond* child = new Diamond( _mesh, TileKey(), d->_level+1 ); //Diamond* child = new Diamond( _mesh, 0L, d->_level+1, " ("+_name+" + "+d0->_name+") " ); //OE_NOTICE << "...neighbor: " << d0->_name << std::endl; @@ -806,7 +806,7 @@ qa->q(2) == child ? 3 : 1; } - child->_key = qa->_key->createSubkey( quadrant ); + child->_key = qa->_key.createChildKey( quadrant ); child->_orientation = quadrant == 1 ? 0 : diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_droam/DRoamNode osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_droam/DRoamNode --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_droam/DRoamNode 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_droam/DRoamNode 2011-11-04 19:44:43.000000000 +0000 @@ -24,7 +24,6 @@ #include #include #include -#include /** * D-ROAM: A diamond-based continuous manifold terrain engine. @@ -70,7 +69,6 @@ osg::ref_ptr _manifold; osg::ref_ptr _mesh; osg::ref_ptr _map; - osg::ref_ptr _engine; private: void update( osg::NodeVisitor* nv ); diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_droam/DRoamNode.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_droam/DRoamNode.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_droam/DRoamNode.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_droam/DRoamNode.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -19,12 +19,12 @@ #include "DRoamNode" #include "CubeManifold" #include "GeodeticManifold" -#include #include #include using namespace osgEarth::Drivers; +#undef USE_GEODETIC_MANIFOLD DRoamNode::DRoamNode( Map* map ) : _map( map ) diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_droam/GeodeticManifold.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_droam/GeodeticManifold.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_droam/GeodeticManifold.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_droam/GeodeticManifold.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -34,47 +34,47 @@ mesh->_minActiveLevel = 3; // Construct the "vertex diamonds". - _vd[0] = new Diamond(mesh, 0L, 0, "vd0"); // north pole 1 + _vd[0] = new Diamond(mesh, TileKey(), 0, "vd0"); // north pole 1 _vd[0]->setCoord( -90, 90, 0 ); - _vd[1] = new Diamond(mesh, 0L, 0, "vd1"); // north pole 2 - _vd[1]->setCoord( 90, -90, 0 ); + _vd[1] = new Diamond(mesh, TileKey(), 0, "vd1"); // north pole 2 + _vd[1]->setCoord( 90, 90, 0 ); - _vd[2] = new Diamond(mesh, 0L, 0, "vd2"); // south pole 1 + _vd[2] = new Diamond(mesh, TileKey(), 0, "vd2"); // south pole 1 _vd[2]->setCoord( -90, -90, 0 ); - _vd[3] = new Diamond(mesh, 0L, 0, "vd3"); // south pole 2 + _vd[3] = new Diamond(mesh, TileKey(), 0, "vd3"); // south pole 2 _vd[3]->setCoord( 90, -90, 0 ); - _vd[4] = new Diamond(mesh, 0L, 0, "vd4"); + _vd[4] = new Diamond(mesh, TileKey(), 0, "vd4"); _vd[4]->setCoord( -180, 0, 0 ); - _vd[5] = new Diamond(mesh, 0L, 0, "vd5"); + _vd[5] = new Diamond(mesh, TileKey(), 0, "vd5"); _vd[5]->setCoord( 0, 0, 0 ); // The 4 "face diamonds": - _fd[0] = new Diamond(mesh, 0L, 0, "fd0"); + _fd[0] = new Diamond(mesh, TileKey(), 0, "fd0"); _fd[0]->setCoord( -90, 0, 0 ); _fd[0]->_a[GDPARENT] = _vd[0].get(); _fd[0]->_a[QUADTREE] = _vd[2].get(); _fd[0]->_a[PARENT_L] = _vd[4].get(); _fd[0]->_a[PARENT_R] = _vd[5].get(); - _fd[1] = new Diamond(mesh, 0L, 0, "fd1"); + _fd[1] = new Diamond(mesh, TileKey(), 0, "fd1"); _fd[1]->setCoord( 90, 0, 0 ); _fd[1]->_a[GDPARENT] = _vd[3].get(); _fd[1]->_a[QUADTREE] = _vd[1].get(); _fd[1]->_a[PARENT_L] = _vd[4].get(); _fd[1]->_a[PARENT_R] = _vd[5].get(); - _fd[2] = new Diamond(mesh, 0L, 0, "fd2"); // virtual north pole diamond + _fd[2] = new Diamond(mesh, TileKey(), 0, "fd2"); // virtual north pole diamond _fd[2]->setCoord( 0, 90, 0 ); _fd[2]->_a[GDPARENT] = _vd[0].get(); _fd[2]->_a[QUADTREE] = _vd[1].get(); _fd[2]->_a[PARENT_L] = _vd[5].get(); _fd[2]->_a[PARENT_R] = _vd[4].get(); - _fd[3] = new Diamond(mesh, 0L, 0, "fd3"); // virtual south pole diamond + _fd[3] = new Diamond(mesh, TileKey(), 0, "fd3"); // virtual south pole diamond _fd[3]->setCoord( 0, -90, 0 ); _fd[3]->_a[GDPARENT] = _vd[3].get(); _fd[3]->_a[QUADTREE] = _vd[2].get(); @@ -82,7 +82,7 @@ _fd[3]->_a[PARENT_R] = _vd[4].get(); // the 8 "edge diamonds" (first with geometry) - _ed[0] = new Diamond(mesh, new TileKey(1,0,0,_profile.get()), 1, "ed0"); + _ed[0] = new Diamond(mesh, TileKey(1,0,0,_profile.get()), 1, "ed0"); _ed[0]->setCoord( -135, 45, 0 ); _ed[0]->_a[GDPARENT] = _vd[0].get(); _ed[0]->_a[QUADTREE] = _vd[4].get(); @@ -90,7 +90,7 @@ _ed[0]->_a[PARENT_R] = _fd[0].get(); _ed[0]->_orientation = 0; - _ed[1] = new Diamond(mesh, new TileKey(1,1,0,_profile.get()), 1, "ed1"); + _ed[1] = new Diamond(mesh, TileKey(1,1,0,_profile.get()), 1, "ed1"); _ed[1]->setCoord( -45, 45, 0 ); _ed[1]->_a[GDPARENT] = _vd[0].get(); _ed[1]->_a[QUADTREE] = _vd[5].get(); @@ -98,7 +98,7 @@ _ed[1]->_a[PARENT_R] = _fd[2].get(); _ed[1]->_orientation = 2; - _ed[2] = new Diamond(mesh, new TileKey(1,0,1,_profile.get()), 1, "ed2"); + _ed[2] = new Diamond(mesh, TileKey(1,0,1,_profile.get()), 1, "ed2"); _ed[2]->setCoord( -135, -45, 0 ); _ed[2]->_a[GDPARENT] = _vd[2].get(); _ed[2]->_a[QUADTREE] = _vd[4].get(); @@ -106,7 +106,7 @@ _ed[2]->_a[PARENT_R] = _fd[3].get(); _ed[2]->_orientation = 6; - _ed[3] = new Diamond(mesh, new TileKey(1,1,1,_profile.get()), 1, "ed3"); + _ed[3] = new Diamond(mesh, TileKey(1,1,1,_profile.get()), 1, "ed3"); _ed[3]->setCoord( -45, -45, 0 ); _ed[3]->_a[GDPARENT] = _vd[2].get(); _ed[3]->_a[QUADTREE] = _vd[5].get(); @@ -114,7 +114,7 @@ _ed[3]->_a[PARENT_R] = _fd[0].get(); _ed[3]->_orientation = 4; - _ed[4] = new Diamond(mesh, new TileKey(1,2,0,_profile.get()), 1, "ed4"); + _ed[4] = new Diamond(mesh, TileKey(1,2,0,_profile.get()), 1, "ed4"); _ed[4]->setCoord( 45, 45, 0 ); _ed[4]->_a[GDPARENT] = _vd[1].get(); _ed[4]->_a[QUADTREE] = _vd[5].get(); @@ -122,7 +122,7 @@ _ed[4]->_a[PARENT_R] = _fd[1].get(); _ed[4]->_orientation = 0; - _ed[5] = new Diamond(mesh, new TileKey(1,3,0,_profile.get()), 1, "ed5"); + _ed[5] = new Diamond(mesh, TileKey(1,3,0,_profile.get()), 1, "ed5"); _ed[5]->setCoord( 135, 45, 0 ); _ed[5]->_a[GDPARENT] = _vd[1].get(); _ed[5]->_a[QUADTREE] = _vd[4].get(); @@ -130,7 +130,7 @@ _ed[5]->_a[PARENT_R] = _fd[2].get(); _ed[5]->_orientation = 2; - _ed[6] = new Diamond(mesh, new TileKey(1,2,1,_profile.get()), 1, "ed6"); + _ed[6] = new Diamond(mesh, TileKey(1,2,1,_profile.get()), 1, "ed6"); _ed[6]->setCoord( 45, -45, 0 ); _ed[6]->_a[GDPARENT] = _vd[3].get(); _ed[6]->_a[QUADTREE] = _vd[5].get(); @@ -138,7 +138,7 @@ _ed[6]->_a[PARENT_R] = _fd[3].get(); _ed[6]->_orientation = 6; - _ed[7] = new Diamond(mesh, new TileKey(1,3,1,_profile.get()), 1, "ed7"); + _ed[7] = new Diamond(mesh, TileKey(1,3,1,_profile.get()), 1, "ed7"); _ed[7]->setCoord( 135, -45, 0 ); _ed[7]->_a[GDPARENT] = _vd[3].get(); _ed[7]->_a[QUADTREE] = _vd[4].get(); diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_droam/MeshManager.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_droam/MeshManager.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_droam/MeshManager.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_droam/MeshManager.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -24,15 +24,16 @@ struct ImageRequest : public osgEarth::TaskRequest { - ImageRequest( MapLayer* layer, const TileKey* key ) : _layer(layer), _key(key) { } + ImageRequest( ImageLayer* layer, const TileKey& key ) : _layer(layer), _key(key) { } void operator()( ProgressCallback* progress ) { - _result = _layer->createImage( _key.get() ); + _result = _layer->createImage( _key ); } - osg::ref_ptr _layer; - osg::ref_ptr _key; + osg::ref_ptr _layer; + TileKey _key; + GeoImage _result; }; // -------------------------------------------------------------------------- @@ -127,7 +128,7 @@ if ( !d->_queuedForImage && !d->_imageRequest.valid() ) { //OE_NOTICE << "REQ: " << d->_key->str() << " queueing request..." << std::endl; - d->_imageRequest = new ImageRequest( _map->getImageMapLayers()[0].get(), d->_key.get() ); + d->_imageRequest = new ImageRequest( _map->getImageLayerAt(0), d->_key ); _imageService->add( d->_imageRequest.get() ); _imageQueue.push_back( DiamondJob( d, priority ) ); //.insert( DiamondJob( d, priority ) ); d->_queuedForImage = true; diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_droam/Plugin.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_droam/Plugin.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_droam/Plugin.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_droam/Plugin.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include #include "DRoamNode" @@ -40,16 +40,23 @@ virtual ReadResult readNode(const std::string& uri, const Options* options) const { - std::string earthFile = osgDB::getNameLessExtension( uri ); - osgEarth::EarthFile ef; - if ( ef.readXML( earthFile ) ) + if ( "osgearth_engine_droam" == osgDB::getFileExtension( uri ) ) { - return ReadResult( new DRoamNode( ef.getMap() ) ); + std::string earthFile = osgDB::getNameLessExtension( uri ); + + osg::ref_ptr node = osgDB::readNodeFile(earthFile); + osgEarth::MapNode* mapNode = osgEarth::MapNode::findMapNode(node.get()); + if ( mapNode ) + { + return ReadResult( new DRoamNode( mapNode->getMap() ) ); + } + else + { + return ReadResult::FILE_NOT_FOUND; + } } else - { - return ReadResult::FILE_NOT_FOUND; - } + return ReadResult::FILE_NOT_HANDLED; } }; diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/CMakeLists.txt osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/CMakeLists.txt --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/CMakeLists.txt 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/CMakeLists.txt 2011-11-04 19:44:43.000000000 +0000 @@ -1,30 +1,43 @@ -SET(TARGET_COMMON_LIBRARIES ${TARGET_COMMON_LIBRARIES}) +SET(TARGET_COMMON_LIBRARIES ${TARGET_COMMON_LIBRARIES} osgEarthSymbology) SET(TARGET_SRC - CustomTerrain.cpp - CustomTile.cpp + KeyNodeFactory.cpp + LODFactorCallback.cpp MultiPassTerrainTechnique.cpp OSGTerrainEngineNode.cpp OSGTileFactory.cpp + ParallelKeyNodeFactory.cpp Plugin.cpp - TerrainTileEdgeNormalizerUpdateCallback.cpp - SinglePassTerrainTechnique.cpp + SerialKeyNodeFactory.cpp + SinglePassTerrainTechnique.cpp + StreamingTerrain.cpp + StreamingTile.cpp + Terrain.cpp + Tile.cpp + TileBuilder.cpp ) SET(TARGET_H Common - CustomTerrain - CustomTile CustomTerrainTechnique + DynamicLODScaleCallback FileLocationCallback + KeyNodeFactory + LODFactorCallback MultiPassTerrainTechnique + ParallelKeyNodeFactory OSGTerrainEngineNode OSGTerrainOptions OSGTileFactory - TerrainTileEdgeNormalizerUpdateCallback - TransparentLayer + SerialKeyNodeFactory SinglePassTerrainTechnique + StreamingTerrain + StreamingTile + Terrain + Tile + TileBuilder + TransparentLayer ) SETUP_PLUGIN(osgearth_engine_osgterrain) diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/CustomTerrain osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/CustomTerrain --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/CustomTerrain 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/CustomTerrain 2011-11-04 19:44:43.000000000 +0000 @@ -19,6 +19,7 @@ #ifndef OSGEARTH_ENGINE_OSGTERRAIN_CUSTOM_TERRAIN #define OSGEARTH_ENGINE_OSGTERRAIN_CUSTOM_TERRAIN 1 +#error #include "Common" #include "CustomTile" #include "OSGTileFactory" @@ -120,19 +121,16 @@ ~CustomTerrain(); typedef std::map< osgTerrain::TileID, osg::ref_ptr > TileTable; - //typedef std::map< TileKey, osg::ref_ptr > TileTable; - typedef std::queue< osg::ref_ptr > TileQueue; - typedef std::list< osg::ref_ptr > TileList; + typedef std::queue< osg::ref_ptr > TileQueue; + typedef std::list< osg::ref_ptr > TileList; typedef std::vector< osg::ref_ptr > TileVector; - typedef std::queue< osgTerrain::TileID > TileIDQueue; + typedef std::queue< osgTerrain::TileID > TileIDQueue; Threading::ReadWriteMutex _tilesMutex; TileTable _tiles; TileList _tilesToShutDown; TileQueue _tilesToRelease; - //TileQueue _tilesToAdd; - //TileVector _tilesToServiceElevation; Threading::Mutex _tilesToReleaseMutex; @@ -141,7 +139,6 @@ void registerTile( CustomTile* newTile ); - //void getCustomTile( const TileKey& key, osg::ref_ptr& out_tile, bool lock =true ); void getCustomTile( const osgTerrain::TileID&, osg::ref_ptr& out_tile, bool lock =true ); void getCustomTiles( TileVector& out_tiles ); @@ -152,26 +149,24 @@ TaskService* createTaskService( const std::string& name, int id, int numThreads ); TaskService* getTaskService( int id ); - //void refreshFamily( - // const MapInfo& info, const osgTerrain::TileID& tileId, Relative* family, bool tileTableLocked ); - void refreshFamily( const MapInfo& info, const TileKey& key, Relative* family, bool tileTableLocked ); int _revision; OpenThreads::Mutex _revisionMutex; - //osg::ref_ptr _map; - - osg::ref_ptr _tileFactory; // _engine; + osg::ref_ptr _tileFactory; typedef std::map< int, osg::ref_ptr< TaskService > > TaskServiceMap; TaskServiceMap _taskServices; osg::ref_ptr _profile; OpenThreads::Mutex _taskServiceMutex; + bool _alwaysUpdate; int _numLoadingThreads; int _onDemandDelay; // #frames + void setDelay( unsigned frames ); + void decDelay(); bool _registeredWithReleaseGLCallback; //TerrainCallbackList _terrainCallbacks; diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/CustomTerrain.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/CustomTerrain.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/CustomTerrain.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/CustomTerrain.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -16,6 +16,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see */ +#if 0 #include "CustomTerrain" #include "CustomTile" #include "TransparentLayer" @@ -105,6 +106,11 @@ { OpenThreads::ScopedLock lock( _tilesToReleaseMutex ); + //if ( _tilesToRelease.size() > 0 ) + //{ + // OE_INFO << "Releasing " << _tilesToRelease.size() << " tiles" << std::endl; + //} + while( _tilesToRelease.size() > 0 ) { _tilesToRelease.front()->releaseGLObjects( state ); @@ -119,27 +125,33 @@ _revision(0), _tileFactory( tileFactory ), _numLoadingThreads( 0 ), -_onDemandDelay( 2 ), _registeredWithReleaseGLCallback( false ), _update_mapf( update_mapf ), _cull_mapf( cull_mapf ), +_onDemandDelay( 2 ), _quickReleaseGLObjects( quickReleaseGLObjects ), -_quickReleaseCallbackInstalled( false ) +_quickReleaseCallbackInstalled( false ), +_alwaysUpdate( false ) { this->setThreadSafeRefUnref( true ); _loadingPolicy = _tileFactory->getTerrainOptions().loadingPolicy().get(); - if ( _loadingPolicy.mode() != LoadingPolicy::MODE_STANDARD ) + if ( _loadingPolicy.mode() != LoadingPolicy::MODE_SERIAL && _loadingPolicy.mode() != LoadingPolicy::MODE_PARALLEL ) { setNumChildrenRequiringUpdateTraversal( 1 ); + _alwaysUpdate = true; _numLoadingThreads = computeLoadingThreads(_loadingPolicy); OE_INFO << LC << "Using a total of " << _numLoadingThreads << " loading threads " << std::endl; } else { // undo the setting in osgTerrain::Terrain - setNumChildrenRequiringUpdateTraversal( 0 ); + //setNumChildrenRequiringUpdateTraversal( 0 ); + + // the EVENT_VISITOR will reset this to 0 once the "delay" is expired. + _alwaysUpdate = false; + setNumChildrenRequiringUpdateTraversal( 1 ); } // register for events in order to support ON_DEMAND frame scheme @@ -344,6 +356,7 @@ { Threading::ScopedWriteLock exclusiveTileTableLock( _tilesMutex ); _tiles[ newTile->getTileID() ] = newTile; + //OE_INFO << LC << "Registered tiles = " << _tiles.size() << std::endl; } unsigned int @@ -361,6 +374,8 @@ void CustomTerrain::traverse( osg::NodeVisitor &nv ) { + // UPDATE runs whenever a Tile runs its update traversal on the first pass. + // i.e., only runs then a new Tile is born. if ( nv.getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR ) { // if the terrain engine requested "quick release", install the quick release @@ -390,10 +405,14 @@ if ( tile->getNumParents() == 0 && tile->getHasBeenTraversed() ) { _tilesToShutDown.push_back( tile ); + + // i is incremented prior to calling erase, but i's previous value goes to erase, + // maintaining validity _tiles.erase( i++ ); } else ++i; + } } @@ -421,37 +440,42 @@ } } - // update the frame stamp on the task services. This is necessary to support - // automatic request cancelation for image requests. +// OE_NOTICE << "Tiles = " << _tiles.size() << std::endl; + + if ( _loadingPolicy.mode() == LoadingPolicy::MODE_SEQUENTIAL || _loadingPolicy.mode() == LoadingPolicy::MODE_PREEMPTIVE ) { - ScopedLock lock( _taskServiceMutex ); - for (TaskServiceMap::iterator i = _taskServices.begin(); i != _taskServices.end(); ++i) + // update the frame stamp on the task services. This is necessary to support + // automatic request cancelation for image requests. { - i->second->setStamp( stamp ); + ScopedLock lock( _taskServiceMutex ); + for (TaskServiceMap::iterator i = _taskServices.begin(); i != _taskServices.end(); ++i) + { + i->second->setStamp( stamp ); + } } - } - - // next, go through the live tiles and process update-traversal requests. This - // requires a read-lock on the master tiles table. - TileList updatedTiles; - { - Threading::ScopedReadLock tileTableReadLock( _tilesMutex ); - for( TileTable::const_iterator i = _tiles.begin(); i != _tiles.end(); ++i ) + // next, go through the live tiles and process update-traversal requests. This + // requires a read-lock on the master tiles table. + TileList updatedTiles; { - CustomTile* tile = i->second.get(); - - // update the neighbor list for each tile. - refreshFamily( _update_mapf.getMapInfo(), tile->getKey(), tile->getFamily(), true ); + Threading::ScopedReadLock tileTableReadLock( _tilesMutex ); - if ( tile->getUseLayerRequests() ) // i.e., sequential or preemptive mode + for( TileTable::const_iterator i = _tiles.begin(); i != _tiles.end(); ++i ) { - tile->servicePendingElevationRequests( _update_mapf, stamp, true ); - tile->serviceCompletedRequests( _update_mapf, true ); - //if ( tileModified && _terrainCallbacks.size() > 0 ) - //{ - // updatedTiles.push_back( tile ); - //} + CustomTile* tile = i->second.get(); + + // update the neighbor list for each tile. + refreshFamily( _update_mapf.getMapInfo(), tile->getKey(), tile->getFamily(), true ); + + if ( tile->getUseLayerRequests() ) // i.e., sequential or preemptive mode + { + tile->servicePendingElevationRequests( _update_mapf, stamp, true ); + tile->serviceCompletedRequests( _update_mapf, true ); + //if ( tileModified && _terrainCallbacks.size() > 0 ) + //{ + // updatedTiles.push_back( tile ); + //} + } } } } @@ -500,14 +524,16 @@ if ( _tilesToShutDown.size() > 0 ) { - _onDemandDelay = 2; + setDelay( 2 ); } - if ( _onDemandDelay <= 0 ) + else if ( _onDemandDelay <= 0 ) { int numTasks = getNumTasksRemaining(); if ( numTasks > 0 ) - _onDemandDelay = 2; + { + setDelay( 2 ); + } } //OE_INFO << "Tasks = " << numTasks << std::endl; @@ -516,13 +542,33 @@ { osgGA::EventVisitor* ev = dynamic_cast( &nv ); ev->getActionAdapter()->requestRedraw(); - _onDemandDelay--; + decDelay(); } } osgTerrain::Terrain::traverse( nv ); } +void +CustomTerrain::setDelay( unsigned frames ) +{ + if ( _onDemandDelay == 0 && !_alwaysUpdate ) + { + ADJUST_UPDATE_TRAV_COUNT( this, 1 ); + } + _onDemandDelay = frames; +} + +void +CustomTerrain::decDelay() +{ + _onDemandDelay--; + if ( _onDemandDelay == 0 && !_alwaysUpdate ) + { + ADJUST_UPDATE_TRAV_COUNT(this, -1); + } +} + TaskService* CustomTerrain::createTaskService( const std::string& name, int id, int numThreads ) { @@ -631,3 +677,4 @@ getImageryTaskService( itr->get()->getUID() )->setNumThreads( imageThreads ); } } +#endif \ No newline at end of file diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/CustomTerrainTechnique osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/CustomTerrainTechnique --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/CustomTerrainTechnique 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/CustomTerrainTechnique 2011-11-04 19:44:43.000000000 +0000 @@ -21,28 +21,53 @@ #define OSGEARTH_ENGINE_OSGTERRAIN_EXTENDED_TERRAIN_TECHNIQUE 1 #include "Common" -#include "CustomTile" -#include +#include +#include #include using namespace osgEarth; -class CustomTerrainTechnique : public osgTerrain::TerrainTechnique +class Tile; +class TileUpdate; + +class TerrainTechnique : public osg::Object +{ +public: + virtual void init() =0; + + virtual void traverse( osg::NodeVisitor& nv ) =0; + + virtual void releaseGLObjects( osg::State* state ) const =0; + +protected: + TerrainTechnique() : _tile(0L) { } + + TerrainTechnique( const TerrainTechnique& rhs, const osg::CopyOp& op ) : _tile(0L) { } + + Tile* _tile; + + friend class Tile; +}; + +class CustomTerrainTechnique : public TerrainTechnique { public: virtual void compile( const TileUpdate& updateSpec, ProgressCallback* progress ) =0; virtual bool applyTileUpdates() =0; + virtual void setParentTile( class Tile* tile ) =0; + virtual void setOptimizeTriangleOrientation( bool optimizeTriangleOrientation ) =0; virtual bool getOptimizeTriangleOrientation() const =0; + protected: CustomTerrainTechnique() { } CustomTerrainTechnique( const CustomTerrainTechnique& rhs, const osg::CopyOp& op ) - : osgTerrain::TerrainTechnique( rhs, op ) { } + : TerrainTechnique( rhs, op ) { } }; #endif // OSGEARTH_ENGINE_OSGTERRAIN_EXTENDED_TERRAIN_TECHNIQUE diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/CustomTile osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/CustomTile --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/CustomTile 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/CustomTile 2011-11-04 19:44:43.000000000 +0000 @@ -19,6 +19,7 @@ #ifndef OSGEARTH_ENGINE_OSGTERRAIN_CUSTOM_TILE #define OSGEARTH_ENGINE_OSGTERRAIN_CUSTOM_TILE 1 +#error #include "Common" #include "OSGTileFactory" #include "TransparentLayer" @@ -170,6 +171,10 @@ class CustomTerrain* getCustomTerrain(); const class CustomTerrain* getCustomTerrain() const; + /** Gets or sets the terrain mask geometry. */ + osg::Vec3dArray* getTerrainMaskGeometry() { return _mask.get(); } + void setTerrainMaskGeometry(osg::Vec3dArray* terrainMask) { _mask = terrainMask; } + bool getHasBeenTraversed() const; Threading::ReadWriteMutex& getTileLayersMutex(); @@ -247,6 +252,8 @@ osg::observer_ptr _CustomTerrain; + osg::ref_ptr _mask; + Relative _family[5]; Threading::ReadWriteMutex _tileLayersMutex; @@ -271,18 +278,21 @@ { public: CustomTileFrame( CustomTile* tile ) + : _tileKey(tile->getKey()) { Threading::ScopedReadLock sharedLock( tile->_tileLayersMutex ); _colorLayers = tile->_colorLayers; _elevationLayer = tile->getElevationLayer(); _locator = tile->getLocator(); _sampleRatio = tile->getTerrain()->getSampleRatio(); + _mask = tile->getTerrainMaskGeometry(); } - + TileKey _tileKey; ColorLayersByUID _colorLayers; osg::ref_ptr< osgTerrain::Layer > _elevationLayer; osg::ref_ptr< osgTerrain::Locator > _locator; float _sampleRatio; + osg::ref_ptr< osg::Vec3dArray > _mask; bool getCustomColorLayer( UID layerUID, CustomColorLayer& out ) const { ColorLayersByUID::const_iterator i = _colorLayers.find( layerUID ); diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/CustomTile.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/CustomTile.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/CustomTile.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/CustomTile.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -16,6 +16,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see */ +#if 0 #include "CustomTerrain" #include "CustomTerrainTechnique" #include "TransparentLayer" @@ -220,12 +221,17 @@ _quickReleaseGLObjects( quickReleaseGLObjects ), _key( key ), _keyLocator( keyLocator ), -_verticalScale(1.0f) +_verticalScale(1.0f), +_mask( 0L ) { + this->setLocator( keyLocator ); + this->setThreadSafeRefUnref( true ); this->setTileID( key.getTileId() ); + this->setName( key.str() ); + // because the lowest LOD (1) is always loaded fully: _elevationLayerUpToDate = _key.getLevelOfDetail() <= 1; @@ -1036,7 +1042,8 @@ oldLayer.getMapLayer(), oldLayer.getImage(), oldLayer.getLocator(), - _key.getLevelOfDetail() ) ); + _key.getLevelOfDetail(), + _key )); itr = _requests.erase( itr ); increment = false; @@ -1209,3 +1216,4 @@ _terrainTechnique->releaseGLObjects( state ); } } +#endif \ No newline at end of file diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/DynamicLODScaleCallback osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/DynamicLODScaleCallback --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/DynamicLODScaleCallback 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/DynamicLODScaleCallback 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,78 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph +* Copyright 2008-2010 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 +*/ + +#ifndef OSGEARTH_ENGINE_OSGTERRAIN_DYNAMIC_LOD_SCALE_CALLBACK_H +#define OSGEARTH_ENGINE_OSGTERRAIN_DYNAMIC_LOD_SCALE_CALLBACK_H 1 + +#include +#include + +/** + * Cull callback that dynamically computes an LOD scale based on + * distance to the camera and a "fall off" metric. As the fall off + * increases, farther objects' LOD scale will increase. A good + * range for the fall-off number is 0..5. + */ +struct DynamicLODScaleCallback : public osg::NodeCallback +{ + DynamicLODScaleCallback( float fallOff ) : _fallOff(fallOff) { } + + void operator()( osg::Node* node, osg::NodeVisitor* nv ) + { + osg::CullStack* cs = dynamic_cast(nv); + if ( cs ) + { + osg::LOD* lod = static_cast( node ); + osg::Vec3 center = lod->getCenter(); + + osg::Vec3 eye = nv->getEyePoint(); + osg::Vec3 eyeVec = eye; eyeVec.normalize(); + float has = osg::clampAbove( eye.length() - 6356752.3142f, 0.0f ); + float centerToEye = nv->getDistanceToViewPoint(center, false); + float bsToEye = centerToEye - lod->getChild(0)->getBound().radius(); + + float scaleAdj = 1.0f; + if ( bsToEye > has ) + { + float denom = osg::maximum(0.1f, (1.0f/_fallOff)) * 10000.0f; + scaleAdj = osg::clampBetween( log10f(bsToEye/denom), 1.0f, 3.0f ); + + //OE_INFO << LC + // << std::fixed + // << "centerToEye=" << centerToEye + // << ", bsToEye=" << bsToEye + // << ", scaleAdj=" << scaleAdj + // << std::endl; + } + + float lodScale = cs->getLODScale(); + cs->setLODScale( lodScale * scaleAdj ); + traverse( node, nv ); + cs->setLODScale( lodScale ); + } + else + { + traverse( node, nv ); + } + } + + float _fallOff; +}; + +#endif //OSGEARTH_ENGINE_OSGTERRAIN_DYNAMIC_LOD_SCALE_CALLBACK_H diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/FileLocationCallback osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/FileLocationCallback --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/FileLocationCallback 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/FileLocationCallback 2011-11-04 19:44:43.000000000 +0000 @@ -45,17 +45,30 @@ unsigned int lod, x, y, id; sscanf(filename.c_str(), "%d_%d_%d.%d", &lod, &x, &y, &id); - osg::ref_ptr engine = OSGTerrainEngineNode::getEngineByUID( (UID)id ); + osg::ref_ptr engine; + OSGTerrainEngineNode::getEngineByUID( (UID)id, engine ); if ( engine.valid() ) { const osgEarth::Profile* profile = engine->getMap()->getProfile(); osgEarth::TileKey mapKey( lod, x, y, profile ); - if ( engine->getTileFactory()->areChildrenCached( engine->getMap(), mapKey ) ) + result = LOCAL_FILE; + + MapFrame mapf( engine->getMap() ); + for( unsigned i=0; i<4; ++i ) { - result = LOCAL_FILE; + TileKey childKey = mapKey.createChildKey( i ); + if ( !mapf.isCached( childKey ) ) + { + result = REMOTE_FILE; + break; + } } + //if ( engine->getTileFactory()->areChildrenCached( engine->getMap(), mapKey ) ) + //{ + // result = LOCAL_FILE; + //} } return result; diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/KeyNodeFactory osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/KeyNodeFactory --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/KeyNodeFactory 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/KeyNodeFactory 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,41 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph +* Copyright 2008-2010 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 +*/ +#ifndef OSGEARTH_ENGINE_KEY_NODE_FACTORY +#define OSGEARTH_ENGINE_KEY_NODE_FACTORY 1 + +#include "Common" +#include +#include + +using namespace osgEarth; + +/** + * A scene graph node that holds a terrain tile (as the single child of its + * osg::Group) and can load subtiles when they come into LOD range. + */ +class KeyNodeFactory : public osg::Referenced +{ +public: + virtual osg::Node* createNode( const TileKey& key ) =0; + +protected: + KeyNodeFactory(); +}; + +#endif // OSGEARTH_ENGINE_QUAD_TILE_NODE diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/KeyNodeFactory.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/KeyNodeFactory.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/KeyNodeFactory.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/KeyNodeFactory.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,30 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph +* Copyright 2008-2010 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 +*/ +#include "KeyNodeFactory" + +using namespace osgEarth; + +#define LC "[KeyNodeFactory] " + +//-------------------------------------------------------------------------- + +KeyNodeFactory::KeyNodeFactory() +{ + //NOP +} diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/LODFactorCallback osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/LODFactorCallback --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/LODFactorCallback 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/LODFactorCallback 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,35 @@ +/* -*-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 +*/ + +#ifndef OSGEARTH_ENGINE_OSGTERRAIN_LOD_FACTOR_CALLBACK_H +#define OSGEARTH_ENGINE_OSGTERRAIN_LOD_FACTOR_CALLBACK_H 1 + +#include + +namespace osgEarth +{ + namespace Drivers + { + struct LODFactorCallback : public osg::NodeCallback + { + void operator()(osg::Node* node, osg::NodeVisitor* nv); + }; + } +} +#endif diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/LODFactorCallback.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/LODFactorCallback.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/LODFactorCallback.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/LODFactorCallback.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -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 + */ +#include "LODFactorCallback" + +#include +#include +#include +#include +#include + +namespace osgEarth +{ + namespace Drivers + { + // This callback sets a uniform, osgearth_LODRangeFactor, based on the + // distance from the camera and its relation to the minimum and + // maximum distance for a tile. The maximum distance isn't actually + // available, so 2 * min distance is used as an estimate. The range + // factor's value goes from 0 - at the maximum range - to 1 for the + // minimum range. + + void LODFactorCallback::operator()(osg::Node* node, osg::NodeVisitor* nv) + { + // test the type since this is not always a PagedLOD. + osg::PagedLOD* lod = static_cast(node); + osgUtil::CullVisitor* cv = static_cast(nv); + osg::LOD::RangeMode rangeMode = lod->getRangeMode(); + float requiredRange = 0.0f; + float rangeFactor = 1.0f; + const osg::LOD::RangeList& rangeList = lod->getRangeList(); + if (rangeMode == osg::LOD::DISTANCE_FROM_EYE_POINT) + { + requiredRange = cv->getDistanceToViewPoint(lod->getCenter(), true); + } + else if (cv->getLODScale() > 0.0f) + { + requiredRange = cv->clampedPixelSize(lod->getBound()) / cv->getLODScale(); + } + else + { + // The comment in osg/PagedLOD.cpp says that this algorithm + // finds the highest res tile, but it actually finds the + // lowest res tile! + for (osg::LOD::RangeList::const_iterator itr = rangeList.begin(), end = rangeList.end(); + itr != end; + ++itr) + { + requiredRange = osg::maximum(requiredRange, itr->first); + } + } + // We're counting on only finding one valid LOD, unlike the + // general OSG behavior. + if (!rangeList.empty() && rangeList[0].first <= requiredRange + && requiredRange < rangeList[0].second) + { + rangeFactor = 1.0f - (requiredRange - rangeList[0].first) / rangeList[0].first; + rangeFactor = osg::clampTo(rangeFactor, 0.0f, 1.0f); + } + osg::ref_ptr ufact + = new osg::Uniform("osgearth_LODRangeFactor", rangeFactor); + osg::ref_ptr ss = new osg::StateSet; + ss->addUniform(ufact.get()); + + cv->pushStateSet(ss.get()); + traverse(node, nv); + cv->popStateSet(); + } + } +} diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/MultiPassTerrainTechnique osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/MultiPassTerrainTechnique --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/MultiPassTerrainTechnique 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/MultiPassTerrainTechnique 2011-11-04 19:44:43.000000000 +0000 @@ -33,7 +33,7 @@ #include -class MultiPassTerrainTechnique : public osgTerrain::TerrainTechnique +class MultiPassTerrainTechnique : public TerrainTechnique { public: @@ -42,13 +42,9 @@ /** Copy constructor using CopyOp to manage deep vs shallow copy.*/ MultiPassTerrainTechnique(const MultiPassTerrainTechnique&,const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY); - META_Object(osgEarth, MultiPassTerrainTechnique); + META_Object( osgEarth, MultiPassTerrainTechnique ); -#if OSG_MIN_VERSION_REQUIRED(2,9,8) - virtual void init(int dirtyMask, bool assumeMultiThreaded); -#else virtual void init(); -#endif virtual osgTerrain::Locator* computeMasterLocator(); diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/MultiPassTerrainTechnique.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/MultiPassTerrainTechnique.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/MultiPassTerrainTechnique.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/MultiPassTerrainTechnique.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -17,11 +17,10 @@ * along with this program. If not, see */ #include "MultiPassTerrainTechnique" +#include "Terrain" #include "TransparentLayer" #include -#include -#include #include #include @@ -67,7 +66,8 @@ //------------------------------------------------------------------------- MultiPassTerrainTechnique::MultiPassTerrainTechnique( TextureCompositor* texCompositor ) : -osgTerrain::TerrainTechnique(), +TerrainTechnique(), +//osgTerrain::TerrainTechnique(), _terrainTileInitialized(false), _texCompositor( texCompositor ) { @@ -85,16 +85,21 @@ { } +#if 0 void #if OSG_MIN_VERSION_REQUIRED(2,9,8) MultiPassTerrainTechnique::init(int dirtyMask, bool assumeMultiThreaded) #else MultiPassTerrainTechnique::init() #endif +#endif + +void +MultiPassTerrainTechnique::init() { OE_DEBUG<<"Doing MultiPassTerrainTechnique::init()"<setThreadSafeReferenceCounting(true); } -osgTerrain::Locator* MultiPassTerrainTechnique::computeMasterLocator() +osgTerrain::Locator* +MultiPassTerrainTechnique::computeMasterLocator() { - osgTerrain::Layer* elevationLayer = _terrainTile->getElevationLayer(); - osgTerrain::Layer* colorLayer = _terrainTile->getColorLayer(0); + osgTerrain::Layer* elevationLayer = _tile->getElevationLayer(); + osgTerrain::Locator* locator = elevationLayer ? elevationLayer->getLocator() : 0L; - osgTerrain::Locator* elevationLocator = elevationLayer ? elevationLayer->getLocator() : 0; - osgTerrain::Locator* colorLocator = colorLayer ? colorLayer->getLocator() : 0; - - osgTerrain::Locator* masterLocator = elevationLocator ? elevationLocator : colorLocator; - if (!masterLocator) + if ( !locator ) { - OE_NOTICE<<"Problem, no locator found in any of the terrain layers"<getElevationLayer(); - osgTerrain::Layer* colorLayer = _terrainTile->getColorLayer(0); - + osgTerrain::Layer* elevationLayer = _tile->getElevationLayer(); osgTerrain::Locator* elevationLocator = elevationLayer ? elevationLayer->getLocator() : 0; - osgTerrain::Locator* colorLocator = colorLayer ? colorLayer->getLocator() : 0; + osgTerrain::Locator* colorLocator = 0L; if (!elevationLocator) elevationLocator = masterLocator; if (!colorLocator) colorLocator = masterLocator; @@ -154,23 +154,23 @@ } } - if (colorLayer) - { - if (colorLocator!= masterLocator) - { - masterLocator->computeLocalBounds(*colorLocator, bottomLeftNDC, topRightNDC); - } - else - { - bottomLeftNDC.x() = osg::minimum(bottomLeftNDC.x(), 0.0); - bottomLeftNDC.y() = osg::minimum(bottomLeftNDC.y(), 0.0); - topRightNDC.x() = osg::maximum(topRightNDC.x(), 1.0); - topRightNDC.y() = osg::maximum(topRightNDC.y(), 1.0); - } - } + //if (colorLayer) + //{ + // if (colorLocator!= masterLocator) + // { + // masterLocator->computeLocalBounds(*colorLocator, bottomLeftNDC, topRightNDC); + // } + // else + // { + // bottomLeftNDC.x() = osg::minimum(bottomLeftNDC.x(), 0.0); + // bottomLeftNDC.y() = osg::minimum(bottomLeftNDC.y(), 0.0); + // topRightNDC.x() = osg::maximum(topRightNDC.x(), 1.0); + // topRightNDC.y() = osg::maximum(topRightNDC.y(), 1.0); + // } + //} - OE_DEBUG<<"bottomLeftNDC = "<getElevationLayer(); + osgTerrain::Layer* elevationLayer = _tile->getElevationLayer(); //Create a new geode to store the geometry osg::Geode* geode = new osg::Geode; @@ -573,7 +574,7 @@ numRows = elevationLayer->getNumRows(); } - float sampleRatio = _terrainTile->getTerrain() ? _terrainTile->getTerrain()->getSampleRatio() : 1.0f; + float sampleRatio = _tile->getTerrain() ? _tile->getTerrain()->getSampleRatio() : 1.0f; double i_sampleFactor = 1.0; double j_sampleFactor = 1.0; @@ -593,8 +594,8 @@ j_sampleFactor = double(originalNumRows-1)/double(numRows-1); } - bool treatBoundariesToValidDataAsDefaultValue = _terrainTile->getTreatBoundariesToValidDataAsDefaultValue(); - OE_DEBUG<<"TreatBoundariesToValidDataAsDefaultValue="<getTreatBoundariesToValidDataAsDefaultValue(); + //OE_DEBUG<<"TreatBoundariesToValidDataAsDefaultValue="<(elevationLayer); @@ -610,7 +611,7 @@ unsigned int numVertices = numVerticesInBody+numVerticesInSkirt; //float minHeight = 0.0; - float scaleHeight = _terrainTile->getTerrain() ? _terrainTile->getTerrain()->getVerticalScale() : 1.0f; + float scaleHeight = _tile->getTerrain() ? _tile->getTerrain()->getVerticalScale() : 1.0f; osg::ref_ptr texCoords; @@ -624,33 +625,6 @@ const osgTerrain::Locator* colorLocator = locator ? locator : masterLocator; -#if 0 - //osgTerrain::Layer* colorLayer = _terrainTile->getColorLayer(layerNum); - if (colorLayer) - { - const osgTerrain::Locator* locator = colorLayer->getLocator(); //colorLayer->getLocator(); - //if (!locator) - //{ - // osgTerrain::SwitchLayer* switchLayer = dynamic_cast(colorLayer); - // if (switchLayer) - // { - // if (switchLayer->getActiveLayer()>=0 && - // static_cast(switchLayer->getActiveLayer())getNumLayers() && - // switchLayer->getLayer(switchLayer->getActiveLayer())) - // { - // locator = switchLayer->getLayer(switchLayer->getActiveLayer())->getLocator(); - // } - // } - //} - texCoords = new osg::Vec2Array; - texCoords->reserve(numVertices); - - const osgTerrain::Locator* colorLocator = locator ? locator : masterLocator; - _texCompositor->assignTexCoordArray( geometry, colorLayer->getUID(), texCoords.get() ); - //geometry->setTexCoordArray(0, texCoords.get()); // only one texture unit (multipass) - } -#endif - // allocate and assign color osg::ref_ptr colors = new osg::Vec4Array(1); (*colors)[0].set(1.0f,1.0f,1.0f,1.0f); @@ -845,7 +819,7 @@ if (_transform.valid()) { _transform->removeChildren( 0, _transform->getNumChildren() ); - _transform->addChild(_passes); + _transform->addChild(_passes.get()); } typedef std::map > OrderedGeodes; @@ -854,12 +828,12 @@ osg::ref_ptr prototype = createGeometryPrototype( masterLocator, centerModel ); // take a thread-safe snapshot of the layer stack: - CustomTileFrame tilef( static_cast( _terrainTile ) ); + TileFrame tilef( _tile ); if ( tilef._colorLayers.size() == 0 ) { // if there's no data, just make a placeholder pass - osg::Geode* geode = createPass(0, 0L, masterLocator, centerModel, prototype); + osg::Geode* geode = createPass(0, 0L, masterLocator, centerModel, prototype.get()); _passes->addChild( geode ); } else @@ -894,17 +868,23 @@ void MultiPassTerrainTechnique::traverse(osg::NodeVisitor& nv) { - if (!_terrainTile) return; + if (!_tile) return; // initialize the terrain tile on startup - if (_terrainTile->getDirty() && !_terrainTileInitialized) + if (_tile->getDirty() && !_terrainTileInitialized) { + _tile->init(); + _terrainTileInitialized = true; + +#if 0 #if OSG_MIN_VERSION_REQUIRED(2,9,8) _terrainTile->init(~0x0, true); #else _terrainTile->init(); #endif + _terrainTileInitialized = true; +#endif } if ( nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR ) @@ -912,48 +892,9 @@ updateTransparency(); } - // traverse the dynamically-generated geometry. if (_transform.valid()) _transform->accept(nv); - -#if 0 - // if app traversal update the frame count. - if (nv.getVisitorType()==osg::NodeVisitor::UPDATE_VISITOR) - { - if ((_terrainTile->getDirty())) - { -#if OSG_MIN_VERSION_REQUIRED(2,9,8) - _terrainTile->init(~0x0, true); -#else - _terrainTile->init(); -#endif - _terrainTileInitialized = true; - } - - if (_terrainTile) - _terrainTile->osg::Group::traverse( nv ); - } - else if (nv.getVisitorType()==osg::NodeVisitor::CULL_VISITOR) - { - updateTransparency(); - } - - if (_terrainTile->getDirty() && !_terrainTileInitialized) - { - OE_DEBUG<<"******* Doing init ***********"<init(~0x0, true); -#else - _terrainTile->init(); -#endif - _terrainTileInitialized = true; - } - - // traverse the dynamically-generated geometry. - if (_transform.valid()) - _transform->accept(nv); -#endif } void MultiPassTerrainTechnique::updateTransparency() @@ -961,7 +902,7 @@ if ( _passes.valid() ) { ColorLayersByUID colorLayers; - static_cast( _terrainTile )->getCustomColorLayers( colorLayers ); + _tile->getCustomColorLayers( colorLayers ); for( ColorLayersByUID::const_iterator i = colorLayers.begin(); i != colorLayers.end(); ++i ) { @@ -996,4 +937,3 @@ { if (_transform.valid()) _transform->releaseGLObjects( state ); } - diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/OSGTerrainEngineNode osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/OSGTerrainEngineNode --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/OSGTerrainEngineNode 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/OSGTerrainEngineNode 2011-11-04 19:44:43.000000000 +0000 @@ -27,6 +27,8 @@ #include "OSGTerrainOptions" #include "OSGTileFactory" +#include "KeyNodeFactory" +#include "TileBuilder" #include #include @@ -41,6 +43,9 @@ META_Node(osgEarth,OSGTerrainEngineNode); ~OSGTerrainEngineNode(); +public: + osg::Node* createNode(const TileKey& key); + public: // TerrainEngineNode overrides virtual void preInitialize( const Map* map, const TerrainOptions& options ); virtual void postInitialize( const Map* map, const TerrainOptions& options ); @@ -55,12 +60,12 @@ UID getUID() const; OSGTileFactory* getTileFactory() const { return _tileFactory.get(); } - class CustomTerrain* getTerrain() const { return _terrain; } + class Terrain* getTerrain() const { return _terrain; } public: // statics static void registerEngine( OSGTerrainEngineNode* engineNode ); static void unregisterEngine( UID uid ); - static OSGTerrainEngineNode* getEngineByUID( UID uid ); + static void getEngineByUID( UID uid, osg::ref_ptr& output ); private: void init(); @@ -69,28 +74,37 @@ void addImageLayer( ImageLayer* layer ); void addElevationLayer( ElevationLayer* layer ); - void removeImageLayer( ImageLayer* layerRemoved, unsigned int index ); - void removeElevationLayer( ElevationLayer* layerRemoved, unsigned int index ); + void removeImageLayer( ImageLayer* layerRemoved ); + void removeElevationLayer( ElevationLayer* layerRemoved ); void moveImageLayer( unsigned int oldIndex, unsigned int newIndex ); void moveElevationLayer( unsigned int oldIndex, unsigned int newIndex ); - void updateElevation(CustomTile* tile); + void updateElevation( Tile* tile ); void installShaders(); void updateTextureCombining(); - + private: - osg::ref_ptr _tileFactory; - //osg::ref_ptr _texCompositor; - class CustomTerrain* _terrain; - UID _uid; + osg::ref_ptr _tileFactory; + //class CustomTerrain* _terrain; + class Terrain* _terrain; + UID _uid; osgEarth::Drivers::OSGTerrainOptions _terrainOptions; - Revision _shaderLibRev; - osg::ref_ptr _taskServiceMgr; + Revision _shaderLibRev; + osg::ref_ptr _taskServiceMgr; // store a separate map frame for each of the traversal threads MapFrame* _update_mapf; // map frame for the main/update traversal thread MapFrame* _cull_mapf; // map frame for the cull traversal thread + + osg::ref_ptr _tileService; + osg::ref_ptr _keyNodeFactory; + osg::ref_ptr _tileBuilder; + + osg::Timer _timer; + unsigned _tileCount; + double _tileCreationTime; + bool _isStreaming; }; #endif // OSGEARTH_ENGINE_OSGTERRAIN_ENGINE_NODE_H diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/OSGTerrainEngineNode.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/OSGTerrainEngineNode.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/OSGTerrainEngineNode.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/OSGTerrainEngineNode.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -17,9 +17,12 @@ * along with this program. If not, see */ #include "OSGTerrainEngineNode" -#include "SinglePassTerrainTechnique" -#include "CustomTerrain" #include "MultiPassTerrainTechnique" +#include "ParallelKeyNodeFactory" +#include "SinglePassTerrainTechnique" +#include "Terrain" +#include "StreamingTerrain" +#include "TileBuilder" #include "TransparentLayer" #include @@ -27,6 +30,8 @@ #include #include #include +#include +#include #define LC "[OSGTerrainEngine] " @@ -52,7 +57,8 @@ //--------------------------------------------------------------------------- //static -static OpenThreads::ReentrantMutex s_engineNodeCacheMutex; +//static OpenThreads::ReentrantMutex s_engineNodeCacheMutex; +static Threading::ReadWriteMutex s_engineNodeCacheMutex; //Caches the MapNodes that have been created typedef std::map > EngineNodeCache; @@ -66,7 +72,7 @@ void OSGTerrainEngineNode::registerEngine(OSGTerrainEngineNode* engineNode) { - OpenThreads::ScopedLock lock(s_engineNodeCacheMutex); + Threading::ScopedWriteLock exclusiveLock( s_engineNodeCacheMutex ); getEngineNodeCache()[engineNode->_uid] = engineNode; OE_DEBUG << LC << "Registered engine " << engineNode->_uid << std::endl; } @@ -74,7 +80,7 @@ void OSGTerrainEngineNode::unregisterEngine( UID uid ) { - OpenThreads::ScopedLock lock(s_engineNodeCacheMutex); + Threading::ScopedWriteLock exclusiveLock( s_engineNodeCacheMutex ); EngineNodeCache::iterator k = getEngineNodeCache().find( uid ); if (k != getEngineNodeCache().end()) { @@ -83,13 +89,17 @@ } } -OSGTerrainEngineNode* -OSGTerrainEngineNode::getEngineByUID( UID uid ) +// 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 +OSGTerrainEngineNode::getEngineByUID( UID uid, osg::ref_ptr& output ) { - OpenThreads::ScopedLock lock(s_engineNodeCacheMutex); + Threading::ScopedReadLock sharedLock( s_engineNodeCacheMutex ); EngineNodeCache::const_iterator k = getEngineNodeCache().find( uid ); - if (k != getEngineNodeCache().end()) return k->second.get(); - return 0; + if (k != getEngineNodeCache().end()) + output = k->second.get(); } UID @@ -104,7 +114,9 @@ TerrainEngineNode(), _terrain( 0L ), _update_mapf( 0L ), -_cull_mapf( 0L ) +_cull_mapf( 0L ), +_tileCount( 0 ), +_tileCreationTime( 0.0 ) { _uid = Registry::instance()->createUID(); _taskServiceMgr = Registry::instance()->getTaskServiceManager(); @@ -122,10 +134,14 @@ unregisterEngine( _uid ); if ( _update_mapf ) + { delete _update_mapf; + } if ( _cull_mapf ) + { delete _cull_mapf; + } } void @@ -133,9 +149,12 @@ { TerrainEngineNode::preInitialize( map, options ); + _isStreaming = + options.loadingPolicy()->mode() == LoadingPolicy::MODE_PREEMPTIVE || + options.loadingPolicy()->mode() == LoadingPolicy::MODE_SEQUENTIAL; + // in standard mode, try to set the number of OSG DatabasePager threads to use. - if (options.loadingPolicy().isSet() && - options.loadingPolicy()->mode() == LoadingPolicy::MODE_STANDARD ) + if ( options.loadingPolicy().isSet() && !_isStreaming ) { int numThreads = -1; @@ -145,7 +164,7 @@ } else if ( options.loadingPolicy()->numLoadingThreadsPerCore().isSet() ) { - int numThreadsPerCore = *options.loadingPolicy()->numLoadingThreadsPerCore(); + float numThreadsPerCore = *options.loadingPolicy()->numLoadingThreadsPerCore(); numThreads = osg::maximum( (int)1, (int)osg::round( numThreadsPerCore * (float)OpenThreads::GetNumberOfProcessors() ) ); } @@ -167,7 +186,7 @@ // 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::TERRAIN_LAYERS, "osgterrain-update" ); + _update_mapf = new MapFrame( map, Map::MASKED_TERRAIN_LAYERS, "osgterrain-update" ); _cull_mapf = new MapFrame( map, Map::TERRAIN_LAYERS, "osgterrain-cull" ); // merge in the custom options: @@ -183,26 +202,10 @@ // populate the terrain with whatever data is in the map to begin with: if ( _terrain ) { -#if 0 - _update_mapf->sync(); - - unsigned int index = 0; - for( ElevationLayerVector::const_iterator i = _update_mapf->elevationLayers().begin(); i != _update_mapf->elevationLayers().end(); i++ ) - { - addElevationLayer( i->get() ); - } - - index = 0; - for( ImageLayerVector::const_iterator j = _update_mapf->imageLayers().begin(); j != _update_mapf->imageLayers().end(); j++ ) - { - addImageLayer( j->get() ); - } -#endif - // update the terrain revision in threaded mode - if ( _terrainOptions.loadingPolicy()->mode() != LoadingPolicy::MODE_STANDARD ) + if ( _isStreaming ) { - _terrain->updateTaskServiceThreads( *_update_mapf ); + static_cast(_terrain)->updateTaskServiceThreads( *_update_mapf ); } updateTextureCombining(); @@ -221,38 +224,57 @@ osg::BoundingSphere OSGTerrainEngineNode::computeBound() const { - if ( _terrain ) + if ( _terrain && _terrain->getNumChildren() > 0 ) + { return _terrain->getBound(); + } else + { return TerrainEngineNode::computeBound(); + } } void OSGTerrainEngineNode::onMapInfoEstablished( const MapInfo& mapInfo ) { OE_INFO << LC << "Map profile established" << std::endl; + + LoadingPolicy::Mode mode = *_terrainOptions.loadingPolicy()->mode(); + OE_INFO << LC << "Loading policy mode = " << + ( mode == LoadingPolicy::MODE_PREEMPTIVE ? "PREEMPTIVE" : + mode == LoadingPolicy::MODE_SEQUENTIAL ? "SEQUENTIAL" : + mode == LoadingPolicy::MODE_PARALLEL ? "PARALLEL" : + "SERIAL/STANDARD" ) + << std::endl; // create a factory for creating actual tile data _tileFactory = new OSGTileFactory( _uid, *_cull_mapf, _terrainOptions ); // go through and build the root nodesets. - _terrain = new CustomTerrain( - *_update_mapf, *_cull_mapf, _tileFactory.get(), *_terrainOptions.quickReleaseGLObjects() ); + if ( !_isStreaming ) + { + _terrain = new Terrain( + *_update_mapf, *_cull_mapf, _tileFactory.get(), *_terrainOptions.quickReleaseGLObjects() ); + } + else + { + _terrain = new StreamingTerrain( + *_update_mapf, *_cull_mapf, _tileFactory.get(), *_terrainOptions.quickReleaseGLObjects() ); + } this->addChild( _terrain ); // set the initial properties from the options structure: _terrain->setVerticalScale( _terrainOptions.verticalScale().value() ); - _terrain->setSampleRatio( _terrainOptions.heightFieldSampleRatio().value() ); + _terrain->setSampleRatio ( _terrainOptions.heightFieldSampleRatio().value() ); OE_INFO << LC << "Sample ratio = " << _terrainOptions.heightFieldSampleRatio().value() << std::endl; - //// install the proper layer composition technique: - //_texCompositor = new TextureCompositor( _terrainOptions.compositingTechnique().value() ); + // install the proper layer composition technique: if ( _texCompositor->getTechnique() == TerrainOptions::COMPOSITING_MULTIPASS ) { - _terrain->setTerrainTechniquePrototype( new MultiPassTerrainTechnique( _texCompositor.get() ) ); + _terrain->setTechniquePrototype( new MultiPassTerrainTechnique( _texCompositor.get() ) ); OE_INFO << LC << "Compositing technique = MULTIPASS" << std::endl; } @@ -264,59 +286,115 @@ if ( _terrainOptions.elevationInterpolation() == INTERP_TRIANGULATE ) tech->setOptimizeTriangleOrientation( false ); - _terrain->setTerrainTechniquePrototype( tech ); - } - -#if 0 // GW: moved this to TerrainEngine::preInitialize(), because TextureCompositor::createSamplerFunction - // needs to work prior to the postInitialize. - - // prime the texture compositor with any existing layers: - for( unsigned int i=0; i<_update_mapf->imageLayers().size(); ++i ) - { - _texCompositor->applyMapModelChange( MapModelChange( - MapModelChange::ADD_IMAGE_LAYER, - _update_mapf->getRevision(), - _update_mapf->getImageLayerAt(i), - i ) ); + _terrain->setTechniquePrototype( tech ); } -#endif // install the shader program, if applicable: installShaders(); - // apply any pending callbacks: -#if 0 - for( TerrainCallbackList::iterator c = _pendingTerrainCallbacks.begin(); c != _pendingTerrainCallbacks.end(); ++c ) + // calculate a good thread pool size for non-streaming parallel processing + if ( !_isStreaming ) { - terrain->addTerrainCallback( c->get() ); + unsigned num = 2 * OpenThreads::GetNumberOfProcessors(); + if ( _terrainOptions.loadingPolicy().isSet() ) + { + if ( _terrainOptions.loadingPolicy()->numLoadingThreads().isSet() ) + { + num = *_terrainOptions.loadingPolicy()->numLoadingThreads(); + } + else if ( _terrainOptions.loadingPolicy()->numLoadingThreadsPerCore().isSet() ) + { + num = (unsigned)(*_terrainOptions.loadingPolicy()->numLoadingThreadsPerCore() * OpenThreads::GetNumberOfProcessors()); + } + } + _tileService = new TaskService( "TileBuilder", num ); + + // initialize the tile builder + _tileBuilder = new TileBuilder( getMap(), _terrainOptions, _tileService.get() ); + + + // initialize a key node factory. + switch( mode ) + { + case LoadingPolicy::MODE_SERIAL: + _keyNodeFactory = new SerialKeyNodeFactory( _tileBuilder.get(), _terrainOptions, mapInfo, _terrain, _uid ); + break; + + case LoadingPolicy::MODE_PARALLEL: + _keyNodeFactory = new ParallelKeyNodeFactory( _tileBuilder.get(), _terrainOptions, mapInfo, _terrain, _uid ); + break; + + default: + break; + } } - _pendingTerrainCallbacks.clear(); -#endif - // collect the tile keys comprising the root tiles of the terrain. + // Build the first level of the terrain. + // Collect the tile keys comprising the root tiles of the terrain. std::vector< TileKey > keys; _update_mapf->getProfile()->getRootKeys( keys ); - for (unsigned int i = 0; i < keys.size(); ++i) + for( unsigned i=0; icreateNode( keys[i] ); + else + node = _tileFactory->createSubTiles( *_update_mapf, _terrain, keys[i], true ); - osg::Node* node = _tileFactory->createSubTiles( *_update_mapf, _terrain, keys[i], loadNow ); - if (node) - { - _terrain->addChild(node); - } + if ( node ) + _terrain->addChild( node ); else - { OE_WARN << LC << "Couldn't make tile for root key: " << keys[i].str() << std::endl; - } } // we just added the root tiles, so mark the bound in need of recomputation. dirtyBound(); } +osg::Node* +OSGTerrainEngineNode::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; + +#ifdef PROFILING + osg::Timer_t start = _timer.tick(); +#endif + + osg::Node* result = 0L; + + if ( _isStreaming ) + { + // sequential or preemptive mode only. + // create a map frame so we can safely create tiles from this dbpager thread + MapFrame mapf( getMap(), Map::TERRAIN_LAYERS, "dbpager::earth plugin" ); + result = getTileFactory()->createSubTiles( mapf, _terrain, key, false ); + } + else + { + result = _keyNodeFactory->createNode( key ); + } + +#ifdef PROFILING + osg::Timer_t end = osg::Timer::instance()->tick(); + if ( result ) + { + _tileCount++; + _tileCreationTime += _timer.delta_s(start,_timer.tick()); + if ( _tileCount % 60 == 0 ) + { + OE_INFO << LC << "Avg tile = " << 1000.0*(_tileCreationTime/(double)_tileCount) + << " ms, tiles per sec = " << (double)_tileCount/_timer.time_s() << std::endl; + } + } +#endif + + return result; +} + void OSGTerrainEngineNode::onMapModelChanged( const MapModelChange& change ) { @@ -336,13 +414,13 @@ addImageLayer( change.getImageLayer() ); break; case MapModelChange::REMOVE_IMAGE_LAYER: - removeImageLayer( change.getImageLayer(), change.getFirstIndex() ); + removeImageLayer( change.getImageLayer() ); break; case MapModelChange::ADD_ELEVATION_LAYER: addElevationLayer( change.getElevationLayer() ); break; case MapModelChange::REMOVE_ELEVATION_LAYER: - removeElevationLayer( change.getElevationLayer(), change.getFirstIndex() ); + removeElevationLayer( change.getElevationLayer() ); break; case MapModelChange::MOVE_IMAGE_LAYER: moveImageLayer( change.getFirstIndex(), change.getSecondIndex() ); @@ -354,10 +432,10 @@ } // update the terrain revision in threaded mode - if ( _terrainOptions.loadingPolicy()->mode() != LoadingPolicy::MODE_STANDARD ) + if ( _isStreaming ) { - getTerrain()->incrementRevision(); - getTerrain()->updateTaskServiceThreads( *_update_mapf ); + //getTerrain()->incrementRevision(); + static_cast(_terrain)->updateTaskServiceThreads( *_update_mapf ); } } @@ -368,19 +446,20 @@ return; // visit all existing terrain tiles and inform each one of the new image layer: - CustomTileVector tiles; - _terrain->getCustomTiles( tiles ); + TileVector tiles; + _terrain->getTiles( tiles ); - for( CustomTileVector::iterator itr = tiles.begin(); itr != tiles.end(); ++itr ) + for( TileVector::iterator itr = tiles.begin(); itr != tiles.end(); ++itr ) { - CustomTile* tile = itr->get(); + Tile* tile = itr->get(); + + StreamingTile* streamingTile = 0L; GeoImage geoImage; bool needToUpdateImagery = false; int imageLOD = -1; - if ( _terrainOptions.loadingPolicy()->mode() == LoadingPolicy::MODE_STANDARD || - tile->getKey().getLevelOfDetail() == 1) + if ( !_isStreaming || tile->getKey().getLevelOfDetail() == 1 ) { // in standard mode, or at the first LOD in seq/pre mode, fetch the image immediately. TileKey geoImageKey = tile->getKey(); @@ -392,6 +471,7 @@ // in seq/pre mode, set up a placeholder and mark the tile as dirty. geoImage = GeoImage(ImageUtils::createEmptyImage(), tile->getKey().getExtent() ); needToUpdateImagery = true; + streamingTile = static_cast(tile); } if (geoImage.valid()) @@ -415,12 +495,14 @@ tile->setCustomColorLayer( CustomColorLayer( layerAdded, geoImage.getImage(), - img_locator.get(), imageLOD ) ); + img_locator.get(), imageLOD, tile->getKey() ) ); // if necessary, tell the tile to queue up a new imagery request (since we // just installed a placeholder) if ( needToUpdateImagery ) - tile->updateImagery( layerAdded, *_update_mapf, _tileFactory.get() ); + { + streamingTile->updateImagery( layerAdded, *_update_mapf, _tileFactory.get() ); + } } else { @@ -428,57 +510,40 @@ // we will rely on the driver to dump out a warning if this is an error. } - if ( _terrainOptions.loadingPolicy()->mode() == LoadingPolicy::MODE_STANDARD ) - { - tile->applyImmediateTileUpdate( TileUpdate::ADD_IMAGE_LAYER, layerAdded->getUID() ); - } - else - { - tile->applyImmediateTileUpdate( TileUpdate::ADD_IMAGE_LAYER, layerAdded->getUID() ); - } + tile->applyImmediateTileUpdate( TileUpdate::ADD_IMAGE_LAYER, layerAdded->getUID() ); } updateTextureCombining(); } void -OSGTerrainEngineNode::removeImageLayer( ImageLayer* layerRemoved, unsigned int index ) +OSGTerrainEngineNode::removeImageLayer( ImageLayer* layerRemoved ) { // make a thread-safe copy of the tile table - CustomTileVector tiles; - _terrain->getCustomTiles( tiles ); + TileVector tiles; + _terrain->getTiles( tiles ); - for (CustomTileVector::iterator itr = tiles.begin(); itr != tiles.end(); ++itr) + for (TileVector::iterator itr = tiles.begin(); itr != tiles.end(); ++itr) { - CustomTile* tile = itr->get(); + Tile* tile = itr->get(); // critical section - tile->removeCustomColorLayer( index ); - - //if ( _terrainOptions.loadingPolicy()->mode() == LoadingPolicy::MODE_STANDARD ) - // tile->applyImmediateTileUpdate( TileUpdate::REMOVE_IMAGE_LAYER, layerRemoved->getUID() ); - //else - // tile->applyImmediateTileUpdate( TileUpdate::REMOVE_IMAGE_LAYER, layerRemoved->getUID() ); + tile->removeCustomColorLayer( layerRemoved->getUID() ); } updateTextureCombining(); - - OE_DEBUG << "[osgEarth::Map::removeImageSource] end " << std::endl; } void OSGTerrainEngineNode::moveImageLayer( unsigned int oldIndex, unsigned int newIndex ) { // take a thread-safe copy of the tile table - CustomTileVector tiles; - _terrain->getCustomTiles( tiles ); + TileVector tiles; + _terrain->getTiles( tiles ); - for (CustomTileVector::iterator itr = tiles.begin(); itr != tiles.end(); ++itr) + for (TileVector::iterator itr = tiles.begin(); itr != tiles.end(); ++itr) { - CustomTile* tile = itr->get(); - - //tile->moveColorLayer( oldIndex, newIndex ); - + Tile* tile = itr->get(); tile->applyImmediateTileUpdate( TileUpdate::MOVE_IMAGE_LAYER ); } @@ -486,31 +551,24 @@ } void -OSGTerrainEngineNode::updateElevation(CustomTile* tile) +OSGTerrainEngineNode::updateElevation( Tile* tile ) { - Threading::ScopedWriteLock tileLock( tile->getTileLayersMutex() ); + Threading::ScopedWriteLock exclusiveLock( tile->getTileLayersMutex() ); const TileKey& key = tile->getKey(); - bool hasElevation; - { - hasElevation = _update_mapf->elevationLayers().size() > 0; - } - - //Update the elevation hint - tile->setHasElevationHint( hasElevation ); + bool hasElevation = _update_mapf->elevationLayers().size() > 0; osgTerrain::HeightFieldLayer* heightFieldLayer = dynamic_cast(tile->getElevationLayer()); if (heightFieldLayer) { - //In standard mode, just load the elevation data and dirty the tile. - - if ( _terrainOptions.loadingPolicy()->mode() == LoadingPolicy::MODE_STANDARD ) + // In standard mode, just load the elevation data and dirty the tile. + if ( !_isStreaming ) { osg::ref_ptr hf; if (hasElevation) - _update_mapf->getHeightField( key, true, hf, _terrainOptions.elevationInterpolation().value()); + _update_mapf->getHeightField( key, true, hf, 0L, _terrainOptions.elevationInterpolation().value()); if (!hf.valid()) hf = OSGTileFactory::createEmptyHeightField( key ); @@ -519,39 +577,45 @@ hf->setSkirtHeight( tile->getBound().radius() * _terrainOptions.heightFieldSkirtRatio().value() ); //TODO: review this in favor of a tile update... - tile->setDirty(true); + tile->setDirty( true ); } - else + + else // if ( isStreaming ) { - //In preemptive mode, if there is no elevation, just clear out all the elevation on the tiles - if (!hasElevation) + StreamingTile* stile = static_cast(tile); + + //Update the elevation hint + stile->setHasElevationHint( hasElevation ); + + //In seq/pre mode, if there is no elevation, just clear out all the elevation on the tiles + if ( !hasElevation ) { osg::ref_ptr hf = OSGTileFactory::createEmptyHeightField( key ); heightFieldLayer->setHeightField( hf.get() ); - hf->setSkirtHeight( tile->getBound().radius() * _terrainOptions.heightFieldSkirtRatio().value() ); - tile->setElevationLOD( key.getLevelOfDetail() ); - tile->resetElevationRequests( *_update_mapf ); - tile->queueTileUpdate( TileUpdate::UPDATE_ELEVATION ); + hf->setSkirtHeight( stile->getBound().radius() * _terrainOptions.heightFieldSkirtRatio().value() ); + stile->setElevationLOD( key.getLevelOfDetail() ); + stile->resetElevationRequests( *_update_mapf ); + stile->queueTileUpdate( TileUpdate::UPDATE_ELEVATION ); } else { //Always load the first LOD so the children tiles can have something to use for placeholders - if (tile->getKey().getLevelOfDetail() == 1) + if (stile->getKey().getLevelOfDetail() == 1) { osg::ref_ptr hf; - _update_mapf->getHeightField( key, true, hf, _terrainOptions.elevationInterpolation().value()); + _update_mapf->getHeightField( key, true, hf, 0L, _terrainOptions.elevationInterpolation().value()); if (!hf.valid()) hf = OSGTileFactory::createEmptyHeightField( key ); heightFieldLayer->setHeightField( hf.get() ); - hf->setSkirtHeight( tile->getBound().radius() * _terrainOptions.heightFieldSkirtRatio().value() ); - tile->setElevationLOD(tile->getKey().getLevelOfDetail()); - tile->queueTileUpdate( TileUpdate::UPDATE_ELEVATION ); + hf->setSkirtHeight( stile->getBound().radius() * _terrainOptions.heightFieldSkirtRatio().value() ); + stile->setElevationLOD(tile->getKey().getLevelOfDetail()); + stile->queueTileUpdate( TileUpdate::UPDATE_ELEVATION ); } else { //Set the elevation LOD to -1 - tile->setElevationLOD(-1); - tile->resetElevationRequests( *_update_mapf ); + stile->setElevationLOD(-1); + stile->resetElevationRequests( *_update_mapf ); } } } @@ -565,24 +629,24 @@ if ( !layer || !layer->getTileSource() ) return; - CustomTileVector tiles; - _terrain->getCustomTiles( tiles ); + TileVector tiles; + _terrain->getTiles( tiles ); OE_DEBUG << LC << "Found " << tiles.size() << std::endl; - for (CustomTileVector::iterator itr = tiles.begin(); itr != tiles.end(); ++itr) + for (TileVector::iterator itr = tiles.begin(); itr != tiles.end(); ++itr) { updateElevation( itr->get() ); } } void -OSGTerrainEngineNode::removeElevationLayer( ElevationLayer* layerRemoved, unsigned int index ) +OSGTerrainEngineNode::removeElevationLayer( ElevationLayer* layerRemoved ) { - CustomTileVector tiles; - _terrain->getCustomTiles( tiles ); + TileVector tiles; + _terrain->getTiles( tiles ); - for (CustomTileVector::iterator itr = tiles.begin(); itr != tiles.end(); ++itr) + for (TileVector::iterator itr = tiles.begin(); itr != tiles.end(); ++itr) { updateElevation( itr->get() ); } @@ -591,12 +655,12 @@ void OSGTerrainEngineNode::moveElevationLayer( unsigned int oldIndex, unsigned int newIndex ) { - CustomTileVector tiles; - _terrain->getCustomTiles( tiles ); + TileVector tiles; + _terrain->getTiles( tiles ); OE_DEBUG << "Found " << tiles.size() << std::endl; - for (CustomTileVector::iterator itr = tiles.begin(); itr != tiles.end(); ++itr) + for (TileVector::iterator itr = tiles.begin(); itr != tiles.end(); ++itr) { updateElevation( itr->get() ); } @@ -606,12 +670,6 @@ OSGTerrainEngineNode::validateTerrainOptions( TerrainOptions& options ) { TerrainEngineNode::validateTerrainOptions( options ); - - // LOD blending is currently only compatible with STANDARD loading policy - if ( options.lodBlending() == true && options.loadingPolicy()->mode() == LoadingPolicy::MODE_STANDARD ) - { - //options.lodBlending() = false; - } //nop for now. //note: to validate plugin-specific features, we would create an OSGTerrainOptions diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/OSGTerrainOptions osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/OSGTerrainOptions --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/OSGTerrainOptions 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/OSGTerrainOptions 2011-11-04 19:44:43.000000000 +0000 @@ -31,7 +31,8 @@ public: OSGTerrainOptions( const ConfigOptions& options =ConfigOptions() ) : TerrainOptions( options ), _skirtRatio( 0.05 ), - _quickRelease( true ) + _quickRelease( true ), + _lodFallOff( 0.0 ) { setDriver( "osgterrain" ); fromConfig( _conf ); @@ -44,11 +45,15 @@ optional& quickReleaseGLObjects() { return _quickRelease; } const optional& quickReleaseGLObjects() const { return _quickRelease; } + optional& lodFallOff() { return _lodFallOff; } + const optional& lodFallOff() const { return _lodFallOff; } + 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 ); return conf; } @@ -61,10 +66,12 @@ void fromConfig( const Config& conf ) { conf.getIfSet( "skirt_ratio", _skirtRatio ); conf.getIfSet( "quick_release_gl_objects", _quickRelease ); + conf.getIfSet( "lod_fall_off", _lodFallOff ); } optional _skirtRatio; optional _quickRelease; + optional _lodFallOff; }; } } // namespace osgEarth::Drivers diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/OSGTileFactory osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/OSGTileFactory --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/OSGTileFactory 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/OSGTileFactory 2011-11-04 19:44:43.000000000 +0000 @@ -30,7 +30,6 @@ #include #include -#include #include #include #include @@ -38,10 +37,10 @@ using namespace osgEarth; using namespace osgEarth::Drivers; -//class osgEarth::Map; -//class osgEarth::MapFrame; -class CustomTerrain; -class CustomTile; +class Terrain; +class StreamingTerrain; +class Tile; +class StreamingTile; /** * TileFactory is the main workhorse - it generates osg Nodes for TileKeys. @@ -50,8 +49,8 @@ { public: OSGTileFactory( - unsigned int engineId, - const MapFrame& cull_thread_mapf, + unsigned engineId, + const MapFrame& cull_thread_mapf, const OSGTerrainOptions& props =OSGTerrainOptions() ); public: @@ -60,34 +59,34 @@ * subkeys of the provided TileKey. */ osg::Node* createSubTiles( - const MapFrame& mapf, - CustomTerrain* terrain, - const TileKey& key, - bool populateLayers ); + const MapFrame& mapf, + Terrain* terrain, + const TileKey& key, + bool populateLayers ); /** * Creates a single terrain tile corresponding to the provided TileKey. */ osg::Node* createTile( - const MapFrame& mapf, - CustomTerrain* terrain, - const TileKey& key, - bool populateLayers, - bool wrapInPagedLOD, - bool fallback, - bool &validData); + const MapFrame& mapf, + Terrain* terrain, + const TileKey& key, + bool populateLayers, + bool wrapInPagedLOD, + bool fallback, + bool& out_validData); CustomColorLayerRef* createImageLayer( - const MapInfo& mapInfo, - ImageLayer* layer, - const TileKey& key, + const MapInfo& mapInfo, + ImageLayer* layer, + const TileKey& key, ProgressCallback* progress); osgTerrain::HeightFieldLayer* createHeightFieldLayer( const MapFrame& mapf, - const TileKey& key, - bool exactOnly ); + const TileKey& key, + bool exactOnly ); /** * Gets the properties that customize how this engine renders tile data. @@ -95,28 +94,24 @@ const OSGTerrainOptions& getTerrainOptions() const; /** - * Returns true if all the data needed to contruct a tile from the specified tile key is - * in a cache. - */ - bool isCached( const MapFrame& mapf, const TileKey& key ) const; - - /** - * Returns true is data for all the key's child tiles is cached. - */ - bool areChildrenCached( const Map* map, const TileKey& parentKey ) const; - - /** * Gets a pagedLOD child URI given a tile key. */ std::string createURI( unsigned int id, const TileKey& key ); - + /** + * Wraps a tile in a paged LOD and setup up all its parameters + */ + osg::Node* prepareTile( + Tile* tile, + Terrain* terrain, + const MapInfo& mapInfo, + bool wrapInPagedLOD ); bool createValidGeoImage( - ImageLayer* layer, - const TileKey& key, - GeoImage& out_image, - TileKey& out_actualTileKey, + ImageLayer* layer, + const TileKey& key, + GeoImage& out_image, + TileKey& out_actualTileKey, ProgressCallback* progress = 0); osg::Matrixd getTransformFromExtents(double minX, double minY, double maxX, double maxY) const; @@ -126,65 +121,49 @@ static osg::HeightField* createEmptyHeightField( const TileKey& key, - int numCols =8, - int numRows =8 ); - + int numCols =8, + int numRows =8 ); osgTerrain::HeightFieldLayer* createPlaceholderHeightfieldLayer( osg::HeightField* ancestorHF, - const TileKey& ancestorKey, - const TileKey& key, - GeoLocator* locator ); - - bool createLodBlendedImage( - UID layerUID, - const TileKey& key, - const osg::Image* tileImage, - CustomTerrain* terrain, - osg::ref_ptr& output); + const TileKey& ancestorKey, + const TileKey& key, + GeoLocator* locator ); protected: - //virtual ~MapEngine(); - - osg::Node* createPlaceholderTile( - const MapFrame& mapf, - CustomTerrain* terrain, - const TileKey& key ); + const MapFrame& mapf, + StreamingTerrain* terrain, + const TileKey& key ); osg::Node* createPopulatedTile( - const MapFrame& mapf, - CustomTerrain* terrain, - const TileKey& key, - bool wrapInPagedLOD, - bool fallback, - bool &validData); - - void addPlaceholderImageLayers( - CustomTile* tile, - CustomTile* ancestorTile, - const ImageLayerVector& imageLayers, - GeoLocator* defaultLocator, - const TileKey& key); + const MapFrame& mapf, + Terrain* terrain, + const TileKey& key, + bool wrapInPagedLOD, + bool fallback, + bool& out_validData); + + void addPlaceholderImageLayers( Tile* tile, Tile* ancestorTile ); void addPlaceholderHeightfieldLayer( - CustomTile* tile, - CustomTile* ancestorTile, - GeoLocator* defaultLocator, + StreamingTile* tile, + StreamingTile* ancestorTile, + GeoLocator* defaultLocator, const TileKey& key, const TileKey& ancestorKey ); osg::ClusterCullingCallback* createClusterCullingCallback( - osgTerrain::TerrainTile* tile, + Tile* tile, osg::EllipsoidModel* et ); void init(); protected: - unsigned int _engineId; - const MapFrame& _cull_thread_mapf; + unsigned _engineId; + const MapFrame& _cull_thread_mapf; OSGTerrainOptions _terrainOptions; }; diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/OSGTileFactory.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/OSGTileFactory.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/OSGTileFactory.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/OSGTileFactory.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -17,9 +17,9 @@ * along with this program. If not, see */ #include "OSGTileFactory" -#include "CustomTerrain" +#include "Terrain" +#include "StreamingTerrain" #include "FileLocationCallback" -#include "TerrainTileEdgeNormalizerUpdateCallback" #include "TransparentLayer" #include @@ -37,8 +37,6 @@ #include #include #include -#include -#include #include #include #include @@ -56,9 +54,9 @@ namespace { //TODO: get rid of this, and move it to the CustomTerrain CULL traversal....????? - struct PopulateTileDataCallback : public osg::NodeCallback + struct PopulateStreamingTileDataCallback : public osg::NodeCallback { - PopulateTileDataCallback( const MapFrame& mapf ) : _mapf(mapf) { } + PopulateStreamingTileDataCallback( const MapFrame& mapf ) : _mapf(mapf) { } void operator()( osg::Node* node, osg::NodeVisitor* nv ) { @@ -66,11 +64,8 @@ { if ( node->asGroup()->getNumChildren() > 0 ) { - CustomTile* tile = static_cast( node->asGroup()->getChild(0) ); - if ( tile && tile->getUseLayerRequests() ) - { - tile->servicePendingImageRequests( _mapf, nv->getFrameStamp()->getFrameNumber() ); - } + StreamingTile* tile = static_cast( node->asGroup()->getChild(0) ); + tile->servicePendingImageRequests( _mapf, nv->getFrameStamp()->getFrameNumber() ); } } traverse( node, nv ); @@ -91,12 +86,6 @@ _terrainOptions( props ) { LoadingPolicy::Mode mode = _terrainOptions.loadingPolicy()->mode().value(); - - OE_INFO << LC << "Loading policy mode = " << - ( mode == LoadingPolicy::MODE_PREEMPTIVE ? "PREEMPTIVE" : - mode == LoadingPolicy::MODE_SEQUENTIAL ? "SEQUENTIAL" : - "STANDARD" ) - << std::endl; } const OSGTerrainOptions& @@ -131,7 +120,7 @@ } osg::Node* -OSGTileFactory::createSubTiles( const MapFrame& mapf, CustomTerrain* terrain, const TileKey& key, bool populateLayers ) +OSGTileFactory::createSubTiles( const MapFrame& mapf, Terrain* terrain, const TileKey& key, bool populateLayers ) { TileKey k0 = key.createChildKey(0); TileKey k1 = key.createChildKey(1); @@ -258,91 +247,6 @@ return more_levels; } -bool -OSGTileFactory::areChildrenCached( const Map* map, const TileKey& parentKey ) const -{ - MapFrame mapf( map ); - - //Split the key into it's 4 children and check to see if all 4 are cached. - for( unsigned int i=0; i<4; ++i ) - { - if ( !isCached( mapf, parentKey.createChildKey(i) ) ) - return false; - } - - return true; -} - -bool -OSGTileFactory::isCached( const MapFrame& mapf, const osgEarth::TileKey& key ) const -{ - const Profile* mapProfile = mapf.getProfile(); - - //Check the imagery layers - for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); i++ ) - { - ImageLayer* layer = i->get(); - osg::ref_ptr< Cache > cache = layer->getCache(); - - if ( !cache.valid() || !layer->getProfile() ) - return false; - - std::vector< TileKey > keys; - - if ( mapProfile->isEquivalentTo( layer->getProfile() ) ) - { - keys.push_back( key ); - } - else - { - layer->getProfile()->getIntersectingTiles( key, keys ); - } - - for (unsigned int j = 0; j < keys.size(); ++j) - { - if ( layer->isKeyValid( keys[j] ) ) - { - if ( !cache->isCached( keys[j], layer->getCacheSpec() ) ) - { - return false; - } - } - } - } - - for( ElevationLayerVector::const_iterator i =mapf.elevationLayers().begin(); i != mapf.elevationLayers().end(); ++i ) - { - ElevationLayer* layer = i->get(); - osg::ref_ptr< Cache > cache = layer->getCache(); - - if ( !cache.valid() || !layer->getProfile() ) - return false; - - std::vector keys; - - if ( mapProfile->isEquivalentTo( layer->getProfile() ) ) - { - keys.push_back( key ); - } - else - { - layer->getProfile()->getIntersectingTiles( key, keys ); - } - - for (unsigned int j = 0; j < keys.size(); ++j) - { - if ( layer->isKeyValid( keys[j] ) ) - { - if ( !cache->isCached( keys[j], layer->getCacheSpec() ) ) - { - return false; - } - } - } - } - return true; -} - osg::HeightField* OSGTileFactory::createEmptyHeightField( const TileKey& key, int numCols, int numRows ) { @@ -353,11 +257,7 @@ } void -OSGTileFactory::addPlaceholderImageLayers(CustomTile* tile, - CustomTile* ancestorTile, - const ImageLayerVector& imageLayers, - GeoLocator* defaultLocator, - const TileKey& key) +OSGTileFactory::addPlaceholderImageLayers(Tile* tile, Tile* ancestorTile ) { if ( !ancestorTile ) { @@ -375,9 +275,9 @@ void -OSGTileFactory::addPlaceholderHeightfieldLayer(CustomTile* tile, - CustomTile* ancestorTile, - GeoLocator* defaultLocator, +OSGTileFactory::addPlaceholderHeightfieldLayer(StreamingTile* tile, + StreamingTile* ancestorTile, + GeoLocator* defaultLocator, const TileKey& key, const TileKey& ancestorKey) { @@ -436,9 +336,9 @@ osgTerrain::HeightFieldLayer* OSGTileFactory::createPlaceholderHeightfieldLayer(osg::HeightField* ancestorHF, - const TileKey& ancestorKey, - const TileKey& key, - GeoLocator* keyLocator ) + const TileKey& ancestorKey, + const TileKey& key, + GeoLocator* keyLocator ) { osgTerrain::HeightFieldLayer* hfLayer = NULL; @@ -456,38 +356,45 @@ } osg::Node* -OSGTileFactory::createTile(const MapFrame& mapf, CustomTerrain* terrain, - const TileKey& key, bool populateLayers, - bool wrapInPagedLOD, bool fallback, - bool& validData ) +OSGTileFactory::createTile(const MapFrame& mapf, + Terrain* terrain, + const TileKey& key, + bool populateLayers, + bool wrapInPagedLOD, + bool fallback, + bool& out_validData ) { if ( populateLayers ) { - return createPopulatedTile( mapf, terrain, key, wrapInPagedLOD, fallback, validData); + return createPopulatedTile( mapf, terrain, key, wrapInPagedLOD, fallback, out_validData); } else { //Placeholders always contain valid data - validData = true; - return createPlaceholderTile( mapf, terrain, key ); + out_validData = true; + + return createPlaceholderTile( + mapf, + static_cast(terrain), + key ); } } osg::Node* -OSGTileFactory::createPlaceholderTile(const MapFrame& mapf, - CustomTerrain* terrain, - const TileKey& key ) +OSGTileFactory::createPlaceholderTile(const MapFrame& mapf, + StreamingTerrain* terrain, + const TileKey& key ) { // Start out by finding the nearest registered ancestor tile, since the placeholder is // going to be based on inherited data. Note- the ancestor may not be the immediate // parent, b/c the parent may or may not be in the scene graph. TileKey ancestorKey = key.createParentKey(); - osg::ref_ptr ancestorTile; + osg::ref_ptr ancestorTile; while( !ancestorTile.valid() && ancestorKey.valid() ) { - terrain->getCustomTile( ancestorKey.getTileId(), ancestorTile ); + terrain->getTile( ancestorKey.getTileId(), ancestorTile ); if ( !ancestorTile.valid() ) ancestorKey = ancestorKey.createParentKey(); } @@ -511,12 +418,11 @@ osg::ref_ptr locator = GeoLocator::createForKey( key, mapInfo ); // The empty tile: - CustomTile* tile = new CustomTile( key, locator.get(), terrain->getQuickReleaseGLObjects() ); - tile->setTerrainTechnique( osg::clone(terrain->getTerrainTechniquePrototype(), osg::CopyOp::DEEP_COPY_ALL) ); + StreamingTile* tile = new StreamingTile( key, locator.get(), terrain->getQuickReleaseGLObjects() ); + tile->setTerrainTechnique( terrain->cloneTechnique() ); tile->setVerticalScale( _terrainOptions.verticalScale().value() ); - tile->setRequiresNormals( true ); tile->setDataVariance( osg::Object::DYNAMIC ); - tile->setLocator( locator.get() ); + //tile->setLocator( locator.get() ); // Attach an updatecallback to normalize the edges of TerrainTiles. #if 0 @@ -531,7 +437,7 @@ // ancestor tile. { //Threading::ScopedReadLock parentLock( ancestorTile->getTileLayersMutex() ); - addPlaceholderImageLayers( tile, ancestorTile.get(), mapf.imageLayers(), locator.get(), key ); + addPlaceholderImageLayers ( tile, ancestorTile.get() ); addPlaceholderHeightfieldLayer( tile, ancestorTile.get(), locator.get(), key, ancestorKey ); } @@ -558,18 +464,16 @@ if ( _terrainOptions.loadingPolicy()->mode().get() != LoadingPolicy::MODE_STANDARD ) { markTileLoaded = true; - tile->setUseLayerRequests( true ); tile->setHasElevationHint( hasElevation ); } // install a tile switcher: - tile->setTerrainRevision( terrain->getRevision() ); - tile->setTerrain( terrain ); - terrain->registerTile( tile ); + tile->attachToTerrain( terrain ); + //tile->setTerrain( terrain ); + //terrain->registerTile( tile ); osg::Node* result = 0L; - // create a PLOD so we can keep subdividing: osg::PagedLOD* plod = new osg::PagedLOD(); plod->setCenter( bs.center() ); @@ -585,7 +489,7 @@ plod->setRange( 0, 0, FLT_MAX ); } -#if USE_FILELOCATIONCALLBACK +#if 0 //USE_FILELOCATIONCALLBACK osgDB::Options* options = new osgDB::Options; options->setFileLocationCallback( new FileLocationCallback); plod->setDatabaseOptions( options ); @@ -594,7 +498,7 @@ result = plod; // Install a callback that will load the actual tile data via the pager. - result->addCullCallback( new PopulateTileDataCallback( _cull_thread_mapf ) ); + result->addCullCallback( new PopulateStreamingTileDataCallback( _cull_thread_mapf ) ); // Install a cluster culler (FIXME for cube mode) //bool isCube = map->getMapOptions().coordSysType() == MapOptions::CSTYPE_GEOCENTRIC_CUBE; @@ -618,10 +522,14 @@ }; } + osg::Node* -OSGTileFactory::createPopulatedTile(const MapFrame& mapf, CustomTerrain* terrain, - const TileKey& key, bool wrapInPagedLOD, - bool fallback, bool& validData ) +OSGTileFactory::createPopulatedTile(const MapFrame& mapf, + Terrain* terrain, + const TileKey& key, + bool wrapInPagedLOD, + bool fallback, + bool& validData ) { const MapInfo& mapInfo = mapf.getMapInfo(); bool isPlateCarre = !mapInfo.isGeocentric() && mapInfo.isGeographicSRS(); @@ -657,7 +565,7 @@ osg::ref_ptr hf; if ( mapf.elevationLayers().size() > 0 ) { - mapf.getHeightField( key, false, hf, _terrainOptions.elevationInterpolation().value()); + mapf.getHeightField( key, false, hf, 0L, _terrainOptions.elevationInterpolation().value()); } //If we are on the first LOD and we couldn't get a heightfield tile, just create an empty one. Otherwise you can run into the situation @@ -725,7 +633,7 @@ else { //Try to get a heightfield again, but this time fallback on parent tiles - if ( mapf.getHeightField( key, true, hf, _terrainOptions.elevationInterpolation().value() ) ) + if ( mapf.getHeightField( key, true, hf, 0L, _terrainOptions.elevationInterpolation().value() ) ) { hasElevation = true; } @@ -749,12 +657,16 @@ hf_layer->setLocator( locator.get() ); hf_layer->setHeightField( hf.get() ); - CustomTile* tile = new CustomTile( key, locator.get(), terrain->getQuickReleaseGLObjects() ); - tile->setTerrainTechnique( osg::clone(terrain->getTerrainTechniquePrototype(), osg::CopyOp::DEEP_COPY_ALL) ); + bool isStreaming = + _terrainOptions.loadingPolicy()->mode() == LoadingPolicy::MODE_SEQUENTIAL || + _terrainOptions.loadingPolicy()->mode() == LoadingPolicy::MODE_PREEMPTIVE; + + Tile* tile = terrain->createTile( key, locator.get() ); + tile->setTerrainTechnique( terrain->cloneTechnique() ); tile->setVerticalScale( _terrainOptions.verticalScale().value() ); - tile->setLocator( locator.get() ); + //tile->setLocator( locator.get() ); tile->setElevationLayer( hf_layer ); - tile->setRequiresNormals( true ); + //tile->setRequiresNormals( true ); tile->setDataVariance(osg::Object::DYNAMIC); #if 0 @@ -804,30 +716,12 @@ if ( mapInfo.isGeocentric() ) img_locator->setCoordinateSystemType( osgTerrain::Locator::GEOCENTRIC ); - // if blending is on, AND if actual new data was loaded (instead of just reusing a parent tile), - // create a custom blended image. - if ( _terrainOptions.lodBlending() == true && key == image_tiles[i]._imageTileKey ) - { - osg::ref_ptr blendedImage; - if ( ! createLodBlendedImage( image_tiles[i]._layerUID, key, geo_image.getImage(), terrain, blendedImage ) ) - { - blendedImage = geo_image.getImage(); - } - - tile->setCustomColorLayer( CustomColorLayer( - mapf.getImageLayerAt(i), - blendedImage.get(), - img_locator.get(), - key.getLevelOfDetail() ) ); - } - else - { - tile->setCustomColorLayer( CustomColorLayer( - mapf.getImageLayerAt(i), - geo_image.getImage(), - img_locator.get(), - key.getLevelOfDetail() ) ); - } + tile->setCustomColorLayer( CustomColorLayer( + mapf.getImageLayerAt(i), + geo_image.getImage(), + img_locator.get(), + key.getLevelOfDetail(), + key) ); double upp = geo_image.getUnitsPerPixel(); @@ -872,21 +766,15 @@ // // If there's already a placeholder tile registered, this will be ignored. If there isn't, // this will register the new tile. - tile->setTerrain( terrain ); - terrain->registerTile( tile ); - - // Set the tile's revision to the current terrain revision - tile->setTerrainRevision( static_cast(terrain)->getRevision() ); + tile->attachToTerrain( terrain ); + //tile->setTerrain( terrain ); + //terrain->registerTile( tile ); - if ( _terrainOptions.loadingPolicy()->mode() != LoadingPolicy::MODE_STANDARD && key.getLevelOfDetail()) + if ( isStreaming && key.getLevelOfDetail() > 0 ) { - tile->setUseLayerRequests( true ); - tile->setHasElevationHint( hasElevation ); + static_cast(tile)->setHasElevationHint( hasElevation ); } - tile->setTerrainRevision( terrain->getRevision() ); - tile->setDataVariance( osg::Object::DYNAMIC ); - osg::Node* result = 0L; if (wrapInPagedLOD) @@ -917,8 +805,8 @@ #endif result = plod; - if ( tile->getUseLayerRequests() ) - result->addCullCallback( new PopulateTileDataCallback( _cull_thread_mapf ) ); + if ( isStreaming ) + result->addCullCallback( new PopulateStreamingTileDataCallback( _cull_thread_mapf ) ); } else { @@ -928,55 +816,15 @@ return result; } -bool -OSGTileFactory::createLodBlendedImage(UID layerUID, const TileKey& key, - const osg::Image* tileImage, - CustomTerrain* terrain, - osg::ref_ptr& output) -{ - TileKey parentKey = key.createParentKey(); - if ( parentKey.valid() ) - { - osg::ref_ptr parentTile; - terrain->getCustomTile( parentKey.getTileId(), parentTile ); - if ( parentTile.valid() ) - { - CustomColorLayer parentLayer; - if ( parentTile->getCustomColorLayer( layerUID, parentLayer ) ) - { - if ( parentLayer.getImage() && parentLayer.getLocator() ) - { - GeoImage parentGI( - const_cast( parentLayer.getImage() ), - static_cast(parentLayer.getLocator())->getDataExtent() ); - - GeoImage cropped = parentGI.crop( key.getExtent() ); - - osg::ref_ptr parentImage; - ImageUtils::resizeImage( - cropped.getImage(), - tileImage->s(), - tileImage->t(), - parentImage ); - - output = ImageUtils::createMipmapBlendedImage( - tileImage, - parentImage.get() ); - - return true; - } - } - } - } - return false; -} - CustomColorLayerRef* -OSGTileFactory::createImageLayer(const MapInfo& mapInfo, - ImageLayer* layer, - const TileKey& key, +OSGTileFactory::createImageLayer(const MapInfo& mapInfo, + ImageLayer* layer, + const TileKey& key, ProgressCallback* progress) { + if ( !layer ) + return 0L; + GeoImage geoImage; //If the key is valid, try to get the image from the MapLayer @@ -998,33 +846,12 @@ if ( mapInfo.isGeocentric() ) imgLocator->setCoordinateSystemType( osgTerrain::Locator::GEOCENTRIC ); -#if 0 - // since this method is only called in SEQ/PRE mode, we don't need to bother - // supporting LOD blending here. - - if ( keyValid && _terrainOptions.lodBlending() == true ) - { - osg::ref_ptr blendedImage; - createLodBlendedImage( layer->getUID(), key, geoImage.getImage(), terrain, blendedImage ); - - return new CustomColorLayerRef( CustomColorLayer( - layer, - blendedImage.get(), - imgLocator.get(), - key.getLevelOfDetail() ) ); - } - else -#endif - { - return new CustomColorLayerRef( CustomColorLayer( - layer, - geoImage.getImage(), - imgLocator.get(), - key.getLevelOfDetail() ) ); - } - - //CustomColorLayer result( layer, geoImage.getImage(), imgLocator.get(), key.getLevelOfDetail() ); - //return new CustomColorLayerRef( result ); + return new CustomColorLayerRef( CustomColorLayer( + layer, + geoImage.getImage(), + imgLocator.get(), + key.getLevelOfDetail(), + key) ); } return NULL; @@ -1038,7 +865,7 @@ // try to create a heightfield at native res: osg::ref_ptr hf; - if ( !mapf.getHeightField( key, !exactOnly, hf, _terrainOptions.elevationInterpolation().value() ) ) + if ( !mapf.getHeightField( key, !exactOnly, hf, 0L, _terrainOptions.elevationInterpolation().value() ) ) { if ( exactOnly ) return NULL; @@ -1049,7 +876,7 @@ // In a Plate Carre tesselation, scale the heightfield elevations from meters to degrees if ( isPlateCarre ) { - HeightFieldUtils::scaleHeightFieldToDegrees( hf ); + HeightFieldUtils::scaleHeightFieldToDegrees( hf.get() ); } osgTerrain::HeightFieldLayer* hfLayer = new osgTerrain::HeightFieldLayer( hf.get() ); @@ -1061,14 +888,14 @@ } osg::ClusterCullingCallback* -OSGTileFactory::createClusterCullingCallback(osgTerrain::TerrainTile* tile, osg::EllipsoidModel* et) +OSGTileFactory::createClusterCullingCallback(Tile* tile, osg::EllipsoidModel* et) { //This code is a very slightly modified version of the DestinationTile::createClusterCullingCallback in VirtualPlanetBuilder. osg::HeightField* grid = ((osgTerrain::HeightFieldLayer*)tile->getElevationLayer())->getHeightField(); if (!grid) return 0; float verticalScale = 1.0f; - CustomTile* customTile = dynamic_cast(tile); + Tile* customTile = dynamic_cast(tile); if (customTile) { verticalScale = customTile->getVerticalScale(); diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/ParallelKeyNodeFactory osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/ParallelKeyNodeFactory --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/ParallelKeyNodeFactory 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/ParallelKeyNodeFactory 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,40 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph +* Copyright 2008-2010 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 +*/ +#ifndef OSGEARTH_ENGINE_PARALLEL_KEY_NODE_FACTORY +#define OSGEARTH_ENGINE_PARALLEL_KEY_NODE_FACTORY 1 + +#include "Common" +#include "SerialKeyNodeFactory" + +using namespace osgEarth; + +class ParallelKeyNodeFactory : public SerialKeyNodeFactory +{ +public: + ParallelKeyNodeFactory( + TileBuilder* builder, + const OSGTerrainOptions& options, + const MapInfo& mapInfo, + Terrain* terrain, + UID engineUID ); + + osg::Node* createNode( const TileKey& key ); +}; + +#endif // OSGEARTH_ENGINE_PARALLEL_KEY_NODE_FACTORY diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/ParallelKeyNodeFactory.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/ParallelKeyNodeFactory.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/ParallelKeyNodeFactory.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/ParallelKeyNodeFactory.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,86 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph +* Copyright 2008-2010 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 +*/ +#include "ParallelKeyNodeFactory" +#include +#include + +using namespace osgEarth; +using namespace OpenThreads; + +#define LC "[ParallelKeyNodeFactory] " + +//-------------------------------------------------------------------------- + +ParallelKeyNodeFactory::ParallelKeyNodeFactory(TileBuilder* builder, + const OSGTerrainOptions& options, + const MapInfo& mapInfo, + Terrain* terrain, + UID engineUID ) : + +SerialKeyNodeFactory( builder, options, mapInfo, terrain, engineUID ) +{ + //NOP +} + +osg::Node* +ParallelKeyNodeFactory::createNode( const TileKey& key ) +{ + // An event for synchronizing the completion of all requests: + Threading::MultiEvent semaphore; + + // Collect all the jobs that can run in parallel (from all 4 subtiles) + osg::ref_ptr jobs[4]; + unsigned numTasks = 0; + for( unsigned i=0; i<4; ++i ) + { + jobs[i] = _builder->createJob( key.createChildKey(i), semaphore ); + if ( jobs[i].valid() ) + numTasks += jobs[i]->_tasks.size(); + } + + // Set up the sempahore to block for the correct number of tasks: + semaphore.reset( numTasks ); + + // Run all the tasks in parallel: + for( unsigned i=0; i<4; ++i ) + if ( jobs[i].valid() ) + _builder->runJob( jobs[i].get() ); + + // Wait for them to complete: + semaphore.wait(); + + // Now postprocess them and assemble into a tile group. + osg::Group* root = new osg::Group(); + + for( unsigned i=0; i<4; ++i ) + { + if ( jobs[i].valid() ) + { + osg::ref_ptr tile; + bool hasRealData; + bool hasLodBlending; + _builder->finalizeJob( jobs[i].get(), tile, hasRealData, hasLodBlending ); + if ( tile.valid() ) + addTile( tile.get(), hasRealData, hasLodBlending, root ); + } + } + + //TODO: need to check to see if the group is empty, and do something different. + return root; +} diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/Plugin.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/Plugin.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/Plugin.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/Plugin.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -24,6 +24,8 @@ #include #include +#define LC "[osgterrain_engine Plugin] " + using namespace osgEarth::Drivers; class OSGTerrainEnginePlugin : public osgDB::ReaderWriter @@ -40,15 +42,14 @@ { return osgDB::equalCaseInsensitive( extension, "osgearth_engine_osgterrain" ) || - osgDB::equalCaseInsensitive( extension, "osgearth_osgterrain_tile" ) || - osgDB::equalCaseInsensitive( extension, "test_osgearth_engine_osgterrain" ); + osgDB::equalCaseInsensitive( extension, "osgearth_osgterrain_tile" ); } virtual ReadResult readObject(const std::string& uri, const Options* options) const { if ( "osgearth_engine_osgterrain" == osgDB::getFileExtension( uri ) ) { - if ( "earth" == osgDB::getNameLessExtension( osgDB::getFileExtension( uri ) ) ) + if ( "earth" == osgDB::getNameLessExtension( osgDB::getFileExtension( uri ) ) ) { return readNode( uri, options ); } @@ -62,67 +63,64 @@ { return readNode( uri, options ); } - } + } virtual ReadResult readNode(const std::string& uri, const Options* options) const { - // Handler for PagedLOD targets created by the OSGTileFactory: - if ( "osgearth_osgterrain_tile" == osgDB::getFileExtension( uri ) ) - { - //See if the filename starts with server: and strip it off. This will trick OSG into passing in the filename to our plugin - //instead of using the CURL plugin if the filename contains a URL. So, if you want to read a URL, you can use the following format - //osgDB::readNodeFile("server:http://myserver/myearth.earth"). This should only be necessary for the first level as the other files will have - //a tilekey prepended to them. + static int s_tileCount = 0; + static double s_tileTime = 0.0; + static osg::Timer_t s_startTime; + + if ( "osgearth_osgterrain_tile" == osgDB::getFileExtension(uri) ) + { + if ( s_tileCount == 0 ) + s_startTime = osg::Timer::instance()->tick(); + + // See if the filename starts with server: and strip it off. This will trick OSG + // into passing in the filename to our plugin instead of using the CURL plugin if + // the filename contains a URL. So, if you want to read a URL, you can use the + // following format: osgDB::readNodeFile("server:http://myserver/myearth.earth"). + // This should only be necessary for the first level as the other files will have + // a tilekey prepended to them. if ((uri.length() > 7) && (uri.substr(0, 7) == "server:")) - { return readNode(uri.substr(7), options); - } - - osg::Node* node = 0; + // parse the tile key and engine ID: std::string tileDef = osgDB::getNameLessExtension(uri); + unsigned int lod, x, y, engineID; + sscanf(tileDef.c_str(), "%d_%d_%d.%d", &lod, &x, &y, &engineID); - unsigned int lod, x, y, id; - sscanf(tileDef.c_str(), "%d_%d_%d.%d", &lod, &x, &y, &id); - - //Get the Map from the cache. It is important that we use a ref_ptr here - //to prevent the Map from being deleted while it is is still in use. - //osg::ref_ptr engine = MapEngine::getMapEngineById( id ); - osg::ref_ptr engineNode = OSGTerrainEngineNode::getEngineByUID( (UID)id ); - + // find the appropriate engine: + osg::ref_ptr engineNode; + OSGTerrainEngineNode::getEngineByUID( (UID)engineID, engineNode ); if ( engineNode.valid() ) { + osg::Timer_t start = osg::Timer::instance()->tick(); + + // assemble the key and create the node: const Profile* profile = engineNode->getMap()->getProfile(); TileKey key( lod, x, y, profile ); - - bool populateLayers = engineNode->getTileFactory()->getTerrainOptions().loadingPolicy()->mode() - == LoadingPolicy::MODE_STANDARD; - - // create a map frame so we can safely create tiles from this dbpager thread - MapFrame mapf( engineNode->getMap(), Map::TERRAIN_LAYERS, "dbpager::earth plugin" ); - - node = engineNode->getTileFactory()->createSubTiles( - mapf, - engineNode->getTerrain(), - key, - populateLayers ); - - //Blacklist the tile if we couldn't load it - if (!node) + osg::Node* node = engineNode->createNode( key ); + + // Blacklist the tile if we couldn't load it + if ( !node ) { - OE_DEBUG << "Blacklisting " << uri << std::endl; - osgEarth::Registry::instance()->blacklist(uri); + OE_DEBUG << LC << "Blacklisting " << uri << std::endl; + osgEarth::Registry::instance()->blacklist( uri ); + return ReadResult::FILE_NOT_FOUND; } + + return ReadResult( node, ReadResult::FILE_LOADED ); } else { - OE_NOTICE << "Error: Could not find Map with id=" << id << std::endl; + return ReadResult::FILE_NOT_FOUND; } - - return node ? ReadResult(node) : ReadResult::FILE_NOT_FOUND; } - - return ReadResult::FILE_NOT_HANDLED; + else + { + return ReadResult::FILE_NOT_HANDLED; + } } }; diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/SerialKeyNodeFactory osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/SerialKeyNodeFactory --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/SerialKeyNodeFactory 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/SerialKeyNodeFactory 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,51 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph +* Copyright 2008-2010 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 +*/ +#ifndef OSGEARTH_ENGINE_SERIAL_KEY_NODE_FACTORY +#define OSGEARTH_ENGINE_SERIAL_KEY_NODE_FACTORY 1 + +#include "Common" +#include "KeyNodeFactory" +#include "Terrain" +#include "TileBuilder" + +using namespace osgEarth; + +class SerialKeyNodeFactory : public KeyNodeFactory +{ +public: + SerialKeyNodeFactory( + TileBuilder* builder, + const OSGTerrainOptions& options, + const MapInfo& mapInfo, + Terrain* terrain, + UID engineUID ); + + osg::Node* createNode( const TileKey& key ); + +protected: + void addTile(Tile* tile, bool tileHasRealData, bool tileHasLodBlending, osg::Group* parent ); + + TileBuilder* _builder; + const OSGTerrainOptions& _options; + const MapInfo _mapInfo; + Terrain* _terrain; + UID _engineUID; +}; + +#endif // OSGEARTH_ENGINE_PARALLEL_KEY_NODE_FACTORY diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/SerialKeyNodeFactory.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/SerialKeyNodeFactory.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/SerialKeyNodeFactory.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/SerialKeyNodeFactory.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,156 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph +* Copyright 2008-2010 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 +*/ +#include "SerialKeyNodeFactory" +#include "DynamicLODScaleCallback" +#include "FileLocationCallback" +#include "LODFactorCallback" +#include +#include +#include +#include + +#include + +using namespace osgEarth; +using namespace OpenThreads; + +#define LC "[SerialKeyNodeFactory] " + +SerialKeyNodeFactory::SerialKeyNodeFactory(TileBuilder* builder, + const OSGTerrainOptions& options, + const MapInfo& mapInfo, + Terrain* terrain, + UID engineUID ) : +_builder( builder ), +_options( options ), +_mapInfo( mapInfo ), +_terrain( terrain ), +_engineUID( engineUID ) +{ + //NOP +} + +void +SerialKeyNodeFactory::addTile(Tile* tile, bool tileHasRealData, bool tileHasLodBlending, osg::Group* parent ) +{ + // associate this tile with the terrain: + tile->setTerrainTechnique( _terrain->cloneTechnique() ); + tile->attachToTerrain( _terrain ); + + // assemble a URI for this tile's child group: + std::stringstream buf; + buf << tile->getKey().str() << "." << _engineUID << ".osgearth_osgterrain_tile"; + std::string uri; uri = buf.str(); + + osg::Node* result = 0L; + + // Only add the next tile if it hasn't been blacklisted + bool wrapInPagedLOD = + tileHasRealData && + !osgEarth::Registry::instance()->isBlacklisted( uri ) && + tile->getKey().getLevelOfDetail() < (unsigned)*_options.maxLOD(); + + if ( wrapInPagedLOD ) + { + osg::BoundingSphere bs = tile->getBound(); + double maxRange = 1e10; + double minRange = bs.radius() * _options.minTileRangeFactor().value(); + + // create a PLOD so we can keep subdividing: + osg::PagedLOD* plod = new osg::PagedLOD(); + plod->setCenter( bs.center() ); + plod->addChild( tile, minRange, maxRange ); + + plod->setFileName( 1, uri ); + plod->setRange ( 1, 0, minRange ); + + plod->setUserData( new MapNode::TileRangeData(minRange, maxRange) ); + +#if USE_FILELOCATIONCALLBACK + osgDB::Options* options = new osgDB::Options; + options->setFileLocationCallback( new FileLocationCallback() ); + plod->setDatabaseOptions( options ); + +#endif + result = plod; + + if ( tileHasLodBlending ) + { + // Make the LOD transition distance, and a measure of how + // close the tile is to an LOD change, to shaders. + result->addCullCallback(new Drivers::LODFactorCallback); + } + } + else + { + result = tile; + } + + // this cull callback dynamically adjusts the LOD scale based on distance-to-camera: + if ( _options.lodFallOff().isSet() && *_options.lodFallOff() > 0.0 ) + { + result->addCullCallback( new DynamicLODScaleCallback(*_options.lodFallOff()) ); + } + + // this one rejects back-facing tiles: + if ( _mapInfo.isGeocentric() ) + { + result->addCullCallback( HeightFieldUtils::createClusterCullingCallback( + static_cast(tile->getElevationLayer())->getHeightField(), + tile->getLocator()->getEllipsoidModel(), + tile->getVerticalScale() ) ); + } + + parent->addChild( result ); +} + +osg::Node* +SerialKeyNodeFactory::createNode( const TileKey& key ) +{ + osg::ref_ptr tiles[4]; + bool realData[4]; + bool lodBlending[4]; + bool tileHasAnyRealData = false; + + for( unsigned i = 0; i < 4; ++i ) + { + TileKey child = key.createChildKey( i ); + _builder->createTile( child, false, tiles[i], realData[i], lodBlending[i] ); + if ( tiles[i].valid() && realData[i] ) + tileHasAnyRealData = true; + } + + osg::Group* root = 0L; + + if ( tileHasAnyRealData ) + { + // Now postprocess them and assemble into a tile group. + root = new osg::Group(); + + for( unsigned i = 0; i < 4; ++i ) + { + if ( tiles[i].valid() ) + { + addTile( tiles[i].get(), realData[i], lodBlending[i], root ); + } + } + } + + return root; +} diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/SinglePassTerrainTechnique osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/SinglePassTerrainTechnique --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/SinglePassTerrainTechnique 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/SinglePassTerrainTechnique 2011-11-04 19:44:43.000000000 +0000 @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -35,9 +36,13 @@ #include #include #include +#include #include +class Tile; +class TileFrame; + // -------------------------------------------------------------------------- /** @@ -61,15 +66,22 @@ SinglePassTerrainTechnique( TextureCompositor* compositor =0L ); //osgTerrain::Locator* locator =0L ); SinglePassTerrainTechnique(const SinglePassTerrainTechnique&,const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY); - META_Object(osgEarth, SinglePassTerrainTechnique); + META_Object( osgEarth, SinglePassTerrainTechnique ); public: /* overrides */ + + virtual void init(); + +#if 0 #if OSG_MIN_VERSION_REQUIRED(2,9,8) virtual void init(int dirtyMask, bool assumeMultiThreaded); #else virtual void init(); #endif +#endif + + void setParentTile( Tile* tile ) { _parentTile = tile; } void compile( const TileUpdate& updateSpec, ProgressCallback* progress ); @@ -77,7 +89,7 @@ bool applyTileUpdates(); /** Traverse the terrain subgraph.*/ - virtual void traverse(osg::NodeVisitor& nv); + virtual void traverse( osg::NodeVisitor& nv ); public: @@ -105,6 +117,9 @@ * for all graphics contexts. */ virtual void releaseGLObjects(osg::State* = 0) const; + osg::StateSet* getActiveStateSet() const; + + protected: void calculateSampling( unsigned int& out_rows, unsigned int& out_cols, double& out_i, double& out_j ); @@ -112,6 +127,7 @@ private: virtual ~SinglePassTerrainTechnique(); + bool _debug; OpenThreads::Mutex _compileMutex; //OpenThreads::Mutex _writeBufferMutex; @@ -126,12 +142,10 @@ bool _pendingFullUpdate; bool _pendingGeometryUpdate; - TileUpdate _lastUpdate; - struct ImageLayerUpdate { GeoImage _image; UID _layerUID; - //int _layerIndex; + bool _isRealData; // versus fallback data }; typedef std::queue ImageLayerUpdates; ImageLayerUpdates _pendingImageLayerUpdates; @@ -140,7 +154,9 @@ typedef std::map< UID, int > LayerUIDtoIndexMap; LayerUIDtoIndexMap _layerUIDtoIndexMap; + // XXX Are these both necessary? GeoExtent _tileExtent; + TileKey _tileKey; bool _optimizeTriangleOrientation; @@ -150,17 +166,22 @@ private: osg::Vec3d computeCenterModel(); - GeoImage createGeoImage( const CustomColorLayer& layer ) const; //const osgTerrain::Layer* imageLayer ) const; - osg::Geode* createGeometry( const CustomTileFrame& tilef ); - osg::StateSet* createStateSet( const CustomTileFrame& tilef ); - void prepareImageLayerUpdate( int layerIndex, const CustomTileFrame& tilef ); + bool createGeoImage( const CustomColorLayer& layer, GeoImage& image ) const; //const osgTerrain::Layer* imageLayer ) const; + osg::Geode* createGeometry( const TileFrame& tilef ); + osg::StateSet* createStateSet( const TileFrame& tilef ); + void prepareImageLayerUpdate( int layerIndex, const TileFrame& tilef ); //Threading::ReadWriteMutex& getMutex(); - inline osg::Geode* getFrontGeode() { return static_cast( _transform->getChild(0) ); } + inline osg::Geode* getFrontGeode() const { + if (_transform.valid() && _transform->getNumChildren() > 0) + return static_cast( _transform->getChild(0) ); + return NULL; + } + osg::StateSet* getParentStateSet() const; //const CustomColorLayer* getLayerByUID( UID uid ) const; int getIndexOfColorLayerWithUID( UID uid ) const; - + osg::observer_ptr _parentTile; }; #endif // OSGEARTH_ENGINE_OSGTERRAIN_SINGLE_PASS_TERRAIN_TECHNIQUE diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/SinglePassTerrainTechnique.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/SinglePassTerrainTechnique.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/SinglePassTerrainTechnique.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/SinglePassTerrainTechnique.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -17,11 +17,13 @@ * along with this program. If not, see */ #include "SinglePassTerrainTechnique" -#include "CustomTerrain" +#include "Terrain" +#include "Tile" #include #include +#include #include #include #include @@ -29,30 +31,50 @@ #include #include #include +#include +#include + +#include +#include #include using namespace osgEarth; +using namespace osgEarth::Symbology; using namespace OpenThreads; #define LC "[SinglePassTechnique] " -// OSG 2.9.8 changed the osgTerrain API... -#if OSG_VERSION_GREATER_OR_EQUAL(2,9,8) -# define USE_NEW_OSGTERRAIN_298_API 1 -#endif +#define MATCH_TOLERANCE 0.000001 + +// -------------------------------------------------------------------------- + +namespace +{ + struct MaskRecord + { + osg::ref_ptr _boundary; + osg::Vec3d _ndcMin, _ndcMax; + osg::Geometry* _geom; + + MaskRecord(osg::Vec3dArray* boundary, osg::Vec3d& ndcMin, osg::Vec3d& ndcMax, osg::Geometry* geom) : _boundary(boundary), _ndcMin(ndcMin), _ndcMax(ndcMax), _geom(geom) { } + }; + + typedef std::vector MaskRecordVector; +} // -------------------------------------------------------------------------- SinglePassTerrainTechnique::SinglePassTerrainTechnique( TextureCompositor* compositor ) : +CustomTerrainTechnique(), _verticalScaleOverride(1.0f), _initCount(0), _pendingFullUpdate( false ), _pendingGeometryUpdate(false), -_lastUpdate( TileUpdate::UPDATE_ALL ), _optimizeTriangleOrientation(true), _texCompositor( compositor ), -_frontGeodeInstalled( false ) +_frontGeodeInstalled( false ), +_debug( false ) { this->setThreadSafeRefUnref(true); } @@ -63,10 +85,11 @@ _initCount( 0 ), _pendingFullUpdate( false ), _pendingGeometryUpdate( false ), -_lastUpdate( rhs._lastUpdate ), _optimizeTriangleOrientation( rhs._optimizeTriangleOrientation ), _texCompositor( rhs._texCompositor.get() ), -_frontGeodeInstalled( rhs._frontGeodeInstalled ) +_frontGeodeInstalled( rhs._frontGeodeInstalled ), +_debug( rhs._debug ), +_parentTile( rhs._parentTile ) { //NOP } @@ -100,16 +123,10 @@ return _optimizeTriangleOrientation; } -void -#ifdef USE_NEW_OSGTERRAIN_298_API -SinglePassTerrainTechnique::init(int dirtyMask, bool assumeMultiThreaded) -#else +void SinglePassTerrainTechnique::init() -#endif { compile( TileUpdate(TileUpdate::UPDATE_ALL), 0L ); - - //_pendingFullUpdate = true; applyTileUpdates(); } @@ -117,19 +134,22 @@ SinglePassTerrainTechnique::compile( const TileUpdate& update, ProgressCallback* progress ) { // safety check - if ( !_terrainTile ) + if ( !_tile ) { OE_WARN << LC << "Illegal; terrain tile is null" << std::endl; return; } + //if ( _debug ) + //{ + // OE_NOTICE << LC << "compile() " << std::endl; + //} + // serialize access to the compilation procedure. OpenThreads::ScopedLock exclusiveLock( _compileMutex ); - + // make a frame to use during compilation. - CustomTileFrame tilef( static_cast(_terrainTile) ); - - _lastUpdate = update; + TileFrame tilef( _tile ); // establish the master tile locator if this is the first compilation: if ( !_masterLocator.valid() || !_transform.valid() ) @@ -218,20 +238,14 @@ } _initCount++; - //if ( _initCount > 1 ) - // OE_WARN << LC << "Tile was fully build " << _initCount << " times" << std::endl; + if ( _initCount > 1 ) + OE_WARN << LC << "Tile was fully build " << _initCount << " times" << std::endl; if ( _backGeode.valid() && !_backGeode->getStateSet() ) OE_WARN << LC << "ILLEGAL! no stateset in BackGeode!!" << std::endl; _pendingFullUpdate = true; } - -#ifdef USE_NEW_OSGTERRAIN_298_API - // In the updated API, the technique is now responsible for clearing the dirty flag. - // It used to be the tile that cleared it. - _terrainTile->setDirtyMask(0); -#endif } // from the UPDATE traversal thread: @@ -240,8 +254,6 @@ { bool applied = false; - //Threading::ScopedReadLock lock( getMutex() ); - // serialize access to the compilation mechanism. OpenThreads::ScopedLock exclusiveLock( _compileMutex ); @@ -265,58 +277,62 @@ if ( _pendingGeometryUpdate ) { osg::Geode* frontGeode = getFrontGeode(); - - if ( _texCompositor->requiresUnitTextureSpace() ) - { - // in "unit-texture-space" mode, we can take the shortcut of just updating - // the geometry VBOs. The texture coordinates never change. - for( unsigned int i=0; i<_backGeode->getNumDrawables(); ++i ) - { - osg::Geometry* backGeom = static_cast( _backGeode->getDrawable(i) ); - osg::Vec3Array* backVerts = static_cast( backGeom->getVertexArray() ); - osg::Geometry* frontGeom = static_cast( frontGeode->getDrawable(i) ); - osg::Vec3Array* frontVerts = static_cast( frontGeom->getVertexArray() ); + if (frontGeode) + { - if ( backVerts->size() == frontVerts->size() ) + if ( _texCompositor->requiresUnitTextureSpace() ) + { + // in "unit-texture-space" mode, we can take the shortcut of just updating + // the geometry VBOs. The texture coordinates never change. + for( unsigned int i=0; i<_backGeode->getNumDrawables(); ++i ) { - // simple VBO update: - std::copy( backVerts->begin(), backVerts->end(), frontVerts->begin() ); - frontVerts->dirty(); + osg::Geometry* backGeom = static_cast( _backGeode->getDrawable(i) ); + osg::Vec3Array* backVerts = static_cast( backGeom->getVertexArray() ); + + osg::Geometry* frontGeom = static_cast( frontGeode->getDrawable(i) ); + osg::Vec3Array* frontVerts = static_cast( frontGeom->getVertexArray() ); - osg::Vec3Array* backNormals = static_cast( backGeom->getNormalArray() ); - if ( backNormals ) + if ( backVerts->size() == frontVerts->size() ) { - osg::Vec3Array* frontNormals = static_cast( frontGeom->getNormalArray() ); - std::copy( backNormals->begin(), backNormals->end(), frontNormals->begin() ); - frontNormals->dirty(); - } + // simple VBO update: + std::copy( backVerts->begin(), backVerts->end(), frontVerts->begin() ); + frontVerts->dirty(); + + osg::Vec3Array* backNormals = static_cast( backGeom->getNormalArray() ); + if ( backNormals ) + { + osg::Vec3Array* frontNormals = static_cast( frontGeom->getNormalArray() ); + std::copy( backNormals->begin(), backNormals->end(), frontNormals->begin() ); + frontNormals->dirty(); + } - osg::Vec2Array* backTexCoords = static_cast( backGeom->getTexCoordArray(0) ); - if ( backTexCoords ) + osg::Vec2Array* backTexCoords = static_cast( backGeom->getTexCoordArray(0) ); + if ( backTexCoords ) + { + osg::Vec2Array* frontTexCoords = static_cast( frontGeom->getTexCoordArray(0) ); + std::copy( backTexCoords->begin(), backTexCoords->end(), frontTexCoords->begin() ); + frontTexCoords->dirty(); + } + } + else { - osg::Vec2Array* frontTexCoords = static_cast( frontGeom->getTexCoordArray(0) ); - std::copy( backTexCoords->begin(), backTexCoords->end(), frontTexCoords->begin() ); - frontTexCoords->dirty(); + frontGeom->setVertexArray( backVerts ); + frontGeom->setTexCoordArray( 0, backGeom->getTexCoordArray( 0 ) ); // TODO: un-hard-code + if ( backGeom->getNormalArray() ) + frontGeom->setNormalArray( backGeom->getNormalArray() ); } } - else - { - frontGeom->setVertexArray( backVerts ); - frontGeom->setTexCoordArray( 0, backGeom->getTexCoordArray( 0 ) ); // TODO: un-hard-code - if ( backGeom->getNormalArray() ) - frontGeom->setNormalArray( backGeom->getNormalArray() ); - } } - } - else - { - // copy the drawables from the back buffer to the front buffer. By doing this, - // we don't touch the front geode's stateset (which contains the textures) and - // therefore they don't get re-applied. - for( unsigned int i=0; i<_backGeode->getNumDrawables(); ++i ) + else { - frontGeode->setDrawable( i, _backGeode->getDrawable( i ) ); + // copy the drawables from the back buffer to the front buffer. By doing this, + // we don't touch the front geode's stateset (which contains the textures) and + // therefore they don't get re-applied. + for( unsigned int i=0; i<_backGeode->getNumDrawables(); ++i ) + { + frontGeode->setDrawable( i, _backGeode->getDrawable( i ) ); + } } } @@ -326,45 +342,68 @@ } // process any pending LIVE per-layer updates: + osg::StateSet* parentStateSet = 0; + + if ( !_pendingImageLayerUpdates.empty() ) + { + parentStateSet = getParentStateSet(); + } + while( _pendingImageLayerUpdates.size() > 0 ) { const ImageLayerUpdate& update = _pendingImageLayerUpdates.front(); - _texCompositor->applyLayerUpdate( - getFrontGeode()->getStateSet(), - update._layerUID, - update._image, - _tileExtent ); + osg::ref_ptr< osg::Geode > frontGeode = getFrontGeode(); + if (frontGeode.valid()) + { + _texCompositor->applyLayerUpdate( + frontGeode->getStateSet(), + update._layerUID, + update._image, + _tileKey, + update._isRealData ? parentStateSet : 0L ); + } _pendingImageLayerUpdates.pop(); applied = true; + } } + if ( _debug ) + { + OE_NOTICE << "applyTileUpdates()" << std::endl; + } + return applied; } void -SinglePassTerrainTechnique::prepareImageLayerUpdate( UID layerUID, const CustomTileFrame& tilef ) +SinglePassTerrainTechnique::prepareImageLayerUpdate( UID layerUID, const TileFrame& tilef ) { CustomColorLayer layer; if ( tilef.getCustomColorLayer( layerUID, layer ) ) { - GeoImage geoImage = createGeoImage( layer ); - if ( geoImage.valid() ) + GeoImage geoImage, secondaryImage; + + if ( createGeoImage( layer, geoImage ) ) { ImageLayerUpdate update; + update._image = _texCompositor->prepareImage( geoImage, _tileExtent ); update._layerUID = layerUID; + update._isRealData = !layer.isFallbackData(); if ( update._image.valid() ) _pendingImageLayerUpdates.push( update ); } + } } -GeoImage -SinglePassTerrainTechnique::createGeoImage( const CustomColorLayer& colorLayer ) const +bool +SinglePassTerrainTechnique::createGeoImage( const CustomColorLayer& colorLayer, + GeoImage& image) const { osg::ref_ptr layerLocator = dynamic_cast( colorLayer.getLocator() ); if ( layerLocator.valid() ) @@ -373,13 +412,41 @@ layerLocator = layerLocator->getGeographicFromGeocentric(); const GeoExtent& imageExtent = layerLocator->getDataExtent(); - return GeoImage( colorLayer.getImage(), imageExtent ); //const_cast(colorLayer.getImage()), imageExtent ); + image = GeoImage( colorLayer.getImage(), imageExtent ); //const_cast(colorLayer.getImage()), imageExtent ); + return true; + } + return false; +} + +osg::StateSet* +SinglePassTerrainTechnique::getActiveStateSet() const +{ + OpenThreads::ScopedLock exclusiveLock( const_cast(this)->_compileMutex ); + + osg::StateSet* result = 0L; + osg::Geode* front = getFrontGeode(); + if ( front ) + result = front->getStateSet(); + if ( !result && _backGeode.valid() ) + result = _backGeode->getStateSet(); + + return result; +} + +osg::StateSet* +SinglePassTerrainTechnique::getParentStateSet() const +{ + osg::StateSet* parentStateSet = 0; + osg::ref_ptr parentTile_safe = _parentTile.get(); + if ( parentTile_safe.valid() ) + { + return static_cast(_parentTile->getTerrainTechnique())->getActiveStateSet(); } - return GeoImage::INVALID; + else return 0L; } osg::StateSet* -SinglePassTerrainTechnique::createStateSet( const CustomTileFrame& tilef ) +SinglePassTerrainTechnique::createStateSet( const TileFrame& tilef ) { // establish the tile extent. we will calculate texture coordinate offset/scale based on this if ( !_tileExtent.isValid() ) @@ -392,18 +459,29 @@ _tileExtent = tileLocator->getDataExtent(); } + _tileKey = tilef._tileKey; } osg::StateSet* stateSet = new osg::StateSet(); + osg::StateSet* parentStateSet = getParentStateSet(); for( ColorLayersByUID::const_iterator i = tilef._colorLayers.begin(); i != tilef._colorLayers.end(); ++i ) { const CustomColorLayer& colorLayer = i->second; - GeoImage image = createGeoImage( colorLayer ); - if ( image.valid() ) + + bool isRealData = !colorLayer.isFallbackData(); + + GeoImage image; + if ( createGeoImage( colorLayer, image ) ) { image = _texCompositor->prepareImage( image, _tileExtent ); - _texCompositor->applyLayerUpdate( stateSet, colorLayer.getUID(), image, _tileExtent ); + + _texCompositor->applyLayerUpdate( + stateSet, + colorLayer.getUID(), + image, + _tileKey, + isRealData ? parentStateSet : 0L ); } } @@ -413,14 +491,14 @@ void SinglePassTerrainTechnique::calculateSampling( unsigned int& out_rows, unsigned int& out_cols, double& out_i, double& out_j ) { - osgTerrain::Layer* elevationLayer = _terrainTile->getElevationLayer(); + osgTerrain::Layer* elevationLayer = _tile->getElevationLayer(); out_rows = elevationLayer->getNumRows(); out_cols = elevationLayer->getNumColumns(); out_i = 1.0; out_j = 1.0; - float sampleRatio = _terrainTile->getTerrain() ? _terrainTile->getTerrain()->getSampleRatio() : 1.0f; + float sampleRatio = _tile->getTerrain() ? _tile->getTerrain()->getSampleRatio() : 1.0f; if ( sampleRatio != 1.0f ) { unsigned int originalNumColumns = out_cols; @@ -459,17 +537,19 @@ struct RenderLayer { CustomColorLayer _layer; osg::ref_ptr _locator; - osg::Vec2Array* _texCoords; - osg::Vec2Array* _skirtTexCoords; + osg::ref_ptr _texCoords; + osg::ref_ptr _skirtTexCoords; + osg::ref_ptr _stitchTexCoords; + osg::ref_ptr _stitchSkirtTexCoords; bool _ownsTexCoords; - RenderLayer() : _texCoords(0L), _ownsTexCoords(false) { } + RenderLayer() : _ownsTexCoords(false) { } }; typedef std::vector< RenderLayer > RenderLayerVector; } osg::Geode* -SinglePassTerrainTechnique::createGeometry( const CustomTileFrame& tilef ) +SinglePassTerrainTechnique::createGeometry( const TileFrame& tilef ) { osg::ref_ptr masterTextureLocator = _masterLocator.get(); //GeoLocator* geoMasterLocator = dynamic_cast(_masterLocator.get()); @@ -483,7 +563,7 @@ masterTextureLocator = masterTextureLocator->getGeographicFromGeocentric(); } - osgTerrain::Layer* elevationLayer = _terrainTile->getElevationLayer(); + osgTerrain::Layer* elevationLayer = _tile->getElevationLayer(); // fire up a brand new geode. osg::Geode* geode = new osg::Geode(); @@ -495,13 +575,105 @@ osg::Geometry* surface = new osg::Geometry(); surface->setThreadSafeRefUnref(true); // TODO: probably unnecessary. surface->setDataVariance( osg::Object::DYNAMIC ); + surface->setUseDisplayList(false); + surface->setUseVertexBufferObjects(true); geode->addDrawable( surface ); osg::Geometry* skirt = new osg::Geometry(); skirt->setThreadSafeRefUnref(true); // TODO: probably unnecessary. skirt->setDataVariance( osg::Object::DYNAMIC ); + skirt->setUseDisplayList(false); + skirt->setUseVertexBufferObjects(true); geode->addDrawable( skirt ); - + + osg::ref_ptr geoLocator = _masterLocator; + // Avoid coordinates conversion when GEOCENTRIC, so get a GEOGRAPHIC version of Locator + if (_masterLocator->getCoordinateSystemType() == osgTerrain::Locator::GEOCENTRIC) { + geoLocator = _masterLocator->getGeographicFromGeocentric(); + } + + float scaleHeight = + _verticalScaleOverride != 1.0? _verticalScaleOverride : + _tile->getTerrain() ? _tile->getTerrain()->getVerticalScale() : + 1.0f; + + MaskRecordVector masks; + for (MaskLayerVector::const_iterator it = tilef._masks.begin(); it != tilef._masks.end(); ++it) + { + // When displaying Plate Carre, Heights have to be converted from meters to degrees. + // This is also true for mask feature + // TODO: adjust this calculation based on the actual EllipsoidModel. + float scale = scaleHeight; + if (_masterLocator->getCoordinateSystemType() == osgEarth::GeoLocator::GEOGRAPHIC) + scale = scaleHeight / 111319.0f; + + // TODO: Get the map SRS if possible instead of masterLocator's one + osg::Vec3dArray* boundary = (*it)->getOrCreateBoundary(scale, _masterLocator->getDataExtent().getSRS()); + + if ( boundary ) + { + osg::Vec3d min, max; + min = max = boundary->front(); + + for (osg::Vec3dArray::iterator it = boundary->begin(); it != boundary->end(); ++it) + { + if (it->x() < min.x()) + min.x() = it->x(); + + if (it->y() < min.y()) + min.y() = it->y(); + + if (it->x() > max.x()) + max.x() = it->x(); + + if (it->y() > max.y()) + max.y() = it->y(); + } + + osg::Vec3d min_ndc, max_ndc; + geoLocator->convertModelToLocal(min, min_ndc); + geoLocator->convertModelToLocal(max, max_ndc); + + bool x_match = ((min_ndc.x() >= 0.0 && max_ndc.x() <= 1.0) || + (min_ndc.x() <= 0.0 && max_ndc.x() > 0.0) || + (min_ndc.x() < 1.0 && max_ndc.x() >= 1.0)); + + bool y_match = ((min_ndc.y() >= 0.0 && max_ndc.y() <= 1.0) || + (min_ndc.y() <= 0.0 && max_ndc.y() > 0.0) || + (min_ndc.y() < 1.0 && max_ndc.y() >= 1.0)); + + if (x_match && y_match) + { + osg::Geometry* mask_geom = new osg::Geometry(); + mask_geom->setThreadSafeRefUnref(true); + mask_geom->setDataVariance( osg::Object::DYNAMIC ); + mask_geom->setUseDisplayList(false); + mask_geom->setUseVertexBufferObjects(true); + //mask_geom->getOrCreateStateSet()->setAttribute(new osg::Point( 5.0f ), osg::StateAttribute::ON); + geode->addDrawable(mask_geom); + + masks.push_back(MaskRecord(boundary, min_ndc, max_ndc, mask_geom)); + } + } + } + + osg::Geometry* stitching_skirts = 0L; + osg::Vec3Array* ss_verts = 0L; + if (masks.size() > 0) + { + stitching_skirts = new osg::Geometry(); + stitching_skirts->setThreadSafeRefUnref(true); + stitching_skirts->setDataVariance( osg::Object::DYNAMIC ); + stitching_skirts->setUseDisplayList(false); + stitching_skirts->setUseVertexBufferObjects(true); + //stitching_skirts->getOrCreateStateSet()->setAttribute(new osg::Point( 5.0f ), osg::StateAttribute::ON); + geode->addDrawable( stitching_skirts); + + ss_verts = new osg::Vec3Array(); + stitching_skirts->setVertexArray(ss_verts); + } + + unsigned int numRows = 20; unsigned int numColumns = 20; @@ -541,10 +713,13 @@ // skirt texture coordinates, if applicable: osg::Vec2Array* unifiedSkirtTexCoords = 0L; + // stitching skirt texture coordinates, if applicable: + osg::Vec2Array* unifiedStitchSkirtTexCoords = 0L; + // allocate and assign texture coordinates osg::Vec2Array* unifiedSurfaceTexCoords = 0L; - //int numColorLayers = _terrainTile->getNumColorLayers(); + //int numColorLayers = _tile->getNumColorLayers(); RenderLayerVector renderLayers; if ( _texCompositor->requiresUnitTextureSpace() ) @@ -559,6 +734,13 @@ unifiedSkirtTexCoords->reserve( numVerticesInSkirt ); skirt->setTexCoordArray( 0, unifiedSkirtTexCoords ); } + + if (masks.size() > 0) + { + unifiedStitchSkirtTexCoords = new osg::Vec2Array(); + //unifiedStitchSkirtTexCoords->reserve( ? ); + stitching_skirts->setTexCoordArray( 0, unifiedStitchSkirtTexCoords ); + } } else // if ( !_texCompositor->requiresUnitTextureSpace() ) @@ -578,17 +760,23 @@ if ( locator ) { r._texCoords = locatorToTexCoordTable.find( locator ); - if ( !r._texCoords ) + if ( !r._texCoords.valid() ) { r._texCoords = new osg::Vec2Array(); r._texCoords->reserve( numVerticesInSurface ); r._ownsTexCoords = true; - locatorToTexCoordTable.push_back( LocatorTexCoordPair(locator, r._texCoords) ); + locatorToTexCoordTable.push_back( LocatorTexCoordPair(locator, r._texCoords.get()) ); } r._skirtTexCoords = new osg::Vec2Array(); r._skirtTexCoords->reserve( numVerticesInSkirt ); + if ( masks.size() > 0 ) + { + r._stitchTexCoords = new osg::Vec2Array(); + r._stitchSkirtTexCoords = new osg::Vec2Array(); + } + r._locator = locator; if ( locator->getCoordinateSystemType() == osgTerrain::Locator::GEOCENTRIC ) { @@ -597,8 +785,15 @@ r._locator = geo->getGeographicFromGeocentric(); } - _texCompositor->assignTexCoordArray( surface, colorLayer.getUID(), r._texCoords ); - _texCompositor->assignTexCoordArray( skirt, colorLayer.getUID(), r._skirtTexCoords ); + _texCompositor->assignTexCoordArray( surface, colorLayer.getUID(), r._texCoords.get() ); + _texCompositor->assignTexCoordArray( skirt, colorLayer.getUID(), r._skirtTexCoords.get() ); + + for (MaskRecordVector::iterator mr = masks.begin(); mr != masks.end(); ++mr) + _texCompositor->assignTexCoordArray( (*mr)._geom, colorLayer.getUID(), r._stitchTexCoords.get() ); + + if (stitching_skirts) + _texCompositor->assignTexCoordArray( stitching_skirts, colorLayer.getUID(), r._stitchSkirtTexCoords.get() ); + //surface->setTexCoordArray( renderLayers.size(), r._texCoords ); renderLayers.push_back( r ); } @@ -609,11 +804,6 @@ } } - float scaleHeight = - _verticalScaleOverride != 1.0? _verticalScaleOverride : - _terrainTile->getTerrain() ? _terrainTile->getTerrain()->getVerticalScale() : - 1.0f; - osg::ref_ptr elevations = new osg::FloatArray; if (elevations.valid()) elevations->reserve(numVerticesInSurface); @@ -629,7 +819,6 @@ // populate vertex and tex coord arrays unsigned int i, j; //, k=0; - for(j=0; jgetValidValue(i_equiv,j_equiv, value); ndc.z() = value*scaleHeight; } + + //Invalidate if point falls within mask bounding box + if (validValue && masks.size() > 0) + { + for (MaskRecordVector::iterator mr = masks.begin(); mr != masks.end(); ++mr) + { + if(ndc.x() >= (*mr)._ndcMin.x() && ndc.x() <= (*mr)._ndcMax.x() && + ndc.y() >= (*mr)._ndcMin.y() && ndc.y() <= (*mr)._ndcMax.y()) + { + validValue = false; + indices[iv] = -2; + break; + } + } + } if (validValue) { @@ -705,13 +908,453 @@ //(*normals)[k] = model_one; (*normals).push_back(model_one); } - else + } + } + + + for (MaskRecordVector::iterator mr = masks.begin(); mr != masks.end(); ++mr) + { + int min_i = (int)floor((*mr)._ndcMin.x() * (double)(numColumns-1)); + if (min_i < 0) min_i = 0; + if (min_i >= numColumns) min_i = numColumns - 1; + + int min_j = (int)floor((*mr)._ndcMin.y() * (double)(numRows-1)); + if (min_j < 0) min_j = 0; + if (min_j >= numColumns) min_j = numColumns - 1; + + int max_i = (int)ceil((*mr)._ndcMax.x() * (double)(numColumns-1)); + if (max_i < 0) max_i = 0; + if (max_i >= numColumns) max_i = numColumns - 1; + + int max_j = (int)ceil((*mr)._ndcMax.y() * (double)(numRows-1)); + if (max_j < 0) max_j = 0; + if (max_j >= numColumns) max_j = numColumns - 1; + + if (min_i >= 0 && max_i >= 0 && min_j >= 0 && max_j >= 0) + { + int num_i = max_i - min_i + 1; + int num_j = max_j - min_j + 1; + + osg::ref_ptr maskSkirtPoly = new osgEarth::Symbology::Polygon(); + maskSkirtPoly->resize(num_i * 2 + num_j * 2 - 4); + for (int i = 0; i < num_i; i++) + { + //int index = indices[min_j*numColumns + i + min_i]; + { + osg::Vec3d ndc( ((double)(i + min_i))/(double)(numColumns-1), ((double)min_j)/(double)(numRows-1), 0.0); + + if (elevationLayer) + { + unsigned int i_equiv = i_sampleFactor==1.0 ? i + min_i : (unsigned int) (double(i + min_i)*i_sampleFactor); + unsigned int j_equiv = j_sampleFactor==1.0 ? min_j : (unsigned int) (double(min_j)*j_sampleFactor); + + float value = 0.0f; + if (elevationLayer->getValidValue(i_equiv,j_equiv, value)) + ndc.z() = value*scaleHeight; + } + + (*maskSkirtPoly)[i] = ndc; + } + + //index = indices[max_j*numColumns + i + min_i]; + { + osg::Vec3d ndc( ((double)(i + min_i))/(double)(numColumns-1), ((double)max_j)/(double)(numRows-1), 0.0); + + if (elevationLayer) + { + unsigned int i_equiv = i_sampleFactor==1.0 ? i + min_i : (unsigned int) (double(i + min_i)*i_sampleFactor); + unsigned int j_equiv = j_sampleFactor==1.0 ? max_j : (unsigned int) (double(max_j)*j_sampleFactor); + + float value = 0.0f; + if (elevationLayer->getValidValue(i_equiv,j_equiv, value)) + ndc.z() = value*scaleHeight; + } + + (*maskSkirtPoly)[i + (2 * num_i + num_j - 3) - 2 * i] = ndc; + } + } + for (int j = 0; j < num_j - 2; j++) + { + //int index = indices[(min_j + j + 1)*numColumns + max_i]; + { + osg::Vec3d ndc( ((double)max_i)/(double)(numColumns-1), ((double)(min_j + j + 1))/(double)(numRows-1), 0.0); + + if (elevationLayer) { - indices[iv] = -1; + unsigned int i_equiv = i_sampleFactor==1.0 ? max_i : (unsigned int) (double(max_i)*i_sampleFactor); + unsigned int j_equiv = j_sampleFactor==1.0 ? min_j + j + 1 : (unsigned int) (double(min_j + j + 1)*j_sampleFactor); + + float value = 0.0f; + if (elevationLayer->getValidValue(i_equiv,j_equiv, value)) + ndc.z() = value*scaleHeight; } + + (*maskSkirtPoly)[j + num_i] = ndc; + } + + //index = indices[(min_j + j + 1)*numColumns + min_i]; + { + osg::Vec3d ndc( ((double)min_i)/(double)(numColumns-1), ((double)(min_j + j + 1))/(double)(numRows-1), 0.0); + + if (elevationLayer) + { + unsigned int i_equiv = i_sampleFactor==1.0 ? min_i : (unsigned int) (double(min_i)*i_sampleFactor); + unsigned int j_equiv = j_sampleFactor==1.0 ? min_j + j + 1 : (unsigned int) (double(min_j + j + 1)*j_sampleFactor); + + float value = 0.0f; + if (elevationLayer->getValidValue(i_equiv,j_equiv, value)) + ndc.z() = value*scaleHeight; + } + + (*maskSkirtPoly)[j + (2 * num_i + 2 * num_j - 5) - 2 * j] = ndc; + } } + + //Create local polygon representing mask + osg::ref_ptr maskPoly = new osgEarth::Symbology::Polygon(); + for (osg::Vec3dArray::iterator it = (*mr)._boundary->begin(); it != (*mr)._boundary->end(); ++it) + { + osg::Vec3d local; + geoLocator->convertModelToLocal(*it, local); + maskPoly->push_back(local); + } + + +//Change the following two #if statements to see mask skirt polygons +//before clipping and adjusting +#if 1 + //Do a diff on the polygons to get the actual mask skirt + osg::ref_ptr outPoly; + maskSkirtPoly->difference(maskPoly.get(), outPoly); +#else + osg::ref_ptr outPoly = maskSkirtPoly; +#endif + + osg::Vec3Array* outVerts = new osg::Vec3Array(); + + osg::Geometry* stitch_geom = (*mr)._geom; + stitch_geom->setVertexArray(outVerts); + + bool multiParent = false; + if (outPoly.valid()) + multiParent = outPoly->getType() == osgEarth::Symbology::Geometry::TYPE_MULTI; + + std::vector skirtIndices; + + osgEarth::Symbology::GeometryIterator i( outPoly.get(), false ); + while( i.hasMore() ) + { + osgEarth::Symbology::Geometry* part = i.next(); + if (!part) + continue; + + if (part->getType() == osgEarth::Symbology::Geometry::TYPE_POLYGON) + { + osg::Vec3Array* partVerts = part->toVec3Array(); + outVerts->insert(outVerts->end(), partVerts->begin(), partVerts->end()); + stitch_geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::POLYGON, outVerts->size() - partVerts->size(), partVerts->size())); + skirtIndices.push_back(outVerts->size()); + + if (!multiParent) + { + osg::ref_ptr holePoly = static_cast(outPoly.get()); + if (holePoly) + { + osgEarth::Symbology::RingCollection holes = holePoly->getHoles(); + + for (osgEarth::Symbology::RingCollection::iterator hit = holes.begin(); hit != holes.end(); ++hit) + { + (*hit)->rewind(osgEarth::Symbology::Ring::ORIENTATION_CCW); + outVerts->insert(outVerts->end(), (*hit)->begin(), (*hit)->end()); + stitch_geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::POLYGON, outVerts->size() - (*hit)->size(), (*hit)->size())); + skirtIndices.push_back(outVerts->size()); + } + } + } + } + } + + if (stitch_geom->getNumPrimitiveSets() > 0) + { +#if 1 + // Tessellate mask skirt + osg::ref_ptr tscx=new osgUtil::Tessellator; + tscx->setTessellationType(osgUtil::Tessellator::TESS_TYPE_GEOMETRY); + tscx->setBoundaryOnly(false); + tscx->setWindingType(osgUtil::Tessellator::TESS_WINDING_ODD); + tscx->retessellatePolygons(*stitch_geom); + + // Assign normals to the stitching polygon: -gw + osg::Vec3Array* sgVerts = dynamic_cast(stitch_geom->getVertexArray()); + osg::Vec3Array* sgNormals = new osg::Vec3Array(sgVerts->size()); + stitch_geom->setNormalArray( sgNormals ); + stitch_geom->setNormalBinding( osg::Geometry::BIND_PER_VERTEX ); + + // calculate the normal and convert to model space. + for( unsigned v=0; vsize(); ++v ) + { + const osg::Vec3& vert = (*sgVerts)[v]; + osg::Vec3d local_one(vert); + osg::Vec3d model; + _masterLocator->convertLocalToModel( local_one, model ); + local_one.z() += 1.0; + osg::Vec3d model_one; + _masterLocator->convertLocalToModel( local_one, model_one ); + model_one = model_one - model; + model_one.normalize(); + (*sgNormals)[v] = model_one; + } + + //Initialize tex coords + osg::Vec2Array* unifiedStitchTexCoords = 0L; + if (_texCompositor->requiresUnitTextureSpace()) + { + unifiedStitchTexCoords = new osg::Vec2Array(); + unifiedStitchTexCoords->reserve(outVerts->size()); + stitch_geom->setTexCoordArray(0, unifiedStitchTexCoords); + } + else if ( renderLayers.size() > 0 ) + { + for (unsigned int i = 0; i < renderLayers.size(); ++i) + { + renderLayers[i]._stitchTexCoords->reserve(outVerts->size()); + } + } + + + //Retrieve z values for mask skirt verts + std::vector isZSet; + for (osg::Vec3Array::iterator it = outVerts->begin(); it != outVerts->end(); ++it) + { + int zSet = 0; + + //Look for verts that belong to the original mask skirt polygon + for (osgEarth::Symbology::Polygon::iterator mit = maskSkirtPoly->begin(); mit != maskSkirtPoly->end(); ++mit) + { + if (osg::absolute((*mit).x() - (*it).x()) < MATCH_TOLERANCE && osg::absolute((*mit).y() - (*it).y()) < MATCH_TOLERANCE) + { + (*it).z() = (*mit).z(); + zSet += 1; + break; + } + } + + //Look for verts that belong to the mask polygon + for (osgEarth::Symbology::Polygon::iterator mit = maskPoly->begin(); mit != maskPoly->end(); ++mit) + { + if (osg::absolute((*mit).x() - (*it).x()) < MATCH_TOLERANCE && osg::absolute((*mit).y() - (*it).y()) < MATCH_TOLERANCE) + { + (*it).z() = (*mit).z(); + zSet += 2; + break; + } + } + + isZSet.push_back(zSet); + } + + //Any mask skirt verts that are still unset are newly created verts where the skirt + //meets the mask. Find the mask segment the point lies along and calculate the + //appropriate z value for the point. + // + //Now that all the z values are set, convert each vert into model coords. + // + //Also, while we are iterating through the verts, set up tex coords. + int count = 0; + for (osg::Vec3Array::iterator it = outVerts->begin(); it != outVerts->end(); ++it) + { + //If the z-value was set from a mask vertex there is no need to change it. If + //it was set from a vertex from the stitching polygon it may need overriden if + //the vertex lies along a mask edge. Or if it is unset, it will need to be set. + if (isZSet[count] < 2) + { + osg::Vec3d p2 = *it; + double closestZ = 0.0; + double closestRatio = DBL_MAX; + for (osgEarth::Symbology::Polygon::iterator mit = maskPoly->begin(); mit != maskPoly->end(); ++mit) + { + osg::Vec3d p1 = *mit; + osg::Vec3d p3 = mit == --maskPoly->end() ? maskPoly->front() : (*(mit + 1)); + + //Truncated vales to compensate for accuracy issues + double p1x = ((int)(p1.x() * 1000000)) / 1000000.0L; + double p3x = ((int)(p3.x() * 1000000)) / 1000000.0L; + double p2x = ((int)(p2.x() * 1000000)) / 1000000.0L; + + double p1y = ((int)(p1.y() * 1000000)) / 1000000.0L; + double p3y = ((int)(p3.y() * 1000000)) / 1000000.0L; + double p2y = ((int)(p2.y() * 1000000)) / 1000000.0L; + + if ((p1x < p3x ? p2x >= p1x && p2x <= p3x : p2x >= p3x && p2x <= p1x) && + (p1y < p3y ? p2y >= p1y && p2y <= p3y : p2y >= p3y && p2y <= p1y)) + { + double l1 =(osg::Vec2d(p2.x(), p2.y()) - osg::Vec2d(p1.x(), p1.y())).length(); + double lt = (osg::Vec2d(p3.x(), p3.y()) - osg::Vec2d(p1.x(), p1.y())).length(); + double zmag = p3.z() - p1.z(); + + double foundZ = (l1 / lt) * zmag + p1.z(); + + double mRatio = 1.0; + if (osg::absolute(p1x - p3x) < MATCH_TOLERANCE) + { + if (osg::absolute(p1x-p2x) < MATCH_TOLERANCE) + mRatio = 0.0; + } + else + { + double m1 = p1x == p2x ? 0.0 : (p2y - p1y) / (p2x - p1x); + double m2 = p1x == p3x ? 0.0 : (p3y - p1y) / (p3x - p1x); + mRatio = m2 == 0.0 ? m1 : osg::absolute(1.0L - m1 / m2); + } + + if (mRatio < 0.01) + { + (*it).z() = foundZ; + isZSet[count] = 2; + break; + } + else if (mRatio < closestRatio) + { + closestRatio = mRatio; + closestZ = foundZ; + } + } + } + + if (!isZSet[count] && closestRatio < DBL_MAX) + { + (*it).z() = closestZ; + isZSet[count] = 2; + } + } + + if (!isZSet[count]) + OE_WARN << LC << "Z-value not set for stitching polygon vertex" << std::endl; + + count++; + + //Convert to model coords + osg::Vec3d model; + _masterLocator->convertLocalToModel(*it, model); + model = model - _centerModel; + (*it).set(model.x(), model.y(), model.z()); + + //Setup tex coords + osg::Vec3d ndc; + _masterLocator->convertModelToLocal(*it + _centerModel, ndc); + + if (_texCompositor->requiresUnitTextureSpace()) + { + unifiedStitchTexCoords->push_back(osg::Vec2(ndc.x(), ndc.y())); + } + else if (renderLayers.size() > 0) + { + for (unsigned int i = 0; i < renderLayers.size(); ++i) + { + if (!renderLayers[i]._locator->isEquivalentTo(*masterTextureLocator.get())) + { + osg::Vec3d color_ndc; + osgTerrain::Locator::convertLocalCoordBetween(*masterTextureLocator.get(), ndc, *renderLayers[i]._locator.get(), color_ndc); + renderLayers[i]._stitchTexCoords->push_back(osg::Vec2(color_ndc.x(), color_ndc.y())); + } + else + { + renderLayers[i]._stitchTexCoords->push_back(osg::Vec2(ndc.x(), ndc.y())); + } + } + } + } +#else + for (osg::Vec3Array::iterator it = outVerts->begin(); it != outVerts->end(); ++it) + { + //Convert to model coords + osg::Vec3d model; + _masterLocator->convertLocalToModel(*it, model); + model = model - _centerModel; + (*it).set(model.x(), model.y(), model.z()); + } +#endif + + //Create stitching skirts + if (createSkirt && skirtIndices.size() > 0) + { + ss_verts->reserve(ss_verts->size() + outVerts->size() * 4 + skirtIndices.size() * 2); + + //Add a primative set for each continuous skirt strip + for (int p=0; p < skirtIndices.size(); p++) + { + int cursor = ss_verts->size(); + + int outStart = p == 0 ? 0 : skirtIndices[p-1]; + for (int i=outStart; i < skirtIndices[p]; i++) + { + ss_verts->push_back((*outVerts)[i]); + ss_verts->push_back((*outVerts)[i] - (*sgNormals)[i] * skirtHeight); + + if ( _texCompositor->requiresUnitTextureSpace() ) + { + unifiedStitchSkirtTexCoords->push_back( (*unifiedStitchTexCoords)[i] ); + unifiedStitchSkirtTexCoords->push_back( (*unifiedStitchTexCoords)[i] ); + } + else if ( renderLayers.size() > 0 ) + { + for (unsigned int r = 0; r < renderLayers.size(); ++r) + { + const osg::Vec2& tc = (*renderLayers[r]._stitchTexCoords.get())[i]; + renderLayers[r]._stitchSkirtTexCoords->push_back( tc ); + renderLayers[r]._stitchSkirtTexCoords->push_back( tc ); + } + } + } + + //Add the first vert again to complete the loop + ss_verts->push_back((*outVerts)[outStart]); + ss_verts->push_back((*outVerts)[outStart] - (*sgNormals)[outStart] * skirtHeight); + + if ( _texCompositor->requiresUnitTextureSpace() ) + { + unifiedStitchSkirtTexCoords->push_back( (*unifiedStitchTexCoords)[outStart] ); + unifiedStitchSkirtTexCoords->push_back( (*unifiedStitchTexCoords)[outStart] ); + } + else if ( renderLayers.size() > 0 ) + { + for (unsigned int r = 0; r < renderLayers.size(); ++r) + { + const osg::Vec2& tc = (*renderLayers[r]._stitchTexCoords.get())[outStart]; + renderLayers[r]._stitchSkirtTexCoords->push_back( tc ); + renderLayers[r]._stitchSkirtTexCoords->push_back( tc ); + } + } + + //Now go back the opposite direction to create a skirt facing the other direction + for (int i=skirtIndices[p] - 1; i >= outStart; i--) + { + ss_verts->push_back((*outVerts)[i]); + ss_verts->push_back((*outVerts)[i] - (*sgNormals)[i] * skirtHeight); + + if ( _texCompositor->requiresUnitTextureSpace() ) + { + unifiedStitchSkirtTexCoords->push_back( (*unifiedStitchTexCoords)[i] ); + unifiedStitchSkirtTexCoords->push_back( (*unifiedStitchTexCoords)[i] ); + } + else if ( renderLayers.size() > 0 ) + { + for (unsigned int r = 0; r < renderLayers.size(); ++r) + { + const osg::Vec2& tc = (*renderLayers[r]._stitchTexCoords.get())[i]; + renderLayers[r]._stitchSkirtTexCoords->push_back( tc ); + renderLayers[r]._stitchSkirtTexCoords->push_back( tc ); + } + } + } + + stitching_skirts->addPrimitiveSet(new osg::DrawArrays( GL_TRIANGLE_STRIP, cursor, ss_verts->size() - cursor)); + } + } + } + } } - + // populate primitive sets bool swapOrientation = !(_masterLocator->orientationOpenGL()); @@ -721,8 +1364,8 @@ surface->addPrimitiveSet(elements.get()); osg::ref_ptr skirtVectors = new osg::Vec3Array( *normals ); - - if (!normals) + + if (!normals) createSkirt = false; // New separated skirts. @@ -730,28 +1373,50 @@ { // build the verts first: osg::Vec3Array* skirtVerts = new osg::Vec3Array(); + osg::Vec3Array* skirtNormals = new osg::Vec3Array(); + skirtVerts->reserve( numVerticesInSkirt ); + skirtNormals->reserve( numVerticesInSkirt ); + + Indices skirtBreaks; + skirtBreaks.push_back(0); // bottom: for( unsigned int c=0; cpush_back( (*surfaceVerts)[orig_i] ); - skirtVerts->push_back( (*surfaceVerts)[orig_i] - ((*skirtVectors)[orig_i])*skirtHeight ); - if ( _texCompositor->requiresUnitTextureSpace() ) + //int offset = 0; + //while (orig_i < 0 && offset < numRows - 1) + // orig_i = indices[c + ++offset * numColumns]; + + if (orig_i < 0) { - unifiedSkirtTexCoords->push_back( (*unifiedSurfaceTexCoords)[orig_i] ); - unifiedSkirtTexCoords->push_back( (*unifiedSurfaceTexCoords)[orig_i] ); + if (skirtBreaks.back() != skirtVerts->size()) + skirtBreaks.push_back(skirtVerts->size()); } - else if ( renderLayers.size() > 0 ) + else { - for (unsigned int i = 0; i < renderLayers.size(); ++i) - { - const osg::Vec2& tc = (*renderLayers[i]._texCoords)[orig_i]; - renderLayers[i]._skirtTexCoords->push_back( tc ); - renderLayers[i]._skirtTexCoords->push_back( tc ); - } + skirtVerts->push_back( (*surfaceVerts)[orig_i] ); + skirtVerts->push_back( (*surfaceVerts)[orig_i] - ((*skirtVectors)[orig_i])*skirtHeight ); + skirtNormals->push_back( (*normals)[orig_i] ); + skirtNormals->push_back( (*normals)[orig_i] ); + + + if ( _texCompositor->requiresUnitTextureSpace() ) + { + unifiedSkirtTexCoords->push_back( (*unifiedSurfaceTexCoords)[orig_i] ); + unifiedSkirtTexCoords->push_back( (*unifiedSurfaceTexCoords)[orig_i] ); + } + else if ( renderLayers.size() > 0 ) + { + for (unsigned int i = 0; i < renderLayers.size(); ++i) + { + const osg::Vec2& tc = (*renderLayers[i]._texCoords.get())[orig_i]; + renderLayers[i]._skirtTexCoords->push_back( tc ); + renderLayers[i]._skirtTexCoords->push_back( tc ); + } + } } } @@ -759,22 +1424,32 @@ for( unsigned int r=0; rpush_back( (*surfaceVerts)[orig_i] ); - skirtVerts->push_back( (*surfaceVerts)[orig_i] - ((*skirtVectors)[orig_i])*skirtHeight ); - - if ( _texCompositor->requiresUnitTextureSpace() ) + if (orig_i < 0) { - unifiedSkirtTexCoords->push_back( (*unifiedSurfaceTexCoords)[orig_i] ); - unifiedSkirtTexCoords->push_back( (*unifiedSurfaceTexCoords)[orig_i] ); + if (skirtBreaks.back() != skirtVerts->size()) + skirtBreaks.push_back(skirtVerts->size()); } - else if ( renderLayers.size() > 0 ) + else { - for (unsigned int i = 0; i < renderLayers.size(); ++i) - { - const osg::Vec2& tc = (*renderLayers[i]._texCoords)[orig_i]; - renderLayers[i]._skirtTexCoords->push_back( tc ); - renderLayers[i]._skirtTexCoords->push_back( tc ); - } + skirtVerts->push_back( (*surfaceVerts)[orig_i] ); + skirtVerts->push_back( (*surfaceVerts)[orig_i] - ((*skirtVectors)[orig_i])*skirtHeight ); + skirtNormals->push_back( (*normals)[orig_i] ); + skirtNormals->push_back( (*normals)[orig_i] ); + + if ( _texCompositor->requiresUnitTextureSpace() ) + { + unifiedSkirtTexCoords->push_back( (*unifiedSurfaceTexCoords)[orig_i] ); + unifiedSkirtTexCoords->push_back( (*unifiedSurfaceTexCoords)[orig_i] ); + } + else if ( renderLayers.size() > 0 ) + { + for (unsigned int i = 0; i < renderLayers.size(); ++i) + { + const osg::Vec2& tc = (*renderLayers[i]._texCoords.get())[orig_i]; + renderLayers[i]._skirtTexCoords->push_back( tc ); + renderLayers[i]._skirtTexCoords->push_back( tc ); + } + } } } @@ -782,22 +1457,32 @@ for( int c=numColumns-1; c>0; --c ) { int orig_i = indices[(numRows-1)*numColumns+c]; - skirtVerts->push_back( (*surfaceVerts)[orig_i] ); - skirtVerts->push_back( (*surfaceVerts)[orig_i] - ((*skirtVectors)[orig_i])*skirtHeight ); - - if ( _texCompositor->requiresUnitTextureSpace() ) + if (orig_i < 0) { - unifiedSkirtTexCoords->push_back( (*unifiedSurfaceTexCoords)[orig_i] ); - unifiedSkirtTexCoords->push_back( (*unifiedSurfaceTexCoords)[orig_i] ); + if (skirtBreaks.back() != skirtVerts->size()) + skirtBreaks.push_back(skirtVerts->size()); } - else if ( renderLayers.size() > 0 ) + else { - for (unsigned int i = 0; i < renderLayers.size(); ++i) - { - const osg::Vec2& tc = (*renderLayers[i]._texCoords)[orig_i]; - renderLayers[i]._skirtTexCoords->push_back( tc ); - renderLayers[i]._skirtTexCoords->push_back( tc ); - } + skirtVerts->push_back( (*surfaceVerts)[orig_i] ); + skirtVerts->push_back( (*surfaceVerts)[orig_i] - ((*skirtVectors)[orig_i])*skirtHeight ); + skirtNormals->push_back( (*normals)[orig_i] ); + skirtNormals->push_back( (*normals)[orig_i] ); + + if ( _texCompositor->requiresUnitTextureSpace() ) + { + unifiedSkirtTexCoords->push_back( (*unifiedSurfaceTexCoords)[orig_i] ); + unifiedSkirtTexCoords->push_back( (*unifiedSurfaceTexCoords)[orig_i] ); + } + else if ( renderLayers.size() > 0 ) + { + for (unsigned int i = 0; i < renderLayers.size(); ++i) + { + const osg::Vec2& tc = (*renderLayers[i]._texCoords.get())[orig_i]; + renderLayers[i]._skirtTexCoords->push_back( tc ); + renderLayers[i]._skirtTexCoords->push_back( tc ); + } + } } } @@ -805,29 +1490,45 @@ for( int r=numRows-1; r>=0; --r ) { int orig_i = indices[r*numColumns]; - skirtVerts->push_back( (*surfaceVerts)[orig_i] ); - skirtVerts->push_back( (*surfaceVerts)[orig_i] - ((*skirtVectors)[orig_i])*skirtHeight ); - - if ( _texCompositor->requiresUnitTextureSpace() ) + if (orig_i < 0) { - unifiedSkirtTexCoords->push_back( (*unifiedSurfaceTexCoords)[orig_i] ); - unifiedSkirtTexCoords->push_back( (*unifiedSurfaceTexCoords)[orig_i] ); + if (skirtBreaks.back() != skirtVerts->size()) + skirtBreaks.push_back(skirtVerts->size()); } - else if ( renderLayers.size() > 0 ) + else { - for (unsigned int i = 0; i < renderLayers.size(); ++i) - { - const osg::Vec2& tc = (*renderLayers[i]._texCoords)[orig_i]; - renderLayers[i]._skirtTexCoords->push_back( tc ); - renderLayers[i]._skirtTexCoords->push_back( tc ); - } + skirtVerts->push_back( (*surfaceVerts)[orig_i] ); + skirtVerts->push_back( (*surfaceVerts)[orig_i] - ((*skirtVectors)[orig_i])*skirtHeight ); + skirtNormals->push_back( (*normals)[orig_i] ); + skirtNormals->push_back( (*normals)[orig_i] ); + + if ( _texCompositor->requiresUnitTextureSpace() ) + { + unifiedSkirtTexCoords->push_back( (*unifiedSurfaceTexCoords)[orig_i] ); + unifiedSkirtTexCoords->push_back( (*unifiedSurfaceTexCoords)[orig_i] ); + } + else if ( renderLayers.size() > 0 ) + { + for (unsigned int i = 0; i < renderLayers.size(); ++i) + { + const osg::Vec2& tc = (*renderLayers[i]._texCoords.get())[orig_i]; + renderLayers[i]._skirtTexCoords->push_back( tc ); + renderLayers[i]._skirtTexCoords->push_back( tc ); + } + } } } skirt->setVertexArray( skirtVerts ); - skirt->addPrimitiveSet( new osg::DrawArrays( GL_TRIANGLE_STRIP, 0, skirtVerts->size() ) ); - } + skirt->setNormalArray( skirtNormals ); + skirt->setNormalBinding( osg::Geometry::BIND_PER_VERTEX ); + //Add a primative set for each continuous skirt strip + skirtBreaks.push_back(skirtVerts->size()); + for (int p=1; p < skirtBreaks.size(); p++) + skirt->addPrimitiveSet( new osg::DrawArrays( GL_TRIANGLE_STRIP, skirtBreaks[p-1], skirtBreaks[p] - skirtBreaks[p-1] ) ); + } + bool recalcNormals = elevationLayer != NULL; //Clear out the normals @@ -876,10 +1577,26 @@ if (numValid==4) { - float e00 = (*elevations)[i00]; - float e10 = (*elevations)[i10]; - float e01 = (*elevations)[i01]; - float e11 = (*elevations)[i11]; + + bool VALID = true; + for (MaskRecordVector::iterator mr = masks.begin(); mr != masks.end(); ++mr) { + float min_i = (*mr)._ndcMin.x() * (double)(numColumns-1); + float min_j = (*mr)._ndcMin.y() * (double)(numRows-1); + float max_i = (*mr)._ndcMax.x() * (double)(numColumns-1); + float max_j = (*mr)._ndcMax.y() * (double)(numRows-1); + + // We test if mask is completely in square + if(i+1 >= min_i && i <= max_i && j+1 >= min_j && j <= max_j) { + VALID = false; + break; + } + } + + if (VALID) { + float e00 = (*elevations)[i00]; + float e10 = (*elevations)[i10]; + float e01 = (*elevations)[i01]; + float e11 = (*elevations)[i11]; osg::Vec3f &v00 = (*surfaceVerts)[i00]; osg::Vec3f &v10 = (*surfaceVerts)[i10]; @@ -932,8 +1649,10 @@ (*normals)[i11] += normal2; } } + } } - else if (numValid==3) + // As skirtPoly is filling the mask bbox, we don't need to create isolated triangle + /*else if (numValid==3) { int validIndices[3]; int indexPtr = 0; @@ -971,7 +1690,7 @@ (*normals)[validIndices[1]] += normal; (*normals)[validIndices[2]] += normal; } - } + } */ } } @@ -987,22 +1706,22 @@ } } - surface->setUseDisplayList(false); - surface->setUseVertexBufferObjects(true); + - skirt->setUseDisplayList(false); - skirt->setUseVertexBufferObjects(true); - + MeshConsolidator::run( *surface ); + + if ( skirt ) + MeshConsolidator::run( *skirt ); + + for (MaskRecordVector::iterator mr = masks.begin(); mr != masks.end(); ++mr) + MeshConsolidator::run( *((*mr)._geom) ); + if (osgDB::Registry::instance()->getBuildKdTreesHint()==osgDB::ReaderWriter::Options::BUILD_KDTREES && osgDB::Registry::instance()->getKdTreeBuilder()) { - //osg::Timer_t before = osg::Timer::instance()->tick(); - //OE_NOTICE<<"osgTerrain::GeometryTechnique::build kd tree"< builder = osgDB::Registry::instance()->getKdTreeBuilder()->clone(); geode->accept(*builder); - //osg::Timer_t after = osg::Timer::instance()->tick(); - //OE_NOTICE<<"KdTree build time "<delta_m(before, after)<getDirty()) _terrainTile->init(~0x0,true); -#else - if (_terrainTile->getDirty()) _terrainTile->init(); -#endif - - _terrainTile->osg::Group::traverse( nv ); - - // traverse the actual geometry in the tile. this is especially - // important for geometry with ImageSequences and other things - // that require an update traversal. - if ( _transform.valid() ) - _transform->accept( nv ); - - return; - } - - else if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR) - { - if ( _transform.valid() ) - _transform->accept( nv ); - return; - } - - // the code from here on accounts for user traversals (intersections, etc) - - //TODO: evaluate this and see if we can get rid of it. - - if ( _terrainTile->getDirty() ) - { -#if OSG_MIN_VERSION_REQUIRED(2,9,8) - _terrainTile->init(~0x0, true); -#else - _terrainTile->init(); -#endif - } - if ( _transform.valid() ) + { _transform->accept( nv ); + } } void @@ -1063,11 +1744,12 @@ { SinglePassTerrainTechnique* ncThis = const_cast(this); - Threading::ScopedWriteLock lock( - static_cast( ncThis->_terrainTile )->getTileLayersMutex() ); + Threading::ScopedWriteLock lock( static_cast(ncThis->_tile)->getTileLayersMutex() ); if ( _transform.valid() ) + { _transform->releaseGLObjects( state ); + } if ( _backGeode.valid() ) { diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/StreamingTerrain osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/StreamingTerrain --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/StreamingTerrain 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/StreamingTerrain 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,87 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph +* Copyright 2008-2010 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 +*/ +#ifndef OSGEARTH_ENGINE_OSGTERRAIN_STREAMING_TERRAIN +#define OSGEARTH_ENGINE_OSGTERRAIN_STREAMING_TERRAIN 1 + +#include "Terrain" +#include "StreamingTile" +#include + +using namespace osgEarth; + +/** + * Terrain implementation that supports the SEQUENTIAL and PREEMPTIVE loading policies. + */ +class StreamingTerrain : public Terrain +{ +public: + StreamingTerrain( + const MapFrame& update_mapf, + const MapFrame& cull_mapf, + OSGTileFactory* factory, + bool quickReleaseGLObjects ); + + virtual const char* libraryName() const { return "osgEarth"; } + virtual const char* className() const { return "StreamingTerrain"; } + + //override + virtual Tile* createTile(const TileKey& key, GeoLocator* locator) const; + +public: + + TaskService* getImageryTaskService(int layerId); + TaskService* getElevationTaskService(); + TaskService* getTileGenerationTaskService(); + + /** + * Updates the catalog of task service threads - this gets called by the OSGTerrainEngine + * in response to a change in the Map's data model. The map frame is that of the terrain + * engine. + */ + void updateTaskServiceThreads( const MapFrame& mapf ); + + const LoadingPolicy& getLoadingPolicy() const { return _loadingPolicy; } + +protected: + + virtual ~StreamingTerrain(); + + //override + virtual unsigned getNumActiveTasks() const; + + //override + virtual void updateTraversal( osg::NodeVisitor& nv ); + +private: + + TaskService* createTaskService( const std::string& name, int id, int numThreads ); + TaskService* getTaskService( int id ); + + void refreshFamily( const MapInfo& info, const TileKey& key, StreamingTile::Relative* family, bool tileTableLocked ); + + typedef std::map< int, osg::ref_ptr< TaskService > > TaskServiceMap; + + TaskServiceMap _taskServices; + OpenThreads::Mutex _taskServiceMutex; + int _numLoadingThreads; + LoadingPolicy _loadingPolicy; + UID _elevationTaskServiceUID; +}; + +#endif // OSGEARTH_ENGINE_OSGTERRAIN_STREAMING_TERRAIN diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/StreamingTerrain.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/StreamingTerrain.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/StreamingTerrain.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/StreamingTerrain.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,350 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph +* Copyright 2008-2010 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 +*/ +#include "StreamingTerrain" +#include "StreamingTile" +#include "TransparentLayer" + +#include +#include +#include + +#include +#include +#include +#include + +#include + +using namespace osgEarth; +using namespace OpenThreads; + +#define LC "[StreamingTerrain] " + +//---------------------------------------------------------------------------- + +StreamingTerrain::StreamingTerrain(const MapFrame& update_mapf, + const MapFrame& cull_mapf, + OSGTileFactory* tileFactory, + bool quickReleaseGLObjects ) : + +Terrain( update_mapf, cull_mapf, tileFactory, quickReleaseGLObjects ), +_numLoadingThreads( 0 ) +{ + _loadingPolicy = tileFactory->getTerrainOptions().loadingPolicy().get(); + + setNumChildrenRequiringUpdateTraversal( 1 ); + _alwaysUpdate = true; + _numLoadingThreads = computeLoadingThreads(_loadingPolicy); + + OE_INFO << LC << "Using a total of " << _numLoadingThreads << " loading threads " << std::endl; +} + +StreamingTerrain::~StreamingTerrain() +{ + //nop +} + +Tile* +StreamingTerrain::createTile(const TileKey& key, GeoLocator* locator) const +{ + return new StreamingTile( key, locator, this->getQuickReleaseGLObjects() ); +} + +// This method is called by StreamingTerrain::traverse() in the UPDATE TRAVERSAL. +void +StreamingTerrain::refreshFamily(const MapInfo& mapInfo, + const TileKey& key, + StreamingTile::Relative* family, + bool tileTableLocked ) +{ + osgTerrain::TileID tileId = key.getTileId(); + + // geocentric maps wrap around in the X dimension. + bool wrapX = mapInfo.isGeocentric(); + unsigned int tileCountX, tileCountY; + mapInfo.getProfile()->getNumTiles( tileId.level, tileCountX, tileCountY ); + + // Relative::PARENT + { + family[StreamingTile::Relative::PARENT].expected = true; // TODO: is this always correct? + family[StreamingTile::Relative::PARENT].elevLOD = -1; + family[StreamingTile::Relative::PARENT].imageLODs.clear(); + family[StreamingTile::Relative::PARENT].tileID = osgTerrain::TileID( tileId.level-1, tileId.x/2, tileId.y/2 ); + + osg::ref_ptr parent; + getTile( family[StreamingTile::Relative::PARENT].tileID, parent, !tileTableLocked ); + if ( parent.valid() ) + { + family[StreamingTile::Relative::PARENT].elevLOD = parent->getElevationLOD(); + + ColorLayersByUID relLayers; + parent->getCustomColorLayers( relLayers ); + + for( ColorLayersByUID::const_iterator i = relLayers.begin(); i != relLayers.end(); ++i ) + { + family[StreamingTile::Relative::PARENT].imageLODs[i->first] = i->second.getLevelOfDetail(); + } + } + } + + // Relative::WEST + { + family[StreamingTile::Relative::WEST].expected = tileId.x > 0 || wrapX; + family[StreamingTile::Relative::WEST].elevLOD = -1; + family[StreamingTile::Relative::WEST].imageLODs.clear(); + family[StreamingTile::Relative::WEST].tileID = osgTerrain::TileID( tileId.level, tileId.x > 0? tileId.x-1 : tileCountX-1, tileId.y ); + osg::ref_ptr west; + getTile( family[StreamingTile::Relative::WEST].tileID, west, !tileTableLocked ); + if ( west.valid() ) + { + family[StreamingTile::Relative::WEST].elevLOD = west->getElevationLOD(); + + ColorLayersByUID relLayers; + west->getCustomColorLayers( relLayers ); + + for( ColorLayersByUID::const_iterator i = relLayers.begin(); i != relLayers.end(); ++i ) + { + family[StreamingTile::Relative::WEST].imageLODs[i->first] = i->second.getLevelOfDetail(); + } + } + } + + // Relative::NORTH + { + family[StreamingTile::Relative::NORTH].expected = tileId.y < (int)tileCountY-1; + family[StreamingTile::Relative::NORTH].elevLOD = -1; + family[StreamingTile::Relative::NORTH].imageLODs.clear(); + family[StreamingTile::Relative::NORTH].tileID = osgTerrain::TileID( tileId.level, tileId.x, tileId.y < (int)tileCountY-1 ? tileId.y+1 : 0 ); + osg::ref_ptr north; + getTile( family[StreamingTile::Relative::NORTH].tileID, north, !tileTableLocked ); + if ( north.valid() ) + { + family[StreamingTile::Relative::NORTH].elevLOD = north->getElevationLOD(); + + ColorLayersByUID relLayers; + north->getCustomColorLayers( relLayers ); + + for( ColorLayersByUID::const_iterator i = relLayers.begin(); i != relLayers.end(); ++i ) + { + family[StreamingTile::Relative::NORTH].imageLODs[i->first] = i->second.getLevelOfDetail(); + } + } + } + + // Relative::EAST + { + family[StreamingTile::Relative::EAST].expected = tileId.x < (int)tileCountX-1 || wrapX; + family[StreamingTile::Relative::EAST].elevLOD = -1; + family[StreamingTile::Relative::EAST].imageLODs.clear(); + family[StreamingTile::Relative::EAST].tileID = osgTerrain::TileID( tileId.level, tileId.x < (int)tileCountX-1 ? tileId.x+1 : 0, tileId.y ); + osg::ref_ptr east; + getTile( family[StreamingTile::Relative::EAST].tileID, east, !tileTableLocked ); + if ( east.valid() ) + { + family[StreamingTile::Relative::EAST].elevLOD = east->getElevationLOD(); + + ColorLayersByUID relLayers; + east->getCustomColorLayers( relLayers ); + + for( ColorLayersByUID::const_iterator i = relLayers.begin(); i != relLayers.end(); ++i ) + { + family[StreamingTile::Relative::EAST].imageLODs[i->first] = i->second.getLevelOfDetail(); + } + } + } + + // Relative::SOUTH + { + family[StreamingTile::Relative::SOUTH].expected = tileId.y > 0; + family[StreamingTile::Relative::SOUTH].elevLOD = -1; + family[StreamingTile::Relative::SOUTH].imageLODs.clear(); + family[StreamingTile::Relative::SOUTH].tileID = osgTerrain::TileID( tileId.level, tileId.x, tileId.y > 0 ? tileId.y-1 : tileCountY-1 ); + osg::ref_ptr south; + getTile( family[StreamingTile::Relative::SOUTH].tileID, south, !tileTableLocked ); + if ( south.valid() ) + { + family[StreamingTile::Relative::SOUTH].elevLOD = south->getElevationLOD(); + + ColorLayersByUID relLayers; + south->getCustomColorLayers( relLayers ); + + for( ColorLayersByUID::const_iterator i = relLayers.begin(); i != relLayers.end(); ++i ) + { + family[StreamingTile::Relative::SOUTH].imageLODs[i->first] = i->second.getLevelOfDetail(); + } + } + } +} + +unsigned +StreamingTerrain::getNumActiveTasks() const +{ + ScopedLock lock(const_cast(this)->_taskServiceMutex ); + + unsigned int total = 0; + for (TaskServiceMap::const_iterator itr = _taskServices.begin(); itr != _taskServices.end(); ++itr) + { + total += itr->second->getNumRequests(); + } + return total; +} + +void +StreamingTerrain::updateTraversal( osg::NodeVisitor& nv ) +{ + // this stamp keeps track of when requests are dispatched. If a request's stamp gets too + // old, it is considered "expired" and subject to cancelation + int stamp = nv.getFrameStamp()->getFrameNumber(); + + // update the frame stamp on the task services. This is necessary to support + // automatic request cancelation for image requests. + { + ScopedLock lock( _taskServiceMutex ); + for (TaskServiceMap::iterator i = _taskServices.begin(); i != _taskServices.end(); ++i) + { + i->second->setStamp( stamp ); + } + } + + // next, go through the live tiles and process update-traversal requests. This + // requires a read-lock on the master tiles table. + { + Threading::ScopedReadLock tileTableReadLock( _tilesMutex ); + + for( TileTable::const_iterator i = _tiles.begin(); i != _tiles.end(); ++i ) + { + StreamingTile* tile = static_cast( i->second.get() ); + + // update the neighbor list for each tile. + refreshFamily( _update_mapf.getMapInfo(), tile->getKey(), tile->getFamily(), true ); + + tile->servicePendingElevationRequests( _update_mapf, stamp, true ); + tile->serviceCompletedRequests( _update_mapf, true ); + } + } +} + +TaskService* +StreamingTerrain::createTaskService( const std::string& name, int id, int numThreads ) +{ + ScopedLock lock( _taskServiceMutex ); + + // first, double-check that the service wasn't created during the locking process: + TaskServiceMap::iterator itr = _taskServices.find(id); + if (itr != _taskServices.end()) + return itr->second.get(); + + // ok, make a new one + TaskService* service = new TaskService( name, numThreads ); + _taskServices[id] = service; + return service; +} + +TaskService* +StreamingTerrain::getTaskService(int id) +{ + ScopedLock lock( _taskServiceMutex ); + TaskServiceMap::iterator itr = _taskServices.find(id); + if (itr != _taskServices.end()) + { + return itr->second.get(); + } + return NULL; +} + +#define ELEVATION_TASK_SERVICE_ID 9999 +#define TILE_GENERATION_TASK_SERVICE_ID 10000 + +TaskService* +StreamingTerrain::getElevationTaskService() +{ + TaskService* service = getTaskService( ELEVATION_TASK_SERVICE_ID ); + if (!service) + { + service = createTaskService( "elevation", ELEVATION_TASK_SERVICE_ID, 1 ); + } + return service; +} + + +TaskService* +StreamingTerrain::getImageryTaskService(int layerId) +{ + TaskService* service = getTaskService( layerId ); + if (!service) + { + std::stringstream buf; + buf << "layer " << layerId; + std::string bufStr = buf.str(); + service = createTaskService( bufStr, layerId, 1 ); + } + return service; +} + +TaskService* +StreamingTerrain::getTileGenerationTaskService() +{ + TaskService* service = getTaskService( TILE_GENERATION_TASK_SERVICE_ID ); + if (!service) + { + int numCompileThreads = + _loadingPolicy.numCompileThreads().isSet() ? osg::maximum( 1, _loadingPolicy.numCompileThreads().value() ) : + (int)osg::maximum( 1.0f, _loadingPolicy.numCompileThreadsPerCore().value() * (float)GetNumberOfProcessors() ); + + service = createTaskService( "tilegen", TILE_GENERATION_TASK_SERVICE_ID, numCompileThreads ); + } + return service; +} + +void +StreamingTerrain::updateTaskServiceThreads( const MapFrame& mapf ) +{ + //Get the maximum elevation weight + float elevationWeight = 0.0f; + for (ElevationLayerVector::const_iterator itr = mapf.elevationLayers().begin(); itr != mapf.elevationLayers().end(); ++itr) + { + ElevationLayer* layer = itr->get(); + float w = layer->getTerrainLayerOptions().loadingWeight().value(); + if (w > elevationWeight) elevationWeight = w; + } + + float totalImageWeight = 0.0f; + for (ImageLayerVector::const_iterator itr = mapf.imageLayers().begin(); itr != mapf.imageLayers().end(); ++itr) + { + totalImageWeight += itr->get()->getTerrainLayerOptions().loadingWeight().value(); + } + + float totalWeight = elevationWeight + totalImageWeight; + + if (elevationWeight > 0.0f) + { + //Determine how many threads each layer gets + int numElevationThreads = (int)osg::round((float)_numLoadingThreads * (elevationWeight / totalWeight )); + OE_INFO << LC << "Elevation Threads = " << numElevationThreads << std::endl; + getElevationTaskService()->setNumThreads( numElevationThreads ); + } + + for (ImageLayerVector::const_iterator itr = mapf.imageLayers().begin(); itr != mapf.imageLayers().end(); ++itr) + { + const TerrainLayerOptions& opt = itr->get()->getTerrainLayerOptions(); + int imageThreads = (int)osg::round((float)_numLoadingThreads * (opt.loadingWeight().value() / totalWeight )); + OE_INFO << LC << "Image Threads for " << itr->get()->getName() << " = " << imageThreads << std::endl; + getImageryTaskService( itr->get()->getUID() )->setNumThreads( imageThreads ); + } +} diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/StreamingTile osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/StreamingTile --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/StreamingTile 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/StreamingTile 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,145 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph +* Copyright 2008-2010 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 +*/ +#ifndef OSGEARTH_ENGINE_OSGTERRAIN_STREAMING_TILE +#define OSGEARTH_ENGINE_OSGTERRAIN_STREAMING_TILE 1 + +#include "Tile" +#include + +class TileFactory; +class StreamingTerrain; + +using namespace osgEarth; + +//------------------------------------------------------------------------ + +class StreamingTile : public Tile +{ +public: + // Please refer the StreamingTerrain::refreshFamily() method for more info on this structure. + struct Relative + { + int getImageLOD(unsigned int layerID) + { + LayerIDtoLODMap::iterator itr = imageLODs.find(layerID); + if (itr != imageLODs.end()) return itr->second; + return -1; + } + + typedef std::map LayerIDtoLODMap; + + bool expected; + int elevLOD; + LayerIDtoLODMap imageLODs; + osgTerrain::TileID tileID; + + enum Direction { + PARENT =0, + WEST =1, + NORTH =2, + EAST =3, + SOUTH =4 + }; + + Relative() { } + }; + +public: + StreamingTile( const TileKey& key, GeoLocator* keyLocator, bool quickReleaseGLObjects ); + + virtual const char* libraryName() const { return "osgEarth"; } + virtual const char* className() const { return "StreamingTile"; } + + // Updates and services this tile's image request tasks + void servicePendingImageRequests( const MapFrame& mapf, int stamp ); + + // Updates and services this tile's heightfield request tasks + void servicePendingElevationRequests( const MapFrame& mapf, int stamp, bool tileTableLocked ); + + // returns TRUE if the tile was modified as a result of a completed request. + bool serviceCompletedRequests( const MapFrame& mapf, bool tileTableLocked ); + + /** Setting this hint tells the tile whether it should bother trying to load elevation data. */ + void setHasElevationHint( bool hasElevation ); + + /** Gets whether the tile's real (not placeholder) elevation data has been loaded. */ + bool isElevationLayerUpToDate() const { return _elevationLayerUpToDate; } + + /** Gets or sets the LOD of this tile's current heightfield data. */ + int getElevationLOD() const { return _elevationLOD; } + void setElevationLOD( int lod ); + + /** Gets the terrain object to which this tile belongs. */ + class StreamingTerrain* getStreamingTerrain(); + const class StreamingTerrain* getStreamingTerrain() const; + + // updates one image layer + void updateImagery( ImageLayer* layer, const MapFrame& mapf, OSGTileFactory* factory ); + + // are we using a task service for tile generation? + bool getUseTileGenRequest() const { return _useTileGenRequest; } + + // gets this tile's relatives + Relative* getFamily() { return _family; } + + // marks a request to regenerate the tile based on the specified change(s). + void queueTileUpdate( TileUpdate::Action action, int index =-1 ); + + void applyImmediateTileUpdate( TileUpdate::Action action, int index =-1 ); + + // marks any pending or running requests for cancelation, and returns true if they + // are all canceled. + virtual bool cancelActiveTasks(); //override + + void resetElevationRequests( const MapFrame& mapf ); + +protected: + + ~StreamingTile(); + +private: + + bool _requestsInstalled; + bool _hasElevation; + bool _elevationLayerDirty; + bool _colorLayersDirty; + bool _elevationLayerRequested; + bool _elevationLayerUpToDate; + int _elevationLOD; + bool _useTileGenRequest; + bool _sequentialImagery; + + typedef std::queue TileUpdateQueue; + TileUpdateQueue _tileUpdates; + + TaskRequestList _requests; + osg::ref_ptr _elevRequest; + osg::ref_ptr _elevPlaceholderRequest; + osg::ref_ptr _tileGenRequest; + + Relative _family[5]; + + /** Deals with completed requests during the UPDATE traversal. */ + void installRequests( const MapFrame& mapf, int stamp ); + bool readyForNewElevation(); + bool readyForNewImagery(osgEarth::ImageLayer* layer, int currentLOD); +}; + + +#endif // OSGEARTH_ENGINE_OSGTERRAIN_STREAMING_TILE diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/StreamingTile.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/StreamingTile.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/StreamingTile.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/StreamingTile.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,895 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph +* Copyright 2008-2010 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 +*/ +#include "StreamingTile" +#include "StreamingTerrain" +#include "CustomTerrainTechnique" +#include "TransparentLayer" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +using namespace osgEarth; +using namespace OpenThreads; + +#define LC "[StreamingTile] " + +//---------------------------------------------------------------------------- + +namespace +{ + // this progress callback checks to see whether the request being serviced is + // out of date with respect to the task service that is running it. It checks + // for a disparity in frame stamps, and reports that the request should be + // canceled if it appears the request has been abandoned by the Tile that + // originally scheduled it. + struct StampedProgressCallback : ProgressCallback + { + public: + StampedProgressCallback(TaskRequest* request, TaskService* service): + _request(request), + _service(service) + { + } + + //todo: maybe we should pass TaskRequest in as an argument + bool reportProgress(double current, double total) + { + //Check to see if we were marked cancelled on a previous check + if (_canceled) return _canceled; + + _canceled = (_service->getStamp() - _request->getStamp() > 2); + return _canceled; + } + + TaskRequest* _request; + TaskService* _service; + }; + + + // NOTE: Task requests run in background threads. So we pass in a map frame and + // make a clone of it to use in that thread. Each Task must have its own MapFrame + // so it's operating in its own sandbox. + + struct TileLayerRequest : public TaskRequest + { + TileLayerRequest( const TileKey& key, const MapFrame& mapf, OSGTileFactory* tileFactory ) + : _key( key ), + _mapf(mapf, "osgterrain.TileLayerRequest"), + _tileFactory(tileFactory), + _numTries(0), + _maxTries(3) { } + + TileKey _key; + MapFrame _mapf; + //osg::ref_ptr _map; + osg::ref_ptr _tileFactory; + unsigned int _numTries; + unsigned int _maxTries; + }; + + struct TileColorLayerRequest : public TileLayerRequest + { + TileColorLayerRequest( const TileKey& key, const MapFrame& mapf, OSGTileFactory* tileFactory, UID layerUID ) + : TileLayerRequest( key, mapf, tileFactory ), _layerUID(layerUID) { } + + void operator()( ProgressCallback* progress ) + { + osg::ref_ptr imageLayer = _mapf.getImageLayerByUID( _layerUID ); + if ( imageLayer.valid() ) + { + _result = _tileFactory->createImageLayer( _mapf.getMapInfo(), imageLayer.get(), _key, progress ); + if (!wasCanceled()) + { + _numTries++; + } + } + } + UID _layerUID; + }; + + struct TileElevationLayerRequest : public TileLayerRequest + { + TileElevationLayerRequest( const TileKey& key, const MapFrame& mapf, OSGTileFactory* tileFactory ) + : TileLayerRequest( key, mapf, tileFactory ) + { + //nop + } + + void operator()( ProgressCallback* progress ) + { + _result = _tileFactory->createHeightFieldLayer( _mapf, _key, true ); //exactOnly=true + _numTries++; + } + }; + + struct TileElevationPlaceholderLayerRequest : public TileLayerRequest + { + TileElevationPlaceholderLayerRequest( const TileKey& key, const MapFrame& mapf, OSGTileFactory* tileFactory, GeoLocator* keyLocator ) + : TileLayerRequest( key, mapf, tileFactory ), + _parentKey( key.createParentKey() ), + _keyLocator(keyLocator) + { + //nop + } + + void setParentHF( osg::HeightField* parentHF ) + { + _parentHF = parentHF; + } + + void setNextLOD( int nextLOD ) + { + _nextLOD = nextLOD; + } + + void operator()( ProgressCallback* progress ) + { + if ( !progress->isCanceled() ) + { + _result = _tileFactory->createPlaceholderHeightfieldLayer( + _parentHF.get(), + _parentKey, + _key, + _keyLocator.get() ); + } + } + + osg::ref_ptr _parentHF; + TileKey _parentKey; + osg::ref_ptr _keyLocator; + int _nextLOD; + }; + + // A task request that rebuilds a tile's terrain technique in the background. It + // re-compiles the geometry but does NOT apply the updates (since this constitutes + // altering the scene graph and must therefore be done in the update traversal). + struct TileGenRequest : public TaskRequest + { + TileGenRequest( StreamingTile* tile, const TileUpdate& update ) : + _tile( tile ), _update(update) { } + + void operator()( ProgressCallback* progress ) + { + if (_tile.valid()) + { + CustomTerrainTechnique* tech = dynamic_cast( _tile->getTerrainTechnique() ); + if (tech) + { + tech->compile( _update, progress ); + } + } + + //We don't need the tile anymore + _tile = NULL; + } + + osg::ref_ptr< StreamingTile > _tile; + TileUpdate _update; + }; +} + +//------------------------------------------------------------------------ + +StreamingTile::StreamingTile(const TileKey& key, + GeoLocator* keyLocator, + bool quickReleaseGLObjects ) : + +Tile( key, keyLocator, quickReleaseGLObjects ), + +_requestsInstalled ( false ), +_elevationLayerDirty ( false ), +_colorLayersDirty ( false ), +_elevationLayerUpToDate( true ), +_elevationLOD ( key.getLevelOfDetail() ), +_useTileGenRequest ( true ) +{ + // because the lowest LOD (1) is always loaded fully: + _elevationLayerUpToDate = _key.getLevelOfDetail() <= 1; +} + +StreamingTile::~StreamingTile() +{ + //nop +} + +bool +StreamingTile::cancelActiveTasks() +{ + // This method ensures that all requests owned by this object are stopped and released + // by the corresponding task service prior to destructing the tile. Called by + // CustomTerrain::updateTileTable(). + + bool done = true; + + // Cancel all active requests + if ( _requestsInstalled ) + { + for( TaskRequestList::iterator i = _requests.begin(); i != _requests.end(); ++i ) + { + i->get()->cancel(); + } + + if ( _elevRequest.valid() ) + { + _elevRequest->cancel(); + } + + if (_elevPlaceholderRequest.valid()) + { + _elevPlaceholderRequest->cancel(); + } + + if (_tileGenRequest.valid()) + { + _tileGenRequest->cancel(); + } + } + + return done; +} + +void +StreamingTile::setElevationLOD( int lod ) +{ + _elevationLOD = lod; + _elevationLayerUpToDate = _elevationLOD == (int)_key.getLevelOfDetail(); +} + +StreamingTerrain* +StreamingTile::getStreamingTerrain() +{ + return static_cast( getTerrain() ); +} + +const StreamingTerrain* +StreamingTile::getStreamingTerrain() const +{ + return const_cast(this)->getStreamingTerrain(); +} + +void +StreamingTile::setHasElevationHint( bool hint ) +{ + _hasElevation = hint; +} + +// returns TRUE if it's safe for this tile to load its next elevation data layer. +bool +StreamingTile::readyForNewElevation() +{ + bool ready = true; + + if ( _elevationLOD == (int)_key.getLevelOfDetail() ) + { + ready = false; + } + else if ( _family[Relative::PARENT].elevLOD < 0 ) + { + ready = false; + } + else + { + for( int i=Relative::PARENT; i<=Relative::SOUTH; i++) + { + if ( _family[i].expected && _family[i].elevLOD >= 0 && _family[i].elevLOD < _elevationLOD ) + { + ready = false; + break; + } + } + + // if the next LOD is not the final, but our placeholder is up to date, we're not ready. + if ( ready && _elevationLOD+1 < (int)_key.getLevelOfDetail() && _elevationLOD == _family[Relative::PARENT].elevLOD ) + { + ready = false; + } + } + +#ifdef PREEMPTIVE_DEBUG + OE_NOTICE + << "Tile (" << _key.str() << ") at (" << _elevationLOD << "), parent at (" + << _family[PARENT].elevLOD << "), sibs at ("; + if ( _family[WEST].expected ) osg::notify( osg::NOTICE ) << "W=" << _family[WEST].elevLOD << " "; + if ( _family[NORTH].expected ) osg::notify( osg::NOTICE ) << "N=" << _family[NORTH].elevLOD << " "; + if ( _family[EAST].expected ) osg::notify( osg::NOTICE ) << "E=" << _family[EAST].elevLOD << " "; + if ( _family[SOUTH].expected ) osg::notify( osg::NOTICE ) << "S=" << _family[SOUTH].elevLOD << " "; + OE_NOTICE << "), ready = " << (ready? "YES" : "no") << std::endl; +#endif + + return ready; +} + + + +// returns TRUE if it's safe for this tile to load its next elevation data layer. +bool +StreamingTile::readyForNewImagery(ImageLayer* layer, int currentLOD) +{ + bool ready = true; + + if ( currentLOD == (int)_key.getLevelOfDetail() ) + { + ready = false; + } + else if ( _family[Relative::PARENT].getImageLOD( layer->getUID() ) < 0 ) + { + ready = false; + } + else + { + for( int i=Relative::PARENT; i<=Relative::SOUTH; i++) + { + if (_family[i].expected && + _family[i].getImageLOD( layer->getUID() ) >= 0 && + _family[i].getImageLOD( layer->getUID() ) < currentLOD ) + { + ready = false; + break; + } + } + + // if the next LOD is not the final, but our placeholder is up to date, we're not ready. + if (ready && + currentLOD + 1 < (int)_key.getLevelOfDetail() && + currentLOD == _family[Relative::PARENT].getImageLOD( layer->getUID() ) ) + { + ready = false; + } + } + + return ready; +} + + +#define PRI_IMAGE_OFFSET 0.1f // priority offset of imagery relative to elevation +#define PRI_LAYER_OFFSET 0.1f // priority offset of image layer(x) vs. image layer(x+1) + +void +StreamingTile::installRequests( const MapFrame& mapf, int stamp ) +{ + StreamingTerrain* terrain = getStreamingTerrain(); + OSGTileFactory* tileFactory = terrain->getTileFactory(); + + bool hasElevationLayer; + { + Threading::ScopedReadLock sharedLock( _tileLayersMutex ); + hasElevationLayer = this->getElevationLayer() != NULL; + } + + if ( hasElevationLayer ) + { + resetElevationRequests( mapf ); + } + + // safely loop through the map layers and schedule imagery updates for each: + for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); ++i ) + { + updateImagery( i->get(), mapf, tileFactory ); + } + + _requestsInstalled = true; +} + +void +StreamingTile::resetElevationRequests( const MapFrame& mapf ) +{ + if (_elevRequest.valid() && _elevRequest->isRunning()) _elevRequest->cancel(); + if (_elevPlaceholderRequest.valid() && _elevPlaceholderRequest->isRunning()) _elevPlaceholderRequest->cancel(); + + StreamingTerrain* terrain = getStreamingTerrain(); + + // this request will load real elevation data for the tile: + _elevRequest = new TileElevationLayerRequest(_key, mapf, terrain->getTileFactory()); + float priority = (float)_key.getLevelOfDetail(); + _elevRequest->setPriority( priority ); + std::stringstream ss; + ss << "TileElevationLayerRequest " << _key.str() << std::endl; + std::string ssStr; + ssStr = ss.str(); + _elevRequest->setName( ssStr ); + + // this request will load placeholder elevation data for the tile: + _elevPlaceholderRequest = new TileElevationPlaceholderLayerRequest( + _key, mapf, terrain->getTileFactory(), _locator.get() ); + + _elevPlaceholderRequest->setPriority( priority ); + ss.str(""); + ss << "TileElevationPlaceholderLayerRequest " << _key.str() << std::endl; + ssStr = ss.str(); + _elevPlaceholderRequest->setName( ssStr ); +} + + +// called from installRequests (cull traversal) or terrainengine (main thread) ... so be careful! +// +// this method queues up a new tile imagery request, superceding any existing request that +// might be in the queue. +void +StreamingTile::updateImagery( ImageLayer* imageLayer, const MapFrame& mapf, OSGTileFactory* tileFactory) +{ + StreamingTerrain* terrain = getStreamingTerrain(); + + // imagery is slighty higher priority than elevation data + TaskRequest* r = new TileColorLayerRequest( _key, mapf, tileFactory, imageLayer->getUID() ); + std::stringstream ss; + ss << "TileColorLayerRequest " << _key.str() << std::endl; + std::string ssStr; + ssStr = ss.str(); + r->setName( ssStr ); + r->setState( osgEarth::TaskRequest::STATE_IDLE ); + + // in image-sequential mode, we want to prioritize lower-LOD imagery since it + // needs to come in before higher-resolution stuff. + if ( terrain->getLoadingPolicy().mode() == LoadingPolicy::MODE_SEQUENTIAL ) + { + r->setPriority( -(float)_key.getLevelOfDetail() + PRI_IMAGE_OFFSET ); + } + + // in image-preemptive mode, the highest LOD should get higher load priority: + else // MODE_PREEMPTIVE + { + r->setPriority( PRI_IMAGE_OFFSET + (float)_key.getLevelOfDetail()); + } + + r->setProgressCallback( new StampedProgressCallback( + r, + terrain->getImageryTaskService( imageLayer->getUID() ) ) ); + + //If we already have a request for this layer, remove it from the list and use the new one + for( TaskRequestList::iterator i = _requests.begin(); i != _requests.end(); ) + { + TileColorLayerRequest* r2 = static_cast( i->get() ); + if ( r2->_layerUID == imageLayer->getUID() ) + i = _requests.erase( i ); + else + ++i; + } + + //Add the new imagery request + _requests.push_back( r ); +} + +// This method is called during the UPDATE TRAVERSAL in StreamingTerrain. +void +StreamingTile::servicePendingImageRequests( const MapFrame& mapf, int stamp ) +{ + // Don't do anything until we have been added to the scene graph + if ( !_hasBeenTraversed ) return; + + // install our requests if they are not already installed: + if ( !_requestsInstalled ) + { + // since we're in the CULL thread, use the cull thread map frame: + installRequests( mapf, stamp ); + } + + for( TaskRequestList::iterator i = _requests.begin(); i != _requests.end(); ++i ) + { + TileColorLayerRequest* r = static_cast( i->get() ); + + //If a request has been marked as IDLE, the TaskService has tried to service it + //and it was either deemed out of date or was cancelled, so we need to add it again. + if ( r->isIdle() ) + { + //OE_NOTICE << "Queuing IR (" << _key.str() << ")" << std::endl; + r->setStamp( stamp ); + getStreamingTerrain()->getImageryTaskService( r->_layerUID )->add( r ); + } + else if ( !r->isCompleted() ) + { + r->setStamp( stamp ); + } + } +} + +// This method is called from the UPDATE TRAVERSAL, from CustomTerrain::traverse. +void +StreamingTile::servicePendingElevationRequests( const MapFrame& mapf, int stamp, bool tileTableLocked ) +{ + //Don't do anything until we have been added to the scene graph + if ( !_hasBeenTraversed ) return; + + // install our requests if they are not already installed: + if ( !_requestsInstalled ) + { + installRequests( mapf, stamp ); + } + + if ( _hasElevation && !_elevationLayerUpToDate && _elevRequest.valid() && _elevPlaceholderRequest.valid() ) + { + StreamingTerrain* terrain = getStreamingTerrain(); + + // update the main elevation request if it's running: + if ( !_elevRequest->isIdle() ) + { +#ifdef PREEMPTIVE_DEBUG + OE_NOTICE << "Tile (" << _key.str() << ") .. ER not idle" << std::endl; +#endif + + if ( !_elevRequest->isCompleted() ) + { + _elevRequest->setStamp( stamp ); + } + } + + // update the placeholder request if it's running: + else if ( !_elevPlaceholderRequest->isIdle() ) + { +#ifdef PREEMPTIVE_DEBUG + OE_NOTICE << "Tile (" << _key.str() << ") .. PR not idle" << std::endl; +#endif + if ( !_elevPlaceholderRequest->isCompleted() ) + { + _elevPlaceholderRequest->setStamp( stamp ); + } + } + + // otherwise, see if it is legal yet to start a new request: + else if ( readyForNewElevation() ) + { + if ( _elevationLOD + 1 == _key.getLevelOfDetail() ) + { + _elevRequest->setStamp( stamp ); + _elevRequest->setProgressCallback( new ProgressCallback() ); + terrain->getElevationTaskService()->add( _elevRequest.get() ); +#ifdef PREEMPTIVE_DEBUG + OE_NOTICE << "..queued FE req for (" << _key.str() << ")" << std::endl; +#endif + } + + else if ( _family[Relative::PARENT].elevLOD > _elevationLOD ) + { + osg::ref_ptr parentTile; + terrain->getTile( _family[Relative::PARENT].tileID, parentTile, !tileTableLocked ); + + if ( _elevationLOD < _family[Relative::PARENT].elevLOD && parentTile.valid() ) + { + TileElevationPlaceholderLayerRequest* er = static_cast(_elevPlaceholderRequest.get()); + + er->setStamp( stamp ); + er->setProgressCallback( new ProgressCallback() ); + float priority = (float)_key.getLevelOfDetail(); + er->setPriority( priority ); + //TODO: should there be a read lock here when accessing the parent tile's elevation layer? GW + osgTerrain::HeightFieldLayer* hfLayer = static_cast(parentTile->getElevationLayer()); + er->setParentHF( hfLayer->getHeightField() ); + er->setNextLOD( _family[Relative::PARENT].elevLOD ); + terrain->getElevationTaskService()->add( er ); +#ifdef PREEMPTIVE_DEBUG + OE_NOTICE << "..queued PH req for (" << _key.str() << ")" << std::endl; +#endif + } + + else + { +#ifdef PREEMPTIVE_DEBUG + OE_NOTICE << "...tile (" << _key.str() << ") ready, but nothing to do." << std::endl; +#endif + } + } + } + } +} + +void +StreamingTile::queueTileUpdate( TileUpdate::Action action, int value ) +{ + if ( _useTileGenRequest ) + { + _tileUpdates.push( TileUpdate(action, value) ); + } + else + { + Tile::queueTileUpdate( action, value ); + } +} + +// called from the UPDATE TRAVERSAL, because this method can potentially alter +// the scene graph. +bool +StreamingTile::serviceCompletedRequests( const MapFrame& mapf, bool tileTableLocked ) +{ + //Don't do anything until we have been added to the scene graph + if (!_hasBeenTraversed) return false; + + bool tileModified = false; + + if ( !_requestsInstalled ) + return false; + + // First service the tile generator: + if ( _tileGenRequest.valid() && _tileGenRequest->isCompleted() ) + { + CustomTerrainTechnique* tech = dynamic_cast( getTerrainTechnique() ); + if ( tech ) + { + //TODO: consider waiting to apply if there are still more tile updates in the queue. + if ( _tileUpdates.size() == 0 ) + { + tileModified = tech->applyTileUpdates(); + } + } + _tileGenRequest = 0L; + } + + + // now deal with imagery. + const LoadingPolicy& lp = getStreamingTerrain()->getLoadingPolicy(); + + StreamingTerrain* terrain = getStreamingTerrain(); + + //Check each layer independently. + for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); ++i ) + { + ImageLayer* imageLayer = i->get(); + + bool checkForFinalImagery = false; + + CustomColorLayer colorLayer; + if ( getCustomColorLayer( imageLayer->getUID(), colorLayer ) ) + { + if ( lp.mode() == LoadingPolicy::MODE_PREEMPTIVE ) + { + // in preemptive mode, always check for the final imagery - there are no intermediate + // placeholders. + checkForFinalImagery = true; + } + else if (lp.mode() == LoadingPolicy::MODE_SEQUENTIAL && + readyForNewImagery(imageLayer, colorLayer.getLevelOfDetail()) ) + { + // in sequential mode, we have to incrementally increase imagery resolution by + // creating placeholders based of parent tiles, one LOD at a time. + if ( colorLayer.getLevelOfDetail() + 1 < (int)_key.getLevelOfDetail() ) + { + // if the parent's image LOD is higher than ours, replace ours with the parent's + // since it is a higher-resolution placeholder: + if ( _family[Relative::PARENT].getImageLOD(colorLayer.getUID()) > colorLayer.getLevelOfDetail() ) + { + osg::ref_ptr parentTile; + getStreamingTerrain()->getTile( _family[Relative::PARENT].tileID, parentTile, !tileTableLocked ); + + // Set the color layer to the parent color layer as a placeholder. + CustomColorLayer parentColorLayer; + if ( parentTile->getCustomColorLayer( colorLayer.getUID(), parentColorLayer ) ) + { + this->setCustomColorLayer( parentColorLayer ); + } + + // ... and queue up an update request. + queueTileUpdate( TileUpdate::UPDATE_IMAGE_LAYER, colorLayer.getUID() ); + } + } + else + { + // we've gone as far as we can with placeholders; time to check for the + // final imagery tile. + checkForFinalImagery = true; + } + } + } + + if ( checkForFinalImagery ) + { + // Then the image requests: + for( TaskRequestList::iterator itr = _requests.begin(); itr != _requests.end(); ) + { + bool increment = true; + TileColorLayerRequest* r = static_cast( itr->get() ); + //We only care about the current layer we are checking + if ( r->_layerUID == imageLayer->getUID() ) + { + if ( itr->get()->isCompleted() ) + { + if ( r->wasCanceled() ) + { + //Reset the cancelled task to IDLE and give it a new progress callback. + r->setState( TaskRequest::STATE_IDLE ); + r->setProgressCallback( new StampedProgressCallback( + r, terrain->getImageryTaskService( r->_layerUID ))); + r->reset(); + } + else // success.. + { + //See if we even care about the request + if ( !mapf.getImageLayerByUID( r->_layerUID ) ) + { + //The maplayer was probably deleted + OE_DEBUG << "Layer uid=" << r->_layerUID << " no longer exists, ignoring TileColorLayerRequest " << std::endl; + itr = _requests.erase(itr); + increment = false; + } + else + { + CustomColorLayerRef* result = static_cast( r->getResult() ); + if ( result ) + { + this->setCustomColorLayer( result->_layer ); + + queueTileUpdate( TileUpdate::UPDATE_IMAGE_LAYER, r->_layerUID ); + + //OE_NOTICE << "Complete IR (" << _key.str() << ") layer=" << r->_layerId << std::endl; + + // remove from the list (don't reference "r" after this!) + itr = _requests.erase( itr ); + increment = false; + } + else + { + if (r->_numTries > r->_maxTries) + { + CustomColorLayer oldLayer; + if ( this->getCustomColorLayer( r->_layerUID, oldLayer ) ) + { + // apply the old color layer but with a new LOD. + this->setCustomColorLayer( CustomColorLayer( + oldLayer.getMapLayer(), + oldLayer.getImage(), + oldLayer.getLocator(), + _key.getLevelOfDetail(), + _key )); + + itr = _requests.erase( itr ); + increment = false; + OE_DEBUG << "Tried (" << _key.str() << ") (layer uid=" << r->_layerUID << "), too many times, moving on...." << std::endl; + } + } + else + { + OE_DEBUG << "IReq error (" << _key.str() << ") (layer uid=" << r->_layerUID << "), retrying" << std::endl; + + //The color layer request failed, probably due to a server error. Reset it. + r->setState( TaskRequest::STATE_IDLE ); + r->reset(); + } + } + } + } + } + } + + if ( increment ) + ++itr; + } + } + } + + // Finally, the elevation requests: + if ( _hasElevation && !_elevationLayerUpToDate && _elevRequest.valid() && _elevPlaceholderRequest.valid() ) + { + // First, check is the Main elevation request is done. If so, we will now have the final HF data + // and can shut down the elevation requests for this tile. + if ( _elevRequest->isCompleted() ) + { + if ( _elevRequest->wasCanceled() ) + { + // If the request was canceled, reset it to IDLE and reset the callback. On the next + _elevRequest->setState( TaskRequest::STATE_IDLE ); + _elevRequest->setProgressCallback( new ProgressCallback() ); + _elevRequest->reset(); + } + else // success: + { + // if the elevation request succeeded, install the new elevation layer! + TileElevationLayerRequest* r = static_cast( _elevRequest.get() ); + osg::ref_ptr newHFLayer = static_cast( r->getResult() ); + if ( newHFLayer.valid() && newHFLayer->getHeightField() != NULL ) + { + newHFLayer->getHeightField()->setSkirtHeight( + terrain->getTileFactory()->getTerrainOptions().heightFieldSkirtRatio().get() * + this->getBound().radius() ); + + // need to write-lock the layer data since we'll be changing it: + { + Threading::ScopedWriteLock lock( _tileLayersMutex ); + this->setElevationLayer( newHFLayer.get() ); + this->dirtyBound(); + } + + // the tile needs rebuilding. This will kick off a TileGenRequest. + queueTileUpdate( TileUpdate::UPDATE_ELEVATION ); + + // finalize the LOD marker for this tile, so other tiles can see where we are. + _elevationLOD = _key.getLevelOfDetail(); + + #ifdef PREEMPTIVE_DEBUG + OE_NOTICE << "Tile (" << _key.str() << ") final HF, LOD (" << _elevationLOD << ")" << std::endl; + #endif + // this was the final elev request, so mark elevation as DONE. + _elevationLayerUpToDate = true; + + // GW- just reset these and leave them alone and let cancelRequests() take care of cleanup later. + // done with our Elevation requests! + //_elevRequest = 0L; + //_elevPlaceholderRequest = 0L; + } + else + { + //We've tried to get the tile's elevation but couldn't. Just mark the elevation layer as up to date and move on. + _elevationLOD = _key.getLevelOfDetail(); + _elevationLayerUpToDate = true; + + //This code will retry indefinitely. We need to have a way to limit the number of retries since + //it will block neighbor tiles from loading. + //_elevRequest->setState( TaskRequest::STATE_IDLE ); + //_elevRequest->reset(); + } + } + } + + else if ( _elevPlaceholderRequest->isCompleted() ) + { + TileElevationPlaceholderLayerRequest* r = + static_cast(_elevPlaceholderRequest.get()); + + if ( r->wasCanceled() ) + { + r->setState( TaskRequest::STATE_IDLE ); + r->setProgressCallback( new ProgressCallback() ); + r->reset(); + } + else // success: + { + osg::ref_ptr newPhLayer = static_cast( r->getResult() ); + if ( newPhLayer.valid() && newPhLayer->getHeightField() != NULL ) + { + // install the new elevation layer. + { + Threading::ScopedWriteLock lock( _tileLayersMutex ); + this->setElevationLayer( newPhLayer.get() ); + this->dirtyBound(); + } + + // tile needs to be recompiled. + queueTileUpdate( TileUpdate::UPDATE_ELEVATION ); + + // update the elevation LOD for this tile, now that the new HF data is installed. This will + // allow other tiles to see where this tile's HF data is. + _elevationLOD = r->_nextLOD; + + #ifdef PREEMPTIVE_DEBUG + OE_NOTICE << "..tile (" << _key.str() << ") is now at (" << _elevationLOD << ")" << std::endl; + #endif + } + _elevPlaceholderRequest->setState( TaskRequest::STATE_IDLE ); + _elevPlaceholderRequest->reset(); + } + } + } + + // if we have a new TileGenRequest, queue it up now. + if ( _tileUpdates.size() > 0 && !_tileGenRequest.valid() ) // _tileGenNeeded && !_tileGenRequest.valid()) + { + _tileGenRequest = new TileGenRequest( this, _tileUpdates.front() ); + _tileUpdates.pop(); + //OE_NOTICE << "tile (" << _key.str() << ") queuing new tile gen" << std::endl; + getStreamingTerrain()->getTileGenerationTaskService()->add( _tileGenRequest.get() ); + } + + return tileModified; +} diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/Terrain osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/Terrain --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/Terrain 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/Terrain 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,152 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph +* Copyright 2008-2010 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 +*/ +#ifndef OSGEARTH_ENGINE_OSGTERRAIN_STANDARD_TERRAIN +#define OSGEARTH_ENGINE_OSGTERRAIN_STANDARD_TERRAIN 1 + +#include "Common" +#include "Tile" +#include "CustomTerrainTechnique" +#include "OSGTileFactory" +#include +#include +#include +#include +#include +#include +#include +#include + +class TileFactory; + +using namespace osgEarth; + +/** + */ +class Terrain : public osg::Group //osgTerrain::Terrain +{ +public: + Terrain( + const MapFrame& update_mapf, + const MapFrame& cull_mapf, + OSGTileFactory* factory, + bool quickReleaseGLObjects ); + + virtual const char* libraryName() const { return "osgEarth"; } + + virtual const char* className() const { return "Terrain"; } + +public: + + OSGTileFactory* getTileFactory() { return _tileFactory.get(); } + + bool getQuickReleaseGLObjects() const { return _quickReleaseGLObjects; } + + const MapFrame& getUpdateThreadMapFrame() { return _update_mapf; } + + const MapFrame& getCullThreadMapFrame() { return _cull_mapf; } + + virtual Tile* createTile( const TileKey& key, GeoLocator* locator ) const; + + void setTechniquePrototype( TerrainTechnique* tech ); + + TerrainTechnique* cloneTechnique() const; + + void setSampleRatio( float value ); + + float getSampleRatio() const { return _sampleRatio; } + + void setVerticalScale( float value ); + + float getVerticalScale() const { return _verticalScale; } + + virtual void traverse( osg::NodeVisitor &nv ); + +protected: + + virtual ~Terrain(); + + // subclass can override to notify of running tasks + virtual unsigned getNumActiveTasks() const { return 0; } + + // subclass can override this to perform addition UPDATE traversal operations + virtual void updateTraversal( osg::NodeVisitor& nv ) { } + + typedef std::map< osgTerrain::TileID, osg::ref_ptr > TileTable; + + typedef std::queue< osg::ref_ptr > TileQueue; + typedef std::list< osg::ref_ptr > TileList; + typedef std::vector< osg::ref_ptr > TileVector; + typedef std::queue< osgTerrain::TileID > TileIDQueue; + + Threading::ReadWriteMutex _tilesMutex; + TileTable _tiles; + TileList _tilesToShutDown; + TileVector _tilesToRelease; + Threading::Mutex _tilesToReleaseMutex; + + float _sampleRatio; + float _verticalScale; + +public: + + void releaseGLObjectsForTiles( osg::State* state ); + + void registerTile( Tile* newTile ); + + /** Gets a thread-safe copy of the entire tile list */ + void getTiles( TileVector& out_tiles ); + + /** Fetches a tile from the repo */ + template + void getTile(const osgTerrain::TileID& id, osg::ref_ptr& out_tile, bool lock =true ) const { + if ( lock ) { + Threading::ScopedReadLock lock( const_cast(this)->_tilesMutex ); + TileTable::const_iterator i = _tiles.find( id ); + out_tile = i != _tiles.end()? static_cast(i->second.get()) : 0L; + } + else { + TileTable::const_iterator i = _tiles.find( id ); + out_tile = i != _tiles.end()? static_cast(i->second.get()) : 0L; + } + } + +protected: + + osg::ref_ptr _tileFactory; + osg::ref_ptr _profile; + + bool _alwaysUpdate; + int _onDemandDelay; // #frames + + void setDelay( unsigned frames ); + void decDelay(); + + bool _registeredWithReleaseGLCallback; + + // store a separate map frame for each of the traversal threads + const MapFrame& _update_mapf; // map frame for the main/update traversal thread + const MapFrame& _cull_mapf; // map frame for the cull traversal thread + + bool _quickReleaseGLObjects; + bool _quickReleaseCallbackInstalled; + + osg::ref_ptr _techPrototype; +}; + +#endif // OSGEARTH_ENGINE_OSGTERRAIN_STANDARD_TERRAIN diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/Terrain.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/Terrain.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/Terrain.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/Terrain.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,328 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph +* Copyright 2008-2010 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 +*/ +#include "Terrain" +#include "Tile" +#include "TransparentLayer" + +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace osgEarth; +using namespace OpenThreads; + +#define LC "[Terrain] " + +//---------------------------------------------------------------------------- + +namespace +{ + /** + * A draw callback to calls another, nested draw callback. + */ + struct NestingDrawCallback : public osg::Camera::DrawCallback + { + NestingDrawCallback( osg::Camera::DrawCallback* next ) : _next(next) { } + + virtual void operator()( osg::RenderInfo& renderInfo ) const + { + dispatch( renderInfo ); + } + + void dispatch( osg::RenderInfo& renderInfo ) const + { + if ( _next ) + _next->operator ()( renderInfo ); + } + + osg::ref_ptr _next; + }; + + + // a simple draw callback, to be installed on a Camera, that tells all Terrains to + // release GL memory on any expired tiles. + struct QuickReleaseGLCallback : public NestingDrawCallback + { + typedef std::vector< osg::observer_ptr > ObserverTerrainList; + + QuickReleaseGLCallback( Terrain* terrain, osg::Camera::DrawCallback* next ) + : NestingDrawCallback(next), _terrain(terrain) { } + + virtual void operator()( osg::RenderInfo& renderInfo ) const + { + osg::ref_ptr terrainSafe = _terrain.get(); + if ( terrainSafe.valid() ) + { + terrainSafe->releaseGLObjectsForTiles( renderInfo.getState() ); + } + dispatch( renderInfo ); + } + + osg::observer_ptr _terrain; + }; +} + +//---------------------------------------------------------------------------- + +Terrain::Terrain(const MapFrame& update_mapf, + const MapFrame& cull_mapf, + OSGTileFactory* tileFactory, + bool quickReleaseGLObjects ) : + +_tileFactory( tileFactory ), +_registeredWithReleaseGLCallback( false ), +_update_mapf( update_mapf ), +_cull_mapf( cull_mapf ), +_onDemandDelay( 2 ), +_quickReleaseGLObjects( quickReleaseGLObjects ), +_quickReleaseCallbackInstalled( false ), +_alwaysUpdate( false ), +_sampleRatio( 1.0f ), +_verticalScale( 1.0f ) +{ + this->setThreadSafeRefUnref( true ); + + // the EVENT_VISITOR will reset this to 0 once the "delay" is expired. + _alwaysUpdate = false; + setNumChildrenRequiringUpdateTraversal( 1 ); + + // register for events in order to support ON_DEMAND frame scheme + setNumChildrenRequiringEventTraversal( 1 ); + + getOrCreateStateSet()->setMode(GL_BLEND , osg::StateAttribute::ON); +} + +Terrain::~Terrain() +{ + // detach all the tiles from the terrain first. Otherwise osgTerrain::Terrain + // will crap out. + for( TileTable::iterator i = _tiles.begin(); i != _tiles.end(); ++i ) + { + i->second->attachToTerrain( 0L ); + } + _tiles.clear(); +} + +void +Terrain::setTechniquePrototype( TerrainTechnique* value ) +{ + _techPrototype = value; +} + +TerrainTechnique* +Terrain::cloneTechnique() const +{ + return osg::clone( _techPrototype.get(), osg::CopyOp::DEEP_COPY_ALL ); +} + +Tile* +Terrain::createTile(const TileKey& key, GeoLocator* keyLocator) const +{ + return new Tile( key, keyLocator, this->getQuickReleaseGLObjects() ); +} + +void +Terrain::setVerticalScale( float value ) +{ + if ( value != _verticalScale ) + { + _verticalScale = value; + } +} + +void +Terrain::setSampleRatio( float value ) +{ + if ( value != _sampleRatio ) + { + _sampleRatio = value; + } +} + +void +Terrain::getTiles( TileVector& out ) +{ + Threading::ScopedReadLock lock( _tilesMutex ); + out.clear(); + out.reserve( _tiles.size() ); + for( TileTable::const_iterator i = _tiles.begin(); i != _tiles.end(); ++i ) + out.push_back( i->second.get() ); +} + +void +Terrain::registerTile( Tile* newTile ) +{ + Threading::ScopedWriteLock exclusiveTileTableLock( _tilesMutex ); + _tiles[ newTile->getTileId() ] = newTile; +} + +// immediately release GL memory for any expired tiles. +// called from the DRAW thread (QuickReleaseGLCallback). +void +Terrain::releaseGLObjectsForTiles(osg::State* state) +{ + OpenThreads::ScopedLock lock( _tilesToReleaseMutex ); + + for( TileVector::iterator i = _tilesToRelease.begin(); i != _tilesToRelease.end(); ++i ) + { + i->get()->releaseGLObjects( state ); + } + + _tilesToRelease.clear(); + + //while( _tilesToRelease.size() > 0 ) + //{ + // _tilesToRelease.front()->releaseGLObjects( state ); + // _tilesToRelease.pop(); + //} +} + +void +Terrain::traverse( osg::NodeVisitor &nv ) +{ + // UPDATE runs whenever a Tile runs its update traversal on the first pass. + // i.e., only runs then a new Tile is born. + if ( nv.getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR ) + { + // if the terrain engine requested "quick release", install the quick release + // draw callback now. + if ( _quickReleaseGLObjects && !_quickReleaseCallbackInstalled ) + { + osg::Camera* cam = findFirstParentOfType( this ); + if ( cam ) + { + cam->setPostDrawCallback( new QuickReleaseGLCallback( this, cam->getPostDrawCallback() ) ); + _quickReleaseCallbackInstalled = true; + OE_INFO << LC << "Quick release enabled" << std::endl; + } + } + + // Collect any "dead" tiles and queue them for shutdown. Since UPDATE only runs + // when new tiles arrive, this clears out old tiles from the queue at that time. + // Another approach might be to use an observer_ptr instead...but then we may + // not be able to use the quick-release. + { + Threading::ScopedWriteLock tileTableExclusiveLock( _tilesMutex ); + + for( TileTable::iterator i = _tiles.begin(); i != _tiles.end(); ) + { + Tile* tile = i->second.get(); + if ( tile->getNumParents() == 0 && tile->getHasBeenTraversed() ) + { + _tilesToShutDown.push_back( tile ); + + // i is incremented prior to calling erase, but i's previous value goes to erase, + // maintaining validity + _tiles.erase( i++ ); + } + else + ++i; + } + } + + // Remove any dead tiles from the main tile table, while at the same time queuing + // any tiles that require quick-release. This criticial section requires an exclusive + // lock on the main tile table. + if ( _tilesToShutDown.size() > 0 ) // quick no-lock check.. + { + Threading::ScopedMutexLock tilesToReleaseExclusiveLock( _tilesToReleaseMutex ); + + // Shut down any dead tiles once there tasks are complete. + for( TileList::iterator i = _tilesToShutDown.begin(); i != _tilesToShutDown.end(); ) + { + Tile* tile = i->get(); + if ( tile && tile->cancelActiveTasks() ) + { + if ( _quickReleaseGLObjects && _quickReleaseCallbackInstalled ) + { + _tilesToRelease.push_back( tile ); + } + + i = _tilesToShutDown.erase( i ); + } + else + ++i; + } + } + + // call subclass to continue update.. + updateTraversal( nv ); + } + + else if ( nv.getVisitorType() == osg::NodeVisitor::EVENT_VISITOR ) + { + // in OSG's "ON_DEMAND" frame scheme, OSG runs the event visitor as part of the + // test to see if a frame is needed. In sequential/preemptive mode, we need to + // check whether there are any pending tasks running. + + // In addition, once the tasks run out, we continue to delay on-demand rendering + // for another full frame so that the event dispatchers can catch up. + + if ( _tilesToShutDown.size() > 0 ) + { + setDelay( 2 ); + } + + else if ( _onDemandDelay <= 0 ) + { + unsigned numActiveTasks = getNumActiveTasks(); + if ( numActiveTasks > 0 ) + { + setDelay( 2 ); + } + } + + //OE_INFO << "Tasks = " << numTasks << std::endl; + + if ( _onDemandDelay > 0 ) + { + osgGA::EventVisitor* ev = dynamic_cast( &nv ); + ev->getActionAdapter()->requestRedraw(); + decDelay(); + } + } + + osg::Group::traverse( nv ); +} + +void +Terrain::setDelay( unsigned frames ) +{ + if ( _onDemandDelay == 0 && !_alwaysUpdate ) + { + ADJUST_UPDATE_TRAV_COUNT( this, 1 ); + } + _onDemandDelay = frames; +} + +void +Terrain::decDelay() +{ + _onDemandDelay--; + if ( _onDemandDelay == 0 && !_alwaysUpdate ) + { + ADJUST_UPDATE_TRAV_COUNT(this, -1); + } +} diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/TerrainTileEdgeNormalizerUpdateCallback osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/TerrainTileEdgeNormalizerUpdateCallback --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/TerrainTileEdgeNormalizerUpdateCallback 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/TerrainTileEdgeNormalizerUpdateCallback 1970-01-01 00:00:00.000000000 +0000 @@ -1,70 +0,0 @@ -/* -*-c++-*- */ -/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph - * Copyright 2008-2010 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 - */ -#if 0 -#ifndef OSGEARTH_TERRAIN_TILE_EDGE_NORMALIZATION_CALLBACK -#define OSGEARTH_TERRAIN_TILE_EDGE_NORMALIZATION_CALLBACK 1 - -#include -#include -#include - -class CustomTile; - -enum CardinalDirection -{ - NORTH, - EAST, - SOUTH, - WEST, - NORTH_EAST, - NORTH_WEST, - SOUTH_EAST, - SOUTH_WEST -}; - -/** -* Update callback that scans adjacent tiles and adjusts the edge verts so that -* they match up. Helps match up mismatched elevation data at tile edges. -*/ -class TerrainTileEdgeNormalizerUpdateCallback : public osg::NodeCallback -{ -public: - TerrainTileEdgeNormalizerUpdateCallback(); - void operator()(osg::Node* node, osg::NodeVisitor* nv); - -protected: - - bool normalizeCorner(CustomTile *tile, CardinalDirection direction); - bool normalizeEdge(CustomTile *tile, CardinalDirection direction); - osgTerrain::TileID getNeighborTile(const osgTerrain::TileID &id, CardinalDirection direction) const; - - bool _normalizedWest; - bool _normalizedEast; - bool _normalizedNorth; - bool _normalizedSouth; - bool _normalizedNorthWest; - bool _normalizedNorthEast; - bool _normalizedSouthWest; - bool _normalizedSouthEast; - -}; - - -#endif //OSGEARTH_TERRAIN_TILE_EDGE_NORMALIZATION_CALLBACK -#endif \ No newline at end of file diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/TerrainTileEdgeNormalizerUpdateCallback.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/TerrainTileEdgeNormalizerUpdateCallback.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/TerrainTileEdgeNormalizerUpdateCallback.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/TerrainTileEdgeNormalizerUpdateCallback.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,398 +0,0 @@ -/* -*-c++-*- */ -/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph - * Copyright 2008-2010 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 - */ -#if 0 -#include "TerrainTileEdgeNormalizerUpdateCallback" -#include "CustomTile" -#include "CustomTerrain" -#include -#include - -using namespace osgEarth; - - -struct NormalizerStats : public osg::Referenced -{ -public: - NormalizerStats(): - _maxTimePerFrame(4.0), - _currentFrame(-1) - { - } - - void updateTime(const int &frame, const double &time) - { - checkFrame(frame); - _totalTime += time; - } - - bool isTimeRemaining(const int &frame) - { - checkFrame(frame); - return (_totalTime < _maxTimePerFrame); - } - - inline void checkFrame(const int &frame) - { - if (frame != _currentFrame) - { - _currentFrame = frame; - _totalTime = 0.0; - } - } - - int _currentFrame; - double _totalTime; - double _maxTimePerFrame; -}; - -NormalizerStats* getNormalizerStats() -{ - static osg::ref_ptr s_stats = new NormalizerStats; - return s_stats.get(); -} - - - -bool normalizeCorner(osg::HeightField* ll, osg::HeightField* lr, osg::HeightField *ul, osg::HeightField *ur) -{ - //Check for NULL - if (ll && lr && ul && ur) - { - float val1 = ll->getHeight(ll->getNumColumns()-1, ll->getNumRows()-1); - float val2 = lr->getHeight(0, lr->getNumRows()-1); - float val3 = ul->getHeight(ul->getNumColumns()-1, 0); - float val4 = ur->getHeight(0, 0); - - //Average the 4 corners - float corner_val = (val1 + val2 + val3 + val4) / 4.0f; - - ll->setHeight(ll->getNumColumns()-1, ll->getNumRows()-1, corner_val); - lr->setHeight(0, lr->getNumRows()-1, corner_val); - ul->setHeight(ul->getNumColumns()-1, 0, corner_val); - ur->setHeight(0, 0, corner_val); - - return true; - } - return false; -} - - -bool normalizeEdge(osg::HeightField* hf1, osg::HeightField *hf2, CardinalDirection direction) -{ - //Check for NULL - if (hf1 && hf2) - { - //Make sure dimensions are equal - if (hf1->getNumColumns() == hf2->getNumColumns() && hf1->getNumRows() == hf2->getNumRows()) - { - int width = hf1->getNumColumns(); - int height = hf1->getNumRows(); - - switch (direction) - { - case NORTH: - //Assume hf2 is to the north of hf1 - for (int c = 1; c < width-1; ++c) - { - float val1 = hf1->getHeight(c, height-1); - float val2 = hf2->getHeight(c, 0); - float val = (val1 + val2) /2.0f; - hf1->setHeight(c, height-1, val); - hf2->setHeight(c, 0, val); - } - break; - case EAST: - //Assume hf2 is to the east of hf1 - for (int r = 1; r < height-1; ++r) - { - float val1 = hf1->getHeight(width-1, r); - float val2 = hf2->getHeight(0, r); - float val = (val1 + val2)/2.0f; - hf1->setHeight(width-1, r, val); - hf2->setHeight(0, r, val); - } - break; - case SOUTH: - //Assume hf2 is to the south of hf1 - for (int c = 1; c < width-1; ++c) - { - float val1 = hf1->getHeight(c, 0); - float val2 = hf2->getHeight(c, height-1); - float val = (val1 + val2) /2.0f; - hf1->setHeight(c, 0, val); - hf2->setHeight(c, height-1, val); - } - break; - case WEST: - //Assume hf2 is to the west of hf1 - for (int r = 1; r < height-1; ++r) - { - float val1 = hf1->getHeight(0, r); - float val2 = hf2->getHeight(width-1, r); - float val = (val1 + val2)/2.0f; - hf1->setHeight(0, r, val); - hf2->setHeight(width-1, r, val); - } - break; - } - - return true; - } - } - return false; -} - - -TerrainTileEdgeNormalizerUpdateCallback::TerrainTileEdgeNormalizerUpdateCallback(): - _normalizedEast(false), - _normalizedNorth(false), - _normalizedSouth(false), - _normalizedWest(false), - _normalizedNorthWest(false), - _normalizedNorthEast(false), - _normalizedSouthWest(false), - _normalizedSouthEast(false) - { - } - - bool TerrainTileEdgeNormalizerUpdateCallback::normalizeCorner(CustomTile *tile, CardinalDirection direction) - { - if (tile && tile->getTerrain()) - { - //TODO: Remove the use of EarthTerrain once getTile fix is included in OpenSceneGraph proper - CustomTerrain* et = static_cast(tile->getTerrain()); - if (et) - { - //Determine the TileID's of the 4 tiles that need to take place in this normalization - osgTerrain::TileID ll_id; - osgTerrain::TileID lr_id; - osgTerrain::TileID ul_id; - osgTerrain::TileID ur_id; - - TileKey ll_key, lu_key, ul_key, ur_key; - - if (direction == NORTH_EAST) - { - ll_id = tile->getTileID(); - ul_id = getNeighborTile(ll_id, NORTH); - lr_id = getNeighborTile(ll_id, EAST); - ur_id = getNeighborTile(ll_id, NORTH_EAST); - } - else if (direction == NORTH_WEST) - { - lr_id = tile->getTileID(); - ll_id = getNeighborTile(lr_id, WEST); - ul_id = getNeighborTile(lr_id, NORTH_WEST); - ur_id = getNeighborTile(lr_id, NORTH); - } - else if (direction == SOUTH_EAST) - { - ul_id = tile->getTileID(); - ll_id = getNeighborTile(ul_id, SOUTH); - lr_id = getNeighborTile(ul_id, SOUTH_EAST); - ur_id = getNeighborTile(ul_id, EAST); - } - else if (direction == SOUTH_WEST) - { - ur_id = tile->getTileID(); - ul_id = getNeighborTile(ur_id, WEST); - ll_id = getNeighborTile(ur_id, SOUTH_WEST); - lr_id = getNeighborTile(ur_id, SOUTH); - } - else - { - OE_WARN << "Invalid CardinalDirection passed to normalizeCorner " << direction << std::endl; - return false; - } - - //Get the terrain tiles - osg::ref_ptr ll_tile, lr_tile, ul_tile, ur_tile; - et->getCustomTile(ll_id, ll_tile); - et->getCustomTile(lr_id, lr_tile); - et->getCustomTile(ul_id, ul_tile); - et->getCustomTile(ur_id, ur_tile); - - if (ll_tile.valid() && lr_tile.valid() && ul_tile.valid() && ur_tile.valid()) - { - osgTerrain::HeightFieldLayer *ll_hfl = static_cast(ll_tile->getElevationLayer()); - osgTerrain::HeightFieldLayer *lr_hfl = static_cast(lr_tile->getElevationLayer()); - osgTerrain::HeightFieldLayer *ul_hfl = static_cast(ul_tile->getElevationLayer()); - osgTerrain::HeightFieldLayer *ur_hfl = static_cast(ur_tile->getElevationLayer()); - - if (ll_hfl && lr_hfl && ul_hfl && ur_hfl) - { - bool normalized = ::normalizeCorner(ll_hfl->getHeightField(), lr_hfl->getHeightField(), ul_hfl->getHeightField(), ur_hfl->getHeightField()); - if (normalized) - { - if (ll_tile->getUseLayerRequests()) - ll_tile->queueTileUpdate( TileUpdate::UPDATE_ELEVATION ); - else - ll_tile->setDirty(true); - - if (lr_tile->getUseLayerRequests()) - lr_tile->queueTileUpdate( TileUpdate::UPDATE_ELEVATION ); - else - lr_tile->setDirty(true); - - if (ul_tile->getUseLayerRequests()) - ul_tile->queueTileUpdate( TileUpdate::UPDATE_ELEVATION ); - else - ul_tile->setDirty(true); - - if (ur_tile->getUseLayerRequests()) - ur_tile->queueTileUpdate( TileUpdate::UPDATE_ELEVATION ); - else - ur_tile->setDirty(true); - } - return normalized; - } - } - } - } - return false; - } - - - bool TerrainTileEdgeNormalizerUpdateCallback::normalizeEdge(CustomTile *tile, CardinalDirection direction) - { - if (tile && tile->getTerrain()) - { - //TODO: Remove the use of EarthTerrain once getTile fix is included in OpenSceneGraph proper - CustomTerrain* et = static_cast(tile->getTerrain()); - if (et) - { - - osgTerrain::TileID id1 = tile->getTileID(); - - osgTerrain::TileID id2 = getNeighborTile(id1, direction); - - //OE_NOTICE << "Tile is " << id1.level << " " << id1.x << " " << id1.y << std::endl; - //OE_NOTICE << "Neighbor tile is " << id2.level << " " << id2.x << " " << id2.y << std::endl; - - if (tile->getTerrain()) - { - osg::ref_ptr tile2; - et->getCustomTile(id2, tile2); - - if (tile2) - { - //OE_NOTICE << "Found neighbor tile " << std::endl; - //This callback will only work if we have a HeightFieldLayer - osg::ref_ptr hfl1 = static_cast(tile->getElevationLayer()); - osg::ref_ptr hfl2 = static_cast(tile2->getElevationLayer()); - - if (hfl1.valid() && hfl2.valid()) - { - bool normalized = ::normalizeEdge(hfl1->getHeightField(), hfl2->getHeightField(), direction); - if (normalized) - { - if (tile->getUseLayerRequests()) - tile->queueTileUpdate( TileUpdate::UPDATE_ELEVATION ); - else - tile->setDirty(true); - - if (tile2->getUseLayerRequests()) - tile2->queueTileUpdate( TileUpdate::UPDATE_ELEVATION ); - else - tile2->setDirty(true); - } - return normalized; - } - } - } - } - } - return false; - } - - void TerrainTileEdgeNormalizerUpdateCallback::operator()(osg::Node* node, osg::NodeVisitor* nv) - { - NormalizerStats* stats = getNormalizerStats(); - if (!stats->isTimeRemaining(nv->getFrameStamp()->getFrameNumber())) - { - //OE_NOTICE << "No time left, returning..." << std::endl; - return; - } - - osg::Timer_t start = osg::Timer::instance()->tick(); - //TODO: Look at heightDelta's to assist with normal generation. - CustomTile* tt = static_cast(node); - - if (!_normalizedNorth) _normalizedNorth = normalizeEdge(tt, NORTH); - if (!_normalizedSouth) _normalizedSouth = normalizeEdge(tt, SOUTH); - if (!_normalizedEast) _normalizedEast = normalizeEdge(tt, EAST); - if (!_normalizedWest) _normalizedWest = normalizeEdge(tt, WEST); - if (!_normalizedNorthWest) _normalizedNorthWest = normalizeCorner(tt, NORTH_WEST); - if (!_normalizedNorthEast) _normalizedNorthEast = normalizeCorner(tt, NORTH_EAST); - if (!_normalizedSouthWest) _normalizedSouthWest = normalizeCorner(tt, SOUTH_WEST); - if (!_normalizedSouthEast) _normalizedSouthEast = normalizeCorner(tt, SOUTH_EAST); - - traverse(node, nv); - - osg::Timer_t end = osg::Timer::instance()->tick(); - - double frameTime = osg::Timer::instance()->delta_m(start, end); - //OE_NOTICE << "TileTime: " << frameTime << std::endl; - stats->updateTime(nv->getFrameStamp()->getFrameNumber(), frameTime); - - if (_normalizedNorth && _normalizedSouth && _normalizedEast && _normalizedWest && - _normalizedNorthWest && _normalizedNorthEast && _normalizedSouthWest && _normalizedSouthEast) - { - node->setUpdateCallback(0); - } - } - - - osgTerrain::TileID TerrainTileEdgeNormalizerUpdateCallback::getNeighborTile(const osgTerrain::TileID &id, CardinalDirection direction) const - { - //The name of the lod changed after OSG 2.6 from layer to level -#if (OPENSCENEGRAPH_MAJOR_VERSION == 2 && OPENSCENEGRAPH_MINOR_VERSION < 7) - int level = id.layer; -#else - int level = id.level; -#endif - - //Determine the edge TileID - osgTerrain::TileID id2(level, id.x, id.y); - int totalTiles = 2 << (level-1); //TileKey::getMapSizeTiles(level); - - if (direction == WEST || direction == SOUTH_WEST || direction == NORTH_WEST) - { - id2.x = (id2.x == 0 ? totalTiles-1 : id2.x-1); - } - - if (direction == EAST || direction == SOUTH_EAST || direction == NORTH_EAST) - { - id2.x = (id2.x == totalTiles-1 ? 0 : id2.x+1); - } - - if (direction == NORTH || direction == NORTH_EAST || direction == NORTH_WEST) - { - id2.y = (id2.y == 0 ? totalTiles-1 : id2.y-1); - } - - if (direction == SOUTH || direction == SOUTH_WEST || direction == SOUTH_EAST) - { - id2.y = (id2.y == totalTiles-1 ? 0 : id2.y+1); - } - - return id2; - } - -#endif \ No newline at end of file diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/Tile osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/Tile --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/Tile 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/Tile 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,201 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph +* Copyright 2008-2010 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 +*/ +#ifndef OSGEARTH_ENGINE_OSGTERRAIN_TILE +#define OSGEARTH_ENGINE_OSGTERRAIN_TILE 1 + +#include "Common" +#include "OSGTileFactory" +#include "TransparentLayer" +#include "CustomTerrainTechnique" +#include +#include +#include +#include +#include +#include "Terrain" +#include "Tile" +#include +#include +#include + +class TileFactory; +class TerrainTechnique; + +using namespace osgEarth; + +typedef std::map ColorLayersByUID; + +//------------------------------------------------------------------------ + +// Records a tile change request so that we can update tiles piecemeal +class TileUpdate +{ +public: + enum Action { + ADD_IMAGE_LAYER, + REMOVE_IMAGE_LAYER, + MOVE_IMAGE_LAYER, + UPDATE_IMAGE_LAYER, + UPDATE_ALL_IMAGE_LAYERS, + UPDATE_ELEVATION, + UPDATE_ALL + }; + + TileUpdate( Action action, UID layerUID =(UID)-1 ) + : _action(action), _layerUID(layerUID) { } + + Action getAction() const { return _action; } + + UID getLayerUID() const { return _layerUID; } + +private: + Action _action; + UID _layerUID; +}; + +//------------------------------------------------------------------------ + +class Tile : public osg::Node +{ +public: + Tile( const TileKey& key, GeoLocator* keyLocator, bool quickReleaseGLObjects ); + + /** Gets the tilekey associated with this tile. */ + const TileKey& getKey() const { return _key; } + + /** Gets the terrain object to which this tile belongs. */ + class Terrain* getTerrain() { return _terrain.get(); } + const class Terrain* getTerrain() const { return _terrain.get(); } + + // attaches this tile to a terrain and registers it. + void attachToTerrain( Terrain* terrain ); + + /** intializes the tile and clears its dirty flag */ + void init(); + + /** Gets or sets the terrain mask geometry. */ + const MaskLayerVector& getTerrainMasks() { return _masks; } + void setTerrainMasks(const MaskLayerVector& terrainMask) { _masks.clear(); std::copy( terrainMask.begin(), terrainMask.end(), std::back_inserter(_masks) ); } + + /** Whether OSG has traversed the tile at least once. */ + bool getHasBeenTraversed() const { return _hasBeenTraversed; } + + /** Mutex that protects access to the tile contents. */ + Threading::ReadWriteMutex& getTileLayersMutex() { return _tileLayersMutex; } + + // marks a request to regenerate the tile based on the specified change(s). + virtual void queueTileUpdate( TileUpdate::Action action, int index =-1 ); + + void applyImmediateTileUpdate( TileUpdate::Action action, int index =-1 ); + + virtual bool cancelActiveTasks() { return true; } + + /** The scale factor for elevation heights */ + float getVerticalScale() const { return _verticalScale; } + void setVerticalScale( float verticalScale ); + + osgTerrain::Locator* getLocator() const { return _locator.get(); } + + const osgTerrain::TileID& getTileId() const { return _tileId; } + + bool getDirty() const { return _dirty; } + void setDirty( bool value ) { _dirty = value; } + + void setTerrainTechnique( TerrainTechnique* value ); + TerrainTechnique* getTerrainTechnique() const { return _tech.get(); } + + /** Tile contents. We don't use the TerrainTile color layer list, we use our own */ + void removeCustomColorLayer( UID layerUID, bool writeLock =true ); + bool getCustomColorLayer( UID layerUID, CustomColorLayer& output, bool readLock =true ) const; + void getCustomColorLayers( ColorLayersByUID& out, bool readLock =true ) const; + void setCustomColorLayers( const ColorLayersByUID& in, bool writeLock =true ); + void setCustomColorLayer( const CustomColorLayer& colorLayer, bool writeLock =true ); + + osgTerrain::HeightFieldLayer* getElevationLayer() const { return _elevationLayer.get(); } + void setElevationLayer( osgTerrain::HeightFieldLayer* value ) { _elevationLayer = value; } + +public: // OVERRIDES + + virtual void traverse( class osg::NodeVisitor& nv ); + + /** If State is non-zero, this function releases any associated OpenGL objects for + * the specified graphics context. Otherwise, releases OpenGL objects + * for all graphics contexts. */ + virtual void releaseGLObjects(osg::State* = 0) const; + + virtual osg::BoundingSphere computeBound() const; + +protected: + + virtual ~Tile(); + + bool _hasBeenTraversed; + bool _quickReleaseGLObjects; + bool _parentTileSet; + bool _dirty; + + TileKey _key; + osgTerrain::TileID _tileId; + osg::ref_ptr _locator; + osg::observer_ptr _terrain; + MaskLayerVector _masks; + + Threading::ReadWriteMutex _tileLayersMutex; + ColorLayersByUID _colorLayers; + float _verticalScale; + + osg::ref_ptr _elevationLayer; + osg::ref_ptr _tech; + + +public: + friend class TileFrame; +}; + +class TileVector : public std::vector< osg::ref_ptr > { }; + +// -------------------------------------------------------------------------- + +/** + * Thread-safe working copy of Tile contents. + */ +class TileFrame +{ +public: + TileFrame( Tile* tile ); + + TileKey _tileKey; + ColorLayersByUID _colorLayers; + osg::ref_ptr< osgTerrain::HeightFieldLayer > _elevationLayer; + osg::ref_ptr< osgTerrain::Locator > _locator; + float _sampleRatio; + MaskLayerVector _masks; + + // convenience funciton to pull out a layer by its UID. + bool getCustomColorLayer( UID layerUID, CustomColorLayer& out ) const { + ColorLayersByUID::const_iterator i = _colorLayers.find( layerUID ); + if ( i != _colorLayers.end() ) { + out = i->second; + return true; + } + return false; + } +}; + +#endif // OSGEARTH_ENGINE_OSGTERRAIN_CUSTOM_TILE diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/TileBuilder osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/TileBuilder --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/TileBuilder 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/TileBuilder 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,95 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph +* Copyright 2008-2010 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 +*/ +#ifndef OSGEARTH_ENGINE_TILE_BUILDER +#define OSGEARTH_ENGINE_TILE_BUILDER 1 + +#include "Common" +#include "Tile" +#include +#include +#include + +using namespace osgEarth; + +class TileBuilder : public osg::Referenced +{ +public: + struct SourceRepo + { + SourceRepo() { } + + void add( const CustomColorLayer& layer ) + { + Threading::ScopedMutexLock lock(_m); + _colorLayers[ layer.getUID() ] = layer; + } + + void set( const CustomElevLayer& elevLayer ) + { + // only one...no lock required + _elevLayer = elevLayer; + } + + ColorLayersByUID _colorLayers; + CustomElevLayer _elevLayer; + Threading::Mutex _m; + }; + + struct Job : public osg::Referenced + { + Job(const TileKey& key, const Map* map) : _key(key), _mapf(map, Map::TERRAIN_LAYERS) { } + + TileKey _key; + MapFrame _mapf; + SourceRepo _repo; + TaskRequestVector _tasks; + }; + +public: + TileBuilder( + const Map* map, + const OSGTerrainOptions& terrainOptions, + TaskService* service ); + + void createTile( + const TileKey& key, + bool parallelize, + osg::ref_ptr& out_tile, + bool& out_hasRealData, + bool& out_hasLodBlendedLayers ); + + Job* createJob( const TileKey& key, Threading::MultiEvent& semaphore ); + + void runJob( Job* job ); + + void finalizeJob( + Job* job, + osg::ref_ptr& out_tile, + bool& out_hasRealData, + bool& out_hasLodBlending ); + + TaskService* getTaskService() const { return _service; } + +private: + const Map* _map; + TaskService* _service; + const OSGTerrainOptions& _terrainOptions; +}; + +#endif // OSGEARTH_ENGINE_TILE_BUILDER diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/TileBuilder.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/TileBuilder.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/TileBuilder.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/TileBuilder.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,466 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph +* Copyright 2008-2010 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 +*/ +#include "TileBuilder" +#include "TransparentLayer" +#include +#include + +using namespace osgEarth; +using namespace OpenThreads; + +#define LC "[TileBuilder] " + +//------------------------------------------------------------------------ + +struct BuildColorLayer +{ + void init( const TileKey& key, ImageLayer* layer, const MapInfo& mapInfo, + const OSGTerrainOptions& opt, TileBuilder::SourceRepo& repo ) + { + _key = key; + _layer = layer; + _mapInfo = &mapInfo; + _opt = &opt; + _repo = &repo; + } + + void execute() + { + GeoImage geoImage; + bool isFallbackData = false; + + // fetch the image from the layer, falling back on parent keys utils we are + // able to find one that works. + TileKey imageKey( _key ); + while( !geoImage.valid() && imageKey.valid() && _layer->isKeyValid(imageKey) ) + { + geoImage = _layer->createImage( imageKey, 0L ); // TODO: include a progress callback? + if ( !geoImage.valid() ) + { + imageKey = imageKey.createParentKey(); + isFallbackData = true; + } + } + + 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 + { + locator = GeoLocator::createForExtent(geoImage.getExtent(), *_mapInfo); + } + // add the color layer to the repo. + _repo->add( CustomColorLayer( + _layer, + geoImage.getImage(), + locator, + _key.getLevelOfDetail(), + _key, + isFallbackData ) ); + } + + TileKey _key; + const MapInfo* _mapInfo; + ImageLayer* _layer; + const OSGTerrainOptions* _opt; + TileBuilder::SourceRepo* _repo; +}; + +//------------------------------------------------------------------------ + +struct BuildElevLayer +{ + void init(const TileKey& key, const MapFrame& mapf, const OSGTerrainOptions& opt, TileBuilder::SourceRepo& repo) + { + _key = key; + _mapf = &mapf; + _opt = &opt; + _repo = &repo; + } + + void execute() + { + const MapInfo& mapInfo = _mapf->getMapInfo(); + + // Request a heightfield from the map, falling back on lower resolution tiles + // if necessary (fallback=true) + osg::ref_ptr hf; + bool isFallback = false; + + if ( _mapf->getHeightField( _key, true, hf, &isFallback, *_opt->elevationInterpolation() ) ) + { + // Treat Plate Carre specially by scaling the height values. (There is no need + // to do this with an empty heightfield) + if ( mapInfo.isPlateCarre() ) + { + HeightFieldUtils::scaleHeightFieldToDegrees( hf.get() ); + } + + // Put it in the repo + osgTerrain::HeightFieldLayer* hfLayer = new osgTerrain::HeightFieldLayer( hf.get() ); + + // Generate a locator. + hfLayer->setLocator( GeoLocator::createForKey( _key, mapInfo ) ); + + _repo->set( CustomElevLayer(hfLayer, isFallback) ); + } + } + + TileKey _key; + const MapFrame* _mapf; + const OSGTerrainOptions* _opt; + TileBuilder::SourceRepo* _repo; +}; + +//------------------------------------------------------------------------ + +struct AssembleTile +{ + void init(const TileKey& key, const MapInfo& mapInfo, const OSGTerrainOptions& opt, TileBuilder::SourceRepo& repo, const MaskLayerVector& masks=MaskLayerVector() ) + { + _key = key; + _mapInfo = &mapInfo; + _opt = &opt; + _repo = &repo; + _tile = 0L; + _masks.clear(); + std::copy( masks.begin(), masks.end(), std::back_inserter(_masks) ); + } + + void execute() + { + _tile = new Tile( _key, GeoLocator::createForKey(_key, *_mapInfo), *_opt->quickReleaseGLObjects() ); + _tile->setVerticalScale( *_opt->verticalScale() ); + + //_tile->setRequiresNormals( true ); + _tile->setDataVariance( osg::Object::DYNAMIC ); + _tile->setTerrainMasks(_masks); + + // copy over the source data. + _tile->setCustomColorLayers( _repo->_colorLayers ); + _tile->setElevationLayer( _repo->_elevLayer.getHFLayer() ); + + osg::BoundingSphere bs = _tile->getBound(); + + // a skirt hides cracks when transitioning between LODs: + osg::HeightField* hf = _repo->_elevLayer.getHFLayer()->getHeightField(); + hf->setSkirtHeight(bs.radius() * _opt->heightFieldSkirtRatio().get() ); + } + + TileKey _key; + const MapInfo* _mapInfo; + const OSGTerrainOptions* _opt; + TileBuilder::SourceRepo* _repo; + Tile* _tile; + MaskLayerVector _masks; +}; + +//------------------------------------------------------------------------ + +TileBuilder::TileBuilder(const Map* map, const OSGTerrainOptions& terrainOptions, TaskService* service) : +_map( map ), +_terrainOptions( terrainOptions ), +_service( service ) +{ + //nop +} + +TileBuilder::Job* +TileBuilder::createJob( const TileKey& key, Threading::MultiEvent& semaphore ) +{ + Job* job = new Job( key, _map ); + + // create the image layer tasks: + for( ImageLayerVector::const_iterator i = job->_mapf.imageLayers().begin(); i != job->_mapf.imageLayers().end(); ++i ) + { + ImageLayer* layer = i->get(); + if ( layer->isKeyValid(key) ) + { + ParallelTask* j = new ParallelTask( &semaphore ); + j->init( key, layer, job->_mapf.getMapInfo(), _terrainOptions, job->_repo ); + j->setPriority( -(float)key.getLevelOfDetail() ); + job->_tasks.push_back( j ); + } + } + + // If we have elevation layers, start an elevation job as well. Otherwise just create an + // empty one while we're waiting for the images to load. + if ( job->_mapf.elevationLayers().size() > 0 ) + { + ParallelTask* ej = new ParallelTask( &semaphore ); + ej->init( key, job->_mapf, _terrainOptions, job->_repo ); + ej->setPriority( -(float)key.getLevelOfDetail() ); + job->_tasks.push_back( ej ); + } + + return job; +} + +void +TileBuilder::runJob( TileBuilder::Job* job ) +{ + for( TaskRequestVector::iterator i = job->_tasks.begin(); i != job->_tasks.end(); ++i ) + _service->add( i->get() ); +} + +void +TileBuilder::finalizeJob(TileBuilder::Job* job, + osg::ref_ptr& out_tile, + bool& out_hasRealData, + bool& out_hasLodBlending) +{ + SourceRepo& repo = job->_repo; + + out_hasRealData = false; + out_hasLodBlending = false; + + // Bail out now if there's no data to be had. + if ( repo._colorLayers.size() == 0 && !repo._elevLayer.getHFLayer() ) + { + return; + } + + const TileKey& key = job->_key; + const MapInfo& mapInfo = job->_mapf.getMapInfo(); + + // OK we are making a tile, so if there's no heightfield yet, make an empty one. + if ( !repo._elevLayer.getHFLayer() ) + { + osg::HeightField* hf = key.getProfile()->getVerticalSRS()->createReferenceHeightField( key.getExtent(), 8, 8 ); + osgTerrain::HeightFieldLayer* hfLayer = new osgTerrain::HeightFieldLayer( hf ); + hfLayer->setLocator( GeoLocator::createForKey(key, mapInfo) ); + repo._elevLayer = CustomElevLayer( 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 emptyImage; + osgTerrain::Locator* locator = repo._elevLayer.getHFLayer()->getLocator(); + + for( ImageLayerVector::const_iterator i = job->_mapf.imageLayers().begin(); i != job->_mapf.imageLayers().end(); ++i ) + { + if ( !i->get()->isKeyValid(key) ) + { + if ( !emptyImage.valid() ) + emptyImage = ImageUtils::createEmptyImage(); + + repo.add( CustomColorLayer( + i->get(), emptyImage.get(), + locator, + key.getLevelOfDetail(), + key, + true ) ); + } + + if ( i->get()->getImageLayerOptions().lodBlending() == true ) + out_hasLodBlending = true; + } + + // Ready to create the actual tile. + AssembleTile assemble; + assemble.init( key, mapInfo, _terrainOptions, repo ); + assemble.execute(); + + // Check the results and see if we have any real data. + for( ColorLayersByUID::const_iterator i = repo._colorLayers.begin(); i != repo._colorLayers.end(); ++i ) + { + if ( !i->second.isFallbackData() ) + { + out_hasRealData = true; + break; + } + } + if ( !out_hasRealData && !repo._elevLayer.isFallbackData() ) + { + out_hasRealData = true; + } + + out_tile = assemble._tile; +} + +void +TileBuilder::createTile(const TileKey& key, + bool parallelize, + osg::ref_ptr& out_tile, + bool& out_hasRealData, + bool& out_hasLodBlendedLayers ) +{ + MapFrame mapf( _map, Map::MASKED_TERRAIN_LAYERS ); + + SourceRepo repo; + + // init this to false, then search for real data. "Real data" is data corresponding + // directly to the key, as opposed to fallback data, which is derived from a lower + // LOD key. + out_hasRealData = false; + out_hasLodBlendedLayers = false; + + const MapInfo& mapInfo = mapf.getMapInfo(); + + // If we need more than one layer, fetch them in parallel. + // TODO: change the test based on isKeyValid total. + if ( parallelize && (mapf.imageLayers().size() + mapf.elevationLayers().size() > 1) ) + { + // count the valid layers. + int jobCount = 0; + + for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); ++i ) + { + if ( i->get()->isKeyValid( key ) ) + ++jobCount; + + if ( i->get()->getImageLayerOptions().lodBlending() == true ) + out_hasLodBlendedLayers = true; + } + + if ( mapf.elevationLayers().size() > 0 ) + ++jobCount; + + // A thread job monitoring event: + Threading::MultiEvent semaphore( jobCount ); + + // Start the image layer jobs: + for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); ++i ) + { + ImageLayer* layer = i->get(); + if ( layer->isKeyValid(key) ) + { + ParallelTask* j = new ParallelTask( &semaphore ); + j->init( key, layer, mapInfo, _terrainOptions, repo ); + j->setPriority( -(float)key.getLevelOfDetail() ); + _service->add( j ); + } + } + + // If we have elevation layers, start an elevation job as well. Otherwise just create an + // empty one while we're waiting for the images to load. + if ( mapf.elevationLayers().size() > 0 ) + { + ParallelTask* ej = new ParallelTask( &semaphore ); + ej->init( key, mapf, _terrainOptions, repo ); + ej->setPriority( -(float)key.getLevelOfDetail() ); + _service->add( ej ); + } + else + { + BuildElevLayer build; + build.init( key, mapf, _terrainOptions, repo ); + build.execute(); + } + + // Wait for all the jobs to finish. + semaphore.wait(); + } + + // Fetch the image data serially: + else + { + // gather all the image layers serially. + for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); ++i ) + { + ImageLayer* layer = i->get(); + if ( layer->isKeyValid(key) ) + { + BuildColorLayer build; + build.init( key, layer, mapInfo, _terrainOptions, repo ); + build.execute(); + } + + if ( layer->getImageLayerOptions().lodBlending() == true ) + out_hasLodBlendedLayers = true; + } + + // make an elevation layer. + BuildElevLayer build; + build.init( key, mapf, _terrainOptions, repo ); + build.execute(); + } + + // Bail out now if there's no data to be had. + if ( repo._colorLayers.size() == 0 && !repo._elevLayer.getHFLayer() ) + { + return; + } + + // OK we are making a tile, so if there's no heightfield yet, make an empty one. + if ( !repo._elevLayer.getHFLayer() ) + { + osg::HeightField* hf = key.getProfile()->getVerticalSRS()->createReferenceHeightField( key.getExtent(), 8, 8 ); + osgTerrain::HeightFieldLayer* hfLayer = new osgTerrain::HeightFieldLayer( hf ); + hfLayer->setLocator( GeoLocator::createForKey(key, mapInfo) ); + repo._elevLayer = CustomElevLayer( 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 emptyImage; + osgTerrain::Locator* locator = repo._elevLayer.getHFLayer()->getLocator(); + + for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); ++i ) + { + if ( !i->get()->isKeyValid(key) ) + { + if ( !emptyImage.valid() ) + emptyImage = ImageUtils::createEmptyImage(); + + repo.add( CustomColorLayer( + i->get(), emptyImage.get(), + locator, + key.getLevelOfDetail(), + key, + true ) ); + } + } + + //osg::Vec3dArray* maskBounds = 0L; + //osgEarth::MaskLayer* mask = mapf.getTerrainMaskLayer(); + //if (mask) + // maskBounds = mask->getOrCreateBoundary(); + + // Ready to create the actual tile. + AssembleTile assemble; + assemble.init( key, mapInfo, _terrainOptions, repo, mapf.terrainMaskLayers() ); + assemble.execute(); + + if (!out_hasRealData) + { + // Check the results and see if we have any real data. + for( ColorLayersByUID::const_iterator i = repo._colorLayers.begin(); i != repo._colorLayers.end(); ++i ) + { + if ( !i->second.isFallbackData() ) + { + out_hasRealData = true; + break; + } + } + } + + if ( !out_hasRealData && !repo._elevLayer.isFallbackData() ) + { + out_hasRealData = true; + } + + out_tile = assemble._tile; +} diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/Tile.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/Tile.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/Tile.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/Tile.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,368 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph +* Copyright 2008-2010 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 +*/ +#include "Tile" +#include "Terrain" +#include "CustomTerrainTechnique" +#include "TransparentLayer" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + + +using namespace osgEarth; +using namespace OpenThreads; + +#define LC "[Tile] " + +//---------------------------------------------------------------------------- + +Tile::Tile( const TileKey& key, GeoLocator* keyLocator, bool quickReleaseGLObjects ) : +_key( key ), +_locator( keyLocator ), +_quickReleaseGLObjects( quickReleaseGLObjects ), +_hasBeenTraversed( false ), +_verticalScale( 1.0f ), +_parentTileSet( false ), +_tileId( key.getTileId() ), +_dirty( true ) +{ + this->setThreadSafeRefUnref( true ); + this->setName( key.str() ); + + // initially bump the update requirement so that this tile will receive an update + // traversal the first time through. It is on the first update traversal that we + // know the tile is in the scene graph and that it can be registered with the terrain. + ADJUST_UPDATE_TRAV_COUNT( this, 1 ); +} + +Tile::~Tile() +{ + //nop +} + +void +Tile::init() +{ + if ( _tech.valid() ) + { + _tech->init(); + _dirty = false; + } +} + +void +Tile::setTerrainTechnique( TerrainTechnique* tech ) +{ + tech->_tile = this; + _tech = tech; + _dirty = true; +} + +void +Tile::attachToTerrain( Terrain* terrain ) +{ + _terrain = terrain; + if ( terrain ) + terrain->registerTile( this ); +} + +void +Tile::setVerticalScale (float verticalScale ) +{ + if (_verticalScale != verticalScale) + { + _verticalScale = verticalScale; + dirtyBound(); + } +} + +void +Tile::setCustomColorLayer( const CustomColorLayer& layer, bool writeLock ) +{ + if ( writeLock ) + { + Threading::ScopedWriteLock exclusiveTileLock( _tileLayersMutex ); + setCustomColorLayer( layer, false ); + } + else + { + int delta = 0; + ColorLayersByUID::const_iterator i = _colorLayers.find( layer.getUID() ); + if ( i != _colorLayers.end() && i->second.getMapLayer()->isDynamic() ) + --delta; + + _colorLayers[layer.getUID()] = layer; + + if ( layer.getMapLayer()->isDynamic() ) + ++delta; + + if ( delta != 0 ) + ADJUST_UPDATE_TRAV_COUNT( this, delta ); + } +} + +void +Tile::removeCustomColorLayer( UID layerUID, bool writeLock ) +{ + if ( writeLock ) + { + Threading::ScopedWriteLock exclusiveTileLock( _tileLayersMutex ); + removeCustomColorLayer( layerUID, false ); + } + else + { + ColorLayersByUID::iterator i = _colorLayers.find(layerUID); + if ( i != _colorLayers.end() ) + { + if ( i->second.getMapLayer()->isDynamic() ) + ADJUST_UPDATE_TRAV_COUNT( this, -1 ); + + _colorLayers.erase( i ); + } + } +} + +bool +Tile::getCustomColorLayer( UID layerUID, CustomColorLayer& out, bool readLock ) const +{ + if ( readLock ) + { + Threading::ScopedReadLock sharedTileLock( const_cast(this)->_tileLayersMutex ); + return getCustomColorLayer( layerUID, out, false ); + } + else + { + ColorLayersByUID::const_iterator i = _colorLayers.find( layerUID ); + if ( i != _colorLayers.end() ) + { + out = i->second; + return true; + } + } + return false; +} + +void +Tile::getCustomColorLayers( ColorLayersByUID& out, bool readLock ) const +{ + if ( readLock ) + { + Threading::ScopedReadLock sharedTileLock( const_cast(this)->_tileLayersMutex ); + return getCustomColorLayers( out, false ); + } + else + out = _colorLayers; +} + +void +Tile::setCustomColorLayers( const ColorLayersByUID& in, bool writeLock ) +{ + if ( writeLock ) + { + Threading::ScopedWriteLock exclusiveLock( _tileLayersMutex ); + setCustomColorLayers( in, false ); + } + else + { + int delta = 0; + for( ColorLayersByUID::const_iterator i = _colorLayers.begin(); i != _colorLayers.end(); ++i ) + if ( i->second.getMapLayer()->isDynamic() ) + --delta; + + _colorLayers = in; + + for( ColorLayersByUID::const_iterator i = _colorLayers.begin(); i != _colorLayers.end(); ++i ) + if ( i->second.getMapLayer()->isDynamic() ) + ++delta; + + if ( delta != 0 ) + ADJUST_UPDATE_TRAV_COUNT( this, delta ); + } +} + +osg::BoundingSphere +Tile::computeBound() const +{ + //Overriden computeBound that takes into account the vertical scale. + //OE_NOTICE << "Tile::computeBound verticalScale = " << _verticalScale << std::endl; + + osg::BoundingSphere bs; + + if (_elevationLayer.valid()) + { + if (!_elevationLayer->getLocator()) return bs; + + osg::BoundingBox bb; + unsigned int numColumns = _elevationLayer->getNumColumns(); + unsigned int numRows = _elevationLayer->getNumRows(); + for(unsigned int r=0;rgetValidValue(c,r, value); + if (validValue) + { + //Multiply by the vertical scale. + value *= _verticalScale; + osg::Vec3d ndc, v; + ndc.x() = ((double)c)/(double)(numColumns-1), + ndc.y() = ((double)r)/(double)(numRows-1); + ndc.z() = value; + + if (_elevationLayer->getLocator()->convertLocalToModel(ndc, v)) + { + bb.expandBy(v); + } + } + } + } + bs.expandBy(bb); + + } + else + { + for(ColorLayersByUID::const_iterator i = _colorLayers.begin(); i != _colorLayers.end(); ++i ) + { + bs.expandBy( i->second.computeBound() ); + } + } + + return bs; +} + +void +Tile::queueTileUpdate( TileUpdate::Action action, int value ) +{ + _dirty = true; +} + +void +Tile::applyImmediateTileUpdate( TileUpdate::Action action, int value ) +{ + CustomTerrainTechnique* tech = dynamic_cast( _tech.get() ); + if ( tech ) + { + tech->compile( TileUpdate(action, value), 0L ); + tech->applyTileUpdates(); + } + else + { + queueTileUpdate( action, value ); + } +} + +void +Tile::traverse( osg::NodeVisitor& nv ) +{ + // set the parent tile in the technique: + if ( !_parentTileSet && _terrain.valid() ) + { + osg::ref_ptr parentTile; + //Take a reference + osg::ref_ptr< Terrain > terrain = _terrain.get(); + if (terrain.valid()) + { + terrain->getTile( _key.createParentKey().getTileId(), parentTile ); + CustomTerrainTechnique* tech = dynamic_cast( _tech.get() ); + if ( tech ) + tech->setParentTile( parentTile.get() ); + _parentTileSet = true; + } + } + + // this block runs the first time the tile is traversed while in the scene graph. + if ( !_hasBeenTraversed ) + { + if ( nv.getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR ) + { + Threading::ScopedWriteLock lock( this->_tileLayersMutex ); + { + if ( !_hasBeenTraversed && _terrain.valid() ) + { + _hasBeenTraversed = true; + + // we constructed this tile with an update traversal count of 1 so it would get + // here and we could register the tile. Now we can decrement it back to normal. + // this MUST be called from the UPDATE traversal. + ADJUST_UPDATE_TRAV_COUNT( this, -1 ); + } + } + } + } + + // code copied from osgTerrain::TerrainTile... TODO: evaluate this... -gw + if ( nv.getVisitorType()==osg::NodeVisitor::CULL_VISITOR ) + { + osg::ClusterCullingCallback* ccc = dynamic_cast(getCullCallback()); + if (ccc) + { + if (ccc->cull(&nv,0,static_cast(0))) return; + } + } + + if ( _dirty ) + { + init(); + } + + if ( _tech.valid() ) + { + _tech->traverse( nv ); + } +} + +void +Tile::releaseGLObjects(osg::State* state) const +{ + osg::Node::releaseGLObjects( state ); + + if ( _tech.valid() ) + { + //NOTE: crashes sometimes if OSG_RELEASE_DELAY is set -gw + _tech->releaseGLObjects( state ); + } +} + +//------------------------------------------------------------------------ + +TileFrame::TileFrame( Tile* tile ) : +_tileKey(tile->getKey()) +{ + Threading::ScopedReadLock sharedLock( tile->_tileLayersMutex ); + _colorLayers = tile->_colorLayers; + _elevationLayer = tile->getElevationLayer(); + _locator = tile->getLocator(); + osg::ref_ptr< Terrain > terrain = tile->getTerrain(); + if (terrain.valid()) + { + _sampleRatio = terrain->getSampleRatio(); + } + _masks = MaskLayerVector(tile->getTerrainMasks()); +} diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/TransparentLayer osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/TransparentLayer --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_osgterrain/TransparentLayer 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_osgterrain/TransparentLayer 2011-11-04 19:44:43.000000000 +0000 @@ -23,7 +23,9 @@ #include "Common" #include #include +#include #include +#include #include class CustomColorLayer @@ -35,14 +37,18 @@ const osgEarth::ImageLayer* imageLayer, osg::Image* image, const osgTerrain::Locator* locator, - int lod ) - : _layer(imageLayer), _locator(locator), _image(image), _lod(lod) { } + int lod, + const osgEarth::TileKey& tileKey, + bool fallbackData =false ) + : _layer(imageLayer), _locator(locator), _image(image), _tileKey(tileKey), _lod(lod), _fallbackData(fallbackData) { } CustomColorLayer( const CustomColorLayer& rhs ) : _layer( rhs._layer.get() ), _locator( rhs._locator.get() ), _image( rhs._image.get() ), - _lod( rhs._lod ) { } + _tileKey( rhs._tileKey ), + _lod( rhs._lod ), + _fallbackData( rhs._fallbackData ) { } osgEarth::UID getUID() const { return _layer->getUID(); @@ -55,12 +61,18 @@ osg::Image* getImage() const { return _image.get(); } + const osgEarth::TileKey& getTileKey() const { + return _tileKey; } + const osgEarth::ImageLayer* getMapLayer() const { return _layer.get(); } int getLevelOfDetail() const { return _lod; } + bool isFallbackData() const { + return _fallbackData; } + osg::BoundingSphere computeBound() const { osg::BoundingSphere bs; osg::Vec3d v; @@ -78,7 +90,9 @@ osg::ref_ptr _layer; osg::ref_ptr _locator; osg::ref_ptr _image; + osgEarth::TileKey _tileKey; int _lod; + bool _fallbackData; }; class CustomColorLayerRef : public osg::Referenced @@ -88,52 +102,23 @@ CustomColorLayer _layer; }; -#if 0 -class CustomColorLayer : public osgTerrain::ImageLayer +class CustomElevLayer { public: - CustomColorLayer(const osg::Image* image, osgEarth::ImageLayer* mapLayer): - osgTerrain::ImageLayer( const_cast(image) ), - _mapLayer(mapLayer), - _lod(-1) - { - } - - UID getUID() const - { - return _mapLayer->getUID(); - } - - float getOpacity() const - { - return _mapLayer->getOpacity(); - } - - bool getEnabled() const - { - return _mapLayer->getEnabled(); - } - - int getLevelOfDetail() const - { - return _lod; - } - - void setLevelOfDetail(int lod) - { - _lod = lod; - } - - osgEarth::ImageLayer* getMapLayer() const - { - return _mapLayer.get(); - } + CustomElevLayer() { } + + CustomElevLayer( osgTerrain::HeightFieldLayer* hfLayer, bool fallbackData =false ) + : _hfLayer(hfLayer), _fallbackData(fallbackData) { } - const osgTerrain::Locator* getLocator() const { return _locator; } + osgTerrain::HeightFieldLayer* getHFLayer() { + return _hfLayer.get(); } - osg::ref_ptr _mapLayer; - int _lod; + bool isFallbackData() const { + return _fallbackData; } + +private: + osg::ref_ptr _hfLayer; + bool _fallbackData; }; -#endif #endif // OSGEARTH_ENGINE_TRANSPARENT_LAYER diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_seamless/Geographic.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_seamless/Geographic.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_seamless/Geographic.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_seamless/Geographic.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -313,7 +313,7 @@ int resolution) { osg::ref_ptr hf; - mapf.getHeightField(key, true, hf, INTERP_BILINEAR); + mapf.getHeightField(key, true, hf, 0L, INTERP_BILINEAR); if (!hf) hf = key.getProfile()->getVerticalSRS() ->createReferenceHeightField(key.getExtent(), diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_seamless/Projected.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_seamless/Projected.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_seamless/Projected.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_seamless/Projected.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -144,7 +144,7 @@ ref_ptr hf; GeoImage gimage; { - _mapf->getHeightField(key, true, hf, INTERP_BILINEAR); + _mapf->getHeightField(key, true, hf, 0L, INTERP_BILINEAR); const ImageLayerVector& layers = _mapf->imageLayers(); if (!layers.empty()) gimage = layers[0]->createImage(key); diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/engine_seamless/SeamlessPlugin.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_seamless/SeamlessPlugin.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/engine_seamless/SeamlessPlugin.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/engine_seamless/SeamlessPlugin.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -60,6 +60,8 @@ else return ReadResult(new SeamlessEngineNode); } + else + return ReadResult::FILE_NOT_HANDLED; } virtual ReadResult diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/feature_ogr/CMakeLists.txt osgearth-2.1.1+dfsg/src/osgEarthDrivers/feature_ogr/CMakeLists.txt --- osgearth-2.0+dfsg/src/osgEarthDrivers/feature_ogr/CMakeLists.txt 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/feature_ogr/CMakeLists.txt 2011-11-04 19:44:43.000000000 +0000 @@ -4,8 +4,7 @@ ) SET(TARGET_H - FeatureCursorOGR - GeometryUtils + FeatureCursorOGR OGRFeatureOptions ) diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/feature_ogr/FeatureCursorOGR osgearth-2.1.1+dfsg/src/osgEarthDrivers/feature_ogr/FeatureCursorOGR --- osgearth-2.0+dfsg/src/osgEarthDrivers/feature_ogr/FeatureCursorOGR 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/feature_ogr/FeatureCursorOGR 2011-11-04 19:44:43.000000000 +0000 @@ -73,8 +73,7 @@ const FeatureFilterList& _filters; private: - void readChunk(); - Feature* createFeature( OGRFeatureH handle ); + void readChunk(); }; diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/feature_ogr/FeatureCursorOGR.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/feature_ogr/FeatureCursorOGR.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/feature_ogr/FeatureCursorOGR.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/feature_ogr/FeatureCursorOGR.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -17,7 +17,7 @@ * along with this program. If not, see */ #include "FeatureCursorOGR" -#include "GeometryUtils" +#include #include #include #include @@ -160,7 +160,7 @@ if ( _nextHandleToQueue ) { - Feature* f = createFeature( _nextHandleToQueue ); + Feature* f = OgrUtils::createFeature( _nextHandleToQueue ); if ( f ) { _queue.push( f ); @@ -179,7 +179,7 @@ OGRFeatureH handle = OGR_L_GetNextFeature( _resultSetHandle ); if ( handle ) { - Feature* f = createFeature( handle ); + Feature* f = OgrUtils::createFeature( handle ); if ( f ) { _queue.push( f ); @@ -212,34 +212,3 @@ //OE_NOTICE << "read " << _queue.size() << " features ... " << std::endl; } -// NOTE: ASSUMES that OGR_SCOPED_LOCK is already in effect upon entry! -Feature* -FeatureCursorOGR::createFeature( OGRFeatureH handle ) -{ - long fid = OGR_F_GetFID( handle ); - - Feature* feature = new Feature( fid ); - - OGRGeometryH geomRef = OGR_F_GetGeometryRef( handle ); - if ( geomRef ) - { - Symbology::Geometry* geom = GeometryUtils::createGeometry( geomRef ); - feature->setGeometry( geom ); - } - - int numAttrs = OGR_F_GetFieldCount(handle); - for (int i = 0; i < numAttrs; ++i) - { - void* field_handle_ref = OGR_F_GetFieldDefnRef( handle, i ); - const char* field_name = OGR_Fld_GetNameRef( field_handle_ref ); - const char* field_value= OGR_F_GetFieldAsString(handle, i); - std::string name = std::string( field_name ); - std::string value = std::string( field_value); - //Make the name lower case - std::transform( name.begin(), name.end(), name.begin(), ::tolower ); - feature->setAttr(name, value); - } - - return feature; -} - diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/feature_ogr/FeatureSourceOGR.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/feature_ogr/FeatureSourceOGR.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/feature_ogr/FeatureSourceOGR.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/feature_ogr/FeatureSourceOGR.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -25,7 +25,7 @@ #include #include "OGRFeatureOptions" #include "FeatureCursorOGR" -#include "GeometryUtils" +#include #include #include #include @@ -48,16 +48,19 @@ class OGRFeatureSource : public FeatureSource { public: - OGRFeatureSource( const FeatureSourceOptions& options ) : FeatureSource( options ), + OGRFeatureSource( const OGRFeatureOptions& options ) : FeatureSource( options ), _dsHandle( 0L ), _layerHandle( 0L ), _ogrDriverHandle( 0L ), - _options( options ) + _options( options ), + _featureCount(-1), + _needsSync(false), + _writable(false) { - _geometry = _options.geometry().valid() ? _options.geometry().get() : - _options.geometryConfig().isSet() ? parseGeometry( _options.geometryConfig().value() ) : + _options.geometryConfig().isSet() ? parseGeometry( *_options.geometryConfig() ) : + _options.geometryUrl().isSet() ? parseGeometryUrl( *_options.geometryUrl() ) : 0L; } @@ -68,7 +71,16 @@ if ( _layerHandle ) { - // OGR_L_SyncToDisk( _layerHandle ); // for writing only + if (_needsSync) + { + OGR_L_SyncToDisk( _layerHandle ); // for writing only + const char* name = OGR_FD_GetName( OGR_L_GetLayerDefn( _layerHandle ) ); + std::stringstream buf; + buf << "REPACK " << name; + std::string bufStr; + bufStr = buf.str(); + OGR_DS_ExecuteSQL( _dsHandle, bufStr.c_str(), 0L, 0L ); + } _layerHandle = 0L; } @@ -84,7 +96,11 @@ { if ( _options.url().isSet() ) { - _absUrl = osgEarth::getFullPath( referenceURI, _options.url().value() ); + _source = osgEarth::getFullPath( referenceURI, _options.url()->full() ); + } + else if ( _options.connection().isSet() ) + { + _source = _options.connection().value(); } } @@ -94,17 +110,20 @@ { FeatureProfile* result = 0L; + // see if we have a custom profile. + osg::ref_ptr profile; + if ( _options.profile().isSet() ) + { + profile = Profile::create( *_options.profile() ); + } + if ( _geometry.valid() ) { // if the user specified explicit geometry/profile, use that: GeoExtent ex; - if ( _options.geometryProfileOptions().isSet() ) + if ( profile.valid() ) { - osg::ref_ptr _profile = Profile::create( - ProfileOptions(_options.geometryProfileOptions().value()) ); - - if ( _profile.valid() ) - ex = _profile->getExtent(); + ex = profile->getExtent(); } if ( !ex.isValid() ) @@ -114,7 +133,8 @@ } result = new FeatureProfile( ex ); } - else if ( !_absUrl.empty() ) + + else if ( !_source.empty() ) { // otherwise, assume we're loading from the URL: OGR_SCOPED_LOCK; @@ -126,29 +146,42 @@ _ogrDriverHandle = OGRGetDriverByName( driverName.c_str() ); // attempt to open the dataset: - _dsHandle = OGROpenShared( _absUrl.c_str(), 0, &_ogrDriverHandle ); + int openMode = _options.openWrite().isSet() && _options.openWrite().value() ? 1 : 0; + + _dsHandle = OGROpenShared( _source.c_str(), openMode, &_ogrDriverHandle ); if ( _dsHandle ) { + if (openMode == 1) _writable = true; + _layerHandle = OGR_DS_GetLayer( _dsHandle, 0 ); // default to layer 0 for now if ( _layerHandle ) - { + { GeoExtent extent; - // extract the SRS and Extent: - OGRSpatialReferenceH srHandle = OGR_L_GetSpatialRef( _layerHandle ); - if ( srHandle ) + // if the user provided a profile, user that: + if ( profile.valid() ) + { + result = new FeatureProfile( profile->getExtent() ); + } + + else { - osg::ref_ptr srs = SpatialReference::createFromHandle( srHandle, false ); - if ( srs.valid() ) + // extract the SRS and Extent: + OGRSpatialReferenceH srHandle = OGR_L_GetSpatialRef( _layerHandle ); + if ( srHandle ) { - // extract the full extent of the layer: - OGREnvelope env; - if ( OGR_L_GetExtent( _layerHandle, &env, 1 ) == OGRERR_NONE ) + osg::ref_ptr srs = SpatialReference::createFromHandle( srHandle, false ); + if ( srs.valid() ) { - GeoExtent extent( srs.get(), env.MinX, env.MinY, env.MaxX, env.MaxY ); - - // got enough info to make the profile! - result = new FeatureProfile( extent ); + // extract the full extent of the layer: + OGREnvelope env; + if ( OGR_L_GetExtent( _layerHandle, &env, 1 ) == OGRERR_NONE ) + { + GeoExtent extent( srs.get(), env.MinX, env.MinY, env.MaxX, env.MaxY ); + + // got enough info to make the profile! + result = new FeatureProfile( extent ); + } } } } @@ -156,22 +189,61 @@ // assuming we successfully opened the layer, build a spatial index if requested. if ( _options.buildSpatialIndex() == true ) { - OE_INFO << LC << "Building spatial index for " << getName() << " ..." << std::flush; - + OE_INFO << LC << "Building spatial index for " << getName() << std::endl; 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(); OGR_DS_ExecuteSQL( _dsHandle, bufStr.c_str(), 0L, 0L ); + } - OE_INFO << LC << "...done." << std::endl; + //Get the feature count + _featureCount = OGR_L_GetFeatureCount( _layerHandle, 1 ); + + initSchema(); + + OGRwkbGeometryType wkbType = OGR_FD_GetGeomType( OGR_L_GetLayerDefn( _layerHandle ) ); + if ( + wkbType == wkbPolygon || + wkbType == wkbPolygon25D ) + { + _geometryType = Geometry::TYPE_POLYGON; + } + else if ( + wkbType == wkbLineString || + wkbType == wkbLineString25D ) + { + _geometryType = Geometry::TYPE_LINESTRING; + } + else if ( + wkbType == wkbLinearRing ) + { + _geometryType = Geometry::TYPE_RING; + } + else if ( + wkbType == wkbPoint || + wkbType == wkbPoint25D ) + { + _geometryType = Geometry::TYPE_POINTSET; + } + else if ( + wkbType == wkbGeometryCollection || + wkbType == wkbGeometryCollection25D || + wkbType == wkbMultiPoint || + wkbType == wkbMultiPoint25D || + wkbType == wkbMultiLineString || + wkbType == wkbMultiLineString25D || + wkbType == wkbMultiPolygon || + wkbType == wkbMultiPolygon25D ) + { + _geometryType = Geometry::TYPE_MULTI; } } } else { - OE_INFO << LC << "failed to open dataset at " << _absUrl << std::endl; + OE_INFO << LC << "failed to open dataset \"" << _source << "\"" << std::endl; } } else @@ -202,7 +274,7 @@ // 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( _absUrl.c_str(), 0, &_ogrDriverHandle ); + OGRDataSourceH dsHandle = OGROpenShared( _source.c_str(), 0, &_ogrDriverHandle ); if ( dsHandle ) { OGRLayerH layerHandle = OGR_DS_GetLayer( dsHandle, 0 ); @@ -221,41 +293,184 @@ } } -protected: + virtual bool deleteFeature(FeatureID fid) + { + if (_writable && _layerHandle) + { + if (OGR_L_DeleteFeature( _layerHandle, fid ) == OGRERR_NONE) + { + _needsSync = true; + return true; + } + } + return false; + } - // closes any open OGR objects and releases the handles - bool cleanup() + virtual int getFeatureCount() const { - OGR_SCOPED_LOCK; + return _featureCount; + } - if ( _layerHandle ) + virtual Feature* getFeature( FeatureID fid ) + { + Feature* result = NULL; + OGRFeatureH handle = OGR_L_GetFeature( _layerHandle, fid); + if (handle) { - // OGR_L_SyncToDisk( _layerHandle ); // for writing only - _layerHandle = 0L; + result = OgrUtils::createFeature( handle ); + OGR_F_Destroy( handle ); } + return result; + } - if ( _dsHandle ) + virtual bool isWritable() const + { + return _writable; + } + + const FeatureSchema& getSchema() const + { + return _schema; + } + + virtual bool insertFeature(Feature* feature) + { + OGR_SCOPED_LOCK; + OGRFeatureH feature_handle = OGR_F_Create( OGR_L_GetLayerDefn( _layerHandle ) ); + if ( feature_handle ) { - OGRReleaseDataSource( _dsHandle ); - _dsHandle = 0L; + const AttributeTable& attrs = feature->getAttrs(); + + // assign the attributes: + int num_fields = OGR_F_GetFieldCount( feature_handle ); + for( int i=0; isecond.getInt(0) ); + break; + case OFTReal: + OGR_F_SetFieldDouble( feature_handle, field_index, a->second.getDouble(0.0) ); + break; + case OFTString: + OGR_F_SetFieldString( feature_handle, field_index, a->second.getString().c_str() ); + break; + } + } + } + + // std::string value = feature->getAttr( name ); + // if (!value.empty()) + // { + // switch( OGR_Fld_GetType( field_handle_ref ) ) + // { + // case OFTInteger: + // OGR_F_SetFieldInteger( feature_handle, field_index, as(value, 0) ); + // break; + // case OFTReal: + // OGR_F_SetFieldDouble( feature_handle, field_index, as(value, 0.0) ); + // break; + // case OFTString: + // OGR_F_SetFieldString( feature_handle, field_index, value.c_str() ); + // break; + // } + // } + //} + + // assign the geometry: + OGRFeatureDefnH def = ::OGR_L_GetLayerDefn( _layerHandle ); + + OGRwkbGeometryType reported_type = OGR_FD_GetGeomType( def ); + + OGRGeometryH ogr_geometry = OgrUtils::createOgrGeometry( feature->getGeometry(), reported_type ); + if ( OGR_F_SetGeometryDirectly( feature_handle, ogr_geometry ) != OGRERR_NONE ) + { + OE_WARN << LC << "OGR_F_SetGeometryDirectly failed!" << std::endl; + } + + if ( OGR_L_CreateFeature( _layerHandle, feature_handle ) != OGRERR_NONE ) + { + //TODO: handle error better + OE_WARN << LC << "OGR_L_CreateFeature failed!" << std::endl; + OGR_F_Destroy( feature_handle ); + return false; + } + + // clean up the feature + OGR_F_Destroy( feature_handle ); + } + else + { + //TODO: handle error better + OE_WARN << LC << "OGR_F_Create failed." << std::endl; + return false; } return true; } + virtual osgEarth::Symbology::Geometry::Type getGeometryType() const + { + return _geometryType; + } + +protected: + // parses an explicit WKT geometry string into a Geometry. Symbology::Geometry* parseGeometry( const Config& geomConf ) { - return GeometryUtils::createGeometryFromWKT( geomConf.value() ); + return OgrUtils::createGeometryFromWKT( geomConf.value() ); } + // read the WKT geometry from a URL, then parse into a Geometry. + Symbology::Geometry* parseGeometryUrl( const std::string& geomUrl ) + { + std::string wkt; + if ( HTTPClient::readString( geomUrl, wkt ) == HTTPClient::RESULT_OK ) + { + Config conf( "geometry", wkt ); + return parseGeometry( conf ); + } + return 0L; + } + + void initSchema() + { + OGRFeatureDefnH layerDef = OGR_L_GetLayerDefn( _layerHandle ); + for (int i = 0; i < OGR_FD_GetFieldCount( layerDef ); i++) + { + OGRFieldDefnH fieldDef = OGR_FD_GetFieldDefn( layerDef, i ); + std::string name; + name = std::string( OGR_Fld_GetNameRef( fieldDef ) ); + OGRFieldType ogrType = OGR_Fld_GetType( fieldDef ); + _schema[ name ] = OgrUtils::getAttributeType( ogrType ); + } + } + + + + + private: - std::string _absUrl; + std::string _source; OGRDataSourceH _dsHandle; OGRLayerH _layerHandle; OGRSFDriverH _ogrDriverHandle; osg::ref_ptr _geometry; // explicit geometry. const OGRFeatureOptions _options; + int _featureCount; + bool _needsSync; + bool _writable; + FeatureSchema _schema; + Geometry::Type _geometryType; }; diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/feature_ogr/GeometryUtils osgearth-2.1.1+dfsg/src/osgEarthDrivers/feature_ogr/GeometryUtils --- osgearth-2.0+dfsg/src/osgEarthDrivers/feature_ogr/GeometryUtils 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/feature_ogr/GeometryUtils 1970-01-01 00:00:00.000000000 +0000 @@ -1,187 +0,0 @@ -/* -*-c++-*- */ -/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph - * Copyright 2008-2010 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 - */ -#ifndef OSGEARTHFEATURES_FEATURE_OGR_GEOM_UTILS -#define OSGEARTHFEATURES_FEATURE_OGR_GEOM_UTILS 1 - -#include -#include -#include -#include - -using namespace osgEarth; -using namespace osgEarth::Features; - -struct GeometryUtils -{ - 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 Symbology::Polygon* - 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; - } - - 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; ngetComponents().push_back( geom ); - } - } - - output = multi; - } - - return output; - } - - static Symbology::Geometry* - createGeometryFromWKT( const std::string& wkt ) - { - OGRwkbGeometryType type = - startsWith( wkt, "POINT" ) ? wkbPoint : - startsWith( wkt, "LINESTRING" ) ? wkbLineString : - startsWith( wkt, "POLYGON" ) ? wkbPolygon : - startsWith( wkt, "MULTIPOINT" ) ? wkbMultiPoint : - startsWith( wkt, "MULTILINESTRING" ) ? wkbMultiLineString : - startsWith( wkt, "MULTIPOLYGON" ) ? wkbMultiPolygon : - startsWith( wkt, "GEOMETRYCOLLECTION" ) ? wkbGeometryCollection : - wkbNone; - - Symbology::Geometry* output = 0L; - if ( type != wkbNone ) - { - OGRGeometryH geom = OGR_G_CreateGeometry( type ); - if ( geom ) - { - char* ptr = (char*)wkt.c_str(); - if ( OGRERR_NONE == OGR_G_ImportFromWkt( geom, &ptr ) ) - { - output = createGeometry( geom ); - OGR_G_DestroyGeometry( geom ); - } - else - { - OE_NOTICE - << "OGR Feature Source: malformed WKT geometry" << std::endl; - } - } - } - return output; - } -}; - - -#endif // OSGEARTHFEATURES_FEATURE_OGR_GEOM_UTILS - diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/feature_ogr/OGRFeatureOptions osgearth-2.1.1+dfsg/src/osgEarthDrivers/feature_ogr/OGRFeatureOptions --- osgearth-2.0+dfsg/src/osgEarthDrivers/feature_ogr/OGRFeatureOptions 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/feature_ogr/OGRFeatureOptions 2011-11-04 19:44:43.000000000 +0000 @@ -30,8 +30,11 @@ class OGRFeatureOptions : public FeatureSourceOptions // NO EXPORT; header only { public: - optional& url() { return _url; } - const optional& url() const { return _url; } + optional& url() { return _url; } + const optional& url() const { return _url; } + + optional& connection() { return _connection; } + const optional& connection() const { return _connection; } optional& ogrDriver() { return _ogrDriver; } const optional& ogrDriver() const { return _ogrDriver; } @@ -42,9 +45,10 @@ optional& geometryConfig() { return _geometryConf; } const optional& geometryConfig() const { return _geometryConf; } - optional& geometryProfileOptions() { return _geometryProfileConf; } - const optional& geometryProfileOptions() const { return _geometryProfileConf; } + optional& geometryUrl() { return _geometryUrl; } + const optional& geometryUrl() const { return _geometryUrl; } + // does not serialize osg::ref_ptr& geometry() { return _geometry; } const osg::ref_ptr& geometry() const { return _geometry; } @@ -58,10 +62,12 @@ Config getConfig() const { Config conf = FeatureSourceOptions::getConfig(); conf.updateIfSet( "url", _url ); + conf.updateIfSet( "connection", _connection ); conf.updateIfSet( "ogr_driver", _ogrDriver ); conf.updateIfSet( "build_spatial_index", _buildSpatialIndex ); conf.updateIfSet( "geometry", _geometryConf ); - conf.updateIfSet( "geometry_profile", _geometryProfileConf ); + conf.updateIfSet( "geometry_url", _geometryUrl ); + conf.updateNonSerializable( "OGRFeatureOptions::geometry", _geometry.get() ); return conf; } @@ -74,17 +80,21 @@ private: void fromConfig( const Config& conf ) { conf.getIfSet( "url", _url ); + conf.getIfSet( "connection", _connection ); conf.getIfSet( "ogr_driver", _ogrDriver ); conf.getIfSet( "build_spatial_index", _buildSpatialIndex ); conf.getIfSet( "geometry", _geometryConf ); - conf.getIfSet( "geometry_profile", _geometryProfileConf ); + conf.getIfSet( "geometry_url", _geometryUrl ); + _geometry = conf.getNonSerializable( "OGRFeatureOptions::geometry" ); } - optional _url; - optional _ogrDriver; - optional _buildSpatialIndex; - optional _geometryConf; - optional _geometryProfileConf; + optional _url; + optional _connection; + optional _ogrDriver; + optional _buildSpatialIndex; + optional _geometryConf; + optional _geometryProfileConf; + optional _geometryUrl; osg::ref_ptr _geometry; }; diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/feature_wfs/CMakeLists.txt osgearth-2.1.1+dfsg/src/osgEarthDrivers/feature_wfs/CMakeLists.txt --- osgearth-2.0+dfsg/src/osgEarthDrivers/feature_wfs/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/feature_wfs/CMakeLists.txt 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,18 @@ +SET(TARGET_SRC + FeatureSourceWFS.cpp +) + +SET(TARGET_H + WFSFeatureOptions +) + +INCLUDE_DIRECTORIES( ${GDAL_INCLUDE_DIR} ) +SET(TARGET_COMMON_LIBRARIES ${TARGET_COMMON_LIBRARIES} osgEarthFeatures osgEarthSymbology osgEarthUtil) +SET(TARGET_LIBRARIES_VARS GDAL_LIBRARY) +SETUP_PLUGIN(osgearth_feature_wfs) + + +# to install public driver includes: +SET(LIB_NAME feature_wfs) +SET(LIB_PUBLIC_HEADERS ${TARGET_H}) +INCLUDE(ModuleInstallOsgEarthDriverIncludes OPTIONAL) diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/feature_wfs/FeatureSourceWFS.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/feature_wfs/FeatureSourceWFS.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/feature_wfs/FeatureSourceWFS.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/feature_wfs/FeatureSourceWFS.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,373 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include "WFSFeatureOptions" +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef WIN32 +#include +#endif + +#define LC "[WFS FeatureSource] " + +using namespace osgEarth; +using namespace osgEarth::Util; +using namespace osgEarth::Features; +using namespace osgEarth::Drivers; + +#define OGR_SCOPED_LOCK GDAL_SCOPED_LOCK + +std::string getTempPath() +{ +#if defined(WIN32) && !defined(__CYGWIN__) + BOOL fSuccess = FALSE; + + TCHAR lpTempPathBuffer[MAX_PATH]; + + // Gets the temp path env string (no guarantee it's a valid path). + DWORD dwRetVal = ::GetTempPath(MAX_PATH, // length of the buffer + lpTempPathBuffer); // buffer for path + + if (dwRetVal > MAX_PATH || (dwRetVal == 0)) + { + OE_NOTICE << "GetTempPath failed" << std::endl; + return "."; + } + + return std::string(lpTempPathBuffer); +#else + return "/tmp/"; +#endif +} + +std::string getTempName(const std::string& prefix="", const std::string& suffix="") +{ + //tmpname is kind of busted on Windows, it always returns a file of the form \blah which gets put in your root directory but + //oftentimes can't get opened by some drivers b/c it doesn't have a drive letter in front of it. + bool valid = false; + while (!valid) + { + std::stringstream ss; + ss << prefix << "~" << rand() << suffix; + if (!osgDB::fileExists(ss.str())) return ss.str(); + } + return ""; +} + +/** + * A FeatureSource that reads features from a WFS layer + * + * This FeatureSource does NOT support styling. + */ +class WFSFeatureSource : public FeatureSource +{ +public: + WFSFeatureSource( const WFSFeatureOptions& options ) : FeatureSource( options ), + _options( options ) + { + } + + /** Destruct the object, cleaning up and OGR handles. */ + virtual ~WFSFeatureSource() + { + } + + //override + void initialize( const std::string& referenceURI ) + { + char sep = _options.url()->full().find_first_of('?') == std::string::npos? '?' : '&'; + + std::string capUrl; + if ( capUrl.empty() ) + { + capUrl = + _options.url()->full() + + sep + + "SERVICE=WFS&VERSION=1.0.0&REQUEST=GetCapabilities"; + } + + _capabilities = WFSCapabilitiesReader::read( capUrl, 0L ); + if ( !_capabilities.valid() ) + { + OE_WARN << "[osgEarth::WFS] Unable to read WFS GetCapabilities." << std::endl; + //return; + } + else + { + OE_NOTICE << "[osgEarth::WFS] Got capabilities from " << capUrl << std::endl; + } + } + + /** Called once at startup to create the profile for this feature set. Successful profile + creation implies that the datasource opened succesfully. */ + const FeatureProfile* createFeatureProfile() + { + FeatureProfile* result = NULL; + if (_capabilities.valid()) + { + //Find the feature type by name + osg::ref_ptr< WFSFeatureType > featureType = _capabilities->getFeatureTypeByName( _options.typeName().get() ); + if (featureType.valid()) + { + if (featureType->getExtent().isValid()) + { + result = new FeatureProfile(featureType->getExtent()); + + if (featureType->getTiled()) + { + result->setTiled( true ); + result->setFirstLevel( featureType->getFirstLevel() ); + result->setMaxLevel( featureType->getMaxLevel() ); + result->setProfile( osgEarth::Profile::create(osgEarth::SpatialReference::create("epsg:4326"), featureType->getExtent().xMin(), featureType->getExtent().yMin(), featureType->getExtent().xMax(), featureType->getExtent().yMax(), 0, 1, 1) ); + } + } + } + } + + if (!result) + { + result = new FeatureProfile(GeoExtent(SpatialReference::create( "epsg:4326" ), -180, -90, 180, 90)); + } + return result; + } + + void saveResponse(HTTPResponse& response, const std::string& filename) + { + std::ofstream fout; + fout.open(filename.c_str(), std::ios::out | std::ios::binary); + + std::istream& input_stream = response.getPartStream(0); + input_stream.seekg (0, std::ios::end); + int length = input_stream.tellg(); + input_stream.seekg (0, std::ios::beg); + + char *buffer = new char[length]; + input_stream.read(buffer, length); + fout.write(buffer, length); + delete[] buffer; + fout.close(); + } + + std::string getExtensionForMimeType(const std::string& mime) + { + //OGR is particular sometimes about the extension of files when it's reading them so it's good to have + //the temp file have an appropriate extension + if ((mime.compare("text/xml") == 0) || + (mime.compare("text/xml; subtype=gml/2.1.2") == 0) || + (mime.compare("text/xml; subtype=gml/3.1.1") == 0) + ) + { + return ".xml"; + } + else if ((mime.compare("application/json") == 0) || + (mime.compare("json") == 0) || + + (mime.compare("application/x-javascript") == 0) || + (mime.compare("text/javascript") == 0) || + (mime.compare("text/x-javascript") == 0) || + (mime.compare("text/x-json") == 0) + ) + { + return ".json"; + } + return ""; + } + + void getFeatures(HTTPResponse &response, FeatureList& features) + { + //OE_NOTICE << "mimetype=" << response.getMimeType() << std::endl; + //TODO: Handle more than just geojson... + std::string ext = getExtensionForMimeType(response.getMimeType()); + //Save the response to a temp file + std::string tmpPath = getTempPath(); + std::string name = getTempName(tmpPath, ext); + saveResponse(response, name ); + //OE_NOTICE << "Saving to " << name << std::endl; + + //OGRDataSourceH ds = OGROpen(name.c_str(), FALSE, &driver); + OGRDataSourceH ds = OGROpen(name.c_str(), FALSE, NULL); + if (!ds) + { + OE_NOTICE << "Error opening data with contents " << std::endl + << response.getPartAsString(0) << std::endl; + } + + OGRLayerH layer = OGR_DS_GetLayer(ds, 0); + //Read all the features + if (layer) + { + OGR_L_ResetReading(layer); + OGRFeatureH feat_handle; + while ((feat_handle = OGR_L_GetNextFeature( layer )) != NULL) + { + if ( feat_handle ) + { + Feature* f = OgrUtils::createFeature( feat_handle ); + if ( f ) + { + features.push_back( f ); + } + OGR_F_Destroy( feat_handle ); + } + } + } + + //Destroy the datasource + OGR_DS_Destroy( ds ); + //Remove the temporary file + remove( name.c_str() ); + } + + std::string createURL(const Symbology::Query& query) + { + std::stringstream buf; + buf << _options.url()->full() << "?SERVICE=WFS&VERSION=1.0.0&REQUEST=getfeature"; + buf << "&TYPENAME=" << _options.typeName().get(); + + std::string outputFormat = "geojson"; + if (_options.outputFormat().isSet()) outputFormat = _options.outputFormat().get(); + buf << "&OUTPUTFORMAT=" << outputFormat; + + if (_options.maxFeatures().isSet()) + { + buf << "&MAXFEATURES=" << _options.maxFeatures().get(); + } + + if (query.tileKey().isSet()) + { + buf << "&Z=" << query.tileKey().get().getLevelOfDetail() << + "&X=" << query.tileKey().get().getTileX() << + "&Y=" << query.tileKey().get().getTileY(); + } + else if (query.bounds().isSet()) + { + buf << "&BBOX=" << query.bounds().get().xMin() << "," << query.bounds().get().yMin() << "," + << query.bounds().get().xMax() << "," << query.bounds().get().yMax(); + } + return buf.str(); + } + + //override + FeatureCursor* createFeatureCursor( const Symbology::Query& query ) + { + std::string url = createURL( query ); + OE_DEBUG << LC << "URL: " << url << std::endl; + HTTPResponse response = HTTPClient::get(url); + FeatureList features; + if (response.isOK()) + { + getFeatures(response, features); + std::string data = response.getPartAsString(0); + } + else + { + OE_INFO << "Error getting url " << url << std::endl; + } + return new FeatureListCursor( features ); + } + + virtual bool deleteFeature(FeatureID fid) + { + return false; + } + + virtual int getFeatureCount() const + { + return -1; + } + + virtual bool insertFeature(Feature* feature) + { + return false; + } + + /** + * Gets the Feature with the given FID + * @returns + * The Feature with the given FID or NULL if not found. + */ + virtual Feature* getFeature( FeatureID fid ) + { + return 0; + } + + virtual bool isWritable() const + { + return false; + } + + virtual const FeatureSchema& getSchema() const + { + //TODO: Populate the schema from the DescribeFeatureType call + return _schema; + } + + virtual osgEarth::Symbology::Geometry::Type getGeometryType() const + { + return Geometry::TYPE_UNKNOWN; + } + + + +private: + const WFSFeatureOptions _options; + osg::ref_ptr< WFSCapabilities > _capabilities; + FeatureSchema _schema; +}; + + +class WFSFeatureSourceFactory : public FeatureSourceDriver +{ +public: + WFSFeatureSourceFactory() + { + supportsExtension( "osgearth_feature_wfs", "WFS feature driver for osgEarth" ); + } + + virtual const char* className() + { + return "WFS Feature Reader"; + } + + virtual ReadResult readObject(const std::string& file_name, const Options* options) const + { + if ( !acceptsExtension(osgDB::getLowerCaseFileExtension( file_name ))) + return ReadResult::FILE_NOT_HANDLED; + + return ReadResult( new WFSFeatureSource( getFeatureSourceOptions(options) ) ); + } +}; + +REGISTER_OSGPLUGIN(osgearth_feature_wfs, WFSFeatureSourceFactory) + diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/feature_wfs/WFSFeatureOptions osgearth-2.1.1+dfsg/src/osgEarthDrivers/feature_wfs/WFSFeatureOptions --- osgearth-2.0+dfsg/src/osgEarthDrivers/feature_wfs/WFSFeatureOptions 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/feature_wfs/WFSFeatureOptions 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,91 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#ifndef OSGEARTH_DRIVER_WFS_FEATURE_SOURCE_OPTIONS +#define OSGEARTH_DRIVER_WFS_FEATURE_SOURCE_OPTIONS 1 + +#include +#include + +namespace osgEarth { namespace Drivers +{ + using namespace osgEarth; + using namespace osgEarth::Features; + + class WFSFeatureOptions : public FeatureSourceOptions // NO EXPORT; header only + { + public: + optional& url() { return _url; } + const optional& url() const { return _url; } + + optional& geometryProfileOptions() { return _geometryProfileConf; } + const optional& geometryProfileOptions() const { return _geometryProfileConf; } + + optional& typeName() { return _typename; } + const optional& typeName() const { return _typename; } + + optional& maxFeatures() { return _maxFeatures; } + const optional& maxFeatures() const { return _maxFeatures;} + + optional& outputFormat() { return _outputFormat; } + const optional& outputFormat() const { return _outputFormat; } + + + + public: + WFSFeatureOptions( const ConfigOptions& opt =ConfigOptions() ) : FeatureSourceOptions( opt ) { + fromConfig( _conf ); + } + + public: + Config getConfig() const { + Config conf = FeatureSourceOptions::getConfig(); + conf.updateIfSet( "url", _url ); + conf.updateIfSet( "geometry_profile", _geometryProfileConf ); + conf.updateIfSet( "typename", _typename ); + conf.updateIfSet( "outputformat", _outputFormat); + conf.updateIfSet( "maxfeatures", _maxFeatures ); + return conf; + } + + protected: + void mergeConfig( const Config& conf ) { + FeatureSourceOptions::mergeConfig( conf ); + fromConfig( conf ); + } + + private: + void fromConfig( const Config& conf ) { + conf.getIfSet( "url", _url ); + conf.getIfSet( "geometry_profile", _geometryProfileConf ); + conf.getIfSet( "typename", _typename); + conf.getIfSet( "outputformat", _outputFormat ); + conf.getIfSet( "maxfeatures", _maxFeatures ); + } + + optional _url; + optional _typename; + optional _geometryProfileConf; + optional _outputFormat; + optional _maxFeatures; + }; + +} } // namespace osgEarth::Drivers + +#endif // OSGEARTH_DRIVER_WFS_FEATURE_SOURCE_OPTIONS + diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/gdal/GDALOptions osgearth-2.1.1+dfsg/src/osgEarthDrivers/gdal/GDALOptions --- osgearth-2.0+dfsg/src/osgEarthDrivers/gdal/GDALOptions 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/gdal/GDALOptions 2011-11-04 19:44:43.000000000 +0000 @@ -31,8 +31,8 @@ { public: // properties - optional& url() { return _url; } - const optional& url() const { return _url; } + optional& url() { return _url; } + const optional& url() const { return _url; } optional& extensions() { return _extensions; } const optional& extensions() const { return _extensions; } @@ -43,11 +43,15 @@ optional& maxDataLevel() { return _maxDataLevel;} const optional& maxDataLevel() const { return _maxDataLevel;} + optional& interpolateImagery() { return _interpolateImagery;} + const optional& interpolateImagery() const { return _interpolateImagery;} + public: // ctors GDALOptions( const TileSourceOptions& options =TileSourceOptions() ) : TileSourceOptions( options ), - _interpolation( INTERP_AVERAGE ) + _interpolation( INTERP_AVERAGE ), + _interpolateImagery( false ) { setDriver( "gdal" ); fromConfig( _conf ); @@ -68,6 +72,8 @@ } conf.updateIfSet( "max_data_level", _maxDataLevel); + + conf.updateIfSet( "interp_imagery", _interpolateImagery); return conf; } @@ -84,12 +90,15 @@ else if ( in == "average" ) _interpolation = osgEarth::INTERP_AVERAGE; else if ( in == "bilinear" ) _interpolation = osgEarth::INTERP_BILINEAR; conf.getIfSet( "max_data_level", _maxDataLevel); + + conf.getIfSet("interp_imagery", _interpolateImagery); } - optional _url; - optional _extensions; + optional _url; + optional _extensions; optional _interpolation; - optional _maxDataLevel; + optional _interpolateImagery; + optional _maxDataLevel; }; } } // namespace osgEarth::Drivers diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/gdal/ReaderWriterGDAL.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/gdal/ReaderWriterGDAL.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/gdal/ReaderWriterGDAL.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/gdal/ReaderWriterGDAL.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -78,15 +78,6 @@ using namespace osgEarth; using namespace osgEarth::Drivers; -//#define PROPERTY_URL "url" -//#define PROPERTY_TILE_SIZE "tile_size" -//#define PROPERTY_EXTENTSIONS "extensions" -//#define PROPERTY_INTERPOLATION "interpolation" -//#define PROPERTY_DEFAULT_TILE_SIZE "default_tile_size" - -//static OpenThreads::ReentrantMutex s_mutex; - - #define GEOTRSFRM_TOPLEFT_X 0 #define GEOTRSFRM_WE_RES 1 #define GEOTRSFRM_ROTATION_PARAM1 2 @@ -122,28 +113,6 @@ double noDataValue; } BandProperty; - -static void -tokenize(const string& str, - vector& tokens, - const string& delimiters = " ") -{ - // Skip delimiters at beginning. - string::size_type lastPos = str.find_first_not_of(delimiters, 0); - // Find first "non-delimiter". - string::size_type pos = str.find_first_of(delimiters, lastPos); - - while (string::npos != pos || string::npos != lastPos) - { - // Found a token, add it to the vector. - tokens.push_back(str.substr(lastPos, pos - lastPos)); - // Skip delimiters. Note the "not_of" - lastPos = str.find_first_not_of(delimiters, pos); - // Find next "non-delimiter" - pos = str.find_first_of(delimiters, lastPos); - } -} - static void getFiles(const std::string &file, const std::vector &exts, std::vector &files) { @@ -246,6 +215,16 @@ if (hDS) { const char* proj = GDALGetProjectionRef(hDS); + if (!proj || strlen(proj) == 0) + { + std::string prjLocation = osgDB::getNameLessExtension( std::string(dsFileName) ) + std::string(".prj"); + std::string wkt; + if ( HTTPClient::readString( prjLocation, wkt ) == HTTPClient::RESULT_OK ) + { + proj = CPLStrdup( wkt.c_str() ); + } + } + GDALGetGeoTransform(hDS, psDatasetProperties[i].adfGeoTransform); if (psDatasetProperties[i].adfGeoTransform[GEOTRSFRM_ROTATION_PARAM1] != 0 || psDatasetProperties[i].adfGeoTransform[GEOTRSFRM_ROTATION_PARAM2] != 0) @@ -405,9 +384,10 @@ if (projectionRef) { + //OE_NOTICE << "Setting projection to " << projectionRef << std::endl; GDALSetProjection(hVRTDS, projectionRef); } - + double adfGeoTransform[6]; adfGeoTransform[GEOTRSFRM_TOPLEFT_X] = minX; adfGeoTransform[GEOTRSFRM_WE_RES] = we_res; @@ -637,20 +617,23 @@ _warpedDS(NULL), _options(options), _maxDataLevel(30) - { - //nop + { } - ~GDALTileSource() - { + virtual ~GDALTileSource() + { GDAL_SCOPED_LOCK; if (_warpedDS != _srcDS) { - delete _warpedDS; + GDALClose( _warpedDS ); } + //Close the datasets if it exists - if (_srcDS) delete _srcDS; + if (_srcDS) + { + GDALClose(_srcDS); + } } @@ -664,30 +647,35 @@ return; } - std::string path = _options.url().value(); + URI uri = _options.url().value(); //Find the full path to the URL //If we have a relative path and the map file contains a server address, just concat the server path and the _url together - if (osgEarth::isRelativePath(path) && osgDB::containsServerAddress(referenceURI)) + + if (osgEarth::isRelativePath(uri.full()) && osgDB::containsServerAddress(referenceURI)) { - path = osgDB::getFilePath(referenceURI) + std::string("/") + path; + uri = URI(osgDB::getFilePath(referenceURI) + std::string("/") + uri.full()); } //If the path doesn't contain a server address, get the full path to the file. - if (!osgDB::containsServerAddress(path)) + if (!osgDB::containsServerAddress(uri.full())) { - path = osgEarth::getFullPath(referenceURI, path); + uri = URI(uri.full(), referenceURI); } + StringTokenizer izer( ";" ); + StringVector exts; + izer.tokenize( *_options.extensions(), exts ); - std::vector exts; - tokenize( _options.extensions().value(), exts, ";"); + //std::vector exts; + + //tokenize( _options.extensions().value(), exts, ";"); for (unsigned int i = 0; i < exts.size(); ++i) { OE_DEBUG << LC << "Using Extension: " << exts[i] << std::endl; } std::vector files; - getFiles(path, exts, files); + getFiles(uri.full(), exts, files); OE_INFO << LC << "Driver found " << files.size() << " files:" << std::endl; for (unsigned int i = 0; i < files.size(); ++i) @@ -735,7 +723,6 @@ if ( overrideProfile ) { src_srs = overrideProfile->getSRS(); - _srcDS->SetProjection( src_srs->getWKT().c_str() ); // will this work? } else if ( srcProj ) { @@ -746,7 +733,7 @@ if ( !src_srs.valid() ) { // not found in the dataset; try loading a .prj file - std::string prjLocation = osgDB::getNameLessExtension( path ) + std::string(".prj"); + std::string prjLocation = osgDB::getNameLessExtension( uri.full() ) + std::string(".prj"); std::string wkt; if ( HTTPClient::readString( prjLocation, wkt ) == HTTPClient::RESULT_OK ) { @@ -755,7 +742,7 @@ if ( !src_srs.valid() ) { - OE_WARN << LC << "Dataset has no spatial reference information: " << path << std::endl; + OE_WARN << LC << "Dataset has no spatial reference information: " << uri.full() << std::endl; return; } } @@ -814,14 +801,27 @@ } else { - _warpedDS = _srcDS; - warpedSRSWKT = _srcDS->GetProjectionRef(); - if ( warpedSRSWKT.empty() ) - warpedSRSWKT = src_srs->getWKT(); + _warpedDS = _srcDS; + warpedSRSWKT = src_srs->getWKT(); } //Get the _geotransform - _warpedDS->GetGeoTransform(_geotransform); + if (overrideProfile) + { + _geotransform[0] = overrideProfile->getExtent().xMin(); //Top left x + _geotransform[1] = overrideProfile->getExtent().width() / (double)_warpedDS->GetRasterXSize();//pixel width + _geotransform[2] = 0; + + _geotransform[3] = overrideProfile->getExtent().yMax(); //Top left y + _geotransform[4] = 0; + _geotransform[5] = -overrideProfile->getExtent().height() / (double)_warpedDS->GetRasterYSize();//pixel height + + } + else + { + _warpedDS->GetGeoTransform(_geotransform); + } + GDALInvGeoTransform(_geotransform, _invtransform); @@ -865,7 +865,7 @@ //_warpedDS->GetProjectionRef(), _extentsMin.x(), _extentsMin.y(), _extentsMax.x(), _extentsMax.y() ); - OE_INFO << LC << "" << path << " is projected, SRS = " + OE_INFO << LC << "" << uri.full() << " is projected, SRS = " << warpedSRSWKT << std::endl; //<< _warpedDS->GetProjectionRef() << std::endl; } @@ -932,6 +932,84 @@ return 0; } + static void getPalleteIndexColor(GDALRasterBand* band, int index, osg::Vec4ub& color) + { + const GDALColorEntry *colorEntry = band->GetColorTable()->GetColorEntry( index ); + GDALPaletteInterp interp = band->GetColorTable()->GetPaletteInterpretation(); + if (!colorEntry) + { + //FIXME: What to do here? + + //OE_INFO << "NO COLOR ENTRY FOR COLOR " << rawImageData[i] << std::endl; + color.r() = 255; + color.g() = 0; + color.b() = 0; + color.a() = 1; + + } + else + { + if (interp == GPI_RGB) + { + color.r() = colorEntry->c1; + color.g() = colorEntry->c2; + color.b() = colorEntry->c3; + color.a() = colorEntry->c4; + } + else if (interp == GPI_CMYK) + { + // from wikipedia.org + short C = colorEntry->c1; + short M = colorEntry->c2; + short Y = colorEntry->c3; + short K = colorEntry->c4; + color.r() = 255 - C*(255 - K) - K; + color.g() = 255 - M*(255 - K) - K; + color.b() = 255 - Y*(255 - K) - K; + color.a() = 255; + } + else if (interp == GPI_HLS) + { + // from easyrgb.com + float H = colorEntry->c1; + float S = colorEntry->c3; + float L = colorEntry->c2; + float R, G, B; + if ( S == 0 ) //HSL values = 0 - 1 + { + R = L; //RGB results = 0 - 1 + G = L; + B = L; + } + else + { + float var_2, var_1; + if ( L < 0.5 ) + var_2 = L * ( 1 + S ); + else + var_2 = ( L + S ) - ( S * L ); + + var_1 = 2 * L - var_2; + + R = Hue_2_RGB( var_1, var_2, H + ( 1 / 3 ) ); + G = Hue_2_RGB( var_1, var_2, H ); + B = Hue_2_RGB( var_1, var_2, H - ( 1 / 3 ) ); + } + color.r() = static_cast(R*255.0f); + color.g() = static_cast(G*255.0f); + color.b() = static_cast(B*255.0f); + color.a() = static_cast(255.0f); + } + else if (interp == GPI_Gray) + { + color.r() = static_cast(colorEntry->c1*255.0f); + color.g() = static_cast(colorEntry->c1*255.0f); + color.b() = static_cast(colorEntry->c1*255.0f); + color.a() = static_cast(255.0f); + } + } + } + void pixelToGeo(double x, double y, double &geoX, double &geoY) { geoX = _geotransform[0] + _geotransform[1] * x + _geotransform[2] * y; @@ -953,12 +1031,16 @@ int tileSize = _options.tileSize().value(); osg::ref_ptr image; - if (intersects(key)) + if (intersects(key)) //TODO: I think this test is OBE -gw { //Get the extents of the tile double xmin, ymin, xmax, ymax; key.getExtent().getBounds(xmin, ymin, xmax, ymax); + double dx = (xmax - xmin) / (tileSize-1); + double dy = (ymax - ymin) / (tileSize-1); + + int target_width = tileSize; int target_height = tileSize; int tile_offset_left = 0; @@ -988,7 +1070,7 @@ if (off_y + height > _warpedDS->GetRasterYSize()) { int oversize_bottom = off_y + height - _warpedDS->GetRasterYSize(); - target_height = target_height - osg::round(float(oversize_bottom) / height * target_height); + target_height = target_height - (int)osg::round(float(oversize_bottom) / height * target_height); height = _warpedDS->GetRasterYSize() - off_y; } @@ -1035,36 +1117,58 @@ //Initialize the alpha values to 255. memset(alpha, 255, target_width * target_height); - - bandRed->RasterIO(GF_Read, off_x, off_y, width, height, red, target_width, target_height, GDT_Byte, 0, 0); - bandGreen->RasterIO(GF_Read, off_x, off_y, width, height, green, target_width, target_height, GDT_Byte, 0, 0); - bandBlue->RasterIO(GF_Read, off_x, off_y, width, height, blue, target_width, target_height, GDT_Byte, 0, 0); - - if (bandAlpha) - { - bandAlpha->RasterIO(GF_Read, off_x, off_y, width, height, alpha, target_width, target_height, GDT_Byte, 0, 0); - } - image = new osg::Image; image->allocateImage(tileSize, tileSize, 1, pixelFormat, GL_UNSIGNED_BYTE); memset(image->data(), 0, image->getImageSizeInBytes()); - for (int src_row = 0, dst_row = tile_offset_top; - src_row < target_height; - src_row++, dst_row++) + //Nearest interpolation just uses RasterIO to sample the imagery and should be very fast. + if (!*_options.interpolateImagery() || _options.interpolation() == INTERP_NEAREST) { - for (int src_col = 0, dst_col = tile_offset_left; - src_col < target_width; - ++src_col, ++dst_col) + bandRed->RasterIO(GF_Read, off_x, off_y, width, height, red, target_width, target_height, GDT_Byte, 0, 0); + bandGreen->RasterIO(GF_Read, off_x, off_y, width, height, green, target_width, target_height, GDT_Byte, 0, 0); + bandBlue->RasterIO(GF_Read, off_x, off_y, width, height, blue, target_width, target_height, GDT_Byte, 0, 0); + + if (bandAlpha) { - *(image->data(dst_col, dst_row) + 0) = red[src_col + src_row * target_width]; - *(image->data(dst_col, dst_row) + 1) = green[src_col + src_row * target_width]; - *(image->data(dst_col, dst_row) + 2) = blue[src_col + src_row * target_width]; - *(image->data(dst_col, dst_row) + 3) = alpha[src_col + src_row * target_width]; - } - } + bandAlpha->RasterIO(GF_Read, off_x, off_y, width, height, alpha, target_width, target_height, GDT_Byte, 0, 0); + } + + for (int src_row = 0, dst_row = tile_offset_top; + src_row < target_height; + src_row++, dst_row++) + { + for (int src_col = 0, dst_col = tile_offset_left; + src_col < target_width; + ++src_col, ++dst_col) + { + *(image->data(dst_col, dst_row) + 0) = red[src_col + src_row * target_width]; + *(image->data(dst_col, dst_row) + 1) = green[src_col + src_row * target_width]; + *(image->data(dst_col, dst_row) + 2) = blue[src_col + src_row * target_width]; + *(image->data(dst_col, dst_row) + 3) = alpha[src_col + src_row * target_width]; + } + } - image->flipVertical(); + image->flipVertical(); + } + else + { + //Sample each point exactly + for (unsigned int c = 0; c < tileSize; ++c) + { + double geoX = xmin + (dx * (double)c); + for (unsigned int r = 0; r < tileSize; ++r) + { + double geoY = ymin + (dy * (double)r); + *(image->data(c,r) + 0) = getInterpolatedValue(bandRed, geoX,geoY,false); + *(image->data(c,r) + 1) = getInterpolatedValue(bandGreen,geoX,geoY,false); + *(image->data(c,r) + 2) = getInterpolatedValue(bandBlue, geoX,geoY,false); + if (bandAlpha != NULL) + *(image->data(c,r) + 3) = getInterpolatedValue(bandAlpha,geoX, geoY, false); + else + *(image->data(c,r) + 3) = 255; + } + } + } delete []red; delete []green; @@ -1079,47 +1183,74 @@ //Initialize the alpha values to 255. memset(alpha, 255, target_width * target_height); - bandGray->RasterIO(GF_Read, off_x, off_y, width, height, gray, target_width, target_height, GDT_Byte, 0, 0); - - if (bandAlpha) - { - bandAlpha->RasterIO(GF_Read, off_x, off_y, width, height, alpha, target_width, target_height, GDT_Byte, 0, 0); - } - image = new osg::Image; image->allocateImage(tileSize, tileSize, 1, pixelFormat, GL_UNSIGNED_BYTE); memset(image->data(), 0, image->getImageSizeInBytes()); - for (int src_row = 0, dst_row = tile_offset_top; - src_row < target_height; - src_row++, dst_row++) + + if (!*_options.interpolateImagery() || _options.interpolation() == INTERP_NEAREST) { - for (int src_col = 0, dst_col = tile_offset_left; - src_col < target_width; - ++src_col, ++dst_col) + bandGray->RasterIO(GF_Read, off_x, off_y, width, height, gray, target_width, target_height, GDT_Byte, 0, 0); + + if (bandAlpha) { - *(image->data(dst_col, dst_row) + 0) = gray[src_col + src_row * target_width]; - *(image->data(dst_col, dst_row) + 1) = gray[src_col + src_row * target_width]; - *(image->data(dst_col, dst_row) + 2) = gray[src_col + src_row * target_width]; - *(image->data(dst_col, dst_row) + 3) = alpha[src_col + src_row * target_width]; + bandAlpha->RasterIO(GF_Read, off_x, off_y, width, height, alpha, target_width, target_height, GDT_Byte, 0, 0); + } + + for (int src_row = 0, dst_row = tile_offset_top; + src_row < target_height; + src_row++, dst_row++) + { + for (int src_col = 0, dst_col = tile_offset_left; + src_col < target_width; + ++src_col, ++dst_col) + { + *(image->data(dst_col, dst_row) + 0) = gray[src_col + src_row * target_width]; + *(image->data(dst_col, dst_row) + 1) = gray[src_col + src_row * target_width]; + *(image->data(dst_col, dst_row) + 2) = gray[src_col + src_row * target_width]; + *(image->data(dst_col, dst_row) + 3) = alpha[src_col + src_row * target_width]; + } } - } - image->flipVertical(); + image->flipVertical(); + } + else + { + for (int c = 0; c < tileSize; ++c) + { + double geoX = xmin + (dx * (double)c); + + for (int r = 0; r < tileSize; ++r) + { + double geoY = ymin + (dy * (double)r); + float color = getInterpolatedValue(bandGray,geoX,geoY,false); + + *(image->data(c,r) + 0) = color; + *(image->data(c,r) + 1) = color; + *(image->data(c,r) + 2) = color; + if (bandAlpha != NULL) + *(image->data(c,r) + 3) = getInterpolatedValue(bandAlpha,geoX,geoY,false); + else + *(image->data(c,r) + 3) = 255; + } + } + } delete []gray; delete []alpha; } else if (bandPalette) - { + { + //Pallete indexed imagery doesn't support interpolation currently and only uses nearest + //b/c interpolating pallete indexes doesn't make sense. unsigned char *palette = new unsigned char[target_width * target_height]; - bandPalette->RasterIO(GF_Read, off_x, off_y, width, height, palette, target_width, target_height, GDT_Byte, 0, 0); + image = new osg::Image; + image->allocateImage(tileSize, tileSize, 1, pixelFormat, GL_UNSIGNED_BYTE); + memset(image->data(), 0, image->getImageSizeInBytes()); - image = new osg::Image; - image->allocateImage(tileSize, tileSize, 1, pixelFormat, GL_UNSIGNED_BYTE); - memset(image->data(), 0, image->getImageSizeInBytes()); + bandPalette->RasterIO(GF_Read, off_x, off_y, width, height, palette, target_width, target_height, GDT_Byte, 0, 0); for (int src_row = 0, dst_row = tile_offset_top; src_row < target_height; @@ -1129,109 +1260,39 @@ src_col < target_width; ++src_col, ++dst_col) { - unsigned char r,g,b,a; - const GDALColorEntry *colorEntry = bandPalette->GetColorTable()->GetColorEntry(palette[src_col + src_row * target_width]); - GDALPaletteInterp interp = bandPalette->GetColorTable()->GetPaletteInterpretation(); - if (!colorEntry) - { - //FIXME: What to do here? - - //OE_INFO << "NO COLOR ENTRY FOR COLOR " << rawImageData[i] << std::endl; - r = 255; - g = 0; - b = 0; - a = 1; - - } - else - { - if (interp == GPI_RGB) - { - r = colorEntry->c1; - g = colorEntry->c2; - b = colorEntry->c3; - a = colorEntry->c4; - } - else if (interp == GPI_CMYK) - { - // from wikipedia.org - short C = colorEntry->c1; - short M = colorEntry->c2; - short Y = colorEntry->c3; - short K = colorEntry->c4; - r = 255 - C*(255 - K) - K; - g = 255 - M*(255 - K) - K; - b = 255 - Y*(255 - K) - K; - a = 255; - } - else if (interp == GPI_HLS) - { - // from easyrgb.com - float H = colorEntry->c1; - float S = colorEntry->c3; - float L = colorEntry->c2; - float R, G, B; - if ( S == 0 ) //HSL values = 0 - 1 - { - R = L; //RGB results = 0 - 1 - G = L; - B = L; - } - else - { - float var_2, var_1; - if ( L < 0.5 ) - var_2 = L * ( 1 + S ); - else - var_2 = ( L + S ) - ( S * L ); - - var_1 = 2 * L - var_2; - - R = Hue_2_RGB( var_1, var_2, H + ( 1 / 3 ) ); - G = Hue_2_RGB( var_1, var_2, H ); - B = Hue_2_RGB( var_1, var_2, H - ( 1 / 3 ) ); - } - r = static_cast(R*255.0f); - g = static_cast(G*255.0f); - b = static_cast(B*255.0f); - a = static_cast(255.0f); - } - else if (interp == GPI_Gray) - { - r = static_cast(colorEntry->c1*255.0f); - g = static_cast(colorEntry->c1*255.0f); - b = static_cast(colorEntry->c1*255.0f); - a = static_cast(255.0f); - } - - *(image->data(dst_col, dst_row) + 0) = r; - *(image->data(dst_col, dst_row) + 1) = g; - *(image->data(dst_col, dst_row) + 2) = b; - *(image->data(dst_col, dst_row) + 3) = a; - } + osg::Vec4ub color; + getPalleteIndexColor( bandPalette, palette[src_col + src_row * target_width], color ); + + *(image->data(dst_col, dst_row) + 0) = color.r(); + *(image->data(dst_col, dst_row) + 1) = color.g(); + *(image->data(dst_col, dst_row) + 2) = color.b(); + *(image->data(dst_col, dst_row) + 3) = color.a(); } } image->flipVertical(); delete [] palette; - } + + } else { OE_WARN << LC << "Could not find red, green and blue bands or gray bands in " - << _options.url().value() + << _options.url()->full() << ". Cannot create image. " << std::endl; return NULL; } } - //Create a transparent image if we don't have an image - if (!image.valid()) - { - return ImageUtils::createEmptyImage(); - } + // Moved this logic up into ImageLayer::createImageWrapper. + ////Create a transparent image if we don't have an image + //if (!image.valid()) + //{ + // //OE_WARN << LC << "Illegal state-- should not get here" << std::endl; + // return ImageUtils::createEmptyImage(); + //} return image.release(); } @@ -1264,7 +1325,7 @@ } - float getInterpolatedValue(GDALRasterBand *band, double x, double y) + float getInterpolatedValue(GDALRasterBand *band, double x, double y, bool applyOffset=true) { double r, c; GDALApplyGeoTransform(_invtransform, x, y, &c, &r); @@ -1276,29 +1337,32 @@ if (osg::equivalent(c, (double)_warpedDS->GetRasterXSize(), eps)) c = _warpedDS->GetRasterXSize(); if (osg::equivalent(r, (double)_warpedDS->GetRasterYSize(), eps)) r = _warpedDS->GetRasterYSize(); - //Apply half pixel offset - r-= 0.5; - c-= 0.5; - - //Account for the half pixel offset in the geotransform. If the pixel value is -0.5 we are still technically in the dataset - //since 0,0 is now the center of the pixel. So, if are within a half pixel above or a half pixel below the dataset just use - //the edge values - if (c < 0 && c >= -0.5) - { - c = 0; - } - else if (c > _warpedDS->GetRasterXSize()-1 && c <= _warpedDS->GetRasterXSize()-0.5) + if (applyOffset) { - c = _warpedDS->GetRasterXSize()-1; - } + //Apply half pixel offset + r-= 0.5; + c-= 0.5; - if (r < 0 && r >= -0.5) - { - r = 0; - } - else if (r > _warpedDS->GetRasterYSize()-1 && r <= _warpedDS->GetRasterYSize()-0.5) - { - r = _warpedDS->GetRasterYSize()-1; + //Account for the half pixel offset in the geotransform. If the pixel value is -0.5 we are still technically in the dataset + //since 0,0 is now the center of the pixel. So, if are within a half pixel above or a half pixel below the dataset just use + //the edge values + if (c < 0 && c >= -0.5) + { + c = 0; + } + else if (c > _warpedDS->GetRasterXSize()-1 && c <= _warpedDS->GetRasterXSize()-0.5) + { + c = _warpedDS->GetRasterXSize()-1; + } + + if (r < 0 && r >= -0.5) + { + r = 0; + } + else if (r > _warpedDS->GetRasterYSize()-1 && r <= _warpedDS->GetRasterYSize()-0.5) + { + r = _warpedDS->GetRasterYSize()-1; + } } float result = 0.0f; diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/CMakeLists.txt osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/CMakeLists.txt --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/CMakeLists.txt 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,91 @@ +OPTION(CURL_IS_STATIC "on if curl is a static lib " ON) +MARK_AS_ADVANCED(CURL_IS_STATIC) + +IF(WIN32) + SET(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG} /NODEFAULTLIB:MSVCRT") + IF(CURL_IS_STATIC) + ADD_DEFINITIONS(-DCURL_STATICLIB) + SET(TARGET_EXTERNAL_LIBRARIES ws2_32 winmm) + ENDIF(CURL_IS_STATIC) +ENDIF(WIN32) + +SET(TARGET_COMMON_LIBRARIES ${TARGET_COMMON_LIBRARIES} osgEarthFeatures osgEarthSymbology osgEarthUtil) + +IF( MINIZIP_FOUND ) + SET(TARGET_LIBRARIES_VARS ZLIB_LIBRARY MINIZIP_LIBRARY ) + INCLUDE_DIRECTORIES( ${ZLIB_INCLUDE_DIR} ${MINIZIP_INCLUDE_DIR} ) + ADD_DEFINITIONS(-DOSGEARTH_HAVE_ZLIB -DOSGEARTH_HAVE_MINIZIP ) +ENDIF( MINIZIP_FOUND ) + +SET(TARGET_H + KML + KMLOptions + KMLReader + KML_Common + KML_Container + KML_Document + KML_Feature + KML_Folder + KML_Geometry + KML_GroundOverlay + KML_IconStyle + KML_LabelStyle + KML_LinearRing + KML_LineString + KML_LineStyle + KML_Model + KML_MultiGeometry + KML_NetworkLinkControl + KML_Object + KML_Overlay + KML_PhotoOverlay + KML_Point + KML_Polygon + KML_PolyStyle + KML_Root + KML_Schema + KML_ScreenOverlay + KML_Style + KML_StyleMap + KML_StyleSelector + KMZArchive +) + +SET(TARGET_SRC + ReaderWriterKML.cpp + KMLReader.cpp + + KML_Document.cpp + KML_Feature.cpp + KML_Folder.cpp + KML_Geometry.cpp + KML_GroundOverlay.cpp + KML_IconStyle.cpp + KML_LabelStyle.cpp + KML_LinearRing.cpp + KML_LineString.cpp + KML_LineStyle.cpp + KML_Model.cpp + KML_MultiGeometry.cpp + KML_NetworkLink.cpp + KML_NetworkLinkControl.cpp + KML_Object.cpp + KML_Overlay.cpp + KML_PhotoOverlay.cpp + KML_Placemark.cpp + KML_Point.cpp + KML_Polygon.cpp + KML_PolyStyle.cpp + KML_Root.cpp + KML_Schema.cpp + KML_ScreenOverlay.cpp + KML_Style.cpp + KML_StyleMap.cpp + KMZArchive.cpp +) + +SETUP_PLUGIN(kml) + +SET(LIB_NAME kml) +SET(LIB_PUBLIC_HEADERS KML KMLOptions) +INCLUDE(ModuleInstallOsgEarthDriverIncludes OPTIONAL) diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,56 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#ifndef OSGEARTH_DRIVER_KML +#define OSGEARTH_DRIVER_KML 1 + +#include +#include +#include +#include +#include "KMLOptions" + +#define LC "[KML] " + +namespace osgEarth { namespace Drivers { + + using namespace osgEarth; + + class KML // header-only (no export) + { + public: + /** + * Loads KML from a URI. + */ + static osg::Node* load( const URI& uri, MapNode* mapNode, const KMLOptions& kmlOptions =KMLOptions() ) + { + if ( !mapNode ) { + OE_WARN << LC << "MapNode instance required" << std::endl; + return 0L; + } + osg::ref_ptr options = Registry::instance()->cloneOrCreateOptions(); + options->setPluginData( "osgEarth::MapNode", mapNode ); + options->setPluginData( "osgEarth::KMLOptions", (void*)&kmlOptions ); + + return uri.readNode( options.get() ); + } + }; + +} } // namespace osgEarth::Drivers + +#endif // OSGEARTH_DRIVER_KML diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Common osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Common --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Common 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Common 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,72 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#ifndef OSGEARTH_DRIVER_KML_COMMON +#define OSGEARTH_DRIVER_KML_COMMON 1 + +#include +#include +#include +#include +#include "KMLOptions" + +#define LC "[KML] " + +using namespace osgEarth; +using namespace osgEarth::Drivers; +using namespace osgEarth::Symbology; + +#define for_one( NAME, FUNC, CONF, CX ) \ +{ \ + Config c = conf.child( toLower( #NAME ) ); \ + if ( !c.empty() ) { \ + KML_##NAME instance; \ + instance. FUNC (c, CX); \ + } \ +} + +#define for_many( NAME, FUNC, CONF, CX ) \ +{ \ + ConfigSet c = conf.children( toLower( #NAME ) ); \ + for( ConfigSet::const_iterator i = c.begin(); i != c.end(); ++i ) { \ + KML_##NAME instance; \ + instance. FUNC (*i, CX); \ + } \ +} + +#define for_features( FUNC, CONF, CX ) \ + for_many( Document, FUNC, CONF, CX ); \ + for_many( Folder, FUNC, CONF, CX ); \ + for_many( PhotoOverlay, FUNC, CONF, CX ); \ + for_many( ScreenOverlay, FUNC, CONF, CX ); \ + for_many( GroundOverlay, FUNC, CONF, CX ); \ + for_many( NetworkLink, FUNC, CONF, CX ); \ + for_many( Placemark, FUNC, CONF, CX ); + +struct KMLContext +{ + MapNode* _mapNode; + const KMLOptions* _options; + osg::ref_ptr _sheet; + Style _activeStyle; + std::stack > _groupStack; +}; + + +#endif // OSGEARTH_DRIVER_KML_READER + diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Container osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Container --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Container 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Container 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,47 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#ifndef OSGEARTH_DRIVER_KML_KML_CONTAINER +#define OSGEARTH_DRIVER_KML_KML_CONTAINER 1 + +#include "KML_Common" +#include "KML_Feature" + +using namespace osgEarth; + +struct KML_Container : public KML_Feature +{ + virtual void scan( const Config& conf, KMLContext& cx ) + { + KML_Feature::scan(conf, cx); + } + + virtual void scan2( const Config& conf, KMLContext& cx ) + { + KML_Feature::scan2(conf, cx); + } + + virtual void build( const Config& conf, KMLContext& cx, osg::Node* working ) + { + // assumes the top of the group stack has a new and valid Node. + // don't call this is there was an error in the subclass build() method + KML_Feature::build(conf, cx, working); + } +}; + +#endif // OSGEARTH_DRIVER_KML_KML_CONTAINER diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Document osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Document --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Document 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Document 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,37 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#ifndef OSGEARTH_DRIVER_KML_KML_DOCUMENT +#define OSGEARTH_DRIVER_KML_KML_DOCUMENT 1 + +#include "KML_Common" +#include "KML_Container" + +using namespace osgEarth; + +struct KML_Document : public KML_Container +{ + virtual void scan( const Config& conf, KMLContext& cx ); + + virtual void scan2( const Config& conf, KMLContext& cx ); + + virtual void build( const Config& conf, KMLContext& cx ); +}; + +#endif // OSGEARTH_DRIVER_KML_KML_DOCUMENT + diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Document.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Document.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Document.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Document.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,56 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#include "KML_Document" +#include "KML_Schema" +#include "KML_Folder" +#include "KML_PhotoOverlay" +#include "KML_ScreenOverlay" +#include "KML_GroundOverlay" +#include "KML_NetworkLink" +#include "KML_Placemark" + +void +KML_Document::scan( const Config& conf, KMLContext& cx ) +{ + KML_Container::scan(conf, cx); + for_many ( Schema, scan, conf, cx ); + for_features( scan, conf, cx ); +} + +void +KML_Document::scan2( const Config& conf, KMLContext& cx ) +{ + KML_Container::scan2(conf, cx); + for_many ( Schema, scan2, conf, cx ); + for_features( scan2, conf, cx ); +} + +void +KML_Document::build( const Config& conf, KMLContext& cx ) +{ + // creates an empty group and pushes it on the stack. + osg::Group* group = new osg::Group(); + cx._groupStack.top()->addChild( group ); + cx._groupStack.push( group ); + + KML_Container::build(conf, cx, group); + for_features(build, conf, cx); + + cx._groupStack.pop(); +} diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Feature osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Feature --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Feature 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Feature 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,35 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#ifndef OSGEARTH_DRIVER_KML_KML_FEATURE +#define OSGEARTH_DRIVER_KML_KML_FEATURE 1 + +#include "KML_Object" + +using namespace osgEarth; + +struct KML_Feature : public KML_Object +{ + virtual void scan( const Config& conf, KMLContext& cx ); + + virtual void scan2( const Config& conf, KMLContext& cx ); + + virtual void build( const Config& conf, KMLContext& cx, osg::Node* working ); +}; + +#endif // OSGEARTH_DRIVER_KML_KML_FEATURE diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Feature.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Feature.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Feature.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Feature.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,71 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#include "KML_Feature" +#include "KML_Style" +#include + +using namespace osgEarth::Util; + +void +KML_Feature::scan( const Config& conf, KMLContext& cx ) +{ + KML_Object::scan(conf, cx); + for_many( Style, scan, conf, cx ); +} + +void +KML_Feature::scan2( const Config& conf, KMLContext& cx ) +{ + KML_Object::scan2(conf, cx); + for_many( Style, scan2, conf, cx ); +} + +void +KML_Feature::build( const Config& conf, KMLContext& cx, osg::Node* working ) +{ + KML_Object::build(conf, cx, working); + + // subclass feature is built; now add feature level data if available + if ( working ) + { + // parse the visibility to show/hide the item by default: + if ( conf.hasValue("visibility") ) + working->setNodeMask( conf.value("visibility",true) == true ? ~0 : 0 ); + + // parse a "LookAt" element (stores a viewpoint) + AnnotationData* anno = getOrCreateAnnotationData(working); + + anno->setName( conf.value("name") ); + anno->setDescription( conf.value("description") ); + + const Config& lookat = conf.child("lookat"); + if ( !lookat.empty() ) + { + Viewpoint vp( + lookat.value("longitude", 0.0), + lookat.value("latitude", 0.0), + lookat.value("altitude", 0.0), + lookat.value("heading", 0.0), + -lookat.value("tilt", 45.0), + lookat.value("range", 10000.0) ); + + anno->setViewpoint( vp ); + } + } +} diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Folder osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Folder --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Folder 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Folder 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,36 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#ifndef OSGEARTH_DRIVER_KML_KML_FOLDER +#define OSGEARTH_DRIVER_KML_KML_FOLDER 1 + +#include "KML_Common" +#include "KML_Container" + +using namespace osgEarth; + +struct KML_Folder : public KML_Container +{ + virtual void scan( const Config& conf, KMLContext& cx ); + + virtual void scan2( const Config& conf, KMLContext& cx ); + + virtual void build( const Config& conf, KMLContext& cx ); +}; + +#endif // OSGEARTH_DRIVER_KML_KML_FOLDER diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Folder.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Folder.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Folder.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Folder.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,54 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#include "KML_Folder" + +#include "KML_Document" +#include "KML_PhotoOverlay" +#include "KML_ScreenOverlay" +#include "KML_GroundOverlay" +#include "KML_NetworkLink" +#include "KML_Placemark" + +void +KML_Folder::scan( const Config& conf, KMLContext& cx ) +{ + KML_Container::scan(conf, cx); + for_features( scan, conf, cx ); +} + +void +KML_Folder::scan2( const Config& conf, KMLContext& cx ) +{ + KML_Container::scan2(conf, cx); + for_features( scan2, conf, cx ); +} + +void +KML_Folder::build( const Config& conf, KMLContext& cx ) +{ + // creates an empty group and pushes it on the stack. + osg::Group* group = new osg::Group(); + cx._groupStack.top()->addChild( group ); + cx._groupStack.push( group ); + + KML_Container::build(conf, cx, group); + for_features(build, conf, cx); + + cx._groupStack.pop(); +} diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Geometry osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Geometry --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Geometry 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Geometry 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,40 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#ifndef OSGEARTH_DRIVER_KML_KML_GEOMETRY +#define OSGEARTH_DRIVER_KML_KML_GEOMETRY 1 + +#include "KML_Object" +#include + +using namespace osgEarth; +using namespace osgEarth::Symbology; + +struct KML_Geometry : public KML_Object +{ + KML_Geometry() : _extrude(false), _tessellate(false) { } + virtual void parseCoords( const Config& conf, KMLContext& cx ); + virtual void parseStyle( const Config& conf, KMLContext& cs, Style& style ); + virtual void build( const Config& confParent, KMLContext& cx, Style& style ); + void buildChild( const Config& conf, KMLContext& cx, Style& style ); + osg::ref_ptr _geom; + bool _extrude, _tessellate; +private: +}; + +#endif // OSGEARTH_DRIVER_KML_KML_GEOMETRY diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Geometry.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Geometry.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Geometry.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Geometry.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,159 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#include "KML_Geometry" +#include "KML_Point" +#include "KML_LineString" +#include "KML_LinearRing" +#include "KML_Polygon" +#include "KML_MultiGeometry" +#include "KML_Model" +#include + +void +KML_Geometry::build( const Config& parentConf, KMLContext& cx, Style& style) +{ + const ConfigSet& children = parentConf.children(); + for( ConfigSet::const_iterator i = children.begin(); i != children.end(); ++i ) + { + buildChild( *i, cx, style ); + } +} + +void +KML_Geometry::buildChild( const Config& conf, KMLContext& cx, Style& style) +{ + if ( conf.key() == "point" ) + { + KML_Point g; + g.parseStyle(conf, cx, style); + g.parseCoords(conf, cx); + _geom = g._geom.get(); + } + else if ( conf.key() == "linestring" ) + { + KML_LineString g; + g.parseStyle(conf, cx, style); + g.parseCoords(conf, cx); + _geom = g._geom.get(); + } + else if ( conf.key() == "linearring" ) + { + KML_LinearRing g; + g.parseStyle(conf, cx, style); + g.parseCoords(conf, cx); + _geom = g._geom.get(); + } + else if ( conf.key() == "polygon" || conf.key() == "gx:latlonquad" ) + { + KML_Polygon g; + g.parseStyle(conf, cx, style); + g.parseCoords(conf, cx); + _geom = g._geom.get(); + } + else if ( conf.key() == "multigeometry" ) + { + KML_MultiGeometry g; + g.parseStyle(conf, cx, style); + g.parseCoords(conf, cx); + const ConfigSet& mgChildren = conf.children(); + + for( ConfigSet::const_iterator i = mgChildren.begin(); i != mgChildren.end(); ++i ) + { + const Config& mgChild = *i; + Style subStyle = style; + KML_Geometry subGeom; + subGeom.parseStyle( mgChild, cx, subStyle ); + subGeom.buildChild( mgChild, cx, style ); + if ( subGeom._geom.valid() ) + dynamic_cast(g._geom.get())->getComponents().push_back( subGeom._geom.get() ); + } + //g.parseCoords(conf.child("multigeometry"), cx); + _geom = g._geom.get(); + } + else if ( conf.key() == "model" ) + { + KML_Model g; + g.parseStyle(conf, cx, style); + g.parseCoords(conf, cx); + _geom = g._geom.get(); + } +} + +void +KML_Geometry::parseCoords( const Config& conf, KMLContext& cx ) +{ + const Config& coords = conf.child("coordinates"); + StringVector tuples; + StringTokenizer( coords.value(), tuples, " ", "", false, true ); + for( StringVector::const_iterator s=tuples.begin(); s != tuples.end(); ++s ) + { + StringVector parts; + StringTokenizer( *s, parts, ",", "", false, true ); + if ( parts.size() >= 2 ) + { + osg::Vec3d point; + point.x() = as( parts[0], 0.0 ); + point.y() = as( parts[1], 0.0 ); + if ( parts.size() >= 3 ) { + point.z() = as( parts[2], 0.0 ); + } + _geom->push_back(point); + } + } +} + +void +KML_Geometry::parseStyle( const Config& conf, KMLContext& cx, Style& style ) +{ + _extrude = conf.value("extrude") == "1"; + _tessellate = conf.value("tessellate") == "1"; + + std::string am = conf.value("altitudemode"); + + // clampToGround is the default. We will be draping the geometry UNLESS tessellate is + // set to true. + if ( (am.empty() || am == "clampToGround") && _tessellate ) + { + AltitudeSymbol* af = style.getOrCreate(); + af->clamping() = AltitudeSymbol::CLAMP_TO_TERRAIN; + _extrude = false; + } + + // "relativeToGround" means the coordinates' Z values are relative to the Z of the + // terrain at that point. NOTE: GE flattens rooftops in this mode when extrude=1, + // which seems wrong.. + else if ( am == "relativeToGround" ) + { + AltitudeSymbol* af = style.getOrCreate(); + af->clamping() = AltitudeSymbol::CLAMP_RELATIVE_TO_TERRAIN; + } + + // "absolute" means to treat the Z values as-is + else if ( am == "absolute" ) + { + AltitudeSymbol* af = style.getOrCreate(); + af->clamping() = AltitudeSymbol::CLAMP_ABSOLUTE; + } + + if ( _extrude ) + { + ExtrusionSymbol* es = style.getOrCreate(); + es->flatten() = false; + } +} diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_GroundOverlay osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_GroundOverlay --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_GroundOverlay 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_GroundOverlay 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,33 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#ifndef OSGEARTH_DRIVER_KML_KML_GROUNDOVERLAY +#define OSGEARTH_DRIVER_KML_KML_GROUNDOVERLAY 1 + +#include "KML_Common" +#include "KML_Overlay" + +using namespace osgEarth; + +struct KML_GroundOverlay : public KML_Overlay +{ + virtual void scan( const Config& conf, KMLContext& cx ); + virtual void build( const Config& conf, KMLContext& cx ); +}; + +#endif // OSGEARTH_DRIVER_KML_KML_GROUNDOVERLAY diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_GroundOverlay.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_GroundOverlay.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_GroundOverlay.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_GroundOverlay.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,97 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#include "KML_GroundOverlay" +#include "KML_Geometry" +#include + +using namespace osgEarth::Util; + +void +KML_GroundOverlay::scan( const Config& conf, KMLContext& cx ) +{ + KML_Overlay::scan( conf, cx ); +} + +void +KML_GroundOverlay::build( const Config& conf, KMLContext& cx ) +{ + // the URL of the overlay image + std::string href = conf.child("icon").value("href"); + if ( href.empty() ) { + OE_WARN << LC << "GroundOverlay missing required Icon element" << std::endl; + return; + } + + ImageOverlay* im = 0L; + + // the extent of the overlay image + const Config& llb = conf.child("latlonbox"); + if ( !llb.empty() ) + { + double north = llb.value("north", 0.0); + double south = llb.value("south", 0.0); + double east = llb.value("east", 0.0); + double west = llb.value("west", 0.0); + Angular rotation( llb.value("rotation", 0.0), Units::DEGREES ); + + osg::ref_ptr image = URI(href, conf.uriContext()).readImage(); + if ( !image.valid() ) { + OE_WARN << LC << "GroundOverlay failed to read image from " << href << std::endl; + return; + } + + im = new ImageOverlay( cx._mapNode, image.get() ); + im->setBoundsAndRotation( Bounds(west, south, east, north), rotation ); + cx._groupStack.top()->addChild( im ); + } + + else if ( conf.hasChild("gx:latlonquad") ) + { + const Config& llq = conf.child("gx:latlonquad"); + KML_Geometry g; + Style style; + g.buildChild( llq, cx, style ); + if ( g._geom.valid() && g._geom->size() >= 4 ) + { + osg::ref_ptr image = URI(href, conf.uriContext()).readImage(); + if ( !image.valid() ) { + OE_WARN << LC << "GroundOverlay failed to read image from " << href << std::endl; + return; + } + + const Geometry& p = *(g._geom.get()); + im = new ImageOverlay( cx._mapNode, image.get() ); + im->setCorners( + osg::Vec2d( p[0].x(), p[0].y() ), + osg::Vec2d( p[1].x(), p[1].y() ), + osg::Vec2d( p[3].x(), p[3].y() ), + osg::Vec2d( p[2].x(), p[2].y() ) ); + cx._groupStack.top()->addChild( im ); + } + } + + else { + OE_WARN << LC << "GroundOverlay missing required LatLonBox/gx:LatLonQuad element" << std::endl; + return; + } + + + // superclass build always called last + KML_Overlay::build( conf, cx, im ); +} diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_IconStyle osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_IconStyle --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_IconStyle 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_IconStyle 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,31 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#ifndef OSGEARTH_DRIVER_KML_KML_ICONSTYLE +#define OSGEARTH_DRIVER_KML_KML_ICONSTYLE 1 + +#include "KML_Object" + +using namespace osgEarth; + +struct KML_IconStyle : public KML_Object +{ + virtual void scan( const Config& conf, Style& style ); +}; + +#endif // OSGEARTH_DRIVER_KML_KML_ICONSTYLE diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_IconStyle.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_IconStyle.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_IconStyle.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_IconStyle.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,44 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#include "KML_IconStyle" + +void +KML_IconStyle::scan( const Config& conf, Style& style ) +{ + if ( !conf.empty() ) + { + MarkerSymbol* marker = style.getOrCreate(); + + // Icon/Href or just Icon are both valid + std::string iconHref = conf.child("icon").value("href"); + if ( iconHref.empty() ) + iconHref = conf.value("icon"); + + if ( !iconHref.empty() ) + { + marker->url() = StringExpression( iconHref ); + marker->url()->setURIContext( conf.uriContext() ); + } + + optional scale; + conf.getIfSet( "scale", scale ); + if ( scale.isSet() ) + marker->scale() = NumericExpression( *scale ); + } +} diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_LabelStyle osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_LabelStyle --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_LabelStyle 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_LabelStyle 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,31 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#ifndef OSGEARTH_DRIVER_KML_KML_LABELSTYLE +#define OSGEARTH_DRIVER_KML_KML_LABELSTYLE 1 + +#include "KML_Object" + +using namespace osgEarth; + +struct KML_LabelStyle : public KML_Object +{ + virtual void scan( const Config& conf, Style& style ); +}; + +#endif // OSGEARTH_DRIVER_KML_KML_LABELSTYLE diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_LabelStyle.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_LabelStyle.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_LabelStyle.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_LabelStyle.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,24 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#include "KML_LabelStyle" + +void +KML_LabelStyle::scan( const Config& conf, Style& style ) +{ +} diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_LinearRing osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_LinearRing --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_LinearRing 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_LinearRing 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,31 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#ifndef OSGEARTH_DRIVER_KML_KML_LINEARRING +#define OSGEARTH_DRIVER_KML_KML_LINEARRING 1 + +#include "KML_Geometry" + +using namespace osgEarth; + +struct KML_LinearRing : public KML_Geometry +{ + virtual void parseCoords( const Config& conf, KMLContext& cx ); +}; + +#endif // OSGEARTH_DRIVER_KML_KML_LINEARRING diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_LinearRing.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_LinearRing.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_LinearRing.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_LinearRing.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,26 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#include "KML_LinearRing" + +void +KML_LinearRing::parseCoords( const Config& conf, KMLContext& cx ) +{ + _geom = new Ring(); + KML_Geometry::parseCoords( conf, cx ); +} diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_LineString osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_LineString --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_LineString 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_LineString 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,31 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#ifndef OSGEARTH_DRIVER_KML_KML_LINESTRING +#define OSGEARTH_DRIVER_KML_KML_LINESTRING 1 + +#include "KML_Geometry" + +using namespace osgEarth; + +struct KML_LineString : public KML_Geometry +{ + virtual void parseCoords( const Config& conf, KMLContext& cx ); +}; + +#endif // OSGEARTH_DRIVER_KML_KML_LINESTRING diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_LineString.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_LineString.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_LineString.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_LineString.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,26 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#include "KML_LineString" + +void +KML_LineString::parseCoords( const Config& conf, KMLContext& cx ) +{ + _geom = new LineString(); + KML_Geometry::parseCoords( conf, cx ); +} diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_LineStyle osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_LineStyle --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_LineStyle 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_LineStyle 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,31 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#ifndef OSGEARTH_DRIVER_KML_KML_LINESTYLE +#define OSGEARTH_DRIVER_KML_KML_LINESTYLE 1 + +#include "KML_Object" + +using namespace osgEarth; + +struct KML_LineStyle : public KML_Object +{ + virtual void scan( const Config& conf, Style& style ); +}; + +#endif // OSGEARTH_DRIVER_KML_KML_LINESTYLE diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_LineStyle.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_LineStyle.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_LineStyle.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_LineStyle.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,36 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#include "KML_LineStyle" + +void +KML_LineStyle::scan( const Config& conf, Style& style ) +{ + if ( !conf.empty() ) + { + LineSymbol* line = style.getOrCreate(); + if ( conf.hasValue("color") ) + { + line->stroke()->color() = Color( Stringify() << "#" << conf.value("color"), Color::ABGR ); + } + if ( conf.hasValue("width") ) + { + line->stroke()->width() = as( conf.value("width"), 1.0f ); + } + } +} diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Model osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Model --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Model 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Model 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,30 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#ifndef OSGEARTH_DRIVER_KML_KML_MODEL +#define OSGEARTH_DRIVER_KML_KML_MODEL 1 + +#include "KML_Geometry" + +using namespace osgEarth; + +struct KML_Model : public KML_Geometry +{ +}; + +#endif // OSGEARTH_DRIVER_KML_KML_MODEL diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Model.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Model.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Model.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Model.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,19 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#include "KML_Model" diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_MultiGeometry osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_MultiGeometry --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_MultiGeometry 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_MultiGeometry 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,31 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#ifndef OSGEARTH_DRIVER_KML_KML_MULTIGEOMETRY +#define OSGEARTH_DRIVER_KML_KML_MULTIGEOMETRY 1 + +#include "KML_Geometry" + +using namespace osgEarth; + +struct KML_MultiGeometry : public KML_Geometry +{ + virtual void parseCoords( const Config& conf, KMLContext& cx ); +}; + +#endif // OSGEARTH_DRIVER_KML_KML_MULTIGEOMETRY diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_MultiGeometry.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_MultiGeometry.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_MultiGeometry.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_MultiGeometry.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,26 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#include "KML_MultiGeometry" + +void +KML_MultiGeometry::parseCoords( const Config& conf, KMLContext& cx ) +{ + _geom = new MultiGeometry(); + //KML_Geometry::parseCoords( conf, cx ); +} diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_NetworkLink osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_NetworkLink --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_NetworkLink 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_NetworkLink 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,31 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#ifndef OSGEARTH_DRIVER_KML_KML_NETWORKLINK +#define OSGEARTH_DRIVER_KML_KML_NETWORKLINK 1 + +#include "KML_Object" + +using namespace osgEarth; + +struct KML_NetworkLink : public KML_Object +{ + virtual void build( const Config& conf, KMLContext& cx ); +}; + +#endif // OSGEARTH_DRIVER_KML_KML_NETWORKLINK diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_NetworkLinkControl osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_NetworkLinkControl --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_NetworkLinkControl 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_NetworkLinkControl 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,32 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#ifndef OSGEARTH_DRIVER_KML_KML_NETWORKLINKCONTROL +#define OSGEARTH_DRIVER_KML_KML_NETWORKLINKCONTROL 1 + +#include "KML_Object" + +using namespace osgEarth; + +struct KML_NetworkLinkControl : public KML_Object +{ + virtual void scan( const Config& conf, KMLContext& cx ); + virtual void build( const Config& conf, KMLContext& cx ); +}; + +#endif // OSGEARTH_DRIVER_KML_KML_NETWORKLINKCONTROL diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_NetworkLinkControl.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_NetworkLinkControl.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_NetworkLinkControl.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_NetworkLinkControl.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,31 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#include "KML_NetworkLinkControl" + +void +KML_NetworkLinkControl::scan( const Config& conf, KMLContext& cx ) +{ + KML_Object::scan( conf, cx ); +} + +void +KML_NetworkLinkControl::build( const Config& conf, KMLContext& cx ) +{ + KML_Object::build( conf, cx, 0L ); +} diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_NetworkLink.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_NetworkLink.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_NetworkLink.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_NetworkLink.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,123 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#include "KML_NetworkLink" +#include +#include +#include +#include + +#undef LC +#define LC "[KML_NetworkLink] " + +void +KML_NetworkLink::build( const Config& conf, KMLContext& cx ) +{ + std::string name = conf.value("name"); + + // parse the link: + Config linkConf = conf.child("link"); + if ( linkConf.empty() ) + { + // "url" seems to be acceptable as well + linkConf = conf.child("url"); + if ( linkConf.empty() ) + return; + } + std::string href = linkConf.value("href"); + if ( href.empty() ) + return; + + // "open" determines whether to load it immediately + bool open = conf.value("open", false); + + // if it's region-bound, parse it as a paged LOD: + const Config& regionConf = conf.child("region"); + if ( !regionConf.empty() ) + { + const Config& llaBoxConf = regionConf.child("latlonaltbox"); + if ( llaBoxConf.empty() ) + return; + + GeoExtent llaExtent( + cx._mapNode->getMap()->getProfile()->getSRS()->getGeographicSRS(), + llaBoxConf.value("west", 0.0), + llaBoxConf.value("south", 0.0), + llaBoxConf.value("east", 0.0), + llaBoxConf.value("north", 0.0) ); + + double x, y; + llaExtent.getCentroid( x, y ); + osg::Vec3d lodCenter; + llaExtent.getSRS()->transformToECEF( osg::Vec3d(x,y,0), lodCenter ); + + // figure the tile radius: + double d = 0.5 * GeoMath::distance( + osg::DegreesToRadians(llaExtent.yMin()), osg::DegreesToRadians(llaExtent.xMin()), + osg::DegreesToRadians(llaExtent.yMax()), osg::DegreesToRadians(llaExtent.xMax()) ); + + // parse the LOD ranges: + float minRange = 0, maxRange = 1e6; + const Config& lodConf = regionConf.child("lod"); + if ( !lodConf.empty() ) + { + // swapped + minRange = lodConf.value( "minlodpixels", 0.0f ); + if ( minRange < 0.0f ) + minRange = 0.0f; + maxRange = lodConf.value( "maxlodpixels", FLT_MAX ); + if ( maxRange < 0.0f ) + maxRange = FLT_MAX; + } + + // build the node + osg::PagedLOD* plod = new osg::PagedLOD(); + plod->setRangeMode( osg::LOD::PIXEL_SIZE_ON_SCREEN ); + + plod->setFileName( 0, href ); + plod->setRange( 0, minRange, maxRange ); + plod->setCenter( lodCenter ); + plod->setRadius( d ); +#if OSG_MIN_VERSION_REQUIRED(3,0,0) + osgDB::Options* options = new osgDB::Options(); + options->setPluginData( "osgEarth::MapNode", cx._mapNode ); + plod->setDatabaseOptions( options ); +#endif; + //plod->setNodeMask( open ? ~0 : 0 ); + + OE_DEBUG << LC << + "PLOD: radius = " << d << ", minRange=" << minRange << ", maxRange=" << maxRange << std::endl; + + cx._groupStack.top()->addChild( plod ); + } + + else + { + osg::ProxyNode* proxy = new osg::ProxyNode(); + proxy->setFileName( 0, href ); +#if OSG_MIN_VERSION_REQUIRED(3,0,0) + osgDB::Options* options = new osgDB::Options(); + options->setPluginData( "osgEarth::MapNode", cx._mapNode ); + proxy->setDatabaseOptions( options ); +#endif + //proxy->setNodeMask( open ? ~0 : 0 ); + + cx._groupStack.top()->addChild( proxy ); + } + +} diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Object osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Object --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Object 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Object 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,41 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#ifndef OSGEARTH_DRIVER_KML_KML_OBJECT +#define OSGEARTH_DRIVER_KML_KML_OBJECT 1 + +#include "KML_Common" +#include + +using namespace osgEarth; +using namespace osgEarth::Util::Annotation; + +struct KML_Object +{ + virtual void scan( const Config& conf, KMLContext& cx ) { } + + virtual void scan2( const Config& conf, KMLContext& cx ) { } + + virtual void build( const Config& conf, KMLContext& cx, osg::Node* working ); + +protected: + + AnnotationData* getOrCreateAnnotationData( osg::Node* node ); +}; + +#endif // OSGEARTH_DRIVER_KML_KML_OBJECT diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Object.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Object.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Object.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Object.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,37 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#include "KML_Object" + +void +KML_Object::build( const Config& conf, KMLContext& cx, osg::Node* working ) +{ + //todo - read ID +} + +AnnotationData* +KML_Object::getOrCreateAnnotationData( osg::Node* node ) +{ + AnnotationData* data = dynamic_cast( node->getUserData() ); + if ( !data ) { + data = new AnnotationData(); + node->setUserData( data ); + } + return data; +} + diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KMLOptions osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KMLOptions --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KMLOptions 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KMLOptions 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,60 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#ifndef OSGEARTH_DRIVER_KML_OPTIONS +#define OSGEARTH_DRIVER_KML_OPTIONS 1 + +#include +#include +#include +#include + +namespace osgEarth { namespace Drivers +{ + using namespace osgEarth; + using namespace osgEarth::Symbology; + + class KMLOptions // NO EXPORT; header only + { + public: + /** The default URI to use for placemarks that don't specify an icon. */ + optional& defaultIconURI() { return _defaultIconURI; } + const optional& defaultIconURI() const { return _defaultIconURI; } + + /** The default image to use for placemarks that don't specify an icon (you + can use this instead of the icon uri.) NOT Serializable. */ + osg::ref_ptr& defaultIconImage() { return _defaultIconImage; } + const osg::ref_ptr& defaultIconImage() const { return _defaultIconImage; } + + /** TextSymbol to use when no styles are set in the KML. */ + osg::ref_ptr& defaultTextSymbol() { return _defaultTextSymbol; } + const osg::ref_ptr& defaultTextSymbol() const { return _defaultTextSymbol; } + + public: + KMLOptions() { } + + protected: + optional _defaultIconURI; + osg::ref_ptr _defaultIconImage; + osg::ref_ptr _defaultTextSymbol; + }; + +} } // namespace osgEarth::Drivers + +#endif // OSGEARTH_DRIVER_KML_OPTIONS + diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Overlay osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Overlay --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Overlay 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Overlay 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,33 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#ifndef OSGEARTH_DRIVER_KML_KML_OVERLAY +#define OSGEARTH_DRIVER_KML_KML_OVERLAY 1 + +#include "KML_Common" +#include "KML_Feature" + +using namespace osgEarth; + +struct KML_Overlay : public KML_Feature +{ + virtual void scan( const Config& conf, KMLContext& cx ); + virtual void build( const Config& conf, KMLContext& cx, osg::Node* working ); +}; + +#endif // OSGEARTH_DRIVER_KML_KML_OVERLAY diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Overlay.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Overlay.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Overlay.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Overlay.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,31 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#include "KML_Overlay" + +void +KML_Overlay::scan( const Config& conf, KMLContext& cx ) +{ + KML_Feature::scan( conf, cx ); +} + +void +KML_Overlay::build( const Config& conf, KMLContext& cx, osg::Node* working ) +{ + KML_Feature::build( conf, cx, working ); +} diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_PhotoOverlay osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_PhotoOverlay --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_PhotoOverlay 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_PhotoOverlay 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,33 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#ifndef OSGEARTH_DRIVER_KML_KML_PHOTOOVERLAY +#define OSGEARTH_DRIVER_KML_KML_PHOTOOVERLAY 1 + +#include "KML_Common" +#include "KML_Overlay" + +using namespace osgEarth; + +struct KML_PhotoOverlay : public KML_Overlay +{ + virtual void scan( const Config& conf, KMLContext& cx ); + virtual void build( const Config& conf, KMLContext& cx ); +}; + +#endif // OSGEARTH_DRIVER_KML_KML_PHOTOOVERLAY diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_PhotoOverlay.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_PhotoOverlay.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_PhotoOverlay.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_PhotoOverlay.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,31 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#include "KML_PhotoOverlay" + +void +KML_PhotoOverlay::scan( const Config& conf, KMLContext& cx ) +{ + KML_Overlay::scan( conf, cx ); +} + +void +KML_PhotoOverlay::build( const Config& conf, KMLContext& cx ) +{ + KML_Overlay::build( conf, cx, 0L ); +} diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Placemark osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Placemark --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Placemark 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Placemark 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,31 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#ifndef OSGEARTH_DRIVER_KML_KML_PLACEMARK +#define OSGEARTH_DRIVER_KML_KML_PLACEMARK 1 + +#include "KML_Feature" + +using namespace osgEarth; + +struct KML_Placemark : public KML_Feature +{ + virtual void build( const Config& conf, KMLContext& cx ); +}; + +#endif // OSGEARTH_DRIVER_KML_KML_PLACEMARK diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Placemark.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Placemark.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Placemark.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Placemark.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,132 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#include "KML_Placemark" +#include "KML_Geometry" +#include "KML_Style" +#include + +using namespace osgEarth::Features; +using namespace osgEarth::Util::Annotation; + +void +KML_Placemark::build( const Config& conf, KMLContext& cx ) +{ + Style style; + if ( conf.hasValue("styleurl") ) + { + // process a "stylesheet" style + const Style* ref_style = cx._sheet->getStyle( conf.value("styleurl"), false ); + if ( ref_style ) + style = *ref_style; + } + else if ( conf.hasChild("style") ) + { + // process an "inline" style + KML_Style kmlStyle; + kmlStyle.scan( conf.child("style"), cx ); + style = cx._activeStyle; + } + + URI iconURI; + MarkerSymbol* marker = style.get(); + if ( marker && marker->url().isSet() ) + { + iconURI = URI( marker->url()->expr(), marker->url()->uriContext() ); + } + + std::string text = + conf.hasValue("name") ? conf.value("name") : + conf.hasValue("description") ? conf.value("description") : + "Unnamed"; + + // read in the geometry: + bool isPoly = false; + bool isPoint = false; + osg::Vec3d position; + KML_Geometry geometry; + geometry.build(conf, cx, style); + if ( geometry._geom.valid() && geometry._geom->getTotalPointCount() > 0 ) + { + Geometry* geom = geometry._geom.get(); + position = geom->getBounds().center(); + isPoly = geom->getComponentType() == Geometry::TYPE_POLYGON; + isPoint = geom->getComponentType() == Geometry::TYPE_POINTSET; + } + + FeatureNode* fNode = 0L; + PlacemarkNode* pNode = 0L; + + // if we have a non-single-point geometry, render it. + if ( geometry._geom.valid() && geometry._geom->getTotalPointCount() > 1 ) + { + const ExtrusionSymbol* ex = style.get(); + const AltitudeSymbol* alt = style.get(); + + bool draped = + (ex == 0L && alt == 0L && isPoly) || + (ex == 0L && alt != 0L && alt->clamping() == AltitudeSymbol::CLAMP_TO_TERRAIN); + + // Make a feautre node; drape if we're not extruding. + fNode = new FeatureNode( cx._mapNode, new Feature(geometry._geom.get()), draped ); + fNode->setStyle( style ); + + if ( draped ) + fNode->getOrCreateStateSet()->setMode(GL_LIGHTING, 1); + } + + if ( isPoint ) + { + osg::Image* image = iconURI.readImage(); + if ( !image ) + { + image = cx._options->defaultIconImage().get(); + if ( !image ) + { + image = cx._options->defaultIconURI()->readImage(); + } + } + + // apply the default text symbol for labeling, if necessary: + if ( !style.get() && cx._options->defaultTextSymbol().valid() ) + { + style.addSymbol( cx._options->defaultTextSymbol().get() ); + } + + pNode = new PlacemarkNode( cx._mapNode, position, image, text, style ); + } + + if ( fNode && pNode ) + { + osg::Group* group = new osg::Group(); + group->addChild( fNode ); + group->addChild( pNode ); + cx._groupStack.top()->addChild( group ); + KML_Feature::build( conf, cx, group ); + } + else if ( pNode ) + { + cx._groupStack.top()->addChild( pNode ); + KML_Feature::build( conf, cx, pNode ); + } + else if ( fNode ) + { + cx._groupStack.top()->addChild( fNode ); + KML_Feature::build( conf, cx, fNode ); + } +} diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Point osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Point --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Point 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Point 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,31 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#ifndef OSGEARTH_DRIVER_KML_KML_POINT +#define OSGEARTH_DRIVER_KML_KML_POINT 1 + +#include "KML_Geometry" + +using namespace osgEarth; + +struct KML_Point : public KML_Geometry +{ + virtual void parseCoords( const Config& conf, KMLContext& cx ); +}; + +#endif // OSGEARTH_DRIVER_KML_KML_POINT diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Point.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Point.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Point.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Point.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,26 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#include "KML_Point" + +void +KML_Point::parseCoords( const Config& conf, KMLContext& cx ) +{ + _geom = new PointSet(); + KML_Geometry::parseCoords( conf, cx ); +} diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Polygon osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Polygon --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Polygon 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Polygon 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,32 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#ifndef OSGEARTH_DRIVER_KML_KML_POLYGON +#define OSGEARTH_DRIVER_KML_KML_POLYGON 1 + +#include "KML_Geometry" + +using namespace osgEarth; + +struct KML_Polygon : public KML_Geometry +{ + virtual void parseStyle( const Config& conf, KMLContext& cx, Style& style); + virtual void parseCoords( const Config& conf, KMLContext& cx ); +}; + +#endif // OSGEARTH_DRIVER_KML_KML_POLYGON diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Polygon.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Polygon.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Polygon.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Polygon.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,69 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#include "KML_Polygon" +#include "KML_LinearRing" +#include + +void +KML_Polygon::parseStyle(const Config& conf, KMLContext& cx, Style& style) +{ + KML_Geometry::parseStyle(conf, cx, style); +} + +void +KML_Polygon::parseCoords( const Config& conf, KMLContext& cx ) +{ + Polygon* poly = new Polygon(); + + Config outerConf = conf.child("outerboundaryis"); + if ( !outerConf.empty() ) + { + Config outerRingConf = outerConf.child("linearring"); + if ( !outerRingConf.empty() ) + { + KML_LinearRing outer; + outer.parseCoords( outerRingConf, cx ); + if ( outer._geom.valid() ) + { + dynamic_cast(outer._geom.get())->rewind( Ring::ORIENTATION_CCW ); + poly->reserve( outer._geom->size() ); + std::copy( outer._geom->begin(), outer._geom->end(), std::back_inserter(*poly) ); + } + } + + ConfigSet innerConfs = conf.children("innerboundaryis"); + for( ConfigSet::const_iterator i = innerConfs.begin(); i != innerConfs.end(); ++i ) + { + Config innerRingConf = i->child("linearring"); + if ( !innerRingConf.empty() ) + { + KML_LinearRing inner; + inner.parseCoords( innerRingConf, cx ); + if ( inner._geom.valid() ) + { + Geometry* innerGeom = inner._geom.get(); + dynamic_cast(innerGeom)->rewind( Ring::ORIENTATION_CW ); + poly->getHoles().push_back( dynamic_cast(innerGeom) ); + } + } + } + } + + _geom = poly; +} diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_PolyStyle osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_PolyStyle --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_PolyStyle 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_PolyStyle 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,31 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#ifndef OSGEARTH_DRIVER_KML_KML_POLYSTYLE +#define OSGEARTH_DRIVER_KML_KML_POLYSTYLE 1 + +#include "KML_Object" + +using namespace osgEarth; + +struct KML_PolyStyle : public KML_Object +{ + virtual void scan( const Config& conf, Style& style ); +}; + +#endif // OSGEARTH_DRIVER_KML_KML_POLYSTYLE diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_PolyStyle.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_PolyStyle.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_PolyStyle.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_PolyStyle.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,50 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#include "KML_PolyStyle" + +void +KML_PolyStyle::scan( const Config& conf, Style& style ) +{ + if ( !conf.empty() ) + { + bool fill = true; + if ( conf.hasValue("fill") ) { + fill = as(conf.value("fill"), 1) == 1; + } + + bool outline = false; + if ( conf.hasValue("outline") ) { + outline = as(conf.value("outline"), 0) == 1; + } + + Color color(Color::White); + if ( conf.hasValue("color") ) { + color = Color( Stringify() << "#" << conf.value("color"), Color::ABGR ); + } + + if ( fill ) { + PolygonSymbol* poly = style.getOrCreate(); + poly->fill()->color() = color; + } + else { + LineSymbol* line = style.getOrCreate(); + line->stroke()->color() = color; + } + } +} diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KMLReader osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KMLReader --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KMLReader 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KMLReader 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,50 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#ifndef OSGEARTH_DRIVER_KML_READER +#define OSGEARTH_DRIVER_KML_READER 1 + +#include +#include +#include +#include +#include +#include "KMLOptions" + +using namespace osgEarth; +using namespace osgEarth::Drivers; + +class KMLReader +{ +public: + /** Initialized a KML reader that will work with the provided map node */ + KMLReader( MapNode* mapNode, const KMLOptions* options ); + + /** Reads KML from a stream and returns a node */ + osg::Node* read( std::istream& in, const URIContext& context ); + + /** Reads KML from a Config object */ + osg::Node* read( const Config& conf ); + +private: + MapNode* _mapNode; + const KMLOptions* _options; +}; + +#endif // OSGEARTH_DRIVER_KML_READER + diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KMLReader.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KMLReader.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KMLReader.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KMLReader.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,75 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#include "KMLReader" +#include "KML_Root" +#include +#include +#include + +using namespace osgEarth; + +KMLReader::KMLReader( MapNode* mapNode, const KMLOptions* options ) : +_mapNode( mapNode ), +_options( options ) +{ + //nop +} + +osg::Node* +KMLReader::read( std::istream& in, const URIContext& context ) +{ + // read the KML from an XML stream: + osg::ref_ptr xml = XmlDocument::load( in, context ); + if ( !xml.valid() ) + return 0L; + + // convert to a config: + Config config = xml->getConfig(); + + osg::Node* node = read( config ); + node->setName( context.referrer() ); + + return node; +} + +osg::Node* +KMLReader::read( const Config& conf ) +{ + osg::Group* root = new osg::Group(); + root->ref(); + + root->setName( conf.uriContext().referrer() ); + + KMLContext cx; + cx._mapNode = _mapNode; + cx._sheet = new StyleSheet(); + cx._groupStack.push( root ); + cx._options = _options; + + const Config& kml = conf.child("kml"); + if ( !kml.empty() ) + { + KML_Root kmlRoot; + kmlRoot.scan( kml, cx ); // first pass + kmlRoot.scan2( kml, cx ); // second pass + kmlRoot.build( kml, cx ); // third pass. + } + + return root; +} diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Root osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Root --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Root 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Root 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,31 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#ifndef OSGEARTH_DRIVER_KML_KML_ROOT +#define OSGEARTH_DRIVER_KML_KML_ROOT 1 + +#include "KML_Object" + +struct KML_Root +{ + virtual void scan( const Config& conf, KMLContext& cx ); + virtual void scan2( const Config& conf, KMLContext& cx ); + virtual void build( const Config& conf, KMLContext& cx ); +}; + +#endif // OSGEARTH_DRIVER_KML_KML_SCHEMA diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Root.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Root.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Root.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Root.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,48 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#include "KML_Root" +#include "KML_Document" +#include "KML_Folder" +#include "KML_PhotoOverlay" +#include "KML_ScreenOverlay" +#include "KML_GroundOverlay" +#include "KML_NetworkLink" +#include "KML_Placemark" +#include "KML_NetworkLinkControl" + +void +KML_Root::scan( const Config& conf, KMLContext& cx ) +{ + for_features( scan, conf, cx ); + for_one( NetworkLinkControl, scan, conf, cx ); +} + +void +KML_Root::scan2( const Config& conf, KMLContext& cx ) +{ + for_features( scan2, conf, cx ); + for_one( NetworkLinkControl, scan2, conf, cx ); +} + +void +KML_Root::build( const Config& conf, KMLContext& cx ) +{ + for_features( build, conf, cx ); + for_one( NetworkLink, build, conf, cx ); +} diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Schema osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Schema --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Schema 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Schema 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,32 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#ifndef OSGEARTH_DRIVER_KML_KML_SCHEMA +#define OSGEARTH_DRIVER_KML_KML_SCHEMA 1 + +#include "KML_Common" +#include "KML_Container" + +using namespace osgEarth; + +struct KML_Schema : public KML_Object +{ + +}; + +#endif // OSGEARTH_DRIVER_KML_KML_SCHEMA diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Schema.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Schema.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Schema.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Schema.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,19 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#include "KML_Schema" diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_ScreenOverlay osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_ScreenOverlay --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_ScreenOverlay 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_ScreenOverlay 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,33 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#ifndef OSGEARTH_DRIVER_KML_KML_SCREENOVERLAY +#define OSGEARTH_DRIVER_KML_KML_SCREENOVERLAY 1 + +#include "KML_Common" +#include "KML_Overlay" + +using namespace osgEarth; + +struct KML_ScreenOverlay : public KML_Overlay +{ + virtual void scan( const Config& conf, KMLContext& cx ); + virtual void build( const Config& conf, KMLContext& cx ); +}; + +#endif // OSGEARTH_DRIVER_KML_KML_SCREENOVERLAY diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_ScreenOverlay.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_ScreenOverlay.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_ScreenOverlay.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_ScreenOverlay.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,32 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#include "KML_ScreenOverlay" + +void +KML_ScreenOverlay::scan( const Config& conf, KMLContext& cx ) +{ + KML_Overlay::scan( conf, cx ); +} + +void +KML_ScreenOverlay::build( const Config& conf, KMLContext& cx ) +{ + //todo + //KML_Overlay::build( conf, cx, 0L ); +} diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Style osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Style --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Style 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Style 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,32 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#ifndef OSGEARTH_DRIVER_KML_KML_STYLE +#define OSGEARTH_DRIVER_KML_KML_STYLE 1 + +#include "KML_Common" +#include "KML_StyleSelector" + +using namespace osgEarth; + +struct KML_Style : public KML_StyleSelector +{ + virtual void scan( const Config& conf, KMLContext& cx ); +}; + +#endif // OSGEARTH_DRIVER_KML_KML_STYLE diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Style.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Style.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_Style.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_Style.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,45 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#include "KML_Style" +#include "KML_IconStyle" +#include "KML_LabelStyle" +#include "KML_LineStyle" +#include "KML_PolyStyle" + +void +KML_Style::scan( const Config& conf, KMLContext& cx ) +{ + Style style( conf.value("id") ); + + KML_IconStyle icon; + icon.scan( conf.child("iconstyle"), style ); + + KML_LabelStyle label; + label.scan( conf.child("labelstyle"), style ); + + KML_LineStyle line; + line.scan( conf.child("linestyle"), style ); + + KML_PolyStyle poly; + poly.scan( conf.child("polystyle"), style ); + + cx._sheet->addStyle( style ); + + cx._activeStyle = style; +} diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_StyleMap osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_StyleMap --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_StyleMap 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_StyleMap 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,32 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#ifndef OSGEARTH_DRIVER_KML_KML_STYLEMAP +#define OSGEARTH_DRIVER_KML_KML_STYLEMAP 1 + +#include "KML_Common" +#include "KML_StyleSelector" + +using namespace osgEarth; + +struct KML_StyleMap : public KML_StyleSelector +{ + virtual void scan2( const Config& conf, KMLContext& cx ); +}; + +#endif // OSGEARTH_DRIVER_KML_KML_STYLEMAP diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_StyleMap.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_StyleMap.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_StyleMap.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_StyleMap.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,39 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#include "KML_StyleMap" + +void +KML_StyleMap::scan2( const Config& conf, KMLContext& cx ) +{ + const Config& pair = conf.child("pair"); + if ( !pair.empty() ) + { + const std::string& url = pair.value("styleurl" ); + if ( !url.empty() ) + { + const Style* style = cx._sheet->getStyle( url ); + if ( style ) + { + Style aliasStyle = *style; + aliasStyle.setName( conf.value("id") ); + cx._sheet->addStyle( aliasStyle ); + } + } + } +} diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_StyleSelector osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_StyleSelector --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KML_StyleSelector 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KML_StyleSelector 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,30 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#ifndef OSGEARTH_DRIVER_KML_KML_STYLESELECTOR +#define OSGEARTH_DRIVER_KML_KML_STYLESELECTOR 1 + +#include "KML_Object" + +using namespace osgEarth; + +struct KML_StyleSelector : public KML_Object +{ +}; + +#endif // OSGEARTH_DRIVER_KML_KML_STYLESELECTOR diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KMZArchive osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KMZArchive --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KMZArchive 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KMZArchive 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,93 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#ifndef OSGEARTH_DRIVER_KML_KMZ_ARCHIVE +#define OSGEARTH_DRIVER_KML_KMZ_ARCHIVE 1 + +#ifdef OSGEARTH_HAVE_MINIZIP +# define SUPPORT_KMZ 1 +#endif + +#ifdef SUPPORT_KMZ + +#include +#include +#include "unzip.h" // minizip + +using namespace osgEarth; + + +struct KMZArchive : public osgDB::Archive +{ + KMZArchive( const URI& archiveURI ); + + virtual ~KMZArchive(); + + /** closes the archive */ + void close(); + + /** Get the file name which represents the archived file.*/ + std::string getArchiveFileName() const; + + /** Get the file name which represents the master file recorded in the Archive.*/ + std::string getMasterFileName() const ; + + /** return true if file exists in archive.*/ + bool fileExists(const std::string& filename) const; + + /** return type of file. */ + osgDB::FileType getFileType(const std::string& filename) const; + + typedef osgDB::DirectoryContents FileNameList; + + /** Get the full list of file names available in the archive.*/ + bool getFileNames(FileNameList& fileNames) const; + + /** return the contents of a directory. + * returns an empty array on any error.*/ + osgDB::DirectoryContents getDirectoryContents(const std::string& dirName) const; + + /** reads a file from the archive into an io buffer. */ + bool readToBuffer( const std::string& fileInZip, std::ostream& iobuf ) const; + + ReadResult readImage(const std::string& filename, const Options* options =NULL) const; + + ReadResult readNode(const std::string& filename, const Options* options =NULL) const; + + ReadResult readObject(const std::string& filename, const Options* options =NULL) const; + + // stubs + ReadResult readShader(const std::string&, const Options* =NULL) const { return ReadResult::NOT_IMPLEMENTED; } + ReadResult readHeightField(const std::string&, const Options* =NULL) const { return ReadResult::NOT_IMPLEMENTED; } + WriteResult writeObject(const osg::Object&, const std::string&,const Options* =NULL) const { return WriteResult::NOT_IMPLEMENTED; } + WriteResult writeImage(const osg::Image&, const std::string&,const Options* =NULL) const { return WriteResult::NOT_IMPLEMENTED; } + WriteResult writeHeightField(const osg::HeightField&,const std::string&,const Options* =NULL) const { return WriteResult::NOT_IMPLEMENTED; } + WriteResult writeNode(const osg::Node&, const std::string&,const Options* =NULL) const { return WriteResult::NOT_IMPLEMENTED; } + WriteResult writeShader(const osg::Shader&, const std::string&, const Options* =NULL) const { return WriteResult::NOT_IMPLEMENTED; } + +private: + URI _archiveURI; + unzFile _uf; + void* _buf; + unsigned _bufsize; +}; + + +#endif // SUPPORT_KMZ + +#endif // OSGEARTH_DRIVER_KML_KMZ_ARCHIVE diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KMZArchive.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KMZArchive.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/KMZArchive.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/KMZArchive.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,308 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#include "KMZArchive" +#ifdef SUPPORT_KMZ + +#include +#include +#include +#include +#include +#include + +#define LC "[KMZArchive] " + +using namespace osgEarth; + +namespace +{ + URI downloadToCache( const URI& uri ) + { + // get a handle on the file cache. This is a temporary setup just to get things + // working. + static Threading::Mutex s_fcMutex; + + static URIContext s_cache; + if ( s_cache.empty() ) + { + Threading::ScopedMutexLock exclusiveLock(s_fcMutex); + if ( s_cache.empty() ) + { + const char* osgCacheDir = ::getenv("OSG_FILE_CACHE"); + if ( osgCacheDir ) + s_cache = URIContext(std::string(osgCacheDir) + "/"); + else + s_cache = URIContext("osgearth_kmz_cache/"); + } + } + + URI cachedFile( osgDB::getSimpleFileName(uri.full()), s_cache); + + if ( !osgDB::fileExists(cachedFile.full()) ) + { + osgDB::makeDirectoryForFile(cachedFile.full()); + HTTPClient::download( uri.full(), cachedFile.full() ); + } + + if ( osgDB::fileExists(cachedFile.full()) ) + return cachedFile; + + return URI(); + } +} + +//------------------------------------------------------------------------ + +KMZArchive::KMZArchive( const URI& archiveURI ) : +_archiveURI( archiveURI ), +_buf( 0L ), +_bufsize( 1024000 ) +{ + supportsExtension( "kmz", "KMZ" ); + + // download the KMZ to a local cache - cannot open zip files remotely. + URI localURI( archiveURI ); + if ( osgDB::containsServerAddress(archiveURI.full()) ) + { + localURI = downloadToCache( archiveURI ); + } + + _uf = unzOpen( localURI.full().c_str() ); + _buf = (void*)new char[_bufsize]; +} + +KMZArchive::~KMZArchive() +{ + if ( _buf ) + delete [] _buf; +} + +void +KMZArchive::close() +{ + _uf = 0; +} + +/** Get the file name which represents the archived file.*/ +std::string +KMZArchive::getArchiveFileName() const +{ + return _archiveURI.base(); +} + +/** Get the file name which represents the master file recorded in the Archive.*/ +std::string +KMZArchive::getMasterFileName() const +{ + return "doc.kml"; +} + +/** return true if file exists in archive.*/ +bool +KMZArchive::fileExists(const std::string& filename) const +{ + //todo + return false; +} + +/** return type of file. */ +osgDB::FileType +KMZArchive::getFileType(const std::string& filename) const +{ + return osgDB::REGULAR_FILE; +} + +/** Get the full list of file names available in the archive.*/ +bool +KMZArchive::getFileNames(FileNameList& fileNames) const +{ + return false; +} + +/** return the contents of a directory. +* returns an empty array on any error.*/ +osgDB::DirectoryContents +KMZArchive::getDirectoryContents(const std::string& dirName) const +{ + return osgDB::DirectoryContents(); +} + +/** reads a file from the archive into an io buffer. */ +bool +KMZArchive::readToBuffer( const std::string& fileInZip, std::ostream& iobuf ) const +{ + // help from: + // http://bytes.com/topic/c/answers/764381-reading-contents-zip-files + + int err = UNZ_OK; + unz_file_info file_info; + char filename_inzip[2048]; + bool got_file_info = false; + + if ( _uf == 0 ) + { + OE_WARN << LC << "Archive is not open." << std::endl; + return false; + } + + if ( fileInZip == ".kml" ) + { + // special case; first try the master file (doc.kml), then failing that, look + // for the first KML file in the archive. + if ( unzLocateFile( _uf, "doc.kml", 0 ) != 0 ) + { + if ( unzGoToFirstFile( _uf ) != UNZ_OK ) + { + OE_WARN << LC << "Archive is empty" << std::endl; + return false; + } + + while( err == UNZ_OK ) + { + if ( unzGetCurrentFileInfo( _uf, &file_info, filename_inzip, sizeof(filename_inzip), 0L, 0, 0L, 0) ) + { + OE_WARN << LC << "Error with zipfile " << _archiveURI.base() << std::endl; + return false; + } + + got_file_info = true; + std::string lc = osgEarth::toLower( std::string(filename_inzip) ); + if ( endsWith( lc, ".kml" ) ) + { + break; + } + + err = unzGoToNextFile( _uf ); + } + + if ( err != UNZ_OK ) + { + OE_WARN << LC << "No KML file found in archive" << std::endl; + return false; + } + } + } + + else if ( unzLocateFile( _uf, fileInZip.c_str(), 0 ) ) + { + OE_WARN << LC << "Failed to locate '" << fileInZip << "' in '" << _archiveURI.base() << "'" << std::endl; + return false; + } + + if ( !got_file_info ) + { + if ( unzGetCurrentFileInfo( _uf, &file_info, filename_inzip, sizeof(filename_inzip), 0L, 0, 0L, 0) ) + { + OE_WARN << LC << "Error with zipfile " << _archiveURI.base() << std::endl; + return false; + } + } + + err = unzOpenCurrentFilePassword( _uf, 0L ); + if ( err != UNZ_OK ) + { + OE_WARN << LC << "unzOpenCurrentFilePassword failed" << std::endl; + return false; + } + + do + { + err = unzReadCurrentFile( _uf, _buf, _bufsize ); + if ( err < 0 ) + { + OE_WARN << LC << "Error in unzReadCurrentFile" << std::endl; + break; + } + if ( err > 0 ) + { + for( unsigned i=0; i 0 ); + + err = unzCloseCurrentFile( _uf ); + if ( err != UNZ_OK ) + { + //ignore it... + } + + return true; +} + +osgDB::ReaderWriter::ReadResult +KMZArchive::readImage(const std::string& filename, const osgDB::Options* options) const +{ + osgDB::ReaderWriter* rw = osgDB::Registry::instance()->getReaderWriterForExtension( + osgDB::getLowerCaseFileExtension( filename ) ); + if ( rw ) + { + std::stringstream iobuf; + if ( readToBuffer( filename, iobuf ) ) + { + osg::ref_ptr myOptions = Registry::instance()->cloneOrCreateOptions(options); + URIContext(*_archiveURI).add(filename).store( myOptions.get() ); + return rw->readImage( iobuf, options ); + } + else return ReadResult::ERROR_IN_READING_FILE; + } + else return ReadResult::FILE_NOT_HANDLED; +} + +osgDB::ReaderWriter::ReadResult +KMZArchive::readNode(const std::string& filename, const osgDB::Options* options) const +{ + osgDB::ReaderWriter* rw = osgDB::Registry::instance()->getReaderWriterForExtension( + osgDB::getLowerCaseFileExtension( filename ) ); + if ( rw ) + { + std::stringstream iobuf; + if ( readToBuffer( filename, iobuf ) ) + { + osg::ref_ptr myOptions = Registry::instance()->cloneOrCreateOptions(options); + URIContext(*_archiveURI).add(filename).store( myOptions.get() ); + return rw->readNode( iobuf, options ); + } + else return ReadResult::ERROR_IN_READING_FILE; + } + else return ReadResult::FILE_NOT_HANDLED; +} + +osgDB::ReaderWriter::ReadResult +KMZArchive::readObject(const std::string& filename, const osgDB::Options* options) const +{ + osgDB::ReaderWriter* rw = osgDB::Registry::instance()->getReaderWriterForExtension( + osgDB::getLowerCaseFileExtension( filename ) ); + if ( rw ) + { + std::stringstream iobuf; + if ( readToBuffer( filename, iobuf ) ) + { + osg::ref_ptr myOptions = Registry::instance()->cloneOrCreateOptions(options); + URIContext(*_archiveURI).add(filename).store( myOptions.get() ); + return rw->readObject( iobuf, myOptions.get() ); + } + else return ReadResult::ERROR_IN_READING_FILE; + } + else return ReadResult::FILE_NOT_HANDLED; +} + +#endif // SUPPORT_KMZ diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/kml/ReaderWriterKML.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/ReaderWriterKML.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/kml/ReaderWriterKML.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/kml/ReaderWriterKML.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,123 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "KMLOptions" +#include "KMLReader" +#include "KMZArchive" + +#define LC "[ReaderWriterKML] " + +using namespace osgEarth; +using namespace osgEarth::Drivers; + +//--------------------------------------------------------------------------- + +struct ReaderWriterKML : public osgDB::ReaderWriter +{ + ReaderWriterKML() + { + supportsExtension( "kml", "KML" ); + +#ifdef SUPPORT_KMZ + supportsExtension( "kmz", "KMZ" ); +#endif // SUPPORT_KMZ + } + + ReadResult readObject(const std::string& url, const Options* options) const + { + return readNode( url, options ); + } + + ReadResult readObject(std::istream& in, const Options* options ) const + { + return readNode(in, options); + } + + ReadResult readNode(const std::string& url, const Options* options) const + { + std::string ext = osgDB::getLowerCaseFileExtension(url); + if ( !acceptsExtension(ext) ) + return ReadResult::FILE_NOT_HANDLED; + + if ( ext == "kmz" ) + { + return URI(url + "/.kml").readNode( options ); + } + else + { + // propagate the source URI along to the stream reader + osg::ref_ptr myOptions = Registry::instance()->cloneOrCreateOptions(options); + URIContext(url).store( myOptions.get() ); + return readNode( URIStream(url), myOptions.get() ); + } + } + + ReadResult readNode(std::istream& in, const Options* options ) const + { + if ( !options ) + return ReadResult("Missing required MapNode option"); + + // this plugin requires that you pass in a MapNode* in options. + MapNode* mapNode = const_cast( + static_cast( options->getPluginData("osgEarth::MapNode")) ); + if ( !mapNode ) + return ReadResult("Missing required MapNode option"); + + // grab the KMLOptions if present + const KMLOptions* kmlOptions = + static_cast(options->getPluginData("osgEarth::KMLOptions") ); + + // Grab the URIContext from the options (since we're reading from a stream) + URIContext uriContext( options ); + + // fire up a KML reader and parse the data. + KMLReader reader( mapNode, kmlOptions ); + osg::Node* node = reader.read( in, uriContext ); + return ReadResult(node); + } + +#ifdef SUPPORT_KMZ + + ReadResult openArchive( const std::string& url, ArchiveStatus status, unsigned int dummy, const osgDB::Options* options =0L ) const + { + // Find the archive for this thread. We store one KMZ archive instance per thread + // so that the minizip library can work in parallel + osg::ref_ptr& kmz = const_cast(this)->_archive.get(); + if ( !kmz.valid() ) + kmz = new KMZArchive( URI(url) ); + + return ReadResult( kmz.release() ); + } + +private: + + Threading::PerThread< osg::ref_ptr > _archive; + +#endif // SUPPORT_KMZ +}; + +REGISTER_OSGPLUGIN( kml, ReaderWriterKML ) diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/label_overlay/CMakeLists.txt osgearth-2.1.1+dfsg/src/osgEarthDrivers/label_overlay/CMakeLists.txt --- osgearth-2.0+dfsg/src/osgEarthDrivers/label_overlay/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/label_overlay/CMakeLists.txt 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,9 @@ +SET(TARGET_SRC OverlayLabelSource.cpp) +#SET(TARGET_H OverlayLabelOptions) +SET(TARGET_COMMON_LIBRARIES ${TARGET_COMMON_LIBRARIES} osgEarthFeatures osgEarthSymbology osgEarthUtil) +SETUP_PLUGIN(osgearth_label_overlay) + +# to install public driver includes: +SET(LIB_NAME label_overlay) +#SET(LIB_PUBLIC_HEADERS OverlayLabelOptions) +INCLUDE(ModuleInstallOsgEarthDriverIncludes OPTIONAL) diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/label_overlay/OverlayLabelSource osgearth-2.1.1+dfsg/src/osgEarthDrivers/label_overlay/OverlayLabelSource --- osgearth-2.0+dfsg/src/osgEarthDrivers/label_overlay/OverlayLabelSource 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/label_overlay/OverlayLabelSource 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,74 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#ifndef OSGEARTH_DRIVER_FEATURE_SUB_MODEL_OPTIONS +#define OSGEARTH_DRIVER_FEATURE_SUB_MODEL_OPTIONS 1 + +#include +#include + +namespace osgEarth { namespace Drivers +{ + using namespace osgEarth; + using namespace osgEarth::Features; + + class FeatureSubModelOptions : public FeatureModelSourceOptions // NO EXPORT; header only + { + public: + optional& url() { return _url; } + const optional& url() const { return _url; } + + optional& heightOffset() { return _heightOffset; } + const optional& heightOffset() const { return _heightOffset; } + + public: + FeatureSubModelOptions( const ConfigOptions& options =ConfigOptions() ) : + FeatureModelSourceOptions( options ) + { + setDriver( "feature_geom" ); + fromConfig( _conf ); + } + + public: + Config getConfig() const { + Config conf = FeatureModelSourceOptions::getConfig(); + conf.updateIfSet( "url", _url ); + conf.updateIfSet( "height_offset", _heightOffset ); + return conf; + } + + protected: + virtual void mergeConfig( const Config& conf ) { + FeatureModelSourceOptions::mergeConfig( conf ); + fromConfig( conf ); + } + + private: + void fromConfig( const Config& conf ) { + conf.getIfSet( "url", _url ); + conf.getIfSet( "height_offset", _heightOffset ); + } + + optional _url; + optional _heightOffset; + }; + +} } // namespace osgEarth::Drivers + +#endif // OSGEARTH_DRIVER_FEATURE_SUB_MODEL_OPTIONS + diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/label_overlay/OverlayLabelSource.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/label_overlay/OverlayLabelSource.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/label_overlay/OverlayLabelSource.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/label_overlay/OverlayLabelSource.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,158 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace osgEarth; +using namespace osgEarth::Features; +using namespace osgEarth::Symbology; +using namespace osgEarth::Util; + +class OverlayLabelSource : public LabelSource +{ +public: + OverlayLabelSource( const LabelSourceOptions& options ) : + LabelSource( options ) + { + //nop + } + + osg::Node* createNode( + const FeatureList& input, + const TextSymbol* text, + const FilterContext& context ) + { + osg::Group* group = 0L; + std::set used; // to prevent dupes + bool skipDupes = (text->removeDuplicateLabels() == true); + + StringExpression contentExpr ( *text->content() ); + NumericExpression priorityExpr( *text->priority() ); + + const MapInfo& mi = context.getSession()->getMapInfo(); + bool makeECEF = mi.isGeocentric(); + + for( FeatureList::const_iterator i = input.begin(); i != input.end(); ++i ) + { + const Feature* feature = i->get(); + if ( !feature ) + continue; + + const Geometry* geom = feature->getGeometry(); + if ( !geom ) + continue; + + osg::Vec3d centroid = geom->getBounds().center(); + //osg::Vec3d centroidWorld = context.toWorld(centroid); + + if ( makeECEF ) + { + context.profile()->getSRS()->transformToECEF( centroid, centroid ); + } + +#if 0 + if ( context.isGeocentric() && geom->getComponentType() != Geometry::TYPE_POINTSET ) + { + // "clamp" the centroid to the ellipsoid + osg::Vec3d centroidMap; + mi.worldPointToMapPoint(centroidWorld, centroidMap); + centroidMap.z() = 0.0; + mi.mapPointToWorldPoint(centroidMap, centroidWorld); + centroid = context.toLocal(centroidWorld); + } +#endif + + const std::string& value = feature->eval( contentExpr ); + + if ( !value.empty() && (!skipDupes || used.find(value) == used.end()) ) + { + if ( !group ) + { + group = new osg::Group(); + } + + double priority = feature->eval( priorityExpr ); + + Controls::LabelControl* label = new Controls::LabelControl( value ); + if ( text->fill().isSet() ) + label->setForeColor( text->fill()->color() ); + if ( text->halo().isSet() ) + label->setHaloColor( text->halo()->color() ); + if ( text->size().isSet() ) + label->setFontSize( *text->size() ); + if ( text->font().isSet() ) + label->setFont( osgText::readFontFile(*text->font()) ); + + Controls::ControlNode* node = new Controls::ControlNode( label, priority ); + + osg::MatrixTransform* xform = new osg::MatrixTransform( osg::Matrixd::translate(centroid) ); + xform->addChild( node ); + + // for a geocentric map, do a simple dot product cull. + if ( makeECEF ) + { + xform->setCullCallback( new CullNodeByHorizon(centroid, mi.getProfile()->getSRS()->getEllipsoid()) ); + group->addChild( xform ); + } + else + { + group->addChild( xform ); + } + + if ( skipDupes ) + used.insert( value ); + } + } + + return group; + } +}; + +//------------------------------------------------------------------------ + +class OverlayLabelSourceDriver : public LabelSourceDriver +{ +public: + OverlayLabelSourceDriver() + { + supportsExtension( "osgearth_label_overlay", "osgEarth overlay label plugin" ); + } + + virtual const char* className() + { + return "osgEarth Overlay Label Plugin"; + } + + virtual ReadResult readObject(const std::string& file_name, const Options* options) const + { + if ( !acceptsExtension(osgDB::getLowerCaseFileExtension( file_name ))) + return ReadResult::FILE_NOT_HANDLED; + + return new OverlayLabelSource( getLabelSourceOptions(options) ); + } +}; + +REGISTER_OSGPLUGIN(osgearth_label_overlay, OverlayLabelSourceDriver) diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/mask_feature/CMakeLists.txt osgearth-2.1.1+dfsg/src/osgEarthDrivers/mask_feature/CMakeLists.txt --- osgearth-2.0+dfsg/src/osgEarthDrivers/mask_feature/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/mask_feature/CMakeLists.txt 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,11 @@ +SET(TARGET_SRC FeatureMaskSource.cpp) +SET(TARGET_H FeatureMaskOptions) +SET(TARGET_COMMON_LIBRARIES ${TARGET_COMMON_LIBRARIES} osgEarthFeatures osgEarthSymbology) +SETUP_PLUGIN(osgearth_mask_feature) + + +# to install public driver includes: +SET(LIB_NAME mask_feature) +SET(LIB_PUBLIC_HEADERS FeatureMaskOptions) +INCLUDE(ModuleInstallOsgEarthDriverIncludes OPTIONAL) + diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/mask_feature/FeatureMaskOptions osgearth-2.1.1+dfsg/src/osgEarthDrivers/mask_feature/FeatureMaskOptions --- osgearth-2.0+dfsg/src/osgEarthDrivers/mask_feature/FeatureMaskOptions 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/mask_feature/FeatureMaskOptions 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,75 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#ifndef OSGEARTH_DRIVER_FEATURE_MASK_OPTIONS +#define OSGEARTH_DRIVER_FEATURE_MASK_OPTIONS 1 + +#include +#include +#include + +namespace osgEarth { namespace Drivers +{ + using namespace osgEarth; + using namespace osgEarth::Features; + + class FeatureMaskOptions : public MaskSourceOptions // NO EXPORT; header only + { + public: + /** Feature source from which to read the feature data */ + optional& featureOptions() { return _featureOptions; } + const optional& featureOptions() const { return _featureOptions; } + + /** A live feature source instance to use. Note, this does not serialize. */ + osg::ref_ptr& featureSource() { return _featureSource; } + const osg::ref_ptr& featureSource() const { return _featureSource; } + + public: + FeatureMaskOptions( const ConfigOptions& options =ConfigOptions() ) : + MaskSourceOptions( options ) + { + setDriver( "feature" ); + fromConfig( _conf ); + } + + public: + Config getConfig() const { + Config conf = MaskSourceOptions::getConfig(); + conf.updateObjIfSet( "features", _featureOptions ); + return conf; + } + + protected: + virtual void mergeConfig( const Config& conf ) { + MaskSourceOptions::mergeConfig( conf ); + fromConfig( conf ); + } + + private: + void fromConfig( const Config& conf ) { + if ( conf.hasChild("features") ) + _featureOptions->merge( conf.child("features") ); + } + + optional _featureOptions; + osg::ref_ptr _featureSource; + }; + +} } // namespace osgEarth::Drivers + +#endif // OSGEARTH_DRIVER_FEATURE_MASK_OPTIONS diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/mask_feature/FeatureMaskSource.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/mask_feature/FeatureMaskSource.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/mask_feature/FeatureMaskSource.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/mask_feature/FeatureMaskSource.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,156 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "FeatureMaskOptions" + +#define LC "[FeatureMaskDriver] " + +using namespace osgEarth; +using namespace osgEarth::Features; +using namespace osgEarth::Drivers; + +//------------------------------------------------------------------------ + +class FeatureMaskSource : public MaskSource +{ +public: + FeatureMaskSource( const MaskSourceOptions& options ) + : MaskSource( options ), _options( options ), _failed( false ) + { + // the data source from which to pull features: + if ( _options.featureSource().valid() ) + { + _features = _options.featureSource().get(); + } + else if ( _options.featureOptions().isSet() ) + { + _features = FeatureSourceFactory::create( _options.featureOptions().value() ); + if ( !_features.valid() ) + { + OE_WARN << "FeatureModelSource - no valid feature source provided" << std::endl; + } + } + } + + const MaskSourceOptions& getOptions() const { return _options; } + + //override + void initialize( const std::string& referenceURI, const osgEarth::Map* map ) + { + MaskSource::initialize( referenceURI, map ); + + if ( _features.valid() ) + { + _features->initialize( referenceURI ); + } + else + { + OE_WARN << LC << "No FeatureSource; nothing will be rendered (" << getName() << ")" << std::endl; + } + } + + osg::Vec3dArray* createBoundary( const SpatialReference* srs, ProgressCallback* progress ) + { + if ( _failed ) + return 0L; + + if ( _features.valid() ) + { + if ( _features->getFeatureProfile() ) + { + osg::ref_ptr cursor = _features->createFeatureCursor(); + if ( cursor ) + { + if ( cursor->hasMore() ) + { + Feature* f = cursor->nextFeature(); + if ( f && f->getGeometry() ) + { + // Init a filter to tranform feature in desired SRS + if (!srs->isEquivalentTo(_features->getFeatureProfile()->getSRS())) { + FilterContext cx; + cx.profile() = new FeatureProfile(_features->getFeatureProfile()->getExtent()); + //cx.isGeocentric() = _features->getFeatureProfile()->getSRS()->isGeographic(); + + TransformFilter xform( srs ); + FeatureList featureList; + featureList.push_back(f); + cx = xform.push(featureList, cx); + } + + return f->getGeometry()->toVec3dArray(); + } + } + } + } + else + { + OE_WARN << LC << "Failed to create boundary; feature source has no SRS" << std::endl; + _failed = true; + } + } + else + { + OE_WARN << LC << "Unable to create boundary; invalid feature source" << std::endl; + _failed = true; + } + return 0L; + } + +private: + bool _failed; + const FeatureMaskOptions _options; + osg::ref_ptr _features; +}; + +//------------------------------------------------------------------------ + +class FeatureMaskDriver : public MaskSourceDriver +{ +public: + FeatureMaskDriver() + { + supportsExtension( "osgearth_mask_feature", "osgEarth feature mask plugin" ); + } + + virtual const char* className() + { + return "osgEarth Feature Mask Plugin"; + } + + virtual ReadResult readObject(const std::string& file_name, const Options* options) const + { + if ( !acceptsExtension(osgDB::getLowerCaseFileExtension( file_name ))) + return ReadResult::FILE_NOT_HANDLED; + + return new FeatureMaskSource( getMaskSourceOptions(options) ); + } +}; + +REGISTER_OSGPLUGIN(osgearth_mask_feature, FeatureMaskDriver) diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/mbtiles/CMakeLists.txt osgearth-2.1.1+dfsg/src/osgEarthDrivers/mbtiles/CMakeLists.txt --- osgearth-2.0+dfsg/src/osgEarthDrivers/mbtiles/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/mbtiles/CMakeLists.txt 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,23 @@ +INCLUDE_DIRECTORIES( ${SQLITE3_INCLUDE_DIR} ) + +SET(TARGET_COMMON_LIBRARIES + ${TARGET_COMMON_LIBRARIES} + ${SQLITE3_LIBRARY} +) + +SET(TARGET_SRC + ReaderWriterMBTiles.cpp +) + +# headers to show in IDE +SET(TARGET_H + MBTilesOptions +) + +SETUP_PLUGIN(osgearth_mbtiles) + + +# to install public driver includes: +SET(LIB_NAME mbtiles) +SET(LIB_PUBLIC_HEADERS MBTilesOptions) +INCLUDE(ModuleInstallOsgEarthDriverIncludes OPTIONAL) diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/mbtiles/MBTilesOptions osgearth-2.1.1+dfsg/src/osgEarthDrivers/mbtiles/MBTilesOptions --- osgearth-2.0+dfsg/src/osgEarthDrivers/mbtiles/MBTilesOptions 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/mbtiles/MBTilesOptions 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,71 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#ifndef OSGEARTH_DRIVER_MBTILES_DRIVEROPTIONS +#define OSGEARTH_DRIVER_MBTILES_DRIVEROPTIONS 1 + +#include +#include + +namespace osgEarth { namespace Drivers +{ + using namespace osgEarth; + + class MBTilesOptions : public TileSourceOptions // NO EXPORT; header only + { + public: + optional& filename() { return _filename; } + const optional& filename() const { return _filename; } + + optional& format() { return _format; } + const optional& format() const { return _format; } + + public: + MBTilesOptions( const TileSourceOptions& opt =TileSourceOptions() ) : TileSourceOptions( opt ) + { + setDriver( "mbtiles" ); + fromConfig( _conf ); + } + + public: + Config getConfig() const { + Config conf = TileSourceOptions::getConfig(); + conf.updateIfSet("filename", _filename); + conf.updateIfSet("format", _format); + return conf; + } + + void mergeConfig( const Config& conf ) { + TileSourceOptions::mergeConfig( conf ); + fromConfig( conf ); + } + + void fromConfig( const Config& conf ) { + conf.getIfSet( "filename", _filename ); + conf.getIfSet( "format", _format ); + } + + private: + optional _filename; + optional _format; + }; + +} } // namespace osgEarth::Drivers + +#endif // OSGEARTH_DRIVER_MBTILES_DRIVEROPTIONS + diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/mbtiles/ReaderWriterMBTiles.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/mbtiles/ReaderWriterMBTiles.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/mbtiles/ReaderWriterMBTiles.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/mbtiles/ReaderWriterMBTiles.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,290 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph +* Copyright 2008-2010 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 +*/ + +#include "MBTilesOptions" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace osgEarth; +using namespace osgEarth::Drivers; + +#include + + +#define LC "[MBTilesSource] " + +class MBTilesSource : public TileSource +{ +public: + MBTilesSource( const TileSourceOptions& options ) : + TileSource( options ), + _options( options ), + _database( NULL ), + _minLevel( 0 ), + _maxLevel( 20 ) + { + } + + // override + void initialize( const std::string& referenceURI, const Profile* overrideProfile) + { + //Set the profile + setProfile( osgEarth::Registry::instance()->getGlobalMercatorProfile() ); + + //Open the database + std::string filename = _options.filename().value(); + + //Get the absolute filename + if (!osgDB::containsServerAddress(filename)) + { + filename = osgEarth::getFullPath(referenceURI, filename); + } + + int flags = SQLITE_OPEN_READONLY; + int rc = sqlite3_open_v2( filename.c_str(), &_database, flags, 0L ); + if ( rc != 0 ) + { + OE_WARN << LC << "Failed to open database \"" << filename << "\": " << sqlite3_errmsg(_database) << std::endl; + return; + } + + //Print out some metadata + std::string name, type, version, description, format; + getMetaData( "name", name ); + getMetaData( "type", type); + getMetaData( "version", version ); + getMetaData( "description", description ); + getMetaData( "format", format ); + OE_NOTICE << "name=" << name << std::endl + << "type=" << type << std::endl + << "version=" << version << std::endl + << "description=" << description << std::endl + << "format=" << format << std::endl; + + //Determine the tile format and get a reader writer for it. + if (_options.format().isSet()) + { + //Get an explicitly defined format + _tileFormat = _options.format().value(); + } + else if (!format.empty()) + { + //Try to get it from the database metadata + _tileFormat = format; + } + else + { + //Assume it's PNG + _tileFormat = "png"; + } + + OE_DEBUG << LC << "_tileFormat = " << _tileFormat << std::endl; + + //Get the ReaderWriter + _rw = osgDB::Registry::instance()->getReaderWriterForExtension( _tileFormat ); + + computeLevels(); + } + + // override + osg::Image* createImage( const TileKey& key, + ProgressCallback* progress) + { + int z = key.getLevelOfDetail(); + int x = key.getTileX(); + int y = key.getTileY(); + + if (z < _minLevel) + { + //Return an empty image to make it continue subdividing + return ImageUtils::createEmptyImage(); + } + + if (z > _maxLevel) + { + //If we're at the max level, just return NULL + return NULL; + } + + unsigned int numRows, numCols; + key.getProfile()->getNumTiles(key.getLevelOfDetail(), numCols, numRows); + y = numRows - y - 1; + + //Get the image + sqlite3_stmt* select = NULL; + std::string query = "SELECT tile_data from tiles where zoom_level = ? AND tile_column = ? AND tile_row = ?"; + int rc = sqlite3_prepare_v2( _database, query.c_str(), -1, &select, 0L ); + if ( rc != SQLITE_OK ) + { + OE_WARN << LC << "Failed to prepare SQL: " << query << "; " << sqlite3_errmsg(_database) << std::endl; + return false; + } + + bool valid = true; + sqlite3_bind_int( select, 1, z ); + sqlite3_bind_int( select, 2, x ); + sqlite3_bind_int( select, 3, y ); + + + osg::Image* result = NULL; + rc = sqlite3_step( select ); + if ( rc == SQLITE_ROW) + { + // the pointer returned from _blob gets freed internally by sqlite, supposedly + const char* data = (const char*)sqlite3_column_blob( select, 0 ); + int imageBufLen = sqlite3_column_bytes( select, 0 ); + + // deserialize the image from the buffer: + std::string imageString( data, imageBufLen ); + std::stringstream imageBufStream( imageString ); + osgDB::ReaderWriter::ReadResult rr = _rw->readImage( imageBufStream ); + if (rr.validImage()) + { + result = rr.takeImage(); + } + } + else + { + OE_DEBUG << LC << "SQL QUERY failed for " << query << ": " << std::endl; + valid = false; + } + + sqlite3_finalize( select ); + return result; + + } + + bool getMetaData( const std::string& key, std::string& value ) + { + //get the metadata + sqlite3_stmt* select = NULL; + std::string query = "SELECT value from metadata where name = ?"; + int rc = sqlite3_prepare_v2( _database, query.c_str(), -1, &select, 0L ); + if ( rc != SQLITE_OK ) + { + OE_WARN << LC << "Failed to prepare SQL: " << query << "; " << sqlite3_errmsg(_database) << std::endl; + return false; + } + + + bool valid = true; + std::string keyStr = std::string( key ); + rc = sqlite3_bind_text( select, 1, keyStr.c_str(), keyStr.length(), SQLITE_STATIC ); + if (rc != SQLITE_OK ) + { + OE_WARN << LC << "Failed to bind text: " << query << "; " << sqlite3_errmsg(_database) << std::endl; + return false; + } + + rc = sqlite3_step( select ); + if ( rc == SQLITE_ROW) + { + value = (char*)sqlite3_column_text( select, 0 ); + } + else + { + OE_DEBUG << LC << "SQL QUERY failed for " << query << ": " << std::endl; + valid = false; + } + + sqlite3_finalize( select ); + return valid; + } + + void computeLevels() + { + sqlite3_stmt* select = NULL; + std::string query = "SELECT min(zoom_level), max(zoom_level) from tiles"; + int rc = sqlite3_prepare_v2( _database, query.c_str(), -1, &select, 0L ); + if ( rc != SQLITE_OK ) + { + OE_WARN << LC << "Failed to prepare SQL: " << query << "; " << sqlite3_errmsg(_database) << std::endl; + } + + rc = sqlite3_step( select ); + if ( rc == SQLITE_ROW) + { + _minLevel = sqlite3_column_int( select, 0 ); + _maxLevel = sqlite3_column_int( select, 1 ); + //OE_NOTICE << "Min=" << _minLevel << " Max=" << _maxLevel << std::endl; + } + else + { + OE_DEBUG << LC << "SQL QUERY failed for " << query << ": " << std::endl; + } + + sqlite3_finalize( select ); + } + + // override + virtual std::string getExtension() const + { + return _tileFormat; + } + +private: + const MBTilesOptions _options; + sqlite3* _database; + unsigned int _minLevel; + unsigned int _maxLevel; + + osg::ref_ptr _rw; + std::string _tileFormat; + +}; + + +class MBTilesTileSourceFactory : public TileSourceDriver +{ +public: + MBTilesTileSourceFactory() + { + supportsExtension( "osgearth_mbtiles", "MBTiles Driver" ); + } + + virtual const char* className() + { + return "MBTiles ReaderWriter"; + } + + virtual ReadResult readObject(const std::string& file_name, const Options* options) const + { + if ( !acceptsExtension(osgDB::getLowerCaseFileExtension( file_name ))) + return ReadResult::FILE_NOT_HANDLED; + + return new MBTilesSource( getTileSourceOptions(options) ); + } +}; + +REGISTER_OSGPLUGIN(osgearth_mbtiles, MBTilesTileSourceFactory) + + diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/model_feature_geom/CMakeLists.txt osgearth-2.1.1+dfsg/src/osgEarthDrivers/model_feature_geom/CMakeLists.txt --- osgearth-2.0+dfsg/src/osgEarthDrivers/model_feature_geom/CMakeLists.txt 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/model_feature_geom/CMakeLists.txt 2011-11-04 19:44:43.000000000 +0000 @@ -1,11 +1,15 @@ -SET(TARGET_SRC FeatureGeomModelSource.cpp) -SET(TARGET_H FeatureGeomModelOptions) +SET(TARGET_SRC + FeatureGeomModelSource.cpp ) + +SET(TARGET_H + FeatureGeomModelOptions ) + SET(TARGET_COMMON_LIBRARIES ${TARGET_COMMON_LIBRARIES} osgEarthFeatures osgEarthSymbology) + SETUP_PLUGIN(osgearth_model_feature_geom) # to install public driver includes: SET(LIB_NAME model_feature_geom) -SET(LIB_PUBLIC_HEADERS FeatureGeomModelOptions) +SET(LIB_PUBLIC_HEADERS ${TARGET_H}) INCLUDE(ModuleInstallOsgEarthDriverIncludes OPTIONAL) - diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/model_feature_geom/FeatureGeomModelOptions osgearth-2.1.1+dfsg/src/osgEarthDrivers/model_feature_geom/FeatureGeomModelOptions --- osgearth-2.0+dfsg/src/osgEarthDrivers/model_feature_geom/FeatureGeomModelOptions 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/model_feature_geom/FeatureGeomModelOptions 2011-11-04 19:44:43.000000000 +0000 @@ -20,6 +20,7 @@ #define OSGEARTH_DRIVER_FEATURE_GEOM_MODEL_OPTIONS 1 #include +#include #include namespace osgEarth { namespace Drivers @@ -30,16 +31,12 @@ class FeatureGeomModelOptions : public FeatureModelSourceOptions // NO EXPORT; header only { public: - optional& url() { return _url; } - const optional& url() const { return _url; } - - optional& heightOffset() { return _heightOffset; } - const optional& heightOffset() const { return _heightOffset; } + GeometryCompilerOptions& compilerOptions() { return _compilerOptions; } + const GeometryCompilerOptions& compilerOptions() const { return _compilerOptions; } public: FeatureGeomModelOptions( const ConfigOptions& options =ConfigOptions() ) : - FeatureModelSourceOptions( options ), - _heightOffset( 0.0 ) + FeatureModelSourceOptions( options ) { setDriver( "feature_geom" ); fromConfig( _conf ); @@ -48,25 +45,28 @@ public: Config getConfig() const { Config conf = FeatureModelSourceOptions::getConfig(); - conf.updateIfSet( "url", _url ); - conf.updateIfSet( "height_offset", _heightOffset ); + + // merges the configurations together, so you can still specify + // compiler options at the model level + Config compilerConfig = _compilerOptions.getConfig(); + conf.merge( compilerConfig ); + return conf; } protected: virtual void mergeConfig( const Config& conf ) { FeatureModelSourceOptions::mergeConfig( conf ); + _compilerOptions.mergeConfig( conf ); fromConfig( conf ); } private: void fromConfig( const Config& conf ) { - conf.getIfSet( "url", _url ); - conf.getIfSet( "height_offset", _heightOffset ); + _compilerOptions.mergeConfig(conf); } - optional _url; - optional _heightOffset; + GeometryCompilerOptions _compilerOptions; }; } } // namespace osgEarth::Drivers diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/model_feature_geom/FeatureGeomModelSource.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/model_feature_geom/FeatureGeomModelSource.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/model_feature_geom/FeatureGeomModelSource.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/model_feature_geom/FeatureGeomModelSource.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -20,24 +20,11 @@ #include #include #include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include +#include #include #include #include -#include -#include #include "FeatureGeomModelOptions" @@ -45,206 +32,50 @@ using namespace osgEarth::Features; using namespace osgEarth::Symbology; using namespace osgEarth::Drivers; -using namespace OpenThreads; - -class FeatureGeomModelSource; //------------------------------------------------------------------------ -class FactoryGeomSymbolizer : public SymbolizerFactory +namespace { -protected: - osg::ref_ptr _modelSource; - const FeatureGeomModelOptions _options; + //------------------------------------------------------------------------ -public: - FactoryGeomSymbolizer( FeatureModelSource* modelSource, const FeatureGeomModelOptions& options ) : - _modelSource( modelSource ), - _options( options ) { } - - //override - virtual FeatureModelSource* getFeatureModelSource() { return _modelSource.get(); } - - //override - virtual osg::Node* createNodeForStyle( - const Symbology::Style* style, - const FeatureList& features, - FeatureSymbolizerContext* context, - osg::Node** out_newNode) + /** The model source implementation for feature_geom */ + class FeatureGeomModelSource : public FeatureModelSource { - // break the features out into separate lists for geometries and text annotations: - FeatureList geomFeatureList, textAnnoList; - - for (FeatureList::const_iterator it = features.begin(); it != features.end(); ++it) - { - Feature* f = osg::clone((*it).get(),osg::CopyOp::DEEP_COPY_ALL); - if ( dynamic_cast(f) ) - textAnnoList.push_back( f ); - else - geomFeatureList.push_back( f ); - } - - // a single group to hold the results: - osg::Group* root = new osg::Group; - - // compile the geometry features: - if ( geomFeatureList.size() > 0 ) + public: + FeatureGeomModelSource( const ModelSourceOptions& options ) + : FeatureModelSource( options ), + _options( options ) { - osg::Node* node = compileGeometries( geomFeatureList, style ); - if ( node ) root->addChild( node ); + //nop } - // compile the text annotation features: - if ( textAnnoList.size() > 0 ) - { - osg::Node* node = compileTextAnnotations( textAnnoList, style ); - if ( node ) root->addChild( node ); - } - - // Apply an LOD if required: - if ( _options.minRange().isSet() || _options.maxRange().isSet() ) - { - osg::LOD* lod = new osg::LOD(); - lod->addChild( root, _options.minRange().value(), _options.maxRange().value() ); - root = lod; - } + const FeatureGeomModelOptions& getOptions() const { return _options; } - // set the output node if necessary: - if ( out_newNode ) - *out_newNode = root; - - return root; - } - - osg::Node* - compileGeometries( FeatureList& features, const Style* style ) - { - // A processing context to use with the filters: - FilterContext cx; - cx.profile() = _modelSource->getFeatureSource()->getFeatureProfile(); - - // Transform them into the map's SRS: - TransformFilter xform( _modelSource->getMap()->getProfile()->getSRS() ); - xform.setMakeGeocentric( _modelSource->getMap()->isGeocentric() ); - xform.setLocalizeCoordinates( true ); - - // Apply the height offset if necessary: - if ( _options.heightOffset().isSet() ) - xform.setHeightOffset( _options.heightOffset().value() ); - - cx = xform.push( features, cx ); - - // Build geometry: - BuildGeometryFilter build; - - // apply a type change: - if ( _options.geometryTypeOverride().isSet() ) - build.geomTypeOverride() = *_options.geometryTypeOverride(); - - // subdivide geocentric geometry as required: - if ( _options.maxGranularity().isSet() ) - build.maxGranularity() = *_options.maxGranularity(); - - osg::ref_ptr result; - build.setStyle( style ); - cx = build.push( features, result, cx ); - - // Localize it. - if ( cx.hasReferenceFrame() ) - { - osg::MatrixTransform* delocalizer = new osg::MatrixTransform( cx.inverseReferenceFrame() ); - delocalizer->addChild( result ); - result = delocalizer; - } - - return result.release(); - } - - osg::Node* - compileTextAnnotations( FeatureList& features, const Style* style ) - { - // A processing context to use with the filters: - FilterContext contextFilter; - contextFilter.profile() = _modelSource->getFeatureSource()->getFeatureProfile(); - - // Transform them into the map's SRS: - TransformFilter xform( _modelSource->getMap()->getProfile()->getSRS() ); - xform.setMakeGeocentric( _modelSource->getMap()->isGeocentric() ); - xform.setLocalizeCoordinates( true ); - - // Apply the height offset if necessary: - xform.setHeightOffset( _options.heightOffset().value() ); - contextFilter = xform.push( features, contextFilter ); - - osg::ref_ptr textSymbol = style->getSymbol(); - //Use a default symbol if we have no text symbol - if (! textSymbol) + //override + void initialize( const std::string& referenceURI, const osgEarth::Map* map ) { - TextSymbol* ts = new TextSymbol(); - ts->rotateToScreen() = true; - textSymbol = ts; + FeatureModelSource::initialize( referenceURI, map ); } - // build the text. - BuildTextOperator textOperator; - osg::Node* result = textOperator( features, textSymbol.get(), contextFilter ); - - // install the localization transform if necessary. - if ( contextFilter.hasReferenceFrame() ) + //override + FeatureNodeFactory* createFeatureNodeFactory() { - osg::MatrixTransform* delocalizer = new osg::MatrixTransform( contextFilter.inverseReferenceFrame() ); - delocalizer->addChild( result ); - result = delocalizer; + return new GeomFeatureNodeFactory( _options.compilerOptions() ); } - return result; - } -}; - -//------------------------------------------------------------------------ - -/** The model source implementation for feature_geom */ -class FeatureGeomModelSource : public FeatureModelSource -{ -public: - FeatureGeomModelSource( const ModelSourceOptions& options ) - : FeatureModelSource( options ), _options( options ) - { - //nop - } - - const FeatureGeomModelOptions& getOptions() const { return _options; } - - //override - void initialize( const std::string& referenceURI, const osgEarth::Map* map ) - { - FeatureModelSource::initialize( referenceURI, map ); - } - - osg::Node* createNode( ProgressCallback* progress ) - { - if ( _features.valid() && _features->getFeatureProfile() ) - { - //OE_NOTICE << _options.getConfig().toString() << std::endl; - return new FeatureSymbolizerGraph( new FactoryGeomSymbolizer(this, _options) ); - } - else - { - return 0L; - } - } - -private: - const FeatureGeomModelOptions _options; -}; + private: + const FeatureGeomModelOptions _options; + }; +} //------------------------------------------------------------------------ /** The plugin factory object */ -class FeatureGeomModelSourceFactory : public ModelSourceDriver +class FeatureGeomModelSourceDriver : public ModelSourceDriver { public: - FeatureGeomModelSourceFactory() + FeatureGeomModelSourceDriver() { supportsExtension( "osgearth_model_feature_geom", "osgEarth feature geom plugin" ); } @@ -263,4 +94,4 @@ } }; -REGISTER_OSGPLUGIN(osgearth_model_feature_geom, FeatureGeomModelSourceFactory) +REGISTER_OSGPLUGIN(osgearth_model_feature_geom, FeatureGeomModelSourceDriver) diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/model_feature_label/FeatureLabelModelOptions osgearth-2.1.1+dfsg/src/osgEarthDrivers/model_feature_label/FeatureLabelModelOptions --- osgearth-2.0+dfsg/src/osgEarthDrivers/model_feature_label/FeatureLabelModelOptions 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/model_feature_label/FeatureLabelModelOptions 2011-11-04 19:44:43.000000000 +0000 @@ -36,11 +36,14 @@ optional& heightOffset() { return _heightOffset; } const optional& heightOffset() const { return _heightOffset; } + optional& hideClutter() { return _hideClutter; } + const optional& hideClutter() const { return _hideClutter; } + public: FeatureLabelModelOptions( const ConfigOptions& opt =ConfigOptions() ) : FeatureModelSourceOptions( opt ), _heightOffset( 0.0 ) { - setDriver( "feature_geom" ); + setDriver( "feature_label" ); fromConfig( _conf ); } @@ -49,6 +52,7 @@ Config conf = FeatureModelSourceOptions::getConfig(); conf.updateIfSet( "url", _url ); conf.updateIfSet( "height_offset", _heightOffset ); + conf.updateIfSet( "hide_clutter", _hideClutter ); return conf; } @@ -62,10 +66,12 @@ void fromConfig( const Config& conf ) { conf.getIfSet( "url", _url ); conf.getIfSet( "height_offset", _heightOffset ); + conf.getIfSet( "hide_clutter", _hideClutter ); } optional _url; optional _heightOffset; + optional _hideClutter; }; } } // namespace osgEarth::Drivers diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/model_feature_label/FeatureLabelModelSource.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/model_feature_label/FeatureLabelModelSource.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/model_feature_label/FeatureLabelModelSource.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/model_feature_label/FeatureLabelModelSource.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -28,12 +27,7 @@ #include #include #include -#include -#include #include -#include -#include -#include #include #include "FeatureLabelModelOptions" @@ -42,120 +36,109 @@ using namespace osgEarth::Features; using namespace osgEarth::Symbology; using namespace osgEarth::Drivers; -using namespace OpenThreads; -#define PROP_HEIGHT_OFFSET "height_offset" - - -class FactoryLabelSymbolizer : public SymbolizerFactory +namespace { -protected: - osg::ref_ptr _model; - const FeatureLabelModelOptions _options; - -public: - FactoryLabelSymbolizer(FeatureModelSource* model, const FeatureLabelModelOptions& options) - : _model(model), _options(options) { } - - FeatureModelSource* getFeatureModelSource() { return _model.get(); } - //override - - virtual osg::Node* createNodeForStyle( - const Symbology::Style* style, - const FeatureList& features, - FeatureSymbolizerContext* context, - osg::Node** out_newNode) + class LabelNodeFactory : public FeatureNodeFactory { - // A processing context to use with the filters: - FilterContext contextFilter; - contextFilter.profile() = _model->getFeatureSource()->getFeatureProfile(); - - // Transform them into the map's SRS: - TransformFilter xform( _model->getMap()->getProfile()->getSRS() ); - xform.setMakeGeocentric( _model->getMap()->isGeocentric() ); - xform.setLocalizeCoordinates( true ); - - //const FeatureLabelModelOptions* options = dynamic_cast( - // context->getModelSource()->getFeatureModelOptions()); - - FeatureList featureList; - for (FeatureList::const_iterator it = features.begin(); it != features.end(); ++it) - featureList.push_back(osg::clone((*it).get(),osg::CopyOp::DEEP_COPY_ALL)); - - xform.setHeightOffset( _options.heightOffset().value() ); - contextFilter = xform.push( featureList, contextFilter ); - - //Make some labels - osg::ref_ptr textSymbol = style->getSymbol(); - //Use a default symbol if we have no text symbol - if (!textSymbol) textSymbol = new TextSymbol(); - osg::Node* labels = NULL; - if (textSymbol.valid()) - { - BuildTextOperator textOperator; - labels = textOperator(featureList, textSymbol.get(), contextFilter); - } - - osg::Node* result = labels; + protected: + const FeatureLabelModelOptions _options; - // If the context specifies a reference frame, apply it to the resulting model. - // Q: should this be here, or should the reference frame matrix be passed to the Symbolizer? - // ...probably the latter. - if ( contextFilter.hasReferenceFrame() ) + public: + LabelNodeFactory(const FeatureLabelModelOptions& options) + : _options(options) { } + + //override + bool createOrUpdateNode( + FeatureCursor* cursor, + const Style& style, + const FilterContext& context, + osg::ref_ptr& node ) { - osg::MatrixTransform* delocalizer = new osg::MatrixTransform( - contextFilter.inverseReferenceFrame() ); - delocalizer->addChild( labels ); - result = delocalizer; - } + const MapInfo& mi = context.getSession()->getMapInfo(); - // Apply an LOD if required: - if ( _options.minRange().isSet() || _options.maxRange().isSet() ) - { - osg::LOD* lod = new osg::LOD(); - lod->addChild( result, _options.minRange().value(), _options.maxRange().value() ); - result = lod; - } + // A processing context to use with the filters: + FilterContext cx = context; - // set the output node if necessary: - if ( out_newNode ) - *out_newNode = result; + // Make a working copy of the features: + FeatureList featureList; + cursor->fill( featureList ); + //for (FeatureList::const_iterator it = features.begin(); it != features.end(); ++it) + // featureList.push_back(osg::clone((*it).get(),osg::CopyOp::DEEP_COPY_ALL)); + + // Transform them into the map's SRS: + TransformFilter xform( mi.getProfile()->getSRS(), mi.isGeocentric() ); + xform.setLocalizeCoordinates( true ); + xform.setMatrix( osg::Matrixd::translate( 0, 0, *_options.heightOffset() ) ); + //xform.setHeightOffset( *_options.heightOffset() ); + cx = xform.push( featureList, cx ); + + // Make some labels + osg::ref_ptr textSymbol = style.getSymbol(); + //Use a default symbol if we have no text symbol + if (!textSymbol) textSymbol = new TextSymbol(); + osg::Node* labels = NULL; + if (textSymbol.valid()) + { + BuildTextOperator textOperator; + labels = textOperator(featureList, textSymbol.get(), cx); + } + + osg::Node* result = labels; + + // If the context specifies a reference frame, apply it to the resulting model. + if ( cx.hasReferenceFrame() ) + { + osg::MatrixTransform* delocalizer = new osg::MatrixTransform( cx.inverseReferenceFrame() ); + delocalizer->addChild( labels ); + result = delocalizer; + } + + // Apply an LOD if required: + if ( _options.minRange().isSet() || _options.maxRange().isSet() ) + { + osg::LOD* lod = new osg::LOD(); + lod->addChild( result, *_options.minRange(), *_options.maxRange() ); + result = lod; + } - return result; - } -}; + node = result; + return true; + } + }; -//------------------------------------------------------------------------ + //------------------------------------------------------------------------ -class FeatureLabelModelSource : public FeatureModelSource -{ -public: - FeatureLabelModelSource( const ModelSourceOptions& options ) : FeatureModelSource( options ), - _options( options ) { } - - //override - void initialize( const std::string& referenceURI, const osgEarth::Map* map ) + class FeatureLabelModelSource : public FeatureModelSource { - FeatureModelSource::initialize( referenceURI, map ); - } + public: + FeatureLabelModelSource( const ModelSourceOptions& options ) : FeatureModelSource( options ), + _options( options ) { } - osg::Node* createNode( ProgressCallback* progress ) - { - return new FeatureSymbolizerGraph(new FactoryLabelSymbolizer(this, _options)); - } + //override + void initialize( const std::string& referenceURI, const osgEarth::Map* map ) + { + FeatureModelSource::initialize( referenceURI, map ); + } + //override + FeatureNodeFactory* createFeatureNodeFactory() + { + return new LabelNodeFactory( _options ); + } -protected: - int _sourceId; - ModelSourceOptions _options; -}; + protected: + int _sourceId; + ModelSourceOptions _options; + }; +} //------------------------------------------------------------------------ -class FeatureLabelModelSourceFactory : public ModelSourceDriver +class FeatureLabelModelSourceDriver : public ModelSourceDriver { public: - FeatureLabelModelSourceFactory() + FeatureLabelModelSourceDriver() { supportsExtension( "osgearth_model_feature_label", "osgEarth feature label plugin" ); } @@ -174,4 +157,4 @@ } }; -REGISTER_OSGPLUGIN(osgearth_model_feature_label, FeatureLabelModelSourceFactory) +REGISTER_OSGPLUGIN(osgearth_model_feature_label, FeatureLabelModelSourceDriver) diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/model_feature_stencil/FeatureStencilModelSource.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/model_feature_stencil/FeatureStencilModelSource.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/model_feature_stencil/FeatureStencilModelSource.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/model_feature_stencil/FeatureStencilModelSource.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -26,12 +26,9 @@ #include #include #include -#include #include #include -#include #include -#include #include #include #include @@ -55,549 +52,547 @@ #define RENDER_BIN_START 100 #define MAX_NUM_STYLES 100 - #define OFF_PROTECTED osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED -/** Creates a full-screen quad to fill in the colors on the stencil volume. */ -static -osg::Node* createColorNode( const osg::Vec4f& color ) +namespace { - // make a full screen quad: - osg::Geometry* quad = new osg::Geometry(); - osg::Vec3Array* verts = new osg::Vec3Array(4); - (*verts)[0].set( 0, 1, 0 ); - (*verts)[1].set( 0, 0, 0 ); - (*verts)[2].set( 1, 0, 0 ); - (*verts)[3].set( 1, 1, 0 ); - quad->setVertexArray( verts ); - quad->addPrimitiveSet( new osg::DrawArrays( osg::PrimitiveSet::QUADS, 0, 4 ) ); - osg::Vec4Array* colors = new osg::Vec4Array(1); - (*colors)[0] = color; - quad->setColorArray( colors ); - quad->setColorBinding( osg::Geometry::BIND_OVERALL ); - osg::Geode* quad_geode = new osg::Geode(); - quad_geode->addDrawable( quad ); - - osg::StateSet* quad_ss = quad->getOrCreateStateSet(); - quad_ss->setMode( GL_CULL_FACE, OFF_PROTECTED ); - quad_ss->setMode( GL_DEPTH_TEST, OFF_PROTECTED ); - quad_ss->setMode( GL_LIGHTING, OFF_PROTECTED ); - osg::MatrixTransform* abs = new osg::MatrixTransform(); - abs->setReferenceFrame( osg::Transform::ABSOLUTE_RF ); - abs->setMatrix( osg::Matrix::identity() ); - abs->addChild( quad_geode ); - - osg::Projection* proj = new osg::Projection(); - proj->setMatrix( osg::Matrix::ortho(0, 1, 0, 1, 0, -1) ); - proj->addChild( abs ); - - proj->getOrCreateStateSet()->setMode( GL_BLEND, 1 ); - - return proj; -} - - - + /** Creates a full-screen quad to fill in the colors on the stencil volume. */ + osg::Node* createColorNode( const osg::Vec4f& color ) + { + // make a full screen quad: + osg::Geometry* quad = new osg::Geometry(); + osg::Vec3Array* verts = new osg::Vec3Array(4); + (*verts)[0].set( 0, 1, 0 ); + (*verts)[1].set( 0, 0, 0 ); + (*verts)[2].set( 1, 0, 0 ); + (*verts)[3].set( 1, 1, 0 ); + quad->setVertexArray( verts ); + quad->addPrimitiveSet( new osg::DrawArrays( osg::PrimitiveSet::QUADS, 0, 4 ) ); + osg::Vec4Array* colors = new osg::Vec4Array(1); + (*colors)[0] = color; + quad->setColorArray( colors ); + quad->setColorBinding( osg::Geometry::BIND_OVERALL ); + osg::Geode* quad_geode = new osg::Geode(); + quad_geode->addDrawable( quad ); + + osg::StateSet* quad_ss = quad->getOrCreateStateSet(); + quad_ss->setMode( GL_CULL_FACE, OFF_PROTECTED ); + quad_ss->setMode( GL_DEPTH_TEST, OFF_PROTECTED ); + quad_ss->setMode( GL_LIGHTING, OFF_PROTECTED ); + osg::MatrixTransform* abs = new osg::MatrixTransform(); + abs->setReferenceFrame( osg::Transform::ABSOLUTE_RF ); + abs->setMatrix( osg::Matrix::identity() ); + abs->addChild( quad_geode ); + + osg::Projection* proj = new osg::Projection(); + proj->setMatrix( osg::Matrix::ortho(0, 1, 0, 1, 0, -1) ); + proj->addChild( abs ); + + proj->getOrCreateStateSet()->setMode( GL_BLEND, 1 ); + + return proj; + } + + void tessellate( osg::Geometry* geom ) + { + osgUtil::Tessellator tess; + tess.setTessellationType( osgUtil::Tessellator::TESS_TYPE_GEOMETRY ); + tess.setWindingType( osgUtil::Tessellator::TESS_WINDING_ODD ); + // tess.setWindingType( osgUtil::Tessellator::TESS_WINDING_POSITIVE ); + tess.retessellatePolygons( *geom ); + } + + osg::Geode* + createVolume(osgEarth::Symbology::Geometry* geom, + double offset, + double height, + const FilterContext& context ) + { + if ( !geom ) return 0L; + + int numRings = 0; -static -void tessellate( osg::Geometry* geom ) -{ - osgUtil::Tessellator tess; - tess.setTessellationType( osgUtil::Tessellator::TESS_TYPE_GEOMETRY ); - tess.setWindingType( osgUtil::Tessellator::TESS_WINDING_ODD ); -// tess.setWindingType( osgUtil::Tessellator::TESS_WINDING_POSITIVE ); - tess.retessellatePolygons( *geom ); -} + // start by offsetting the input data and counting the number of rings + { + osgEarth::Symbology::GeometryIterator i( geom ); + while( i.hasMore() ) + { + osgEarth::Symbology::Geometry* part = i.next(); -osg::Geode* -createVolume(osgEarth::Symbology::Geometry* geom, - double offset, - double height, - const FilterContext& context ) -{ - if ( !geom ) return 0L; + if (offset != 0.0) + { + for( osg::Vec3dArray::iterator j = part->begin(); j != part->end(); j++ ) + { + if ( context.isGeocentric() ) + { + osg::Vec3d world = context.toWorld( *j ); + // TODO: get the proper up vector; this is spherical.. or does it really matter for + // stencil volumes? + osg::Vec3d offset_vec = world; + offset_vec.normalize(); + *j = context.toLocal( world + offset_vec * offset ); //(*j) += offset_vec * offset; + } + else + { + (*j).z() += offset; + } + } + } - int numRings = 0; + // in the meantime, count the # of closed geoms. We will need to know this in + // order to pre-allocate the proper # of verts. + if ( part->getType() == osgEarth::Symbology::Geometry::TYPE_POLYGON || part->getType() == osgEarth::Symbology::Geometry::TYPE_RING ) + { + numRings++; + } + } + } - // start by offsetting the input data and counting the number of rings - { + // now, go thru and remove any coplanar segments from the geometry. The tesselator will + // not work include a vert connecting two colinear segments in the tesselation, and this + // will break the stenciling logic. + #define PARALLEL_EPSILON 0.01 osgEarth::Symbology::GeometryIterator i( geom ); - i.traverseMultiGeometry() = true; - i.traversePolygonHoles() = true; while( i.hasMore() ) { osgEarth::Symbology::Geometry* part = i.next(); - - if (offset != 0.0) + if ( part->size() >= 3 ) { - for( osg::Vec3dArray::iterator j = part->begin(); j != part->end(); j++ ) + osg::Vec3d prevVec = part->front() - part->back(); + prevVec.normalize(); + + for( osg::Vec3dArray::iterator j = part->begin(); part->size() >= 3 && j != part->end(); ) { - if ( context.isGeocentric() ) + osg::Vec3d& p0 = *j; + osg::Vec3d& p1 = j+1 != part->end() ? *(j+1) : part->front(); + osg::Vec3d vec = p1-p0; vec.normalize(); + + // if the vectors are essentially parallel, remove the extraneous vertex. + if ( (prevVec ^ vec).length() < PARALLEL_EPSILON ) { - osg::Vec3d world = context.toWorld( *j ); - // TODO: get the proper up vector; this is spherical.. or does it really matter for - // stencil volumes? - osg::Vec3d offset_vec = world; - offset_vec.normalize(); - *j = context.toLocal( world + offset_vec * offset ); //(*j) += offset_vec * offset; + j = part->erase( j ); + //OE_NOTICE << "removed colinear segment" << std::endl; } else { - (*j).z() += offset; + ++j; + prevVec = vec; } } } - - // in the meantime, count the # of closed geoms. We will need to know this in - // order to pre-allocate the proper # of verts. - if ( part->getType() == osgEarth::Symbology::Geometry::TYPE_POLYGON || part->getType() == osgEarth::Symbology::Geometry::TYPE_RING ) - { - numRings++; - } } - } - // now, go thru and remove any coplanar segments from the geometry. The tesselator will - // not work include a vert connecting two colinear segments in the tesselation, and this - // will break the stenciling logic. -#define PARALLEL_EPSILON 0.01 - osgEarth::Symbology::GeometryIterator i( geom ); - i.traverseMultiGeometry() = true; - i.traversePolygonHoles() = true; - while( i.hasMore() ) - { - osgEarth::Symbology::Geometry* part = i.next(); - if ( part->size() >= 3 ) - { - osg::Vec3d prevVec = part->front() - part->back(); - prevVec.normalize(); - for( osg::Vec3dArray::iterator j = part->begin(); part->size() >= 3 && j != part->end(); ) - { - osg::Vec3d& p0 = *j; - osg::Vec3d& p1 = j+1 != part->end() ? *(j+1) : part->front(); - osg::Vec3d vec = p1-p0; vec.normalize(); + bool made_geom = true; + const SpatialReference* srs = context.profile()->getSRS(); - // if the vectors are essentially parallel, remove the extraneous vertex. - if ( (prevVec ^ vec).length() < PARALLEL_EPSILON ) - { - j = part->erase( j ); - //OE_NOTICE << "removed colinear segment" << std::endl; - } - else - { - ++j; - prevVec = vec; - } - } - } - } + // total up all the points so we can pre-allocate the vertex arrays. + int num_cap_verts = geom->getTotalPointCount(); + int num_wall_verts = 2 * (num_cap_verts + numRings); // add in numRings b/c we need to close each wall + osg::Geometry* walls = new osg::Geometry(); + osg::Vec3Array* verts = new osg::Vec3Array( num_wall_verts ); + walls->setVertexArray( verts ); - bool made_geom = true; - const SpatialReference* srs = context.profile()->getSRS(); + osg::Geometry* top_cap = new osg::Geometry(); + osg::Vec3Array* top_verts = new osg::Vec3Array( num_cap_verts ); + top_cap->setVertexArray( top_verts ); - // total up all the points so we can pre-allocate the vertex arrays. - int num_cap_verts = geom->getTotalPointCount(); - int num_wall_verts = 2 * (num_cap_verts + numRings); // add in numRings b/c we need to close each wall - - osg::Geometry* walls = new osg::Geometry(); - osg::Vec3Array* verts = new osg::Vec3Array( num_wall_verts ); - walls->setVertexArray( verts ); - - osg::Geometry* top_cap = new osg::Geometry(); - osg::Vec3Array* top_verts = new osg::Vec3Array( num_cap_verts ); - top_cap->setVertexArray( top_verts ); - - osg::Geometry* bottom_cap = new osg::Geometry(); - osg::Vec3Array* bottom_verts = new osg::Vec3Array( num_cap_verts ); - bottom_cap->setVertexArray( bottom_verts ); - - int wall_vert_ptr = 0; - int top_vert_ptr = 0; - int bottom_vert_ptr = 0; - - //double target_len = height; - - // now generate the extruded geometry. - osgEarth::Symbology::GeometryIterator k( geom ); - while( k.hasMore() ) - { - osgEarth::Symbology::Geometry* part = k.next(); + osg::Geometry* bottom_cap = new osg::Geometry(); + osg::Vec3Array* bottom_verts = new osg::Vec3Array( num_cap_verts ); + bottom_cap->setVertexArray( bottom_verts ); - unsigned int wall_part_ptr = wall_vert_ptr; - unsigned int top_part_ptr = top_vert_ptr; - unsigned int bottom_part_ptr = bottom_vert_ptr; - double part_len = 0.0; + int wall_vert_ptr = 0; + int top_vert_ptr = 0; + int bottom_vert_ptr = 0; - GLenum prim_type = part->getType() == osgEarth::Symbology::Geometry::TYPE_POINTSET ? GL_LINES : GL_TRIANGLE_STRIP; + //double target_len = height; - for( osg::Vec3dArray::const_iterator m = part->begin(); m != part->end(); ++m ) + // now generate the extruded geometry. + osgEarth::Symbology::GeometryIterator k( geom ); + while( k.hasMore() ) { - osg::Vec3d extrude_vec; + osgEarth::Symbology::Geometry* part = k.next(); + + unsigned int wall_part_ptr = wall_vert_ptr; + unsigned int top_part_ptr = top_vert_ptr; + unsigned int bottom_part_ptr = bottom_vert_ptr; + double part_len = 0.0; - if ( srs ) + GLenum prim_type = part->getType() == osgEarth::Symbology::Geometry::TYPE_POINTSET ? GL_LINES : GL_TRIANGLE_STRIP; + + for( osg::Vec3dArray::const_iterator m = part->begin(); m != part->end(); ++m ) { - osg::Vec3d m_world = context.toWorld( *m ); //*m * context.inverseReferenceFrame(); - if ( context.isGeocentric() ) + osg::Vec3d extrude_vec; + + if ( srs ) { - osg::Vec3d p_vec = m_world; // todo: not exactly right; spherical + osg::Vec3d m_world = context.toWorld( *m ); //*m * context.inverseReferenceFrame(); + if ( context.isGeocentric() ) + { + osg::Vec3d p_vec = m_world; // todo: not exactly right; spherical - osg::Vec3d unit_vec = p_vec; - unit_vec.normalize(); - p_vec = p_vec + unit_vec*height; + osg::Vec3d unit_vec = p_vec; + unit_vec.normalize(); + p_vec = p_vec + unit_vec*height; - extrude_vec = context.toLocal( p_vec ); //p_vec * context.referenceFrame(); + extrude_vec = context.toLocal( p_vec ); //p_vec * context.referenceFrame(); + } + else + { + extrude_vec.set( m_world.x(), m_world.y(), height ); + extrude_vec = context.toLocal( extrude_vec ); //extrude_vec * context.referenceFrame(); + } } else { - extrude_vec.set( m_world.x(), m_world.y(), height ); - extrude_vec = context.toLocal( extrude_vec ); //extrude_vec * context.referenceFrame(); + extrude_vec.set( m->x(), m->y(), height ); } - } - else - { - extrude_vec.set( m->x(), m->y(), height ); - } - - (*top_verts)[top_vert_ptr++] = extrude_vec; - (*bottom_verts)[bottom_vert_ptr++] = *m; - - part_len += wall_vert_ptr > (int)wall_part_ptr? - (extrude_vec - (*verts)[wall_vert_ptr-2]).length() : - 0.0; - - int p; - - p = wall_vert_ptr++; - (*verts)[p] = extrude_vec; - - p = wall_vert_ptr++; - (*verts)[p] = *m; - } - - // close the wall if it's a ring/poly: - if ( part->getType() == osgEarth::Symbology::Geometry::TYPE_RING || part->getType() == osgEarth::Symbology::Geometry::TYPE_POLYGON ) - { - part_len += wall_vert_ptr > (int)wall_part_ptr? - ((*verts)[wall_part_ptr] - (*verts)[wall_vert_ptr-2]).length() : - 0.0; - int p; + (*top_verts)[top_vert_ptr++] = extrude_vec; + (*bottom_verts)[bottom_vert_ptr++] = *m; + + part_len += wall_vert_ptr > (int)wall_part_ptr? + (extrude_vec - (*verts)[wall_vert_ptr-2]).length() : + 0.0; - p = wall_vert_ptr++; - (*verts)[p] = (*verts)[wall_part_ptr]; + int p; - p = wall_vert_ptr++; - (*verts)[p] = (*verts)[wall_part_ptr+1]; - } + p = wall_vert_ptr++; + (*verts)[p] = extrude_vec; - walls->addPrimitiveSet( new osg::DrawArrays( - prim_type, - wall_part_ptr, wall_vert_ptr - wall_part_ptr ) ); + p = wall_vert_ptr++; + (*verts)[p] = *m; + } - top_cap->addPrimitiveSet( new osg::DrawArrays( - osg::PrimitiveSet::LINE_LOOP, - top_part_ptr, top_vert_ptr - top_part_ptr ) ); + // close the wall if it's a ring/poly: + if ( part->getType() == osgEarth::Symbology::Geometry::TYPE_RING || part->getType() == osgEarth::Symbology::Geometry::TYPE_POLYGON ) + { + part_len += wall_vert_ptr > (int)wall_part_ptr? + ((*verts)[wall_part_ptr] - (*verts)[wall_vert_ptr-2]).length() : + 0.0; - // reverse the bottom verts so the front face is down: - std::reverse( bottom_verts->begin()+bottom_part_ptr, bottom_verts->begin()+bottom_vert_ptr ); + int p; - bottom_cap->addPrimitiveSet( new osg::DrawArrays( - osg::PrimitiveSet::LINE_LOOP, - bottom_part_ptr, bottom_vert_ptr - bottom_part_ptr ) ); - } + p = wall_vert_ptr++; + (*verts)[p] = (*verts)[wall_part_ptr]; - // build solid surfaces for the caps: - tessellate( top_cap ); - tessellate( bottom_cap ); - - osg::Geode* geode = new osg::Geode(); - geode->addDrawable( walls ); - geode->addDrawable( top_cap ); - geode->addDrawable( bottom_cap ); + p = wall_vert_ptr++; + (*verts)[p] = (*verts)[wall_part_ptr+1]; + } - return geode; -} + walls->addPrimitiveSet( new osg::DrawArrays( + prim_type, + wall_part_ptr, wall_vert_ptr - wall_part_ptr ) ); -struct BuildData : public osg::Referenced -{ - BuildData( int renderBinStart ) : _renderBin( renderBinStart ) { } - int _renderBin; + top_cap->addPrimitiveSet( new osg::DrawArrays( + osg::PrimitiveSet::LINE_LOOP, + top_part_ptr, top_vert_ptr - top_part_ptr ) ); - // pairs a style name with a starting render bin. - typedef std::pair > StyleGroup; - std::vector _styleGroups; + // reverse the bottom verts so the front face is down: + std::reverse( bottom_verts->begin()+bottom_part_ptr, bottom_verts->begin()+bottom_vert_ptr ); - bool getStyleNode( const std::string& styleName, osgEarth::Symbology::StencilVolumeNode*& out_svn ) { - for(std::vector::iterator i = _styleGroups.begin(); i != _styleGroups.end(); ++i ) { - if( i->first == styleName ) { - out_svn = i->second.get(); - return true; - } + bottom_cap->addPrimitiveSet( new osg::DrawArrays( + osg::PrimitiveSet::LINE_LOOP, + bottom_part_ptr, bottom_vert_ptr - bottom_part_ptr ) ); } - return false; - } -}; -class StencilVolumeSymbolizerFactory : public SymbolizerFactory -{ -protected: - osg::ref_ptr _model; - const FeatureStencilModelOptions _options; + // build solid surfaces for the caps: + tessellate( top_cap ); + tessellate( bottom_cap ); -public: - StencilVolumeSymbolizerFactory(FeatureModelSource* model, const FeatureStencilModelOptions& options ) - : _model(model), _options(options) { } - - FeatureModelSource* getFeatureModelSource() { return _model.get(); } + osg::Geode* geode = new osg::Geode(); + geode->addDrawable( walls ); + geode->addDrawable( top_cap ); + geode->addDrawable( bottom_cap ); + return geode; + } - virtual osg::Node* createNodeForStyle( - const Symbology::Style* style, - const FeatureList& features, - FeatureSymbolizerContext* symbolizerContext, - osg::Node** out_createdNode) + struct BuildData // : public osg::Referenced { + //BuildData() { } + BuildData( int renderBinStart ) : _renderBin( renderBinStart ) { } - FeatureSymbolizerContext* context = dynamic_cast(symbolizerContext); + typedef std::pair > StyleGroup; + int _renderBin; + Threading::ReadWriteMutex _mutex; + std::vector _styleGroups; // NOTE: DO NOT ACCESS without a mutex! - FeatureList featureList; - for (FeatureList::const_iterator it = features.begin(); it != features.end(); ++it) - featureList.push_back(osg::clone((*it).get(),osg::CopyOp::DEEP_COPY_ALL)); - //const FeatureStencilModelOptions* options = dynamic_cast( - // _model->getFeatureModelOptions()); - - double extrusionDistance = 1; - double densificationThreshold = 1.0; - if ( _options.extrusionDistance().isSet() ) - { - extrusionDistance = _options.extrusionDistance().value(); - } - else + bool getStyleNode( const std::string& styleName, StencilVolumeNode*& out_svn, bool useLock ) { - if ( _model->getMap()->isGeocentric() ) - extrusionDistance = 300000.0; // meters geocentric - else if ( _model->getMap()->getProfile()->getSRS()->isGeographic() ) - extrusionDistance = 5.0; // degrees-as-meters + if ( useLock ) + { + Threading::ScopedReadLock lock( _mutex ); + return getStyleNodeWithoutLocking( styleName, out_svn ); + } else - extrusionDistance = 12000.0; // meters - } - - densificationThreshold = _options.densificationThreshold().value(); - - BuildData* buildData = dynamic_cast(context->getBuildData()); - - // Scan the geometry to see if it includes line data, since that will require - // buffering: - bool hasLines = false; - for( FeatureList::const_iterator i = featureList.begin(); i != featureList.end(); ++i ) - { - Feature* feature = *i; - // later should be a osgEarth::Symbology::Geometry - Geometry* geom = feature->getGeometry(); - if ( geom && geom->getComponentType() == Geometry::TYPE_LINESTRING ) { - hasLines = true; - break; + return getStyleNodeWithoutLocking( styleName, out_svn ); } } - bool isGeocentric = _model->getMap()->isGeocentric(); - - // A processing context to use with the filters: - FilterContext filterContext; - filterContext.profile() = _model->getFeatureSource()->getFeatureProfile(); - - // If the geometry is lines, we need to buffer them before they will work with stenciling - if ( hasLines ) + private: + bool getStyleNodeWithoutLocking( const std::string& styleName, StencilVolumeNode*& out_svn ) { - const osgEarth::Symbology::LineSymbol* line = style->getSymbol(); - if (line) { - BufferFilter buffer; - buffer.distance() = 0.5 * line->stroke()->width().value(); - buffer.capStyle() = line->stroke()->lineCap().value(); - filterContext = buffer.push( featureList, filterContext ); + for(std::vector::iterator i = _styleGroups.begin(); i != _styleGroups.end(); ++i ) + { + if( i->first == styleName ) + { + out_svn = i->second.get(); + return true; + } } + return false; } + }; - // Transform them into the map's SRS, localizing the verts along the way: - TransformFilter xform( _model->getMap()->getProfile()->getSRS() ); - xform.setMakeGeocentric( isGeocentric ); - xform.setLocalizeCoordinates( !isGeocentric ); - filterContext = xform.push( featureList, filterContext ); - - if ( isGeocentric ) + class StencilVolumeNodeFactory : public FeatureNodeFactory + { + protected: + const FeatureStencilModelOptions _options; + int _renderBinStart; + BuildData _buildData; + + public: + StencilVolumeNodeFactory( const FeatureStencilModelOptions& options, int renderBinStart ) + : _options(options), + _buildData( renderBinStart ) + { } + + //override + bool createOrUpdateNode( + FeatureCursor* cursor, + const Style& style, + const FilterContext& context, + osg::ref_ptr& node ) { - // We need to make sure that on a round globe, the points are sampled such that - // long segments follow the curvature of the earth. By the way, if a Buffer was - // applied, that will also remove colinear segment points. Resample the points to - // achieve a usable tesselation. - ResampleFilter resample; - resample.maxLength() = densificationThreshold; - resample.minLength() = 0.0; - resample.perturbationThreshold() = 0.1; - filterContext = resample.push( featureList, filterContext ); - } + const MapInfo& mi = context.getSession()->getMapInfo(); + + // A processing context to use locally + FilterContext cx = context; - // Extrude and cap the geometry in both directions to build a stencil volume: - osg::Group* volumes = 0L; + // make a working copy of the feature data. + FeatureList featureList; + cursor->fill( featureList ); + + //for (FeatureList::const_iterator it = features.begin(); it != features.end(); ++it) + // featureList.push_back(osg::clone((*it).get(),osg::CopyOp::DEEP_COPY_ALL)); + + // establish the extrusion distance for the stencil volumes + double extrusionDistance = 1; + double densificationThreshold = 1.0; + if ( _options.extrusionDistance().isSet() ) + { + extrusionDistance = *_options.extrusionDistance(); + } + else + { + if ( mi.isGeocentric() ) + extrusionDistance = 300000.0; // meters geocentric + else if ( mi.getProfile()->getSRS()->isGeographic() ) + extrusionDistance = 5.0; // degrees-as-meters + else + extrusionDistance = 12000.0; // meters + } - for( FeatureList::iterator i = featureList.begin(); i != featureList.end(); ++i ) - { - Feature* feature = *i; - Geometry* geom = feature->getGeometry(); - osg::Node* volume = createVolume( - geom, - -extrusionDistance, - extrusionDistance * 2.0, - filterContext ); + densificationThreshold = *_options.densificationThreshold(); - if ( volume ) + // Scan the geometry to see if it includes line data, since that will require buffering: + bool hasLines = false; + for( FeatureList::const_iterator i = featureList.begin(); i != featureList.end(); ++i ) { - if ( !volumes ) - volumes = new osg::Group(); - volumes->addChild( volume ); + Feature* feature = (*i).get(); + Geometry* geom = feature->getGeometry(); + if ( geom && + ( geom->getComponentType() == Geometry::TYPE_LINESTRING || + geom->getComponentType() == Geometry::TYPE_RING ) ) + { + hasLines = true; + break; + } } - } - osg::Node* result = 0L; - - if ( volumes ) - { - // Resolve the localizing reference frame if necessary: - if ( filterContext.hasReferenceFrame() ) + // If the geometry is lines, we need to buffer them before they will work with stenciling + if ( hasLines ) { - osg::MatrixTransform* xform = new osg::MatrixTransform( filterContext.inverseReferenceFrame() ); - xform->addChild( volumes ); - volumes = xform; + const LineSymbol* line = style.getSymbol(); + if (line) + { + BufferFilter buffer; + buffer.distance() = 0.5 * line->stroke()->width().value(); + buffer.capStyle() = line->stroke()->lineCap().value(); + cx = buffer.push( featureList, cx ); + } } - // Apply an LOD if required: - if ( _options.minRange().isSet() || _options.maxRange().isSet() ) + // Transform them into the map's SRS, localizing the verts along the way: + TransformFilter xform( mi.getProfile()->getSRS() ); + xform.setMakeGeocentric( mi.isGeocentric() ); + xform.setLocalizeCoordinates( !mi.isGeocentric() ); + cx = xform.push( featureList, cx ); + + if ( mi.isGeocentric() ) + { + // We need to make sure that on a round globe, the points are sampled such that + // long segments follow the curvature of the earth. By the way, if a Buffer was + // applied, that will also remove colinear segment points. Resample the points to + // achieve a usable tesselation. + ResampleFilter resample; + resample.maxLength() = densificationThreshold; + resample.minLength() = 0.0; + resample.perturbationThreshold() = 0.1; + cx = resample.push( featureList, cx ); + } + + // Extrude and cap the geometry in both directions to build a stencil volume: + osg::Group* volumes = 0L; + + for( FeatureList::iterator i = featureList.begin(); i != featureList.end(); ++i ) + { + Feature* feature = (*i).get(); + Geometry* geom = feature->getGeometry(); + osg::Node* volume = createVolume( geom, -extrusionDistance, extrusionDistance * 2.0, cx ); + + if ( volume ) + { + if ( !volumes ) + volumes = new osg::Group(); + volumes->addChild( volume ); + } + } + + if ( volumes ) { - osg::LOD* lod = new osg::LOD(); - lod->addChild( volumes, _options.minRange().value(), _options.maxRange().value() ); - volumes = lod; + // Resolve the localizing reference frame if necessary: + if ( cx.hasReferenceFrame() ) + { + osg::MatrixTransform* xform = new osg::MatrixTransform( cx.inverseReferenceFrame() ); + xform->addChild( volumes ); + volumes = xform; + } + + // Apply an LOD if required: + if ( _options.minRange().isSet() || _options.maxRange().isSet() ) + { + osg::LOD* lod = new osg::LOD(); + lod->addChild( volumes, _options.minRange().value(), _options.maxRange().value() ); + volumes = lod; + } + + // Add the volumes to the appropriate style group. + StencilVolumeNode* styleNode = dynamic_cast( getOrCreateStyleGroup( style, cx.getSession() ) ); + styleNode->addVolumes( volumes ); } - osgEarth::Symbology::StencilVolumeNode* styleNode = 0L; - bool styleNodeAlreadyCreated = buildData->getStyleNode(style->getName(), styleNode); + node = 0L; // always return null, since we added our geom to the style group. + return volumes != 0L; + } + + //override + osg::Group* getOrCreateStyleGroup( const Style& style, Session* session ) + { if ( _options.showVolumes() == true ) { - result = volumes; + return new osg::Group(); } else { - if ( !styleNodeAlreadyCreated ) + StencilVolumeNode* styleNode = 0L; + if ( !_buildData.getStyleNode(style.getName(), styleNode, true) ) { - if ( _options.mask() == true ) - OE_INFO << LC << "Creating MASK LAYER for feature group" << std::endl; - else - OE_INFO << LC << "Creating new style group for '" << style->getName() << "'" << std::endl; + // did not find; write-lock it and try again (double-check pattern) + Threading::ScopedWriteLock exclusiveLock( _buildData._mutex ); - styleNode = new osgEarth::Symbology::StencilVolumeNode( _options.mask().value(), _options.inverted().value() ); - if ( _options.mask() == false ) + if ( !_buildData.getStyleNode(style.getName(), styleNode, false) ) { - osg::Vec4f maskColor = osg::Vec4(1,1,0,1); - if (hasLines && style->getSymbol()) { - const LineSymbol* line = style->getSymbol(); - maskColor = line->stroke()->color(); - } else { - const PolygonSymbol* poly = style->getSymbol(); - if (poly) - maskColor = poly->fill()->color(); + OE_INFO << LC << "Create style group \"" << style.getName() << "\"" << std::endl; + + styleNode = new StencilVolumeNode( *_options.mask(), *_options.inverted() ); + + if ( _options.mask() == false ) + { + osg::Vec4f maskColor = osg::Vec4(1,1,0,1); + + if (/*hasLines &&*/ style.getSymbol()) + { + const LineSymbol* line = style.getSymbol(); + maskColor = line->stroke()->color(); + } + else + { + const PolygonSymbol* poly = style.getSymbol(); + if (poly) + maskColor = poly->fill()->color(); + } + styleNode->addChild( createColorNode(maskColor) ); + + osg::StateSet* ss = styleNode->getOrCreateStateSet(); + + ss->setMode( GL_LIGHTING, _options.enableLighting() == true? + osg::StateAttribute::ON | osg::StateAttribute::PROTECTED : + osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED ); } - styleNode->addChild( createColorNode(maskColor) ); + + _buildData._renderBin = styleNode->setBaseRenderBin( _buildData._renderBin ); + _buildData._styleGroups.push_back( BuildData::StyleGroup(style.getName(), styleNode) ); } - buildData->_renderBin = styleNode->setBaseRenderBin( buildData->_renderBin ); - buildData->_styleGroups.push_back( BuildData::StyleGroup( style->getName(), styleNode ) ); } - - styleNode->addVolumes( volumes ); - result = styleNodeAlreadyCreated ? 0L : styleNode; - if ( out_createdNode ) *out_createdNode = volumes; + + return styleNode; } } + }; - // apply explicit lighting if necessary: - if ( result && _options.enableLighting().isSet() ) - { - osg::StateSet* ss = result->getOrCreateStateSet(); - ss->setMode( GL_LIGHTING, _options.enableLighting() == true? - osg::StateAttribute::ON | osg::StateAttribute::PROTECTED : - osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED ); - } - return result; - } -}; - -class FeatureStencilModelSource : public FeatureModelSource -{ -public: - FeatureStencilModelSource( const ModelSourceOptions& options, int renderBinStart ) : - FeatureModelSource( options ), - _options( options ), - _renderBinStart( renderBinStart ) + class FeatureStencilModelSource : public FeatureModelSource { - // make sure we have stencil bits. Note, this only works before - // a viewer gets created. You may need to allocate stencil bits - // yourself if you make this object after realizing a viewer. - if ( osg::DisplaySettings::instance()->getMinimumNumStencilBits() < 8 ) + public: + FeatureStencilModelSource( const ModelSourceOptions& options, int renderBinStart ) : + FeatureModelSource( options ), + _options( options ), + _renderBinStart( renderBinStart ) { - osg::DisplaySettings::instance()->setMinimumNumStencilBits( 8 ); + // make sure we have stencil bits. Note, this only works before + // a viewer gets created. You may need to allocate stencil bits + // yourself if you make this object after realizing a viewer. + if ( osg::DisplaySettings::instance()->getMinimumNumStencilBits() < 8 ) + { + osg::DisplaySettings::instance()->setMinimumNumStencilBits( 8 ); + } + } + + //override + virtual const FeatureModelSourceOptions& getFeatureModelOptions() const + { + return _options; } - } - - //override - virtual const FeatureModelSourceOptions& getFeatureModelOptions() const - { - return _options; - } - - //override - void initialize( const std::string& referenceURI, const Map* map ) - { - FeatureModelSource::initialize( referenceURI, map ); - _map = map; - } - - //override - osg::Referenced* createBuildData() - { - return new BuildData( _renderBinStart ); - } - - //override - osg::Node* createNode( ProgressCallback* progress ) - { - if ( !_features.valid() || !_features->getFeatureProfile() ) - return 0L; - - FeatureSymbolizerGraph* graph = new FeatureSymbolizerGraph( new StencilVolumeSymbolizerFactory(this, _options) ); - // for Mask models, we need to immediately traverse the new graph and generate the mask - if ( _options.mask() == true ) + //override + void initialize( const std::string& referenceURI, const Map* map ) { - graph->compile(); + FeatureModelSource::initialize( referenceURI, map ); } - return graph; - } + //override + FeatureNodeFactory* createFeatureNodeFactory() + { + return new StencilVolumeNodeFactory( _options, _renderBinStart ); + } -protected: - int _renderBinStart; - const FeatureStencilModelOptions _options; -}; + protected: + int _renderBinStart; + const FeatureStencilModelOptions _options; + }; +} -class FeatureStencilModelSourceFactory : public ModelSourceDriver +class FeatureStencilModelSourceDriver : public ModelSourceDriver { public: - FeatureStencilModelSourceFactory() : - _renderBinStart( RENDER_BIN_START ) + FeatureStencilModelSourceDriver() : + _renderBinStart( RENDER_BIN_START ) { supportsExtension( "osgearth_model_feature_stencil", "osgEarth feature stencil plugin" ); } @@ -610,8 +605,13 @@ FeatureStencilModelSource* create( const Options* options ) { ScopedLock lock( _createMutex ); - FeatureStencilModelSource* obj = new FeatureStencilModelSource( getModelSourceOptions(options), _renderBinStart ); + + FeatureStencilModelSource* obj = new FeatureStencilModelSource( + getModelSourceOptions(options), + _renderBinStart ); + _renderBinStart += MAX_NUM_STYLES*4; + return obj; } @@ -620,7 +620,7 @@ if ( !acceptsExtension(osgDB::getLowerCaseFileExtension( file_name ))) return ReadResult::FILE_NOT_HANDLED; - FeatureStencilModelSourceFactory* nonConstThis = const_cast(this); + FeatureStencilModelSourceDriver* nonConstThis = const_cast(this); return nonConstThis->create( options ); } @@ -629,4 +629,4 @@ int _renderBinStart; }; -REGISTER_OSGPLUGIN(osgearth_model_feature_stencil, FeatureStencilModelSourceFactory) +REGISTER_OSGPLUGIN(osgearth_model_feature_stencil, FeatureStencilModelSourceDriver) diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/model_simple/SimpleModelOptions osgearth-2.1.1+dfsg/src/osgEarthDrivers/model_simple/SimpleModelOptions --- osgearth-2.0+dfsg/src/osgEarthDrivers/model_simple/SimpleModelOptions 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/model_simple/SimpleModelOptions 2011-11-04 19:44:43.000000000 +0000 @@ -29,8 +29,8 @@ class SimpleModelOptions : public ModelSourceOptions // NO EXPORT; header only { public: - optional& url() { return _url; } - const optional& url() const { return _url; } + optional& url() { return _url; } + const optional& url() const { return _url; } public: SimpleModelOptions( const ConfigOptions& options ) : ModelSourceOptions( options ) { @@ -56,7 +56,7 @@ conf.getIfSet( "url", _url ); } - optional _url; + optional _url; }; } } // namespace osgEarth::Drivers diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/model_simple/SimpleModelSource.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/model_simple/SimpleModelSource.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/model_simple/SimpleModelSource.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/model_simple/SimpleModelSource.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -29,10 +29,6 @@ using namespace osgEarth::Drivers; #include -//#include -//#include -//#include -//#include class SimpleModelSource : public ModelSource { @@ -44,8 +40,11 @@ void initialize( const std::string& referenceURI, const osgEarth::Map* map ) { ModelSource::initialize( referenceURI, map ); - - _url = osgEarth::getFullPath( referenceURI, _options.url().value() ); + + _url = *_options.url(); + + //_url = URI(_options.url()->base(), referenceURI); + //_url = osgEarth::getFullPath( referenceURI, _options.url().value() ); } // override @@ -55,14 +54,14 @@ // required if the model includes local refs, like PagedLOD or ProxyNode: osg::ref_ptr options = new osgDB::Options(); - options->getDatabasePathList().push_back( osgDB::getFilePath(_url) ); + options->getDatabasePathList().push_back( osgDB::getFilePath(*_url) ); - HTTPClient::readNodeFile( _url, result, options.get(), progress ); //_settings.get(), progress ); + HTTPClient::readNodeFile( *_url, result, options.get(), progress ); //_settings.get(), progress ); return result.release(); } protected: - std::string _url; + URI _url; const SimpleModelOptions _options; }; diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/osg/OSGOptions osgearth-2.1.1+dfsg/src/osgEarthDrivers/osg/OSGOptions --- osgearth-2.0+dfsg/src/osgEarthDrivers/osg/OSGOptions 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/osg/OSGOptions 2011-11-04 19:44:43.000000000 +0000 @@ -29,8 +29,8 @@ class OSGOptions : public TileSourceOptions // NO EXPORT; header only { public: - optional& url() { return _url; } - const optional& url() const { return _url; } + optional& url() { return _url; } + const optional& url() const { return _url; } optional& convertLuminanceToRGBA() { return _lum2rgba; } const optional& convertLuminanceToRGBA() const { return _lum2rgba; } @@ -70,7 +70,7 @@ conf.getIfSet("add_alpha", _addAlpha); } - optional _url; + optional _url; optional _lum2rgba; optional _addAlpha; }; diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/osg/OSGTileSource.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/osg/OSGTileSource.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/osg/OSGTileSource.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/osg/OSGTileSource.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -74,27 +74,27 @@ osg::ref_ptr image; - std::string url = _options.url().value(); + URI url = _options.url().value(); if ( !url.empty() ) { - url = osgEarth::getFullPath( referenceURI, url ); - HTTPClient::ResultCode code = HTTPClient::readImageFile( url, image ); + url = URI( url.full(), referenceURI ); // obselete? + HTTPClient::ResultCode code = HTTPClient::readImageFile( url.full(), image ); if ( code != HTTPClient::RESULT_OK ) { - OE_WARN << LC << "Failed to load data from \"" << url << "\", because: " << + OE_WARN << LC << "Failed to load data from \"" << url.full() << "\", because: " << HTTPClient::getResultCodeString(code) << std::endl; } } if ( !image.valid() ) - OE_WARN << LC << "Faild to load data from \"" << url << "\"" << std::endl; + OE_WARN << LC << "Faild to load data from \"" << url.full() << "\"" << std::endl; // calculate and store the maximum LOD for which to return data if ( image.valid() ) { int minSpan = osg::minimum( image->s(), image->t() ); int tileSize = _options.tileSize().value(); - _maxDataLevel = LOG2((minSpan/tileSize)+1); + _maxDataLevel = (int)LOG2((minSpan/tileSize)+1); //OE_NOTICE << "[osgEarth::OSG driver] minSpan=" << minSpan << ", _tileSize=" << tileSize << ", maxDataLevel = " << _maxDataLevel << std::endl; @@ -120,7 +120,7 @@ _image = GeoImage( image.get(), getProfile()->getExtent() ); } - _extension = osgDB::getFileExtension( url ); + _extension = osgDB::getFileExtension( url.full() ); } //override diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/tilecache/ReaderWriterTileCache.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/tilecache/ReaderWriterTileCache.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/tilecache/ReaderWriterTileCache.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/tilecache/ReaderWriterTileCache.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -75,7 +75,7 @@ char buf[2048]; sprintf( buf, "%s/%s/%02d/%03d/%03d/%03d/%03d/%03d/%03d.%s", - _options.url()->c_str(), + _options.url()->full().c_str(), _options.layer()->c_str(), level, (tile_x / 1000000), diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/tilecache/TileCacheOptions osgearth-2.1.1+dfsg/src/osgEarthDrivers/tilecache/TileCacheOptions --- osgearth-2.0+dfsg/src/osgEarthDrivers/tilecache/TileCacheOptions 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/tilecache/TileCacheOptions 2011-11-04 19:44:43.000000000 +0000 @@ -29,8 +29,8 @@ class TileCacheOptions : public TileSourceOptions // NO EXPORT; header only { public: - optional& url() { return _url; } - const optional& url() const { return _url; } + optional& url() { return _url; } + const optional& url() const { return _url; } optional& layer() { return _layer; } const optional& layer() const { return _layer; } @@ -65,7 +65,9 @@ conf.getIfSet( "format", _format ); } - optional _url, _layer, _format; + optional _url; + optional _layer; + optional _format; }; } } // namespace osgEarth::Drivers diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/tileservice/ReaderWriterTileService.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/tileservice/ReaderWriterTileService.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/tileservice/ReaderWriterTileService.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/tileservice/ReaderWriterTileService.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -85,7 +85,7 @@ std::stringstream buf; //http://s0.tileservice.worldwindcentral.com/getTile?interface=map&version=1&dataset=bmng.topo.bathy.200401&level=0&x=0&y=0 - buf << _options.url().value() << "interface=map&version=1" + buf << _options.url()->full() << "interface=map&version=1" << "&dataset=" << _options.dataset().value() << "&level=" << lod << "&x=" << x diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/tileservice/TileServiceOptions osgearth-2.1.1+dfsg/src/osgEarthDrivers/tileservice/TileServiceOptions --- osgearth-2.0+dfsg/src/osgEarthDrivers/tileservice/TileServiceOptions 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/tileservice/TileServiceOptions 2011-11-04 19:44:43.000000000 +0000 @@ -29,8 +29,8 @@ class TileServiceOptions : public TileSourceOptions // NO EXPORT; header only { public: - optional& url() { return _url; } - const optional& url() const { return _url; } + optional& url() { return _url; } + const optional& url() const { return _url; } optional& dataset() { return _dataset; } const optional& dataset() const { return _dataset; } @@ -66,7 +66,8 @@ conf.getIfSet( "format", _format ); } - optional _dataset, _url, _format; + optional _url; + optional _dataset, _format; }; } } // namespace osgEarth::Drivers diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/tms/ReaderWriterTMS.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/tms/ReaderWriterTMS.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/tms/ReaderWriterTMS.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/tms/ReaderWriterTMS.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -55,8 +55,8 @@ { const Profile* result = NULL; - std::string tmsPath = _options.url().value(); - if ( tmsPath.empty() ) + URI tmsURI = _options.url().value(); + if ( tmsURI.empty() ) { OE_WARN << LC << "Fail: TMS driver requires a valid \"url\" property" << std::endl; return; @@ -64,19 +64,20 @@ //Find the full path to the URL //If we have a relative path and the map file contains a server address, just concat the server path and the url together - if (osgEarth::isRelativePath(tmsPath) && osgDB::containsServerAddress(referenceURI)) + if (osgEarth::isRelativePath(tmsURI.full()) && osgDB::containsServerAddress(referenceURI)) { - tmsPath = osgDB::getFilePath(referenceURI) + std::string("/") + tmsPath; + tmsURI = URI( osgDB::getFilePath(referenceURI) + std::string("/") + tmsURI.full() ); } //If the path doesn't contain a server address, get the full path to the file. - if (!osgDB::containsServerAddress(tmsPath)) + if (!osgDB::containsServerAddress(tmsURI.full())) { - tmsPath = osgEarth::getFullPath(referenceURI, tmsPath); + tmsURI = URI( tmsURI.full(), referenceURI ); + //tmsPath = osgEarth::getFullPath(referenceURI, tmsURI); } // Attempt to read the tile map parameters from a TMS TileMap XML tile on the server: - _tileMap = TileMapReaderWriter::read( tmsPath, 0L ); //getOptions() ); + _tileMap = TileMapReaderWriter::read( tmsURI.full(), 0L ); //getOptions() ); //Take the override profile if one is given @@ -85,7 +86,7 @@ OE_INFO << LC << "Using override profile " << overrideProfile->toString() << std::endl; result = overrideProfile; _tileMap = TileMap::create( - _options.url().value(), + _options.url()->full(), overrideProfile, _options.format().value(), _options.tileSize().value(), @@ -99,8 +100,8 @@ } else { - OE_WARN << LC << "Error reading TMS TileMap, and no overrides set (url=" << tmsPath << ")" << std::endl; - return; + OE_WARN << LC << "Error reading TMS TileMap, and no overrides set (url=" << tmsURI.full() << ")" << std::endl; + return; } } diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/tms/TMSOptions osgearth-2.1.1+dfsg/src/osgEarthDrivers/tms/TMSOptions --- osgearth-2.0+dfsg/src/osgEarthDrivers/tms/TMSOptions 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/tms/TMSOptions 2011-11-04 19:44:43.000000000 +0000 @@ -29,8 +29,8 @@ class TMSOptions : public TileSourceOptions // NO EXPORT; header only { public: - optional& url() { return _url; } - const optional& url() const { return _url; } + optional& url() { return _url; } + const optional& url() const { return _url; } optional& tmsType() { return _tmsType; } const optional& tmsType() const { return _tmsType; } @@ -74,7 +74,7 @@ conf.getIfSet( "tms_type", _tmsType ); } - optional _url; + optional _url; optional _tmsType; optional _format; }; diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/vpb/ReaderWriterVPB.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/vpb/ReaderWriterVPB.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/vpb/ReaderWriterVPB.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/vpb/ReaderWriterVPB.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -37,6 +38,8 @@ #include "VPBOptions" +#define LC "[VPB] " + using namespace osgEarth; using namespace osgEarth::Drivers; @@ -69,11 +72,11 @@ osgTerrain::TerrainTile* terrainTile = dynamic_cast(&group); if (terrainTile) { - // OE_DEBUG<<"VPB: Found terrain tile TileID("<< - //TileKey::getLOD(terrainTile->getTileID())<<", "<< - // terrainTile->getTileID().x<<", "<< - // terrainTile->getTileID().y<<")"<getTileID())<<", "<< + terrainTile->getTileID().x<<", "<< + terrainTile->getTileID().y<<")"<getGlobalGeodeticProfile() ), - _maxNumTilesInCache( 128 ) - { + _maxNumTilesInCache( 128 ), + _initialized( false ) + { } void initialize( const std::string& referenceURI) { + Threading::ScopedMutexLock lock( _initializeMutex ); + + if ( _initialized ) + return; + unsigned int numTilesWideAtLod0, numTilesHighAtLod0; _profile->getNumTiles(0, numTilesWideAtLod0, numTilesHighAtLod0); @@ -159,34 +168,36 @@ if ( !_url.empty() ) { //If the path doesn't contain a server address, get the full path to the file. - if (!osgDB::containsServerAddress(_url)) + if (!osgDB::containsServerAddress( *_url )) { - _url = osgEarth::getFullPath(referenceURI, _url); + //todo: obselete..? + _url = URI(_url.full(), referenceURI); + //_url = osgEarth::getFullPath(referenceURI, _url); } osg::ref_ptr localOptions = new osgDB::ReaderWriter::Options; localOptions->setPluginData("osgearth_vpb Plugin",(void*)(1)); //_rootNode = osgDB::readNodeFile( _url, localOptions.get() ); - HTTPClient::ResultCode rc = HTTPClient::readNodeFile( _url, _rootNode, localOptions.get() ); + HTTPClient::ResultCode rc = HTTPClient::readNodeFile( _url.full(), _rootNode, localOptions.get() ); if ( rc == HTTPClient::RESULT_OK && _rootNode.valid() ) { _baseNameToUse = _options.baseName().value(); - _path = osgDB::getFilePath(_url); + _path = osgDB::getFilePath( *_url ); if ( _baseNameToUse.empty() ) - _baseNameToUse = osgDB::getStrippedName(_url); - _extension = osgDB::getFileExtension(_url); + _baseNameToUse = osgDB::getStrippedName( *_url ); + _extension = osgDB::getFileExtension( *_url ); - OE_INFO<<"VPB: Loaded root "<<_url<<", path="<<_path<<" base_name="<<_baseNameToUse<<" extension="<<_extension<getCoordinateSystem()<getCoordinateSystem()<getCoordinateSystem(); } @@ -201,15 +212,15 @@ double min_x, max_x, min_y, max_y; ct.getRange(min_x, min_y, max_x, max_y); - //OE_DEBUG<<"VPB: range("<getCoordinateSystem(); double aspectRatio = (max_x-min_x)/(max_y-min_y); - //OE_DEBUG<<"VPB: aspectRatio = "<& out_tile ) { int level = key.getLevelOfDetail(); unsigned int tile_x, tile_y; @@ -339,28 +351,32 @@ osgTerrain::TileID tileID(level, tile_x, tile_y); - osg::ref_ptr tile = findTile(tileID, false); - if (tile.valid()) return tile.get(); - - //OE_INFO<<"Max_x = "< lock(_blacklistMutex); - if (_blacklistedFilenames.count(filename)==1) - { - //OE_DEBUG<<"VPB: file has been found in black list : "< lock(_blacklistMutex); + // if (_blacklistedFilenames.count(filename)==1) + // { + // OE_DEBUG<<"VPB: file has been found in black list : "< localOptions = new osgDB::ReaderWriter::Options; @@ -372,7 +388,7 @@ HTTPClient::ResultCode result = HTTPClient::readNodeFile( filename, node, localOptions.get(), progress ); if ( result == HTTPClient::RESULT_OK && node.valid() ) { - //OE_INFO<<"Loaded model "<accept(ct); @@ -400,6 +416,9 @@ tile->setTileID(local_tileID); insertTile(local_tileID, tile); + + if ( local_tileID == tileID ) + out_tile = tile; } } @@ -410,18 +429,18 @@ // in the case of an "unrecoverable" error, black-list the URL for this tile. if ( ! HTTPClient::isRecoverable( result ) ) { - //OE_INFO<<"Black listing : "<< filename<< " (" << result << ")" << std::endl; - OpenThreads::ScopedLock lock(_blacklistMutex); - _blacklistedFilenames.insert(filename); + Threading::ScopedWriteLock exclusiveLock( _blacklistMutex ); + _blacklistedFilenames.insert( filename ); } } - return findTile(tileID, false); + //TODO: just return it instead... + //findTile(tileID, false, out_tile); } void insertTile(const osgTerrain::TileID& tileID, osgTerrain::TerrainTile* tile) { - OpenThreads::ScopedLock lock(_tileMapMutex); + Threading::ScopedWriteLock exclusiveLock( _tileMapMutex ); if ( _tileMap.find(tileID) == _tileMap.end() ) { @@ -435,36 +454,40 @@ _tileFIFO.pop_front(); _tileMap.erase(tileToRemove); - //OE_DEBUG<<"VPB: Pruned tileID ("<& out_tile) { + // read with a shared lock { - OpenThreads::ScopedLock lock(_tileMapMutex); + Threading::ScopedReadLock sharedLock( _tileMapMutex ); TileMap::iterator itr = _tileMap.find(tileID); - if (itr != _tileMap.end()) return itr->second.get(); + if (itr != _tileMap.end()) + out_tile = itr->second.get(); } - if (insertBlankTileIfNotFound) insertTile(tileID, 0); + // upgrade lock and write: + if (insertBlankTileIfNotFound) + insertTile(tileID, 0); - return 0; + //return 0; } const VPBOptions _options; - std::string _url; + URI _url; std::string _path; std::string _extension; @@ -477,21 +500,24 @@ typedef std::map > TileMap; TileMap _tileMap; - OpenThreads::Mutex _tileMapMutex; + Threading::ReadWriteMutex _tileMapMutex; typedef std::list TileIDList; TileIDList _tileFIFO; typedef std::set StringSet; StringSet _blacklistedFilenames; - OpenThreads::Mutex _blacklistMutex; + Threading::ReadWriteMutex _blacklistMutex; + + bool _initialized; + Threading::Mutex _initializeMutex; }; class VPBSource : public TileSource { public: - VPBSource( VPBDatabase* vpbDatabase, const VPBOptions& in_options ) : //const VPBOptions* in_options) : + VPBSource( VPBDatabase* vpbDatabase, const VPBOptions& in_options ) : TileSource(in_options), _vpbDatabase(vpbDatabase), _options( in_options ), @@ -502,24 +528,26 @@ void initialize( const std::string& referenceURI, const Profile* overrideProfile) { - _referenceUri = referenceURI; - _vpbDatabase->initialize(referenceURI); - if ( overrideProfile) - { - setProfile( overrideProfile ); - } - else - { - setProfile(_vpbDatabase->_profile.get()); - } + _referenceUri = referenceURI; + + _vpbDatabase->initialize(referenceURI); + + if ( overrideProfile) + { + setProfile( overrideProfile ); + } + else + { + setProfile(_vpbDatabase->_profile.get()); + } } - osg::Image* createImage( const TileKey& key, - ProgressCallback* progress) + osg::Image* createImage( const TileKey& key, ProgressCallback* progress) { osg::Image * ret = NULL; //TODO: Make VPB driver use progress callback - osg::ref_ptr tile = _vpbDatabase->getTerrainTile(key, progress); + osg::ref_ptr tile; + _vpbDatabase->getTerrainTile(key, progress, tile); if (tile.valid()) { int layerNum = _options.layer().value(); @@ -535,7 +563,7 @@ osgTerrain::ImageLayer* imageLayer = dynamic_cast(layer); if (imageLayer) { - //OE_DEBUG<<"VPB: createImage(" << key.str() << " layerNum=" << layerNum << ") successful." <getImage() ); } else @@ -553,19 +581,19 @@ } if(imageLayer) { - //OE_DEBUG<<"VPB: createImage(" << key.str() << " layerSet=" << layerSetName.value() << ") successful." <getImage() ); } } } if(!ret) { - //OE_DEBUG<<"VPB: createImage(" << key.str() << " layerSet=" << layerSetName.value() << " layerNum=" << layerNum << "/" << numColorLayers << ") failed." < tile = _vpbDatabase->getTerrainTile(key, progress); + osg::ref_ptr tile; + _vpbDatabase->getTerrainTile(key, progress, tile); if (tile.valid()) { osgTerrain::Layer* elevationLayer = tile->getElevationLayer(); @@ -622,11 +651,11 @@ VPBOptions vpbOptions( getTileSourceOptions(options) ); - std::string url = vpbOptions.url().value(); + URI url = vpbOptions.url().value(); if ( !url.empty() ) { OpenThreads::ScopedLock lock(vpbDatabaseMapMutex); - osg::observer_ptr& db_ptr = vpbDatabaseMap[url]; //get or create + osg::observer_ptr& db_ptr = vpbDatabaseMap[*url]; //get or create if (!db_ptr) db_ptr = new VPBDatabase( vpbOptions ); diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/vpb/VPBOptions osgearth-2.1.1+dfsg/src/osgEarthDrivers/vpb/VPBOptions --- osgearth-2.0+dfsg/src/osgEarthDrivers/vpb/VPBOptions 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/vpb/VPBOptions 2011-11-04 19:44:43.000000000 +0000 @@ -38,8 +38,8 @@ public: // properties - optional& url() { return _url; } - const optional& url() const { return _url; } + optional& url() { return _url; } + const optional& url() const { return _url; } optional& primarySplitLevel() { return _primarySplitLevel; } const optional& primarySplitLevel() const { return _primarySplitLevel; } @@ -120,8 +120,9 @@ else if ( ds == "nested" ) _dirStruct = DS_NESTED; } - optional _url, _baseName, _layerSetName; - optional _primarySplitLevel, _secondarySplitLevel, _layer, _widthLod0, _heightLod0; + optional _url; + optional _baseName, _layerSetName; + optional _primarySplitLevel, _secondarySplitLevel, _layer, _widthLod0, _heightLod0; optional _dirStruct; }; diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/wcs/WCS11Source.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/wcs/WCS11Source.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/wcs/WCS11Source.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/wcs/WCS11Source.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -156,7 +156,7 @@ double lon_interval = (lon_max-lon_min)/(double)(lon_samples-1); double lat_interval = (lat_max-lat_min)/(double)(lat_samples-1); - HTTPRequest req( _options.url().value() ); + HTTPRequest req( _options.url()->full() ); req.addParameter( "SERVICE", "WCS" ); req.addParameter( "VERSION", "1.1.0" ); diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/wcs/WCS11Source.h osgearth-2.1.1+dfsg/src/osgEarthDrivers/wcs/WCS11Source.h --- osgearth-2.0+dfsg/src/osgEarthDrivers/wcs/WCS11Source.h 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/wcs/WCS11Source.h 2011-11-04 19:44:43.000000000 +0000 @@ -57,4 +57,4 @@ HTTPRequest createRequest( const TileKey& key ) const; }; -#endif // OSGEARTH_WCS_PLUGIN_WCS11SOURCE_H_ \ No newline at end of file +#endif // OSGEARTH_WCS_PLUGIN_WCS11SOURCE_H_ diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/wcs/WCSOptions osgearth-2.1.1+dfsg/src/osgEarthDrivers/wcs/WCSOptions --- osgearth-2.0+dfsg/src/osgEarthDrivers/wcs/WCSOptions 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/wcs/WCSOptions 2011-11-04 19:44:43.000000000 +0000 @@ -30,8 +30,8 @@ { public: // properties - optional& url() { return _url; } - const optional& url() const { return _url; } + optional& url() { return _url; } + const optional& url() const { return _url; } optional& identifier() { return _identifier; } const optional& identifier() const { return _identifier; } @@ -85,7 +85,8 @@ conf.getIfSet("range_subset", _rangeSubset); } - optional _url, _identifier, _format, _elevationUnit, _srs, _rangeSubset; + optional _url; + optional _identifier, _format, _elevationUnit, _srs, _rangeSubset; }; } } // namespace osgEarth::Drivers diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/wms/ReaderWriterWMS.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/wms/ReaderWriterWMS.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/wms/ReaderWriterWMS.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/wms/ReaderWriterWMS.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -37,6 +37,8 @@ #include "TileService" #include "WMSOptions" +#define LC "[WMS] " + using namespace osgEarth; using namespace osgEarth::Util; using namespace osgEarth::Drivers; @@ -45,7 +47,9 @@ // a shared reference time. class SyncImageSequence : public osg::ImageSequence { public: - SyncImageSequence() { } + SyncImageSequence() + { + } virtual void update(osg::NodeVisitor* nv) { setReferenceTime( 0.0 ); @@ -61,7 +65,8 @@ { if ( _options.times().isSet() ) { - osgEarth::split( _options.times().value(), ",", _timesVec, false ); + StringTokenizer( *_options.times(), _timesVec, ",", "", false, true ); + OE_INFO << LC << "WMS-T: found " << _timesVec.size() << " times." << std::endl; } // localize it since we might override them: @@ -74,21 +79,21 @@ { osg::ref_ptr result; - char sep = _options.url()->find_first_of('?') == std::string::npos? '?' : '&'; + char sep = _options.url()->full().find_first_of('?') == std::string::npos? '?' : '&'; - std::string capUrl = _options.capabilitiesUrl().value(); + URI capUrl = _options.capabilitiesUrl().value(); if ( capUrl.empty() ) { - capUrl = - _options.url().value() + + capUrl = URI( + _options.url()->full() + sep + "SERVICE=WMS" + "&VERSION=" + _options.wmsVersion().value() + - "&REQUEST=GetCapabilities"; + "&REQUEST=GetCapabilities" ); } //Try to read the WMS capabilities - osg::ref_ptr capabilities = WMSCapabilitiesReader::read( capUrl, 0L ); //getOptions() ); + osg::ref_ptr capabilities = WMSCapabilitiesReader::read( capUrl.full(), 0L ); //getOptions() ); if ( !capabilities.valid() ) { OE_WARN << "[osgEarth::WMS] Unable to read WMS GetCapabilities." << std::endl; @@ -96,7 +101,7 @@ } else { - OE_INFO << "[osgEarth::WMS] Got capabilities from " << capUrl << std::endl; + OE_INFO << "[osgEarth::WMS] Got capabilities from " << capUrl.full() << std::endl; } if ( _formatToUse.empty() && capabilities.valid() ) @@ -118,7 +123,7 @@ // first the mandatory keys: buf - << std::fixed << _options.url().value() << sep + << std::fixed << _options.url()->full() << sep << "SERVICE=WMS" << "&VERSION=" << _options.wmsVersion().value() << "&REQUEST=GetMap" @@ -203,14 +208,15 @@ // JPL uses an experimental interface called TileService -- ping to see if that's what // we are trying to read: - std::string tsUrl = _options.tileServiceUrl().value(); - if (tsUrl.empty() ) + URI tsUrl = _options.tileServiceUrl().value(); + if ( tsUrl.empty() ) { - tsUrl = _options.url().value() + sep + std::string("request=GetTileService"); + tsUrl = URI( + _options.url()->full() + sep + std::string("request=GetTileService") ); } - OE_INFO << "[osgEarth::WMS] Testing for JPL/TileService at " << tsUrl << std::endl; - _tileService = TileServiceReader::read(tsUrl, 0L); //getOptions()); + OE_INFO << "[osgEarth::WMS] Testing for JPL/TileService at " << tsUrl.full() << std::endl; + _tileService = TileServiceReader::read(tsUrl.full(), 0L); //getOptions()); if (_tileService.valid()) { OE_INFO << "[osgEarth::WMS] Found JPL/TileService spec" << std::endl; @@ -227,7 +233,7 @@ if (patterns.size() > 0) { result = _tileService->createProfile( patterns ); - _prototype = _options.url().value() + sep + patterns[0].getPrototype(); + _prototype = _options.url()->full() + sep + patterns[0].getPrototype(); } } else @@ -235,6 +241,12 @@ OE_INFO << "[osgEarth::WMS] No JPL/TileService spec found; assuming standard WMS" << std::endl; } + //Use the override profile if one is passed in. + if (overrideProfile) + { + result = overrideProfile; + } + //TODO: won't need this for OSG 2.9+, b/c of mime-type support _prototype = _prototype + std::string("&.") + _formatToUse; diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/wms/WMSOptions osgearth-2.1.1+dfsg/src/osgEarthDrivers/wms/WMSOptions --- osgearth-2.0+dfsg/src/osgEarthDrivers/wms/WMSOptions 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/wms/WMSOptions 2011-11-04 19:44:43.000000000 +0000 @@ -30,14 +30,14 @@ class WMSOptions : public TileSourceOptions // NO EXPORT; header only { public: - optional& url() { return _url; } - const optional& url() const { return _url; } + optional& url() { return _url; } + const optional& url() const { return _url; } - optional& capabilitiesUrl() { return _capabilitiesUrl; } - const optional& capabilitiesUrl() const { return _capabilitiesUrl; } + optional& capabilitiesUrl() { return _capabilitiesUrl; } + const optional& capabilitiesUrl() const { return _capabilitiesUrl; } - optional& tileServiceUrl() { return _tileServiceUrl; } - const optional& tileServiceUrl() const { return _tileServiceUrl; } + optional& tileServiceUrl() { return _tileServiceUrl; } + const optional& tileServiceUrl() const { return _tileServiceUrl; } optional& layers() { return _layers; } const optional& layers() const { return _layers; } @@ -122,9 +122,9 @@ conf.getIfSet("seconds_per_frame", _secondsPerFrame ); } - optional _url; - optional _capabilitiesUrl; - optional _tileServiceUrl; + optional _url; + optional _capabilitiesUrl; + optional _tileServiceUrl; optional _layers; optional _style; optional _format; diff -Nru osgearth-2.0+dfsg/src/osgEarthDrivers/yahoo/ReaderWriterYahoo.cpp osgearth-2.1.1+dfsg/src/osgEarthDrivers/yahoo/ReaderWriterYahoo.cpp --- osgearth-2.0+dfsg/src/osgEarthDrivers/yahoo/ReaderWriterYahoo.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthDrivers/yahoo/ReaderWriterYahoo.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -50,8 +50,8 @@ osg::Image* createImage( const TileKey& key, ProgressCallback* progress ) { - //Return NULL if we are given a non-mercator key - if ( !key.isMercator() ) return 0; + //Return NULL if we are given a non global-mercator key + if ( !key.getProfile()->getProfileType() == Profile::TYPE_MERCATOR ) return 0; std::stringstream buf; diff -Nru osgearth-2.0+dfsg/src/osgEarthFeatures/AltitudeFilter osgearth-2.1.1+dfsg/src/osgEarthFeatures/AltitudeFilter --- osgearth-2.0+dfsg/src/osgEarthFeatures/AltitudeFilter 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthFeatures/AltitudeFilter 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,60 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ + +#ifndef OSGEARTHFEATURES_CLAMP_FILTER_H +#define OSGEARTHFEATURES_CLAMP_FILTER_H 1 + +#include +#include +#include + +namespace osgEarth { namespace Features +{ + using namespace osgEarth; + + /** + * Feature filter that will clamp incoming feature geometry to an elevation model. + */ + class OSGEARTHFEATURES_EXPORT AltitudeFilter : public FeatureFilter + { + public: + /** Constructs a new clamping filter */ + AltitudeFilter(); + + public: // properties + + /** Shortcut to set any properties that are represented in a style. */ + void setPropertiesFromStyle( const Style& style ); + + /** Maximum terrain resolution to consider when clamping */ + void setMaxResolution( double value ) { _maxRes = value; } + double getMaxResolution() const { return _maxRes; } + + public: + virtual FilterContext push( FeatureList& input, FilterContext& cx ); + + protected: + osg::ref_ptr _altitude; + double _maxRes; + std::string _maxZAttr, _minZAttr, _terrainZAttr; + }; + +} } // namespace osgEarth::Features + +#endif // OSGEARTHFEATURES_CLAMP_FILTER_H diff -Nru osgearth-2.0+dfsg/src/osgEarthFeatures/AltitudeFilter.cpp osgearth-2.1.1+dfsg/src/osgEarthFeatures/AltitudeFilter.cpp --- osgearth-2.0+dfsg/src/osgEarthFeatures/AltitudeFilter.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthFeatures/AltitudeFilter.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,176 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#include +#include +#include + +#define LC "[AltitudeFilter] " + +using namespace osgEarth; +using namespace osgEarth::Features; +using namespace osgEarth::Symbology; + +//--------------------------------------------------------------------------- + +AltitudeFilter::AltitudeFilter() : +_maxRes ( 0.0f ) +{ + //NOP +} + +void +AltitudeFilter::setPropertiesFromStyle( const Style& style ) +{ + _altitude = style.get(); + if ( _altitude ) + { + setMaxResolution( *_altitude->clampingResolution() ); + } +} + +FilterContext +AltitudeFilter::push( FeatureList& features, FilterContext& cx ) +{ + const Session* session = cx.getSession(); + if ( !session ) { + OE_WARN << LC << "No session - session is required for elevation clamping" << std::endl; + return cx; + } + + // the map against which we'll be doing elevation clamping + MapFrame mapf = session->createMapFrame( Map::ELEVATION_LAYERS ); + + const SpatialReference* mapSRS = mapf.getProfile()->getSRS(); + const SpatialReference* featureSRS = cx.profile()->getSRS(); + + // establish an elevation query interface based on the features' SRS. + ElevationQuery eq( mapf ); + + NumericExpression scaleExpr; + if ( _altitude.valid() && _altitude->verticalScale().isSet() ) + scaleExpr = *_altitude->verticalScale(); + + NumericExpression offsetExpr; + if ( _altitude.valid() && _altitude->verticalOffset().isSet() ) + offsetExpr = *_altitude->verticalOffset(); + + bool clamp = + _altitude->clamping() != AltitudeSymbol::CLAMP_NONE; + + // whether to record a "minimum terrain" value + bool collectHATs = + _altitude->clamping() == AltitudeSymbol::CLAMP_RELATIVE_TO_TERRAIN || + _altitude->clamping() == AltitudeSymbol::CLAMP_ABSOLUTE; + + 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; + double maxHAT = -DBL_MAX; + + double scaleZ = 1.0; + if ( _altitude.valid() && _altitude->verticalScale().isSet() ) + scaleZ = feature->eval( scaleExpr ); + + double offsetZ = 0.0; + if ( _altitude.valid() && _altitude->verticalOffset().isSet() ) + offsetZ = feature->eval( offsetExpr ); + + GeometryIterator gi( feature->getGeometry() ); + while( gi.hasMore() ) + { + Geometry* geom = gi.next(); + + // clamps the entire array to the terrain using the specified resolution. + if ( clamp ) + { + if ( collectHATs ) + { + std::vector elevations; + elevations.reserve( geom->size() ); + eq.getElevations( geom->asVector(), featureSRS, elevations, _maxRes ); + for( unsigned i=0; isize(); ++i ) + { + double z = (*geom)[i].z() * scaleZ + offsetZ; + double hat = + _altitude->clamping() == AltitudeSymbol::CLAMP_ABSOLUTE ? z - elevations[i] : + z; + + if ( hat > maxHAT ) + maxHAT = hat; + if ( hat < minHAT ) + minHAT = hat; + + double elev = elevations[i]; + if ( elev > maxTerrainZ ) + maxTerrainZ = elev; + if ( elev < minTerrainZ ) + minTerrainZ = elev; + + (*geom)[i].z() = + _altitude->clamping() == AltitudeSymbol::CLAMP_ABSOLUTE ? z : + z + elevations[i]; + } + } + else + { + eq.getElevations( geom->asVector(), featureSRS, true, _maxRes ); + } + } + + for( Geometry::iterator i = geom->begin(); i != geom->end(); ++i ) + { + if ( !collectHATs ) + { + i->z() *= scaleZ; + i->z() += offsetZ; + } + + if ( i->z() > maxGeomZ ) + maxGeomZ = i->z(); + if ( i->z() < minGeomZ ) + minGeomZ = i->z(); + } + } + + if ( minHAT != DBL_MAX ) + { + feature->set( "__min_hat", minHAT ); + feature->set( "__max_hat", maxHAT ); + } + + if ( minGeomZ != DBL_MAX ) + { + feature->set( "__min_geom_z", minGeomZ ); + feature->set( "__max_geom_z", maxGeomZ ); + } + + if ( minTerrainZ != DBL_MAX ) + { + feature->set( "__min_terrain_z", minTerrainZ ); + feature->set( "__max_terrain_z", maxTerrainZ ); + } + } + + return cx; +} diff -Nru osgearth-2.0+dfsg/src/osgEarthFeatures/Annotation osgearth-2.1.1+dfsg/src/osgEarthFeatures/Annotation --- osgearth-2.0+dfsg/src/osgEarthFeatures/Annotation 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthFeatures/Annotation 1970-01-01 00:00:00.000000000 +0000 @@ -1,77 +0,0 @@ -/* -*-c++-*- */ -/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph - * Copyright 2008-2010 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 - */ - -#ifndef OSGEARTHFEATURES_ANNOTATION_H -#define OSGEARTHFEATURES_ANNOTATION_H 1 - -#include -#include -#include -#include - -namespace osgEarth { namespace Features -{ - /** - * An "Annotation" is a feature that uses the geometry to place text or graphics; - * the geometry itself is not rendered. - */ - class OSGEARTHFEATURES_EXPORT Annotation : public Feature - { - public: - /** Default constructor */ - Annotation( long fid =0L ); - - /** Copy contructor */ - Annotation( const Annotation& rhs, const osg::CopyOp& op =osg::CopyOp::DEEP_COPY_ALL ); - - META_Object( osgEarthFeatures, Annotation ); - }; - - /** - * A "Text Annotation" is an Annotation consisting of a simple free-floating - * text string. - */ - class OSGEARTHFEATURES_EXPORT TextAnnotation : public Annotation - { - public: - /** Default constructor */ - TextAnnotation( long fid =0L ); - - /** Constructs a new text annotation. */ - TextAnnotation( const std::string& text ); - - /** Copy constructor. */ - TextAnnotation( const TextAnnotation& rhs, const osg::CopyOp& op =osg::CopyOp::DEEP_COPY_ALL ); - - META_Object( osgEarthFeatures, TextAnnotation ); - - public: // properties - - /** The text string to be shown as annotation. */ - std::string& text() { return _text; } - const std::string& text() const { return _text; } - - protected: - std::string _text; - }; - -} } // osgEarth::Features - -#endif // OSGEARTHFEATURES_ANNOTATION_H - diff -Nru osgearth-2.0+dfsg/src/osgEarthFeatures/Annotation.cpp osgearth-2.1.1+dfsg/src/osgEarthFeatures/Annotation.cpp --- osgearth-2.0+dfsg/src/osgEarthFeatures/Annotation.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthFeatures/Annotation.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,51 +0,0 @@ -/* -*-c++-*- */ -/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph - * Copyright 2008-2010 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 - */ -#include -#include - -using namespace osgEarth; -using namespace osgEarth::Features; - -Annotation::Annotation( long fid ) : -Feature( fid ) -{ - //nop -} - -Annotation::Annotation( const Annotation& rhs, const osg::CopyOp& op ) : -Feature( rhs, op ) -{ - //nop -} - -/***************************************************************************/ - -TextAnnotation::TextAnnotation( long fid ) : -Annotation( fid ) -{ - //nop -} - -TextAnnotation::TextAnnotation( const TextAnnotation& rhs, const osg::CopyOp& op ) : -Annotation( rhs, op ), -_text( rhs._text ) -{ - //nop -} - diff -Nru osgearth-2.0+dfsg/src/osgEarthFeatures/BufferFilter osgearth-2.1.1+dfsg/src/osgEarthFeatures/BufferFilter --- osgearth-2.0+dfsg/src/osgEarthFeatures/BufferFilter 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthFeatures/BufferFilter 2011-11-04 19:44:43.000000000 +0000 @@ -24,7 +24,6 @@ #include #include #include -#include #include namespace osgEarth { namespace Features @@ -71,14 +70,12 @@ Stroke::LineCapStyle& capStyle() { return _capStyle; } public: - virtual FilterContext push( FeatureList& input, const FilterContext& context ); + virtual FilterContext push( FeatureList& input, FilterContext& context ); protected: optional _distance; int _numQuadSegs; Stroke::LineCapStyle _capStyle; - - bool push( Feature* input, const FilterContext& context ); }; } } // namespace osgEarth::Features diff -Nru osgearth-2.0+dfsg/src/osgEarthFeatures/BufferFilter.cpp osgearth-2.1.1+dfsg/src/osgEarthFeatures/BufferFilter.cpp --- osgearth-2.0+dfsg/src/osgEarthFeatures/BufferFilter.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthFeatures/BufferFilter.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -18,15 +18,6 @@ */ #include -//#ifdef OSGEARTH_HAVE_GEOS -//# include -//# include -//# include -//# include -// using namespace geos; -// using namespace geos::operation; -//#endif - using namespace osgEarth; using namespace osgEarth::Features; using namespace osgEarth::Symbology; @@ -48,49 +39,23 @@ OE_NOTICE << "BufferFilter NOT SUPPORTED - please compile osgEarth with GEOS" << std::endl; } BufferFilter::BufferFilter() : -_distance( 1.0 ), +_distance ( 1.0 ), _numQuadSegs( 0 ), -_capStyle( Stroke::LINECAP_DEFAULT ) +_capStyle ( Stroke::LINECAP_DEFAULT ) { //NOP } BufferFilter::BufferFilter( const BufferFilter& rhs ) : -_distance( rhs._distance ), +_distance ( rhs._distance ), _numQuadSegs( rhs._numQuadSegs ), -_capStyle( rhs._capStyle ) +_capStyle ( rhs._capStyle ) { //NOP } -bool -BufferFilter::push( Feature* input, const FilterContext& context ) -{ - if ( !input || !input->getGeometry() ) - return true; - - osg::ref_ptr output; - - Symbology::BufferParameters params; - - 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; - - params._cornerSegs = _numQuadSegs; - - if ( input->getGeometry()->buffer( _distance.value(), output, params ) ) - { - input->setGeometry( output.get() ); - } - - return output.valid(); -} - FilterContext -BufferFilter::push( FeatureList& input, const FilterContext& context ) +BufferFilter::push( FeatureList& input, FilterContext& context ) { if ( !isSupported() ) { @@ -99,10 +64,29 @@ } //OE_NOTICE << "Buffer: input = " << input.size() << " features" << std::endl; - bool ok = true; for( FeatureList::iterator i = input.begin(); i != input.end(); ++i ) - if ( !push( i->get(), context ) ) - ok = false; + { + Feature* input = i->get(); + if ( !input || !input->getGeometry() ) + continue; + + osg::ref_ptr output; + + Symbology::BufferParameters params; + + 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; + + params._cornerSegs = _numQuadSegs; + + if ( input->getGeometry()->buffer( _distance.value(), output, params ) ) + { + input->setGeometry( output.get() ); + } + } return context; } diff -Nru osgearth-2.0+dfsg/src/osgEarthFeatures/BuildGeometryFilter osgearth-2.1.1+dfsg/src/osgEarthFeatures/BuildGeometryFilter --- osgearth-2.0+dfsg/src/osgEarthFeatures/BuildGeometryFilter 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthFeatures/BuildGeometryFilter 2011-11-04 19:44:43.000000000 +0000 @@ -22,29 +22,30 @@ #include #include -#include #include #include +#include #include namespace osgEarth { namespace Features { using namespace osgEarth; + using namespace osgEarth::Symbology; /** * Builds geometry from a stream of input features. */ - class OSGEARTHFEATURES_EXPORT BuildGeometryFilter // : public Filter + class OSGEARTHFEATURES_EXPORT BuildGeometryFilter : public FeaturesToNodeFilter { public: - BuildGeometryFilter(); + BuildGeometryFilter( const Style& style =Style() ); - /** The style to apply to feature geometry */ - const Symbology::Style* getStyle() { return _style.get(); } - void setStyle(const Symbology::Style* s) { _style = s; } + /** Pushes a list of features through the filter. */ + osg::Node* push( FeatureList& input, FilterContext& context ); - /** Override and change the geometry type of incoming features. */ - optional& geomTypeOverride() { return _geomTypeOverride; } + /** The style to apply to feature geometry */ + const Style& getStyle() { return _style; } + void setStyle(const Style& s) { _style = s; } /** * For geocentric data, sets the granularity of edges created by the filter. This @@ -56,30 +57,45 @@ optional& maxGranularity() { return _maxAngle_deg; } const optional& maxGranularity() const { return _maxAngle_deg; } - /** Pushes a list of features through the filter. */ - FilterContext push( FeatureList& input, osg::ref_ptr& output, const FilterContext& context ); + /** + * The algorithm to use when interpolating between geodetic locations. + * The default is GEOINTERP_RHUMBLINE. + */ + optional& geoInterp() { return _geoInterp; } + const optional& geoInterp() const { return _geoInterp; } + + /** + * Whether to merge the geometries from mutliple features together. Doing this can + * help performance by batching geometries together. The downside will be that individual + * geometries are no longer addressable in the scene graph. Default is FALSE. + */ + optional& mergeGeometry() { return _mergeGeometry; } + const optional& mergeGeometry() const { return _mergeGeometry; } + + /** + * Sets an expression to evaluate for setting the name of a Geometry. This only + * takes effect if mergeGeometry is false. + */ + optional& featureName() { return _featureNameExpr; } + const optional& featureName() const { return _featureNameExpr; } protected: + osg::ref_ptr _result; osg::ref_ptr _geode; - osg::ref_ptr _style; - optional _geomTypeOverride; + Style _style; optional _maxAngle_deg; + optional _geoInterp; + optional _mergeGeometry; + optional _featureNameExpr; bool _hasPoints; bool _hasLines; + bool _hasPolygons; + void reset(); - bool push( Feature* input, const FilterContext& context ); - bool pushRegularFeature( Feature* input, const FilterContext& context ); - bool pushTextAnnotation( TextAnnotation* input, const FilterContext& context ); + bool process( FeatureList& input, const FilterContext& context ); }; - - OSGEARTHFEATURES_EXPORT osg::Geode* - createVolume(Symbology::Geometry* geom, - double offset, - double height, - const FilterContext& context ); - } } // namespace osgEarth::Features #endif // OSGEARTHFEATURES_BUILD_GEOMETRY_FILTER_H diff -Nru osgearth-2.0+dfsg/src/osgEarthFeatures/BuildGeometryFilter.cpp osgearth-2.1.1+dfsg/src/osgEarthFeatures/BuildGeometryFilter.cpp --- osgearth-2.0+dfsg/src/osgEarthFeatures/BuildGeometryFilter.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthFeatures/BuildGeometryFilter.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -17,11 +17,17 @@ * along with this program. If not, see */ #include -#include +#include +#include +#include +#include #include +#include +#include #include #include #include +#include #include #include #include @@ -29,35 +35,21 @@ #include #include #include - +#include +#include #include +#define LC "[BuildGeometryFilter] " using namespace osgEarth; using namespace osgEarth::Features; using namespace osgEarth::Symbology; -namespace -{ - struct CullPlaneCallback : public osg::Drawable::CullCallback - { - osg::Vec3d _n; - - CullPlaneCallback( const osg::Vec3d& planeNormal ) : _n(planeNormal) { - _n.normalize(); - } - - bool cull(osg::NodeVisitor* nv, osg::Drawable* drawable, osg::RenderInfo* renderInfo) const { - return nv && nv->getEyePoint() * _n <= 0; - } - }; -} - - -BuildGeometryFilter::BuildGeometryFilter() : -_style( new Style() ), -_geomTypeOverride( Symbology::Geometry::TYPE_UNKNOWN ), -_maxAngle_deg( 5.0 ) +BuildGeometryFilter::BuildGeometryFilter( const Style& style ) : +_style ( style ), +_maxAngle_deg ( 5.0 ), +_geoInterp ( GEOINTERP_RHUMB_LINE ), +_mergeGeometry( false ) { reset(); } @@ -65,495 +57,275 @@ void BuildGeometryFilter::reset() { + _result = 0L; _geode = new osg::Geode(); _hasLines = false; _hasPoints = false; + _hasPolygons = false; } bool -BuildGeometryFilter::pushTextAnnotation( TextAnnotation* anno, const FilterContext& context ) +BuildGeometryFilter::process( FeatureList& features, const FilterContext& context ) { - // find the centroid - osg::Vec3d centroid = anno->getGeometry()->getBounds().center(); - - osgText::Text* t = new osgText::Text(); - t->setText( anno->text() ); - t->setFont( "fonts/arial.ttf" ); - t->setAutoRotateToScreen( true ); - t->setCharacterSizeMode( osgText::TextBase::SCREEN_COORDS ); - t->setCharacterSize( 32.0f ); - //t->setCharacterSizeMode( osgText::TextBase::OBJECT_COORDS_WITH_MAXIMUM_SCREEN_SIZE_CAPPED_BY_FONT_HEIGHT ); - //t->setCharacterSize( 300000.0f ); - t->setPosition( centroid ); - t->setAlignment( osgText::TextBase::CENTER_CENTER ); - t->getOrCreateStateSet()->setAttributeAndModes( new osg::Depth(osg::Depth::ALWAYS), osg::StateAttribute::ON ); - t->getOrCreateStateSet()->setRenderBinDetails( 99999, "RenderBin" ); - - // apply styling as appropriate: - osg::Vec4f textColor(1,1,1,1); - osg::Vec4f haloColor(0,0,0,1); + bool makeECEF = context.getSession()->getMapInfo().isGeocentric(); + const SpatialReference* srs = context.extent()->getSRS(); - const TextSymbol* textSymbolizer = getStyle()->getSymbol(); - if ( textSymbolizer ) + for( FeatureList::iterator f = features.begin(); f != features.end(); ++f ) { - textColor = textSymbolizer->fill()->color(); - if ( textSymbolizer->halo().isSet() ) - { - haloColor = textSymbolizer->halo()->color(); - } - } - - t->setColor( textColor ); - t->setBackdropColor( haloColor ); - t->setBackdropType( osgText::Text::OUTLINE ); - - if ( context.isGeocentric() ) - { - // install a cluster culler: note that the CCC control point and normal must be - // in world coordinates - const osg::EllipsoidModel* ellip = context.profile()->getSRS()->getEllipsoid(); - osg::Vec3d cp = centroid * context.inverseReferenceFrame(); - osg::Vec3d normal = ellip->computeLocalUpVector( cp.x(), cp.y(), cp.z() ); - osg::ClusterCullingCallback* ccc = new osg::ClusterCullingCallback( cp, normal, 0.0f ); - t->setCullCallback( ccc ); - } + Feature* input = f->get(); - _geode->addDrawable( t ); - - return true; -} - -bool -BuildGeometryFilter::pushRegularFeature( Feature* input, const FilterContext& context ) -{ - GeometryIterator parts( input->getGeometry() ); - parts.traversePolygonHoles() = false; - while( parts.hasMore() ) - { - Geometry* part = parts.next(); - - osg::PrimitiveSet::Mode primMode = osg::PrimitiveSet::POINTS; - - Geometry::Type renderType = _geomTypeOverride.isSet() ? _geomTypeOverride.get() : part->getType(); - - //OE_NOTICE - // << "BuildGeomFilter: part type = " - // << Geometry::toString( part->getType() ) << ", renderType = " - // << Geometry::toString( renderType ) << std::endl; + GeometryIterator parts( input->getGeometry(), false ); + while( parts.hasMore() ) + { + Geometry* part = parts.next(); + + osg::PrimitiveSet::Mode primMode = osg::PrimitiveSet::POINTS; - const Style* myStyle = input->style().isSet() ? input->style()->get() : _style.get(); + const Style& myStyle = input->style().isSet() ? *input->style() : _style; - osg::Vec4f color = osg::Vec4(1,1,1,1); - bool tessellatePolys = true; + osg::Vec4f color = osg::Vec4(1,1,1,1); + bool tessellatePolys = true; - bool setWidth = input->style().isSet(); // otherwise it will be set globally, we assume - float width = 1.0f; + bool setWidth = input->style().isSet(); // otherwise it will be set globally, we assume + float width = 1.0f; - switch( renderType ) - { - case Geometry::TYPE_POINTSET: + switch( part->getType() ) { - _hasPoints = true; - primMode = osg::PrimitiveSet::POINTS; - const PointSymbol* point = myStyle->getSymbol(); - if (point) + case Geometry::TYPE_POINTSET: { - color = point->fill()->color(); - } - } - break; - - case Geometry::TYPE_LINESTRING: - { - _hasLines = true; - primMode = osg::PrimitiveSet::LINE_STRIP; - const LineSymbol* lineSymbol = myStyle->getSymbol(); - if (lineSymbol) - { - color = lineSymbol->stroke()->color(); - width = lineSymbol->stroke()->width().isSet() ? *lineSymbol->stroke()->width() : 1.0f; + _hasPoints = true; + primMode = osg::PrimitiveSet::POINTS; + const PointSymbol* point = myStyle.getSymbol(); + if (point) + { + color = point->fill()->color(); + } } - } - break; + break; - case Geometry::TYPE_RING: - { - _hasLines = true; - primMode = osg::PrimitiveSet::LINE_LOOP; - const LineSymbol* lineSymbol = myStyle->getSymbol(); - if (lineSymbol) + case Geometry::TYPE_LINESTRING: { - color = lineSymbol->stroke()->color(); - width = lineSymbol->stroke()->width().isSet() ? *lineSymbol->stroke()->width() : 1.0f; + _hasLines = true; + primMode = osg::PrimitiveSet::LINE_STRIP; + const LineSymbol* lineSymbol = myStyle.getSymbol(); + if (lineSymbol) + { + color = lineSymbol->stroke()->color(); + width = lineSymbol->stroke()->width().isSet() ? *lineSymbol->stroke()->width() : 1.0f; + } } - } - break; + break; - case Geometry::TYPE_POLYGON: - { - primMode = osg::PrimitiveSet::LINE_LOOP; // loop will tessellate into polys - const PolygonSymbol* poly = myStyle->getSymbol(); - if (poly) + case Geometry::TYPE_RING: { - color = poly->fill()->color(); - } - else - { - // if we have a line symbol and no polygon symbol, draw as an outline. - const LineSymbol* line = myStyle->getSymbol(); - if ( line ) + _hasLines = true; + primMode = osg::PrimitiveSet::LINE_LOOP; + const LineSymbol* lineSymbol = myStyle.getSymbol(); + if (lineSymbol) { - color = line->stroke()->color(); - width = line->stroke()->width().isSet() ? *line->stroke()->width() : 1.0f; - tessellatePolys = false; + color = lineSymbol->stroke()->color(); + width = lineSymbol->stroke()->width().isSet() ? *lineSymbol->stroke()->width() : 1.0f; } } - } - break; - } - - osg::Geometry* osgGeom = new osg::Geometry(); - - osgGeom->setUseVertexBufferObjects( true ); - osgGeom->setUseDisplayList( false ); - - if ( setWidth && width != 1.0f ) - { - osgGeom->getOrCreateStateSet()->setAttributeAndModes( - new osg::LineWidth( width ), osg::StateAttribute::ON ); - } - - if ( renderType == Geometry::TYPE_POLYGON && part->getType() == Geometry::TYPE_POLYGON && static_cast(part)->getHoles().size() > 0 ) - { - Polygon* poly = static_cast(part); - int totalPoints = poly->getTotalPointCount(); - osg::Vec3Array* allPoints = new osg::Vec3Array( totalPoints ); - - std::copy( part->begin(), part->end(), allPoints->begin() ); - osgGeom->addPrimitiveSet( new osg::DrawArrays( primMode, 0, part->size() ) ); - - int offset = part->size(); - - for( RingCollection::const_iterator h = poly->getHoles().begin(); h != poly->getHoles().end(); ++h ) - { - Geometry* hole = h->get(); - std::copy( hole->begin(), hole->end(), allPoints->begin() + offset ); - osgGeom->addPrimitiveSet( new osg::DrawArrays( primMode, offset, hole->size() ) ); - offset += hole->size(); - } - osgGeom->setVertexArray( allPoints ); - } - else - { - osgGeom->setVertexArray( part->toVec3Array() ); - osgGeom->addPrimitiveSet( new osg::DrawArrays( primMode, 0, part->size() ) ); - } - - // tessellate all polygon geometries. Tessellating each geometry separately - // with TESS_TYPE_GEOMETRY is much faster than doing the whole bunch together - // using TESS_TYPE_DRAWABLE. - - if ( renderType == Geometry::TYPE_POLYGON && tessellatePolys ) - { - osgUtil::Tessellator tess; - tess.setTessellationType( osgUtil::Tessellator::TESS_TYPE_GEOMETRY ); - tess.setWindingType( osgUtil::Tessellator::TESS_WINDING_POSITIVE ); - tess.retessellatePolygons( *osgGeom ); - } - - if ( context.isGeocentric() ) - { - double threshold = osg::DegreesToRadians( *_maxAngle_deg ); - - MeshSubdivider ms( context.referenceFrame(), context.inverseReferenceFrame() ); - //ms.setMaxElementsPerEBO( INT_MAX ); - ms.run( threshold, *osgGeom ); - } - - // set the color array. We have to do this last, otherwise it screws up any modifications - // make by the MeshSubdivider. No idea why. gw - osg::Vec4Array* colors = new osg::Vec4Array(1); - (*colors)[0] = color; - osgGeom->setColorArray( colors ); - osgGeom->setColorBinding( osg::Geometry::BIND_OVERALL ); + break; - // add the part to the geode. - _geode->addDrawable( osgGeom ); - } - - return true; -} - -bool -BuildGeometryFilter::push( Feature* input, const FilterContext& context ) -{ - if ( !input || !input->getGeometry() ) - return true; - else if ( dynamic_cast(input) ) - return pushTextAnnotation( static_cast(input), context ); - else - return pushRegularFeature( input, context ); -} - -FilterContext -BuildGeometryFilter::push( FeatureList& input, osg::ref_ptr& output, const FilterContext& context ) -{ - bool ok = true; - for( FeatureList::iterator i = input.begin(); i != input.end(); i++ ) - if ( !push( i->get(), context ) ) - ok = false; - - if ( ok ) - { - if ( _style.valid() && _geode.valid() ) - { - // could optimize this to only happen is lines or points were created .. - const LineSymbol* lineSymbol = _style->getSymbol(); - float size = 1.0; - if (lineSymbol) - size = lineSymbol->stroke()->width().value(); - _geode->getOrCreateStateSet()->setAttribute( new osg::Point(size), osg::StateAttribute::ON ); - _geode->getOrCreateStateSet()->setAttribute( new osg::LineWidth(size), osg::StateAttribute::ON ); - } - - output = _geode.release(); - - if ( context.hasReferenceFrame() ) - { - osg::MatrixTransform* delocalizer = new osg::MatrixTransform( - context.inverseReferenceFrame() ); - delocalizer->addChild( output.get() ); - output = delocalizer; - } - } - else - { - output = 0L; - } - - FilterContext outCx( context ); - outCx.setReferenceFrame( osg::Matrixd::identity() ); // clear the ref frame. - return outCx; -} - - - -static -void tessellate( osg::Geometry* geom ) -{ - osgUtil::Tessellator tess; - tess.setTessellationType( osgUtil::Tessellator::TESS_TYPE_GEOMETRY ); - tess.setWindingType( osgUtil::Tessellator::TESS_WINDING_ODD ); -// tess.setWindingType( osgUtil::Tessellator::TESS_WINDING_POSITIVE ); - tess.retessellatePolygons( *geom ); -} - -osg::Geode* -osgEarth::Features::createVolume(Geometry* geom, - double offset, - double height, - const FilterContext& context ) -{ - if ( !geom ) return 0L; - - int numRings = 0; - - // start by offsetting the input data and counting the number of rings - { - GeometryIterator i( geom ); - i.traverseMultiGeometry() = true; - i.traversePolygonHoles() = true; - while( i.hasMore() ) - { - Geometry* part = i.next(); - - if (offset != 0.0) - { - for( osg::Vec3dArray::iterator j = part->begin(); j != part->end(); j++ ) + case Geometry::TYPE_POLYGON: { - if ( context.isGeocentric() ) + primMode = osg::PrimitiveSet::LINE_LOOP; // loop will tessellate into polys + const PolygonSymbol* poly = myStyle.getSymbol(); + if (poly) { - osg::Vec3d world = context.toWorld( *j ); - // TODO: get the proper up vector; this is spherical.. or does it really matter for - // stencil volumes? - osg::Vec3d offset_vec = world; - offset_vec.normalize(); - *j = context.toLocal( world + offset_vec * offset ); //(*j) += offset_vec * offset; + _hasPolygons = true; + color = poly->fill()->color(); } else { - (*j).z() += offset; + // if we have a line symbol and no polygon symbol, draw as an outline. + _hasLines = true; + const LineSymbol* line = myStyle.getSymbol(); + if ( line ) + { + color = line->stroke()->color(); + width = line->stroke()->width().isSet() ? *line->stroke()->width() : 1.0f; + tessellatePolys = false; + } } } + break; } + + osg::Geometry* osgGeom = new osg::Geometry(); - // in the meantime, count the # of closed geoms. We will need to know this in - // order to pre-allocate the proper # of verts. - if ( part->getType() == Geometry::TYPE_POLYGON || part->getType() == Geometry::TYPE_RING ) + if ( _featureNameExpr.isSet() ) { - numRings++; + const std::string& name = input->eval( _featureNameExpr.mutable_value() ); + osgGeom->setName( name ); } - } - } - // now, go thru and remove any coplanar segments from the geometry. The tesselator will - // not work include a vert connecting two colinear segments in the tesselation, and this - // will break the stenciling logic. -#define PARALLEL_EPSILON 0.01 - GeometryIterator i( geom ); - i.traverseMultiGeometry() = true; - i.traversePolygonHoles() = true; - while( i.hasMore() ) - { - Geometry* part = i.next(); - if ( part->size() >= 3 ) - { - osg::Vec3d prevVec = part->front() - part->back(); - prevVec.normalize(); + // NOTE: benchmarking reveals VBOs to be much slower (for static data, at least) + //osgGeom->setUseVertexBufferObjects( true ); + //osgGeom->setUseDisplayList( false ); - for( osg::Vec3dArray::iterator j = part->begin(); part->size() >= 3 && j != part->end(); ) + if ( setWidth && width != 1.0f ) + { + osgGeom->getOrCreateStateSet()->setAttributeAndModes( + new osg::LineWidth( width ), osg::StateAttribute::ON ); + } + + if (_hasLines) + { + const LineSymbol* line = myStyle.getSymbol(); + if (line && line->stroke().isSet() && line->stroke()->stipple().isSet()) + { + osg::LineStipple* lineStipple = new osg::LineStipple; + lineStipple->setPattern( *line->stroke()->stipple() ); + osgGeom->getOrCreateStateSet()->setAttributeAndModes( lineStipple, osg::StateAttribute::ON ); + } + } + + if (part->getType() == Geometry::TYPE_POLYGON && static_cast(part)->getHoles().size() > 0 ) { - osg::Vec3d& p0 = *j; - osg::Vec3d& p1 = j+1 != part->end() ? *(j+1) : part->front(); - osg::Vec3d vec = p1-p0; vec.normalize(); + Polygon* poly = static_cast(part); + int totalPoints = poly->getTotalPointCount(); + osg::Vec3Array* allPoints; // = new osg::Vec3Array( totalPoints ); - // if the vectors are essentially parallel, remove the extraneous vertex. - if ( (prevVec ^ vec).length() < PARALLEL_EPSILON ) + if ( makeECEF ) { - j = part->erase( j ); - //OE_NOTICE << "removed colinear segment" << std::endl; + allPoints = new osg::Vec3Array(); + ECEF::transformAndLocalize( part->asVector(), allPoints, srs, _world2local ); } else { - ++j; - prevVec = vec; + allPoints = new osg::Vec3Array( totalPoints ); + std::copy( part->begin(), part->end(), allPoints->begin() ); } - } - } - } - - - const SpatialReference* srs = context.profile()->getSRS(); - - // total up all the points so we can pre-allocate the vertex arrays. - int num_cap_verts = geom->getTotalPointCount(); - int num_wall_verts = 2 * (num_cap_verts + numRings); // add in numRings b/c we need to close each wall + osgGeom->addPrimitiveSet( new osg::DrawArrays( primMode, 0, part->size() ) ); - osg::Geometry* walls = new osg::Geometry(); - osg::Vec3Array* verts = new osg::Vec3Array( num_wall_verts ); - walls->setVertexArray( verts ); + int offset = part->size(); - osg::Geometry* top_cap = new osg::Geometry(); - osg::Vec3Array* top_verts = new osg::Vec3Array( num_cap_verts ); - top_cap->setVertexArray( top_verts ); - - osg::Geometry* bottom_cap = new osg::Geometry(); - osg::Vec3Array* bottom_verts = new osg::Vec3Array( num_cap_verts ); - bottom_cap->setVertexArray( bottom_verts ); - - int wall_vert_ptr = 0; - int top_vert_ptr = 0; - int bottom_vert_ptr = 0; - - //double target_len = height; - - // now generate the extruded geometry. - GeometryIterator k( geom ); - while( k.hasMore() ) - { - Geometry* part = k.next(); - - unsigned int wall_part_ptr = wall_vert_ptr; - unsigned int top_part_ptr = top_vert_ptr; - unsigned int bottom_part_ptr = bottom_vert_ptr; - double part_len = 0.0; - - GLenum prim_type = part->getType() == Geometry::TYPE_POINTSET ? GL_LINES : GL_TRIANGLE_STRIP; - - for( osg::Vec3dArray::const_iterator m = part->begin(); m != part->end(); ++m ) - { - osg::Vec3d extrude_vec; + for( RingCollection::const_iterator h = poly->getHoles().begin(); h != poly->getHoles().end(); ++h ) + { + Geometry* hole = h->get(); + if ( hole->isValid() ) + { + if ( makeECEF ) + ECEF::transformAndLocalize( hole->asVector(), allPoints, srs, _world2local ); + else + std::copy( hole->begin(), hole->end(), allPoints->begin() + offset ); - if ( srs ) + osgGeom->addPrimitiveSet( new osg::DrawArrays( primMode, offset, hole->size() ) ); + offset += hole->size(); + } + } + osgGeom->setVertexArray( allPoints ); + } + else { - osg::Vec3d m_world = context.toWorld( *m ); //*m * context.inverseReferenceFrame(); - if ( context.isGeocentric() ) + if ( makeECEF ) { - osg::Vec3d p_vec = m_world; // todo: not exactly right; spherical - - osg::Vec3d unit_vec = p_vec; - unit_vec.normalize(); - p_vec = p_vec + unit_vec*height; - - extrude_vec = context.toLocal( p_vec ); //p_vec * context.referenceFrame(); + osg::Vec3Array* newPart = new osg::Vec3Array(); + ECEF::transformAndLocalize( part->asVector(), newPart, srs, _world2local ); + osgGeom->setVertexArray( newPart ); } else { - extrude_vec.set( m_world.x(), m_world.y(), height ); - extrude_vec = context.toLocal( extrude_vec ); //extrude_vec * context.referenceFrame(); + osgGeom->setVertexArray( part->toVec3Array() ); } + osgGeom->addPrimitiveSet( new osg::DrawArrays( primMode, 0, part->size() ) ); } - else + + // tessellate all polygon geometries. Tessellating each geometry separately + // with TESS_TYPE_GEOMETRY is much faster than doing the whole bunch together + // using TESS_TYPE_DRAWABLE. + + if ( part->getType() == Geometry::TYPE_POLYGON && tessellatePolys ) { - extrude_vec.set( m->x(), m->y(), height ); + osgUtil::Tessellator tess; + //tess.setTessellationType( osgUtil::Tessellator::TESS_TYPE_DRAWABLE ); + //tess.setWindingType( osgUtil::Tessellator::TESS_WINDING_ODD ); + tess.setTessellationType( osgUtil::Tessellator::TESS_TYPE_GEOMETRY ); + tess.setWindingType( osgUtil::Tessellator::TESS_WINDING_POSITIVE ); + + tess.retessellatePolygons( *osgGeom ); + + // the tessellator results in a collection of trifans, strips, etc. This step will + // consolidate those into one (or more if necessary) GL_TRIANGLES primitive. + //NOTE: this now happens elsewhere + //MeshConsolidator::run( *osgGeom ); + + // mark this geometry as DYNAMIC because otherwise the OSG optimizer will destroy it. + //osgGeom->setDataVariance( osg::Object::DYNAMIC ); } - (*top_verts)[top_vert_ptr++] = extrude_vec; - (*bottom_verts)[bottom_vert_ptr++] = *m; - - part_len += wall_vert_ptr > wall_part_ptr? - (extrude_vec - (*verts)[wall_vert_ptr-2]).length() : - 0.0; + if ( context.getSession()->getMapInfo().isGeocentric() && part->getType() != Geometry::TYPE_POINTSET ) +// if ( context.isGeocentric() && part->getType() != Geometry::TYPE_POINTSET ) + { + double threshold = osg::DegreesToRadians( *_maxAngle_deg ); - int p; + MeshSubdivider ms( _world2local, _local2world ); //context.referenceFrame(), context.inverseReferenceFrame() ); + //ms.setMaxElementsPerEBO( INT_MAX ); + if ( input->geoInterp().isSet() ) + ms.run( *osgGeom, threshold, *input->geoInterp() ); + else + ms.run( *osgGeom, threshold, *_geoInterp ); + } - p = wall_vert_ptr++; - (*verts)[p] = extrude_vec; + // NOTE! per-vertex colors makes the optimizer destroy the geometry.... + osg::Vec4Array* colors = new osg::Vec4Array(1); + (*colors)[0] = color; + osgGeom->setColorArray( colors ); + osgGeom->setColorBinding( osg::Geometry::BIND_OVERALL ); - p = wall_vert_ptr++; - (*verts)[p] = *m; + // add the part to the geode. + _geode->addDrawable( osgGeom ); } + } + + return true; +} - // close the wall if it's a ring/poly: - if ( part->getType() == Geometry::TYPE_RING || part->getType() == Geometry::TYPE_POLYGON ) - { - part_len += wall_vert_ptr > wall_part_ptr? - ((*verts)[wall_part_ptr] - (*verts)[wall_vert_ptr-2]).length() : - 0.0; +osg::Node* +BuildGeometryFilter::push( FeatureList& input, FilterContext& context ) +{ + reset(); - int p; + computeLocalizers( context ); + + bool ok = process( input, context ); + + // convert all geom to triangles and consolidate into minimal set of Geometries + if ( !_featureNameExpr.isSet() ) + { + MeshConsolidator::run( *_geode.get() ); + } + + osg::Node* result = 0L; + + if ( ok ) + { + if ( !_style.empty() && _geode.valid() ) + { + // could optimize this to only happen is lines or points were created .. + const LineSymbol* lineSymbol = _style.getSymbol(); + float size = 1.0; + if (lineSymbol) + size = lineSymbol->stroke()->width().value(); - p = wall_vert_ptr++; - (*verts)[p] = (*verts)[wall_part_ptr]; + _geode->getOrCreateStateSet()->setAttribute( new osg::Point(size), osg::StateAttribute::ON ); + _geode->getOrCreateStateSet()->setAttribute( new osg::LineWidth(size), osg::StateAttribute::ON ); - p = wall_vert_ptr++; - (*verts)[p] = (*verts)[wall_part_ptr+1]; + const PointSymbol* pointSymbol = _style.getSymbol(); + if ( pointSymbol && pointSymbol->size().isSet() ) + _geode->getOrCreateStateSet()->setAttribute( + new osg::Point( *pointSymbol->size() ), osg::StateAttribute::ON ); } - walls->addPrimitiveSet( new osg::DrawArrays( - prim_type, - wall_part_ptr, wall_vert_ptr - wall_part_ptr ) ); - - top_cap->addPrimitiveSet( new osg::DrawArrays( - osg::PrimitiveSet::LINE_LOOP, - top_part_ptr, top_vert_ptr - top_part_ptr ) ); - - // reverse the bottom verts so the front face is down: - std::reverse( bottom_verts->begin()+bottom_part_ptr, bottom_verts->begin()+bottom_vert_ptr ); - - bottom_cap->addPrimitiveSet( new osg::DrawArrays( - osg::PrimitiveSet::LINE_LOOP, - bottom_part_ptr, bottom_vert_ptr - bottom_part_ptr ) ); + result = delocalize( _geode.release() ); + } + else + { + result = 0L; } - // build solid surfaces for the caps: - tessellate( top_cap ); - tessellate( bottom_cap ); - - osg::Geode* geode = new osg::Geode(); - geode->addDrawable( walls ); - geode->addDrawable( top_cap ); - geode->addDrawable( bottom_cap ); - - return geode; + return result; } diff -Nru osgearth-2.0+dfsg/src/osgEarthFeatures/BuildTextFilter osgearth-2.1.1+dfsg/src/osgEarthFeatures/BuildTextFilter --- osgearth-2.0+dfsg/src/osgEarthFeatures/BuildTextFilter 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthFeatures/BuildTextFilter 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,56 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ + +#ifndef OSGEARTHFEATURES_BUILD_TEXT_FILTER_H +#define OSGEARTHFEATURES_BUILD_TEXT_FILTER_H 1 + +#include +#include +#include +#include +#include + +namespace osgEarth { namespace Features +{ + using namespace osgEarth; + using namespace osgEarth::Symbology; + + /** + * Builds text labels from a stream of input features. + */ + class OSGEARTHFEATURES_EXPORT BuildTextFilter : public FeaturesToNodeFilter + { + public: + BuildTextFilter( const Style& style =Style() ); + + /** The style to apply to feature geometry */ + const Style& getStyle() { return _style; } + void setStyle(const Style& s) { _style = s; } + + /** Pushes a list of features through the filter. */ + osg::Node* push( FeatureList& input, FilterContext& context ); + + protected: + osg::ref_ptr _geode; + Style _style; + }; + +} } // namespace osgEarth::Features + +#endif // OSGEARTHFEATURES_BUILD_GEOMETRY_FILTER_H diff -Nru osgearth-2.0+dfsg/src/osgEarthFeatures/BuildTextFilter.cpp osgearth-2.1.1+dfsg/src/osgEarthFeatures/BuildTextFilter.cpp --- osgearth-2.0+dfsg/src/osgEarthFeatures/BuildTextFilter.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthFeatures/BuildTextFilter.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,73 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#include +#include // this should be in symbology -gw +#include +#include +#include + +#define LC "[BuildTextFilter] " + +using namespace osgEarth; +using namespace osgEarth::Features; +using namespace osgEarth::Symbology; + +BuildTextFilter::BuildTextFilter( const Style& style ) : +_style( style ) +{ + //nop +} + +osg::Node* +BuildTextFilter::push( FeatureList& input, FilterContext& context ) +{ + osg::Node* result = 0L; + + const TextSymbol* text = _style.get(); + if ( !text ) + { + OE_WARN << LC << "Insufficient symbology (no TextSymbol)" << std::endl; + return 0L; + } + + // if a provider is set, load the plugin and create the node. + if ( !text->provider()->empty() && !text->provider().isSetTo("legacy") ) + { + LabelSourceOptions options; + options.setDriver( *text->provider() ); + osg::ref_ptr source = LabelSourceFactory::create( options ); + if ( source.valid() ) + { + result = source->createNode( input, text, context ); + } + else + { + OE_WARN << LC << "FAIL, unable to load label provider \"" << (*text->provider()) << "\"" << std::endl; + return 0L; + } + } + + else // legacy behavior... will be deprecated. + { + BuildTextOperator op; + result = op( input, text, context ); + } + + return result; +} diff -Nru osgearth-2.0+dfsg/src/osgEarthFeatures/BuildTextOperator osgearth-2.1.1+dfsg/src/osgEarthFeatures/BuildTextOperator --- osgearth-2.0+dfsg/src/osgEarthFeatures/BuildTextOperator 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthFeatures/BuildTextOperator 2011-11-04 19:44:43.000000000 +0000 @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include namespace osgEarth { namespace Features @@ -34,10 +34,15 @@ class OSGEARTHFEATURES_EXPORT BuildTextOperator { public: + BuildTextOperator(bool hideClutter=false) : _hideClutter(hideClutter) { }; + public: osg::Node* operator()( const FeatureList& features, const osgEarth::Symbology::TextSymbol *symbol, const FilterContext& context); + + protected: + bool _hideClutter; }; } } // namespace osgEarth::Features diff -Nru osgearth-2.0+dfsg/src/osgEarthFeatures/BuildTextOperator.cpp osgearth-2.1.1+dfsg/src/osgEarthFeatures/BuildTextOperator.cpp --- osgearth-2.0+dfsg/src/osgEarthFeatures/BuildTextOperator.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthFeatures/BuildTextOperator.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -17,8 +17,7 @@ * along with this program. If not, see */ #include -#include -#include +#include #include #include #include @@ -33,25 +32,11 @@ #include #include #include +#include +#include #define LC "[BuildTextOperator] " -namespace -{ - struct CullPlaneCallback : public osg::Drawable::CullCallback - { - osg::Vec3d _n; - - CullPlaneCallback( const osg::Vec3d& planeNormal ) : _n(planeNormal) { - _n.normalize(); - } - - bool cull(osg::NodeVisitor* nv, osg::Drawable* drawable, osg::RenderInfo* renderInfo) const { - return nv && nv->getEyePoint() * _n <= 0; - } - }; -} - using namespace osgEarth::Symbology; using namespace osgEarth::Features; @@ -86,7 +71,7 @@ break; } // Add attribute and continue - out += feature->getAttr( content.substr(pos, d-pos) ); + out += feature->getString( content.substr(pos, d-pos) ); pos = d+1; } @@ -103,6 +88,8 @@ bool removeDuplicateLabels = symbol->removeDuplicateLabels().isSet() ? symbol->removeDuplicateLabels().get() : false; + StringExpression contentExpr = *symbol->content(); + osg::Geode* result = new osg::Geode; for (FeatureList::const_iterator itr = features.begin(); itr != features.end(); ++itr) { @@ -110,23 +97,10 @@ if (!feature->getGeometry()) continue; std::string text; - //If the feature is a TextAnnotation, just get the value from it - TextAnnotation* annotation = dynamic_cast(feature); - if (annotation) - { - text = annotation->text(); - } - else if (symbol->content().isSet()) + if (symbol->content().isSet()) { - //Get the text from the specified content and referenced attributes - std::string content = symbol->content().value(); - text = parseAttributes(feature, content, symbol->contentAttributeDelimiter().value()); - } - else if (symbol->attribute().isSet()) - { - //Get the text from the specified attribute - std::string attr = symbol->attribute().value(); - text = feature->getAttr(attr); + //Get the text from the specified content and referenced attributes + text = feature->eval( contentExpr ); } if (text.empty()) continue; @@ -186,10 +160,10 @@ } else { - position = geom->getBounds().center(); + position = geom->getBounds().center(); } } - + osgText::Text* t = new osgText::Text(); t->setText( text ); @@ -201,7 +175,7 @@ t->setFont( font ); t->setAutoRotateToScreen( rotateToScreen ); - + TextSymbol::SizeMode sizeMode = symbol->sizeMode().isSet() ? symbol->sizeMode().get() : TextSymbol::SIZEMODE_SCREEN; if (sizeMode == TextSymbol::SIZEMODE_SCREEN) { t->setCharacterSizeMode( osgText::TextBase::SCREEN_COORDS ); @@ -215,10 +189,10 @@ //TODO: We need to do something to account for autotransformed text that is under a LOD. Setting the initial bound works sometimes but not all the time. if (rotateToScreen) { - //Set the initial bound so that OSG will traverse the text even if it's under an LOD. - osg::BoundingBox bb; - bb.expandBy( osg::BoundingSphere(position, size)); - t->setInitialBound( bb); + //Set the initial bound so that OSG will traverse the text even if it's under an LOD. + osg::BoundingBox bb; + bb.expandBy( osg::BoundingSphere(position, size)); + t->setInitialBound( bb); }*/ //t->setCharacterSizeMode( osgText::TextBase::OBJECT_COORDS_WITH_MAXIMUM_SCREEN_SIZE_CAPPED_BY_FONT_HEIGHT ); //t->setCharacterSize( 300000.0f ); @@ -239,12 +213,27 @@ if ( context.isGeocentric() ) { // install a cluster culler - t->setCullCallback( new CullPlaneCallback( position * context.inverseReferenceFrame() ) ); + t->setCullCallback( new CullDrawableByNormal(position * context.inverseReferenceFrame()) ); } - result->addDrawable( t ); + if (_hideClutter) + { + osg::BoundingBox tBound = t->getBound(); + osg::ref_ptr intersector = new osgUtil::PolytopeIntersector(osgUtil::Intersector::MODEL, tBound.xMin(), tBound.yMin(), tBound.xMax(), tBound.yMax()); + osgUtil::IntersectionVisitor intersectVisitor(intersector.get()); + result->accept(intersectVisitor); - if (removeDuplicateLabels) labelNames.insert(text); + if (!intersector->containsIntersections()) + { + result->addDrawable( t ); + if (removeDuplicateLabels) labelNames.insert(text); + } + } + else + { + result->addDrawable( t ); + if (removeDuplicateLabels) labelNames.insert(text); + } } return result; } diff -Nru osgearth-2.0+dfsg/src/osgEarthFeatures/CentroidFilter osgearth-2.1.1+dfsg/src/osgEarthFeatures/CentroidFilter --- osgearth-2.0+dfsg/src/osgEarthFeatures/CentroidFilter 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthFeatures/CentroidFilter 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,45 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ + +#ifndef OSGEARTHFEATURES_CENTROID_FILTER_H +#define OSGEARTHFEATURES_CENTROID_FILTER_H 1 + +#include +#include + +namespace osgEarth { namespace Features +{ + using namespace osgEarth; + using namespace osgEarth::Symbology; + + /** + * Replaces each feature's geometry with a single-point centroid. + */ + class OSGEARTHFEATURES_EXPORT CentroidFilter : public FeatureFilter + { + public: + CentroidFilter(); + + public: + virtual FilterContext push( FeatureList& input, FilterContext& context ); + }; + +} } // namespace osgEarth::Features + +#endif // OSGEARTHFEATURES_CENTROID_FILTER_H diff -Nru osgearth-2.0+dfsg/src/osgEarthFeatures/CentroidFilter.cpp osgearth-2.1.1+dfsg/src/osgEarthFeatures/CentroidFilter.cpp --- osgearth-2.0+dfsg/src/osgEarthFeatures/CentroidFilter.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthFeatures/CentroidFilter.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,51 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#include + +#define LC "[CentroidFilter] " + +using namespace osgEarth; +using namespace osgEarth::Features; + +//------------------------------------------------------------------------ + +CentroidFilter::CentroidFilter() +{ + //NOP +} + +FilterContext +CentroidFilter::push(FeatureList& features, FilterContext& context ) +{ + for( FeatureList::iterator i = features.begin(); i != features.end(); ++i ) + { + Feature* f = i->get(); + + Geometry* geom = f->getGeometry(); + if ( !geom ) + continue; + + PointSet* newGeom = new PointSet(); + newGeom->push_back( geom->getBounds().center() ); + + f->setGeometry( newGeom ); + } + + return context; +} diff -Nru osgearth-2.0+dfsg/src/osgEarthFeatures/CMakeLists.txt osgearth-2.1.1+dfsg/src/osgEarthFeatures/CMakeLists.txt --- osgearth-2.0+dfsg/src/osgEarthFeatures/CMakeLists.txt 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthFeatures/CMakeLists.txt 2011-11-04 19:44:43.000000000 +0000 @@ -12,45 +12,82 @@ SET(HEADER_PATH ${OSGEARTH_SOURCE_DIR}/include/${LIB_NAME}) SET(LIB_PUBLIC_HEADERS - Annotation + AltitudeFilter BufferFilter BuildGeometryFilter + BuildTextFilter + BuildTextOperator + CentroidFilter Common ConvertTypeFilter + CropFilter + ExtrudeGeometryFilter Feature + FeatureCursor + FeatureDisplayLayout + FeatureGeometryIndex FeatureGridder + FeatureListSource + FeatureModelGraph FeatureModelSource + FeatureNode FeatureSource - FeatureSymbolizer FeatureTileSource Filter + FilterContext + GeometryCompiler + GeometryUtils + LabelSource + OgrUtils + OptimizerHints ResampleFilter ScaleFilter - BuildTextOperator + Session + ScatterFilter + SubstituteModelFilter TransformFilter VirtualFeatureSource ) ADD_LIBRARY(${LIB_NAME} SHARED ${LIB_PUBLIC_HEADERS} - Annotation.cpp + AltitudeFilter.cpp BufferFilter.cpp - BuildGeometryFilter.cpp + BuildGeometryFilter.cpp + BuildTextFilter.cpp + BuildTextOperator.cpp + CentroidFilter.cpp ConvertTypeFilter.cpp + CropFilter.cpp + ExtrudeGeometryFilter.cpp Feature.cpp + FeatureCursor.cpp + FeatureDisplayLayout.cpp + FeatureGeometryIndex.cpp FeatureGridder.cpp + FeatureListSource.cpp + FeatureModelGraph.cpp FeatureModelSource.cpp + FeatureNode.cpp FeatureSource.cpp - FeatureSymbolizer.cpp FeatureTileSource.cpp Filter.cpp + FilterContext.cpp + GeometryCompiler.cpp + GeometryUtils.cpp + LabelSource.cpp + OptimizerHints.cpp ResampleFilter.cpp ScaleFilter.cpp - BuildTextOperator.cpp + Session.cpp + ScatterFilter.cpp + SubstituteModelFilter.cpp TransformFilter.cpp VirtualFeatureSource.cpp ) +INCLUDE_DIRECTORIES(${GDAL_INCLUDE_DIR} ) + IF (WIN32) LINK_EXTERNAL(${LIB_NAME} ${TARGET_EXTERNAL_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${MATH_LIBRARY}) diff -Nru osgearth-2.0+dfsg/src/osgEarthFeatures/ConvertTypeFilter osgearth-2.1.1+dfsg/src/osgEarthFeatures/ConvertTypeFilter --- osgearth-2.0+dfsg/src/osgEarthFeatures/ConvertTypeFilter 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthFeatures/ConvertTypeFilter 2011-11-04 19:44:43.000000000 +0000 @@ -50,12 +50,10 @@ return _toType; } public: - virtual FilterContext push( FeatureList& input, const FilterContext& context ); + virtual FilterContext push( FeatureList& input, FilterContext& context ); protected: Symbology::Geometry::Type _toType; - - bool push( Feature* input, const FilterContext& context ); }; } } // namespace osgEarth::Features diff -Nru osgearth-2.0+dfsg/src/osgEarthFeatures/ConvertTypeFilter.cpp osgearth-2.1.1+dfsg/src/osgEarthFeatures/ConvertTypeFilter.cpp --- osgearth-2.0+dfsg/src/osgEarthFeatures/ConvertTypeFilter.cpp 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthFeatures/ConvertTypeFilter.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -43,41 +43,8 @@ //NOP } -bool -ConvertTypeFilter::push( Feature* input, const FilterContext& context ) -{ - if ( !input || !input->getGeometry() ) - return true; - - if ( input->getGeometry()->getComponentType() == _toType ) - return true; - - bool success = true; - - Geometry* geom = input->getGeometry()->cloneAs( _toType ); - input->setGeometry( geom ); - - //GeometryIterator i( input->getGeometry() ); - //i.traversePolygonHoles() = false; - //i.traverseMultiGeometry() = false; - - //while( i.hasMore() ) - //{ - // Geometry* part = i.next(); - // if ( _toType != part->getComponentType() ) - // { - // part = part->cloneAs( _toType ); - // if ( part ) - // input->setGeometry( part ); - // } - //} - - return success; -} - - FilterContext -ConvertTypeFilter::push( FeatureList& input, const FilterContext& context ) +ConvertTypeFilter::push( FeatureList& input, FilterContext& context ) { if ( !isSupported() ) { @@ -87,8 +54,13 @@ bool ok = true; for( FeatureList::iterator i = input.begin(); i != input.end(); ++i ) - if ( !push( i->get(), context ) ) - ok = false; + { + Feature* input = i->get(); + if ( input && input->getGeometry() && input->getGeometry()->getComponentType() != _toType ) + { + input->setGeometry( input->getGeometry()->cloneAs(_toType) ); + } + } return context; } diff -Nru osgearth-2.0+dfsg/src/osgEarthFeatures/CropFilter osgearth-2.1.1+dfsg/src/osgEarthFeatures/CropFilter --- osgearth-2.0+dfsg/src/osgEarthFeatures/CropFilter 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthFeatures/CropFilter 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,62 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ + +#ifndef OSGEARTHFEATURES_CROP_FILTER_H +#define OSGEARTHFEATURES_CROP_FILTER_H 1 + +#include +#include +#include +#include +#include + +namespace osgEarth { namespace Features +{ + using namespace osgEarth; + using namespace osgEarth::Symbology; + + /** + * Crops feature geometry to an extent, either by centroid or by actually + * cutting the geometry. + */ + class OSGEARTHFEATURES_EXPORT CropFilter : public FeatureFilter + { + public: + enum Method + { + METHOD_CENTROID, // include a feature if its centroid is included + METHOD_CROPPING // crop a feature's geometry to the target extent + }; + + public: + CropFilter( Method method =METHOD_CENTROID ); + + optional& method() { return _method; } + const optional& method() const { return _method; } + + public: + virtual FilterContext push( FeatureList& input, FilterContext& context ); + + protected: + optional _method; + }; + +} } // namespace osgEarth::Features + +#endif // OSGEARTHFEATURES_BUFFER_FILTER_H diff -Nru osgearth-2.0+dfsg/src/osgEarthFeatures/CropFilter.cpp osgearth-2.1.1+dfsg/src/osgEarthFeatures/CropFilter.cpp --- osgearth-2.0+dfsg/src/osgEarthFeatures/CropFilter.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthFeatures/CropFilter.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,147 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#include + +#define LC "[CropFilter] " + +using namespace osgEarth; +using namespace osgEarth::Features; +using namespace osgEarth::Symbology; + +CropFilter::CropFilter( CropFilter::Method method ) : +_method( method ) +{ + //nop +} + +FilterContext +CropFilter::push( FeatureList& input, FilterContext& context ) +{ + if ( !context.extent().isSet() ) + { + OE_WARN << LC << "Extent is not set (and is required)" << std::endl; + return context; + } + + const GeoExtent& extent = *context.extent(); + + GeoExtent newExtent( extent.getSRS() ); + + if ( _method == METHOD_CENTROID ) + { + for( FeatureList::iterator i = input.begin(); i != input.end(); ) + { + bool keepFeature = false; + + Feature* feature = i->get(); + Geometry* featureGeom = feature->getGeometry(); + + if ( featureGeom && featureGeom->isValid() ) + { + Bounds bounds = featureGeom->getBounds(); + if ( bounds.isValid() ) + { + osg::Vec3d centroid = bounds.center(); + if ( extent.contains( centroid.x(), centroid.y() ) ) + { + keepFeature = true; + newExtent.expandToInclude( bounds ); + } + } + } + + if ( keepFeature ) + ++i; + else + i = input.erase( i ); + } + } + + else // METHOD_CROPPING (requires GEOS) + { +#ifdef OSGEARTH_HAVE_GEOS + + // create the intersection polygon: + osg::ref_ptr poly; + + for( FeatureList::iterator i = input.begin(); i != input.end(); ) + { + bool keepFeature = false; + + Feature* feature = i->get(); + Symbology::Geometry* featureGeom = feature->getGeometry(); + if ( featureGeom && featureGeom->isValid() ) + { + // test for trivial acceptance: + const Bounds bounds = featureGeom->getBounds(); + if ( !bounds.isValid() ) + { + //nop + } + + else if ( extent.contains( bounds ) ) + { + keepFeature = true; + newExtent.expandToInclude( bounds ); + } + + // then move on to the cropping operation: + else + { + if ( !poly.valid() ) + { + poly = new Symbology::Polygon(); + poly->push_back( osg::Vec3d( extent.xMin(), extent.yMin(), 0 )); + poly->push_back( osg::Vec3d( extent.xMax(), extent.yMin(), 0 )); + poly->push_back( osg::Vec3d( extent.xMax(), extent.yMax(), 0 )); + poly->push_back( osg::Vec3d( extent.xMin(), extent.yMax(), 0 )); + } + + osg::ref_ptr croppedGeometry; + if ( featureGeom->crop( poly.get(), croppedGeometry ) ) + { + if ( croppedGeometry->isValid() ) + { + feature->setGeometry( croppedGeometry.get() ); + keepFeature = true; + newExtent.expandToInclude( croppedGeometry->getBounds() ); + } + } + } + } + + if ( keepFeature ) + ++i; + else + i = input.erase( i ); + } + +#else // OSGEARTH_HAVE_GEOS + + OE_WARN << "CropFilter - METHOD_CROPPING not available - please compile osgEarth with GEOS" << std::endl; + return context; + +#endif + } + + FilterContext newContext = context; + newContext.extent() = newExtent; + + return newContext; +} diff -Nru osgearth-2.0+dfsg/src/osgEarthFeatures/ExtrudeGeometryFilter osgearth-2.1.1+dfsg/src/osgEarthFeatures/ExtrudeGeometryFilter --- osgearth-2.0+dfsg/src/osgEarthFeatures/ExtrudeGeometryFilter 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthFeatures/ExtrudeGeometryFilter 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,134 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ + +#ifndef OSGEARTHFEATURES_EXTRUDE_GEOMETRY_FILTER_H +#define OSGEARTHFEATURES_EXTRUDE_GEOMETRY_FILTER_H 1 + +#include +#include +#include +#include +#include +#include + +namespace osgEarth { namespace Features +{ + using namespace osgEarth; + using namespace osgEarth::Symbology; + + /** + * Extrudes footprint geometry into 3D geometry + */ + class OSGEARTHFEATURES_EXPORT ExtrudeGeometryFilter : public FeaturesToNodeFilter + { + public: + struct HeightCallback : public osg::Referenced + { + virtual float operator()( Feature* input, const FilterContext& cx ) =0; + }; + + public: + + /** Constructs a new filter that will extrude footprints */ + ExtrudeGeometryFilter(); + + /** + * Sets the style that will govern the geometry generation. + */ + void setStyle( const Style& style ); + + /** + * Pushes a list of features through the filter. + */ + osg::Node* push( FeatureList& input, FilterContext& context ); + + public: // properties + + /** + * Sets the maximum wall angle that doesn't require a new normal vector + */ + void setWallAngleThreshold( float angle_deg ) { _wallAngleThresh_deg = angle_deg; } + + /** + * Sets the expression to evaluate when setting a feature name. + * NOTE: setting this forces geometry-merging to OFF + */ + void setFeatureNameExpr( const StringExpression& expr ) { _featureNameExpr = expr; } + const StringExpression& getFeatureNameExpr() const { return _featureNameExpr; } + + protected: + + // a set of geodes indexed by stateset pointer, for pre-sorting geodes based on + // their texture usage + typedef std::map > SortedGeodeMap; + SortedGeodeMap _geodes; + osg::ref_ptr _noTextureStateSet; + + optional _maxAngle_deg; + optional _mergeGeometry; + float _wallAngleThresh_deg; + float _cosWallAngleThresh; + StringExpression _featureNameExpr; + osg::ref_ptr _heightCallback; + optional _heightOffsetExpr; + optional _heightExpr; + + Style _style; + bool _styleDirty; + + osg::ref_ptr _extrusionSymbol; + osg::ref_ptr _wallSkinSymbol; + osg::ref_ptr _wallPolygonSymbol; + osg::ref_ptr _roofSkinSymbol; + osg::ref_ptr _roofPolygonSymbol; + osg::ref_ptr _outlineSymbol; + osg::ref_ptr _wallResLib; + osg::ref_ptr _roofResLib; + + void reset( const FilterContext& context ); + + void addDrawable( + osg::Drawable* drawable, + osg::StateSet* stateSet, + const std::string& name ); + + bool process( + FeatureList& input, + FilterContext& context ); + + bool extrudeGeometry( + const Geometry* input, + double height, + double offset, + bool uniformHeight, + osg::Geometry* walls, + osg::Geometry* top_cap, + osg::Geometry* bottom_cap, + osg::Geometry* outline, + const osg::Vec4& wallColor, + const osg::Vec4& roofColor, + const osg::Vec4& outlineColor, + const SkinResource* wallSkin, + const SkinResource* roofSkin, + FilterContext& cx ); + }; + +} } // namespace osgEarth::Features + +#endif // OSGEARTHFEATURES_BUILD_GEOMETRY_FILTER_H diff -Nru osgearth-2.0+dfsg/src/osgEarthFeatures/ExtrudeGeometryFilter.cpp osgearth-2.1.1+dfsg/src/osgEarthFeatures/ExtrudeGeometryFilter.cpp --- osgearth-2.0+dfsg/src/osgEarthFeatures/ExtrudeGeometryFilter.cpp 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthFeatures/ExtrudeGeometryFilter.cpp 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,886 @@ +/* -*-c++-*- */ +/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph + * Copyright 2008-2010 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 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LC "[ExtrudeGeometryFilter] " + +using namespace osgEarth; +using namespace osgEarth::Features; +using namespace osgEarth::Symbology; + +namespace +{ + // Calculates the rotation angle of a shape. This conanically applies to + // buildings; it finds the longest edge and compares its angle to the + // x-axis to determine a rotation value. This method is used so we can + // properly rotate textures for rooftop application. + float getApparentRotation( const Geometry* geom ) + { + Segment n; + double maxLen2 = 0.0; + ConstSegmentIterator i( geom, true ); + while( i.hasMore() ) + { + Segment s = i.next(); + double len2 = (s.second - s.first).length2(); + if ( len2 > maxLen2 ) + { + maxLen2 = len2; + n = s; + } + } + + const osg::Vec3d& p1 = n.first.x() < n.second.x() ? n.first : n.second; + const osg::Vec3d& p2 = n.first.x() < n.second.x() ? n.second : n.first; + + return atan2( p2.x()-p1.x(), p2.y()-p1.y() ); + } +} + +//------------------------------------------------------------------------ + +ExtrudeGeometryFilter::ExtrudeGeometryFilter() : +_maxAngle_deg ( 5.0 ), +_mergeGeometry ( true ), +_wallAngleThresh_deg( 60.0 ), +_styleDirty ( true ) +{ + //NOP +} + +void +ExtrudeGeometryFilter::setStyle( const Style& style ) +{ + _style = style; + _styleDirty = true; +} + +void +ExtrudeGeometryFilter::reset( const FilterContext& context ) +{ + _cosWallAngleThresh = cos( _wallAngleThresh_deg ); + _geodes.clear(); + + if ( _styleDirty ) + { + const StyleSheet* sheet = context.getSession()->styles(); + + _wallSkinSymbol = 0L; + _wallPolygonSymbol = 0L; + _roofSkinSymbol = 0L; + _roofPolygonSymbol = 0L; + _extrusionSymbol = 0L; + _outlineSymbol = 0L; + + _extrusionSymbol = _style.get(); + if ( _extrusionSymbol.valid() ) + { + // make a copy of the height expression so we can use it: + if ( _extrusionSymbol->heightExpression().isSet() ) + { + _heightExpr = *_extrusionSymbol->heightExpression(); + } + + // account for a "height" value that is relative to ZERO (MSL/HAE) + AltitudeSymbol* alt = _style.get(); + if ( alt && !_extrusionSymbol->heightExpression().isSet() ) + { + if (alt->clamping() == AltitudeSymbol::CLAMP_ABSOLUTE || + alt->clamping() == AltitudeSymbol::CLAMP_RELATIVE_TO_TERRAIN ) + { + _heightExpr = NumericExpression( "0-[__max_hat]" ); + } + } + + // attempt to extract the wall symbols: + if ( _extrusionSymbol->wallStyleName().isSet() && sheet != 0L ) + { + const Style* wallStyle = sheet->getStyle( *_extrusionSymbol->wallStyleName(), false ); + if ( wallStyle ) + { + _wallSkinSymbol = wallStyle->get(); + _wallPolygonSymbol = wallStyle->get(); + } + } + + // attempt to extract the rooftop symbols: + if ( _extrusionSymbol->roofStyleName().isSet() && sheet != 0L ) + { + const Style* roofStyle = sheet->getStyle( *_extrusionSymbol->roofStyleName(), false ); + if ( roofStyle ) + { + _roofSkinSymbol = roofStyle->get(); + _roofPolygonSymbol = roofStyle->get(); + } + } + + // if there's a line symbol, use it to outline the extruded data. + _outlineSymbol = _style.get(); + } + + // backup plan for skin symbols: + const SkinSymbol* skin = _style.get(); + if ( skin ) + { + if ( !_wallSkinSymbol.valid() ) + _wallSkinSymbol = skin; + if ( !_roofSkinSymbol.valid() ) + _roofSkinSymbol = skin; + } + + // backup plan for poly symbols: + const PolygonSymbol* poly = _style.get(); + if ( poly ) + { + if ( !_wallPolygonSymbol.valid() ) + _wallPolygonSymbol = poly; + if ( !_roofPolygonSymbol.valid() ) + _roofPolygonSymbol = poly; + } + + _styleDirty = false; + } +} + +bool +ExtrudeGeometryFilter::extrudeGeometry(const Geometry* input, + double height, + double heightOffset, + bool flatten, + osg::Geometry* walls, + osg::Geometry* roof, + osg::Geometry* base, + osg::Geometry* outline, + const osg::Vec4& wallColor, + const osg::Vec4& roofColor, + const osg::Vec4& outlineColor, + const SkinResource* wallSkin, + const SkinResource* roofSkin, + FilterContext& cx ) +{ + //todo: establish reference frame for going to geocentric. This will ultimately + // passed in to the function. + const SpatialReference* srs = cx.extent()->getSRS(); + + // whether to convert the final geometry to localized ECEF + bool makeECEF = cx.getSession()->getMapInfo().isGeocentric(); + + bool made_geom = false; + + double tex_width_m = wallSkin ? *wallSkin->imageWidth() : 1.0; + double tex_height_m = wallSkin ? *wallSkin->imageHeight() : 1.0; + bool tex_repeats_y = wallSkin ? *wallSkin->isTiled() : false; + bool useColor = !wallSkin || wallSkin->texEnvMode() != osg::TexEnv::DECAL; + + bool isPolygon = input->getComponentType() == Geometry::TYPE_POLYGON; + + unsigned pointCount = input->getTotalPointCount(); + + // If we are extruding a polygon, and applying a wall texture, we need an extra + // point in the geometry in order to close the polygon and generate a unique + // texture coordinate for that final point. + bool isSkinnedPolygon = isPolygon && wallSkin != 0L; + + // Total number of verts. Add 2 to close a polygon (necessary so the first and last + // points can have unique texture coordinates) + unsigned numWallVerts = 2 * pointCount + (isSkinnedPolygon? (2 * input->getNumGeometries()) : 0); + + // create all the OSG geometry components + osg::Vec3Array* verts = new osg::Vec3Array( numWallVerts ); + walls->setVertexArray( verts ); + + osg::Vec2Array* wallTexcoords = 0L; + if ( wallSkin ) + { + wallTexcoords = new osg::Vec2Array( numWallVerts ); + walls->setTexCoordArray( 0, wallTexcoords ); + } + + if ( useColor ) + { + // per-vertex colors are necessary if we are going to use the MeshConsolidator -gw + osg::Vec4Array* colors = new osg::Vec4Array(); + colors->reserve( numWallVerts ); + colors->assign( numWallVerts, wallColor ); + walls->setColorArray( colors ); + walls->setColorBinding( osg::Geometry::BIND_PER_VERTEX ); + //osg::Vec4Array* colors = new osg::Vec4Array( 1 ); + //(*colors)[0] = wallColor; + //walls->setColorArray( colors ); + //walls->setColorBinding( osg::Geometry::BIND_OVERALL ); + } + + // set up rooftop tessellation and texturing, if necessary: + osg::Vec3Array* roofVerts = 0L; + osg::Vec2Array* roofTexcoords = 0L; + float roofRotation = 0.0f; + Bounds roofBounds; + float sinR, cosR; + double roofTexSpanX, roofTexSpanY; + osg::ref_ptr roofProjSRS; + + if ( roof ) + { + roofVerts = new osg::Vec3Array( pointCount ); + roof->setVertexArray( roofVerts ); + + // per-vertex colors are necessary if we are going to use the MeshConsolidator -gw + osg::Vec4Array* roofColors = new osg::Vec4Array(); + roofColors->reserve( pointCount ); + roofColors->assign( pointCount, roofColor ); + roof->setColorArray( roofColors ); + roof->setColorBinding( osg::Geometry::BIND_PER_VERTEX ); + //osg::Vec4Array* roofColors = new osg::Vec4Array( 1 ); + //(*roofColors)[0] = roofColor; + //roof->setColorArray( roofColors ); + //roof->setColorBinding( osg::Geometry::BIND_OVERALL ); + + if ( roofSkin ) + { + roofTexcoords = new osg::Vec2Array( pointCount ); + roof->setTexCoordArray( 0, roofTexcoords ); + + // Get the orientation of the geometry. This is a hueristic that will help + // us align the roof skin texture properly. TODO: make this optional? It makes + // sense for buildings and such, but perhaps not for all extruded shapes. + roofRotation = getApparentRotation( input ); + + roofBounds = input->getBounds(); + + // if our data is lat/long, we need to reproject the geometry and the bounds into a projected + // coordinate system in order to properly generate tex coords. + if ( srs->isGeographic() ) + { + osg::Vec2d geogCenter = roofBounds.center2d(); + roofProjSRS = srs->createUTMFromLongitude( Angular(geogCenter.x()) ); + roofBounds.transform( srs, roofProjSRS.get() ); + osg::ref_ptr projectedInput = input->clone(); + srs->transformPoints( roofProjSRS.get(), projectedInput->asVector() ); + roofRotation = getApparentRotation( projectedInput.get() ); + } + else + { + roofRotation = getApparentRotation( input ); + } + + sinR = sin(roofRotation); + cosR = cos(roofRotation); + + if ( !roofSkin->isTiled().value() ) + { + //note: doesn't really work + roofTexSpanX = cosR*roofBounds.width() - sinR*roofBounds.height(); + roofTexSpanY = sinR*roofBounds.width() + cosR*roofBounds.height(); + } + else + { + roofTexSpanX = roofSkin->imageWidth().isSet() ? *roofSkin->imageWidth() : roofSkin->imageHeight().isSet() ? *roofSkin->imageHeight() : 10.0; + if ( roofTexSpanX <= 0.0 ) roofTexSpanX = 10.0; + roofTexSpanY = roofSkin->imageHeight().isSet() ? *roofSkin->imageHeight() : roofSkin->imageWidth().isSet() ? *roofSkin->imageWidth() : 10.0; + if ( roofTexSpanY <= 0.0 ) roofTexSpanY = 10.0; + } + } + } + + osg::Vec3Array* baseVerts = NULL; + if ( base ) + { + baseVerts = new osg::Vec3Array( pointCount ); + base->setVertexArray( baseVerts ); + } + + osg::Vec3Array* outlineVerts = 0L; + osg::Vec3Array* outlineNormals = 0L; + if ( outline ) + { + outlineVerts = new osg::Vec3Array( numWallVerts ); + outline->setVertexArray( outlineVerts ); + + osg::Vec4Array* outlineColors = new osg::Vec4Array(); + outlineColors->reserve( numWallVerts ); + outlineColors->assign( numWallVerts, outlineColor ); + outline->setColorArray( outlineColors ); + outline->setColorBinding( osg::Geometry::BIND_PER_VERTEX ); + + // cop out, just point all the outline normals up. fix this later. + outlineNormals = new osg::Vec3Array(); + outlineNormals->reserve( numWallVerts ); + outlineNormals->assign( numWallVerts, osg::Vec3(0,0,1) ); + outline->setNormalArray( outlineNormals ); + } + + unsigned wallVertPtr = 0; + unsigned roofVertPtr = 0; + unsigned baseVertPtr = 0; + + double targetLen = -DBL_MAX; + osg::Vec3d minLoc(DBL_MAX, DBL_MAX, DBL_MAX); + double minLoc_len = DBL_MAX; + osg::Vec3d maxLoc(0,0,0); + double maxLoc_len = 0; + + // Initial pass over the geometry does two things: + // 1: Calculate the minimum Z across all parts. + // 2: Establish a "target length" for extrusion + + double absHeight = fabs(height); + + ConstGeometryIterator zfinder( input ); + while( zfinder.hasMore() ) + { + const Geometry* geom = zfinder.next(); + for( Geometry::const_iterator m = geom->begin(); m != geom->end(); ++m ) + { + osg::Vec3d m_point = *m; + + if ( m_point.z() + absHeight > targetLen ) + targetLen = m_point.z() + absHeight; + + if (m_point.z() < minLoc.z()) + minLoc = m_point; + + if (m_point.z() > maxLoc.z()) + maxLoc = m_point; + } + } + + // apply the height offsets + height -= heightOffset; + targetLen -= heightOffset; + + // now generate the extruded geometry. + ConstGeometryIterator iter( input ); + while( iter.hasMore() ) + { + const Geometry* part = iter.next(); + + double tex_height_m_adj = tex_height_m; + + unsigned wallPartPtr = wallVertPtr; + unsigned roofPartPtr = roofVertPtr; + unsigned basePartPtr = baseVertPtr; + double partLen = 0.0; + double maxHeight = 0.0; + + maxHeight = targetLen - minLoc.z(); + + // Adjust the texture height so it is a multiple of the maximum height + double div = osg::round(maxHeight / tex_height_m); + if (div == 0) div = 1; //Prevent divide by zero + tex_height_m_adj = maxHeight / div; + + osg::DrawElementsUInt* idx = new osg::DrawElementsUInt( GL_TRIANGLES ); + + for( Geometry::const_iterator m = part->begin(); m != part->end(); ++m ) + { + osg::Vec3d basePt = *m; + osg::Vec3d roofPt; + + if ( height >= 0 ) + { + if ( flatten ) + roofPt.set( basePt.x(), basePt.y(), targetLen ); + else + roofPt.set( basePt.x(), basePt.y(), basePt.z() + height ); + } + else // height < 0 + { + roofPt = *m; + basePt.z() += height; + } + + // add to the approprate vertex lists: + int p = wallVertPtr; + + // figure out the rooftop texture coordinates before doing any + // transformations: + if ( roofSkin ) + { + double xr, yr; + + if ( srs->isGeographic() ) + { + osg::Vec3d projRoofPt; + srs->transform( roofPt, roofProjSRS.get(), projRoofPt ); + xr = (projRoofPt.x() - roofBounds.xMin()); + yr = (projRoofPt.y() - roofBounds.yMin()); + } + else + { + xr = (roofPt.x() - roofBounds.xMin()); + yr = (roofPt.y() - roofBounds.yMin()); + } + + float u = (cosR*xr - sinR*yr) / roofTexSpanX; + float v = (sinR*xr + cosR*yr) / roofTexSpanY; + + (*roofTexcoords)[roofVertPtr].set( u, v ); + } + + if ( makeECEF ) + { + ECEF::transformAndLocalize( basePt, basePt, srs, _world2local ); + ECEF::transformAndLocalize( roofPt, roofPt, srs, _world2local ); + } + + if ( base ) + (*baseVerts)[baseVertPtr] = basePt; + if ( roof ) + (*roofVerts)[roofVertPtr] = roofPt; + + baseVertPtr++; + roofVertPtr++; + + (*verts)[p] = roofPt; + (*verts)[p+1] = basePt; + + if ( outline ) + { + (*outlineVerts)[p] = roofPt; + (*outlineVerts)[p+1] = basePt; + } + + partLen += wallVertPtr > wallPartPtr ? ((*verts)[p] - (*verts)[p-2]).length() : 0.0; + double h = tex_repeats_y ? -((*verts)[p] - (*verts)[p+1]).length() : -tex_height_m_adj; + + if ( wallSkin ) + { + (*wallTexcoords)[p].set( partLen/tex_width_m, 0.0f ); + (*wallTexcoords)[p+1].set( partLen/tex_width_m, h/tex_height_m_adj ); + } + + // form the 2 triangles + if ( (m+1) == part->end() ) + { + if ( isPolygon ) + { + // end of the wall; loop around to close it off. + if ( isSkinnedPolygon ) + { + // if we requested an extra geometry point, that means we are generating + // a polygon-closing line so we can have a unique texcoord for it. + idx->push_back(wallVertPtr); + idx->push_back(wallVertPtr+1); + idx->push_back(wallVertPtr+2); + + idx->push_back(wallVertPtr+1); + idx->push_back(wallVertPtr+3); + idx->push_back(wallVertPtr+2); + + (*verts)[p+2] = (*verts)[wallPartPtr]; + (*verts)[p+3] = (*verts)[wallPartPtr+1]; + + if ( wallSkin ) + { + partLen += ((*verts)[p+2] - (*verts)[p]).length(); + double h = tex_repeats_y ? -((*verts)[p+2] - (*verts)[p+3]).length() : -tex_height_m_adj; + (*wallTexcoords)[p+2].set( partLen/tex_width_m, 0.0f ); + (*wallTexcoords)[p+3].set( partLen/tex_width_m, h/tex_height_m_adj ); + } + + wallVertPtr += 2; + } + else + { + // either not a poly, or no wall skin, so we can share the polygon-closing + // loop point. + idx->push_back(wallVertPtr); + idx->push_back(wallVertPtr+1); + idx->push_back(wallPartPtr); + + idx->push_back(wallVertPtr+1); + idx->push_back(wallPartPtr+1); + idx->push_back(wallPartPtr); + } + } + else + { + //nop - no elements required at the end of a line + } + } + else + { + idx->push_back(wallVertPtr); + idx->push_back(wallVertPtr+1); + idx->push_back(wallVertPtr+2); + + idx->push_back(wallVertPtr+1); + idx->push_back(wallVertPtr+3); + idx->push_back(wallVertPtr+2); + } + + wallVertPtr += 2; + made_geom = true; + } + + walls->addPrimitiveSet( idx ); + + if ( roof ) + { + roof->addPrimitiveSet( new osg::DrawArrays( + osg::PrimitiveSet::LINE_LOOP, + roofPartPtr, roofVertPtr - roofPartPtr ) ); + } + + if ( base ) + { + // reverse the base verts: + int len = baseVertPtr - basePartPtr; + for( int i=basePartPtr; iaddPrimitiveSet( new osg::DrawArrays( + osg::PrimitiveSet::LINE_LOOP, + basePartPtr, baseVertPtr - basePartPtr ) ); + } + + if ( outline ) + { + unsigned len = baseVertPtr - basePartPtr; + + GLenum roofLineMode = isPolygon ? GL_LINE_LOOP : GL_LINE_STRIP; + osg::DrawElementsUInt* roofLine = new osg::DrawElementsUInt( roofLineMode ); + roofLine->reserveElements( len ); + for( unsigned i=0; iaddElement( basePartPtr + i*2 ); + outline->addPrimitiveSet( roofLine ); + + osg::DrawElementsUShort* wallLines = new osg::DrawElementsUShort( GL_LINES ); + wallLines->reserve( len*2 ); + for( unsigned i=0; ipush_back( basePartPtr + i*2 ); + wallLines->push_back( basePartPtr + i*2 + 1 ); + } + outline->addPrimitiveSet( wallLines ); + } + } + + return made_geom; +} + +void +ExtrudeGeometryFilter::addDrawable( osg::Drawable* drawable, osg::StateSet* stateSet, const std::string& name ) +{ + // find the geode for the active stateset, creating a new one if necessary. NULL is a + // valid key as well. + osg::Geode* geode = _geodes[stateSet].get(); + if ( !geode ) + { + geode = new osg::Geode(); + geode->setStateSet( stateSet ); + _geodes[stateSet] = geode; + } + + geode->addDrawable( drawable ); + + if ( !name.empty() ) + { + drawable->setName( name ); + } +} + +bool +ExtrudeGeometryFilter::process( FeatureList& features, FilterContext& context ) +{ + // seed our random number generators + Random wallSkinPRNG( _wallSkinSymbol.valid()? *_wallSkinSymbol->randomSeed() : 0, Random::METHOD_FAST ); + Random roofSkinPRNG( _roofSkinSymbol.valid()? *_roofSkinSymbol->randomSeed() : 0, Random::METHOD_FAST ); + + for( FeatureList::iterator f = features.begin(); f != features.end(); ++f ) + { + Feature* input = f->get(); + + GeometryIterator iter( input->getGeometry(), false ); + while( iter.hasMore() ) + { + Geometry* part = iter.next(); + + osg::ref_ptr walls = new osg::Geometry(); + //walls->setUseVertexBufferObjects(true); + + osg::ref_ptr rooflines = 0L; + osg::ref_ptr outlines = 0L; + + if ( part->getType() == Geometry::TYPE_POLYGON ) + { + rooflines = new osg::Geometry(); + //rooflines->setUseVertexBufferObjects(true); + + // prep the shapes by making sure all polys are open: + static_cast(part)->open(); + } + + // fire up the outline geometry if we have a line symbol. + if ( _outlineSymbol != 0L ) + { + outlines = new osg::Geometry(); + } + + // calculate the extrusion height: + float height; + + if ( _heightCallback.valid() ) + { + height = _heightCallback->operator()(input, context); + } + else if ( _heightExpr.isSet() ) + { + height = input->eval( _heightExpr.mutable_value() ); + } + else + { + height = *_extrusionSymbol->height(); + } + + // calculate the height offset from the base: + float offset = 0.0; + if ( _heightOffsetExpr.isSet() ) + { + offset = input->eval( _heightOffsetExpr.mutable_value() ); + } + + osg::StateSet* wallStateSet = 0L; + osg::StateSet* roofStateSet = 0L; + + // calculate the wall texturing: + SkinResource* wallSkin = 0L; + if ( _wallSkinSymbol.valid() ) + { + if ( _wallResLib.valid() ) + { + SkinSymbol querySymbol( *_wallSkinSymbol.get() ); + querySymbol.objectHeight() = fabs(height) - offset; + wallSkin = _wallResLib->getSkin( &querySymbol, wallSkinPRNG ); + } + + else + { + //TODO: simple single texture? + } + } + + // calculate the rooftop texture: + SkinResource* roofSkin = 0L; + if ( _roofSkinSymbol.valid() ) + { + if ( _roofResLib.valid() ) + { + SkinSymbol querySymbol( *_roofSkinSymbol.get() ); + roofSkin = _roofResLib->getSkin( &querySymbol, roofSkinPRNG ); + } + + else + { + //TODO: simple single texture? + } + } + + // calculate the colors: + osg::Vec4f wallColor(1,1,1,1), roofColor(1,1,1,1), outlineColor(1,1,1,1); + + if ( _wallPolygonSymbol.valid() ) + { + wallColor = _wallPolygonSymbol->fill()->color(); + } + if ( _roofPolygonSymbol.valid() ) + { + roofColor = _roofPolygonSymbol->fill()->color(); + } + if ( _outlineSymbol.valid() ) + { + outlineColor = _outlineSymbol->stroke()->color(); + } + + // Create the extruded geometry! + if (extrudeGeometry( + part, height, offset, + *_extrusionSymbol->flatten(), + walls.get(), rooflines.get(), 0L, outlines.get(), + wallColor, roofColor, outlineColor, + wallSkin, roofSkin, + context ) ) + { + if ( wallSkin ) + { + wallStateSet = context.resourceCache()->getStateSet( wallSkin ); + } + + // generate per-vertex normals, altering the geometry as necessary to avoid + // smoothing around sharp corners + #if OSG_MIN_VERSION_REQUIRED(2,9,9) + //Crease angle threshold wasn't added until + osgUtil::SmoothingVisitor::smooth( + *walls.get(), + osg::DegreesToRadians(_wallAngleThresh_deg) ); + #else + osgUtil::SmoothingVisitor::smooth(*walls.get()); + #endif + + // tessellate and add the roofs if necessary: + if ( rooflines.valid() ) + { + osgUtil::Tessellator tess; + tess.setTessellationType( osgUtil::Tessellator::TESS_TYPE_GEOMETRY ); + tess.setWindingType( osgUtil::Tessellator::TESS_WINDING_ODD ); //POSITIVE ); + tess.retessellatePolygons( *(rooflines.get()) ); + + // generate default normals (no crease angle necessary; they are all pointing up) + // TODO do this manually; probably faster + osgUtil::SmoothingVisitor::smooth( *rooflines.get() ); + + // texture the rooflines if necessary + //applyOverlayTexturing( rooflines.get(), input, env ); + + // mark this geometry as DYNAMIC because otherwise the OSG optimizer will destroy it. + // TODO: why?? + rooflines->setDataVariance( osg::Object::DYNAMIC ); + + if ( roofSkin ) + { + roofStateSet = context.resourceCache()->getStateSet( roofSkin ); + } + } + + std::string name; + if ( !_featureNameExpr.empty() ) + name = input->eval( _featureNameExpr ); + + //MeshConsolidator::run( *walls.get() ); + addDrawable( walls.get(), wallStateSet, name ); + + if ( rooflines.valid() ) + { + //MeshConsolidator::run( *rooflines.get() ); + addDrawable( rooflines.get(), roofStateSet, name ); + } + + if ( outlines.valid() ) + { + addDrawable( outlines.get(), 0L, name ); + } + } + } + } + + return true; +} + +osg::Node* +ExtrudeGeometryFilter::push( FeatureList& input, FilterContext& context ) +{ + reset( context ); + + // minimally, we require an extrusion symbol. + if ( !_extrusionSymbol.valid() ) + { + OE_WARN << LC << "Missing required extrusion symbolology; geometry will be empty" << std::endl; + return new osg::Group(); + } + + // establish the active resource library, if applicable. + _wallResLib = 0L; + _roofResLib = 0L; + + const StyleSheet* sheet = context.getSession()->styles(); + + if ( sheet != 0L ) + { + if ( _wallSkinSymbol.valid() && _wallSkinSymbol->libraryName().isSet() ) + { + _wallResLib = sheet->getResourceLibrary( *_wallSkinSymbol->libraryName() ); + if ( !_wallResLib.valid() ) + { + OE_WARN << LC << "Unable to load resource library '" << *_wallSkinSymbol->libraryName() << "'" + << "; wall geometry will not be textured." << std::endl; + } + } + + if ( _roofSkinSymbol.valid() && _roofSkinSymbol->libraryName().isSet() ) + { + _roofResLib = sheet->getResourceLibrary( *_roofSkinSymbol->libraryName() ); + if ( !_roofResLib.valid() ) + { + OE_WARN << LC << "Unable to load resource library '" << *_roofSkinSymbol->libraryName() << "'" + << "; roof geometry will not be textured." << std::endl; + } + } + } + + // calculate the localization matrices (_local2world and _world2local) + computeLocalizers( context ); + + // push all the features through the extruder. + bool ok = process( input, context ); + + // convert everything to triangles and combine drawables. + if ( _mergeGeometry == true && _featureNameExpr.empty() ) + { + for( SortedGeodeMap::iterator i = _geodes.begin(); i != _geodes.end(); ++i ) + { + MeshConsolidator::run( *i->second.get() ); + } + } + + // parent geometry with a delocalizer (if necessary) + osg::Group* group = createDelocalizeGroup(); + + // combines geometries where the statesets are the same. + for( SortedGeodeMap::iterator i = _geodes.begin(); i != _geodes.end(); ++i ) + { + group->addChild( i->second.get() ); + } + _geodes.clear(); + + // if we drew outlines, apply a poly offset too. + if ( _outlineSymbol.valid() ) + { + osg::StateSet* groupStateSet = group->getOrCreateStateSet(); + groupStateSet->setAttributeAndModes( new osg::PolygonOffset(1,1), 1 ); + if ( _outlineSymbol->stroke()->width().isSet() ) + groupStateSet->setAttributeAndModes( new osg::LineWidth(*_outlineSymbol->stroke()->width()), 1 ); + } + + OE_DEBUG << LC << "Sorted geometry into " << group->getNumChildren() << " groups" << std::endl; + + //TODO + // running this after the MC reduces the primitive set count by a huge amount, but I + // have not figured out why yet. + if ( _mergeGeometry == true ) + { + osgUtil::Optimizer o; + o.optimize( group, osgUtil::Optimizer::MERGE_GEOMETRY ); + } + + return group; +} diff -Nru osgearth-2.0+dfsg/src/osgEarthFeatures/Feature osgearth-2.1.1+dfsg/src/osgEarthFeatures/Feature --- osgearth-2.0+dfsg/src/osgEarthFeatures/Feature 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/src/osgEarthFeatures/Feature 2011-11-04 19:44:43.000000000 +0000 @@ -30,6 +30,9 @@ namespace osgEarth { namespace Features { + using namespace osgEarth; + using namespace osgEarth::Symbology; + /** * Metadata and schema information for feature data. */ @@ -46,11 +49,56 @@ const SpatialReference* getSRS() const { return _extent.getSRS(); } + bool getTiled() const; + void setTiled(bool tiled); + + int getFirstLevel() const; + void setFirstLevel(int firstLevel ); + + int getMaxLevel() const; + void setMaxLevel(int maxLevel); + + const osgEarth::Profile* getProfile() const; + void setProfile( const osgEarth::Profile* profile ); + protected: + osg::ref_ptr< const osgEarth::Profile > _profile; GeoExtent _extent; + bool _tiled; + int _firstLevel; + int _maxLevel; }; - typedef std::map AttributeTable; + struct AttributeValueUnion + { + std::string stringValue; + double doubleValue; + int intValue; + bool boolValue; + }; + + enum AttributeType + { + ATTRTYPE_UNSPECIFIED, + ATTRTYPE_STRING, + ATTRTYPE_INT, + ATTRTYPE_DOUBLE, + ATTRTYPE_BOOL + }; + + struct OSGEARTHFEATURES_EXPORT AttributeValue : public std::pair + { + std::string getString() const; + double getDouble( double defaultValue =0.0 ) const; + int getInt( int defaultValue =0 ) const; + bool getBool( bool defaultValue =false ) const; + }; + + typedef std::map AttributeTable; + + typedef unsigned long FeatureID; + + typedef std::map< std::string, AttributeType > FeatureSchema; /** * Basic building block of vector feature data. @@ -58,7 +106,9 @@ class OSGEARTHFEATURES_EXPORT Feature : public osg::Object { public: - Feature( long fid =0L ); + Feature( FeatureID fid =0L ); + + Feature( Geometry* geom, const Style& style =Style(), FeatureID fid =0L ); /** Copy contructor */ Feature( const Feature& rhs, const osg::CopyOp& copyop =osg::CopyOp::DEEP_COPY_ALL ); @@ -67,33 +117,48 @@ public: - long getFID() const; + FeatureID getFID() const; - void setGeometry( Symbology::Geometry* geom ) { - _geom = geom; } + void setGeometry( Symbology::Geometry* geom ) { _geom = geom; } - Symbology::Geometry* getGeometry() { - return _geom; } + Symbology::Geometry* getGeometry() { return _geom.get(); } - const Symbology::Geometry* getGeometry() const { - return _geom; } + const Symbology::Geometry* getGeometry() const { return _geom.get(); } - const AttributeTable& getAttrs() const { - return _attrs; } + const AttributeTable& getAttrs() const { return _attrs; } - void setAttr( const std::string& name, const std::string& value ); + void set( const std::string& name, const std::string& value ); + void set( const std::string& name, double value ); + void set( const std::string& name, int value ); + void set( const std::string& name, bool value ); - const std::string& getAttr( const std::string& name ) const; + bool hasAttr( const std::string& name ) const; + + std::string getString( const std::string& name ) const; + double getDouble( const std::string& name, double defaultValue =0.0 ) const; + int getInt( const std::string& name, int defaultValue =0 ) const; + bool getBool( const std::string& name, bool defaultValue =false ) const; /** Embedded style. */ - optional >& style() { return _style; } - const optional >& style() const { return _style; } + optional + + + true + + + + + false + + 0.5 + + + osgearth_cache + + + + + + + + + + + + diff -Nru osgearth-2.0+dfsg/tests/cloudmade.earth osgearth-2.1.1+dfsg/tests/cloudmade.earth --- osgearth-2.0+dfsg/tests/cloudmade.earth 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/tests/cloudmade.earth 2011-11-04 19:44:43.000000000 +0000 @@ -6,13 +6,16 @@ - - http://b.tile.cloudmade.com/f10ecb40ab2159639c5f2d4b9a273f1d/997/256/ + + ../data/world.tif + + + + http://b.tile.cloudmade.com/f10ecb40ab2159639c5f2d4b9a273f1d/35117/256/ global-mercator png 256 - google - 255 0 0 0 + google diff -Nru osgearth-2.0+dfsg/tests/feature_extrude.earth osgearth-2.1.1+dfsg/tests/feature_extrude.earth --- osgearth-2.0+dfsg/tests/feature_extrude.earth 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/tests/feature_extrude.earth 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,62 @@ + + + + + + + + + + + + ../data/dcbuildings.shp + true + + + + + + + + + true + + + + + false + + 0.2 + + + osgearth_cache + + + + + http://readymap.org/readymap/tiles/1.0.0/7/ + + + + http://readymap.org/readymap/tiles/1.0.0/9/ + + + + + + + + diff -Nru osgearth-2.0+dfsg/tests/feature_geom.earth osgearth-2.1.1+dfsg/tests/feature_geom.earth --- osgearth-2.0+dfsg/tests/feature_geom.earth 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/tests/feature_geom.earth 2011-11-04 19:44:43.000000000 +0000 @@ -16,21 +16,17 @@ ../data/usa.shp - ESRI Shapefile - - line - - - 10000 + + 5.0 diff -Nru osgearth-2.0+dfsg/tests/feature_gridding.earth osgearth-2.1.1+dfsg/tests/feature_gridding.earth --- osgearth-2.0+dfsg/tests/feature_gridding.earth 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/tests/feature_gridding.earth 1970-01-01 00:00:00.000000000 +0000 @@ -1,70 +0,0 @@ - - - - - - false - - - - - ../data/world.tif - - - - - - ../data/usa.shp - ESRI Shapefile - true - - - - - - 200000 - - - - - - - - - - - ../data/istates_dissolve.shp - ESRI Shapefile - - true - - - - - 20000 - - - - - - - diff -Nru osgearth-2.0+dfsg/tests/feature_inline.earth osgearth-2.1.1+dfsg/tests/feature_inline.earth --- osgearth-2.0+dfsg/tests/feature_inline.earth 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/tests/feature_inline.earth 2011-11-04 19:44:43.000000000 +0000 @@ -1,7 +1,16 @@ @@ -14,22 +23,39 @@ ../data/world.tif - - - + + - LINESTRING(-100 30, -100 40, -100 50, -90 50, -80 50, -70 50, -70 50, -70 40, -70 30, -80 30, -90 30, -100 30) + POLYGON((-120 30, -120 50, -70 50, -70 30)) - + great_circle + + + + + + + + + + + POLYGON((-68 30, -68 50, -20 50, -20 30)) + + + + rhumb_line + diff -Nru osgearth-2.0+dfsg/tests/feature_labels.earth osgearth-2.1.1+dfsg/tests/feature_labels.earth --- osgearth-2.0+dfsg/tests/feature_labels.earth 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/tests/feature_labels.earth 2011-11-04 19:44:43.000000000 +0000 @@ -11,25 +11,23 @@ ../data/world.tif - - + - ../data/world.shp - diff -Nru osgearth-2.0+dfsg/tests/feature_model_scatter.earth osgearth-2.1.1+dfsg/tests/feature_model_scatter.earth --- osgearth-2.0+dfsg/tests/feature_model_scatter.earth 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/tests/feature_model_scatter.earth 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,107 @@ + + + + + + + + + + + + ../data/parks.shp + true + + + + + true + + + + + true + + + + + + + + + + + + + + + + + + + + + + false + + osgearth_cache + + + + + http://readymap.org/readymap/tiles/1.0.0/7/ + + + http://readymap.org/readymap/tiles/1.0.0/9/ + + + + + + + + diff -Nru osgearth-2.0+dfsg/tests/feature_models.earth osgearth-2.1.1+dfsg/tests/feature_models.earth --- osgearth-2.0+dfsg/tests/feature_models.earth 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/tests/feature_models.earth 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,54 @@ + + + + + + + + + + ../data/points.shp + true + + + + + + false + false + + + + + false + + osgearth_cache + + + + ../data + + + + + http://readymap.org/readymap/tiles/1.0.0/7/ + + + + + + + diff -Nru osgearth-2.0+dfsg/tests/feature_overlay.earth osgearth-2.1.1+dfsg/tests/feature_overlay.earth --- osgearth-2.0+dfsg/tests/feature_overlay.earth 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/tests/feature_overlay.earth 2011-11-04 19:44:43.000000000 +0000 @@ -7,9 +7,6 @@ false - - false - @@ -22,10 +19,8 @@ ../data/world.shp true + - - - line - + - + 14045470 and POP_CNTRY <= 43410900 ]]> - + 43410900 and POP_CNTRY <= 97228750 ]]> - + 97228750 and POP_CNTRY <= 258833000 ]]> - + 258833000 ]]> diff -Nru osgearth-2.0+dfsg/tests/feature_rasterize.earth osgearth-2.1.1+dfsg/tests/feature_rasterize.earth --- osgearth-2.0+dfsg/tests/feature_rasterize.earth 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/tests/feature_rasterize.earth 2011-11-04 19:44:43.000000000 +0000 @@ -9,6 +9,8 @@ false true + texture_array + diff -Nru osgearth-2.0+dfsg/tests/feature_stencil_line_draping.earth osgearth-2.1.1+dfsg/tests/feature_stencil_line_draping.earth --- osgearth-2.0+dfsg/tests/feature_stencil_line_draping.earth 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/tests/feature_stencil_line_draping.earth 2011-11-04 19:44:43.000000000 +0000 @@ -24,10 +24,8 @@ ../data/world.shp - ESRI Shapefile + - - line + + + + + + + ../data/world.tif + + + + + + http://demo.opengeo.org/geoserver/wfs + states + json + + + line + + + + + + + + diff -Nru osgearth-2.0+dfsg/tests/mask.earth osgearth-2.1.1+dfsg/tests/mask.earth --- osgearth-2.0+dfsg/tests/mask.earth 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/tests/mask.earth 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,22 @@ + + + + + ../data/world.tif + + + + + + + POLYGON(( -111.0466 42.0015 0, -111.0467 40.9979 0, -109.0501 41.0007 0, -109.0452 36.9991 0, -114.0506 37.0004 0, -114.0417 41.9937 0)) + + + global-geodetic + + + + diff -Nru osgearth-2.0+dfsg/tests/mb_tiles.earth osgearth-2.1.1+dfsg/tests/mb_tiles.earth --- osgearth-2.0+dfsg/tests/mb_tiles.earth 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/tests/mb_tiles.earth 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,19 @@ + + + + + + ../data/world.tif + + + + + ../data/haiti-terrain-grey.mbtiles + jpg + + + diff -Nru osgearth-2.0+dfsg/tests/mgrs.earth osgearth-2.1.1+dfsg/tests/mgrs.earth --- osgearth-2.0+dfsg/tests/mgrs.earth 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/tests/mgrs.earth 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,108 @@ + + + + + + false + + osgearth_cache + + + + + http://readymap.org/readymap/tiles/1.0.0/7/ + + + + + + 2.5 + + + + + + + + + + MULTILINESTRING( + (-180 0, -90 0, 0 0, 90 0, 180 0), + (-180 8, -90 8, 0 8, 90 8, 180 8), + (-180 16, -90 16, 0 16, 90 16, 180 16), + (-180 24, -90 24, 0 24, 90 24, 180 24), + (-180 32, -90 32, 0 32, 90 32, 180 32), + (-180 40, -90 40, 0 40, 90 40, 180 40), + (-180 48, -90 48, 0 48, 90 48, 180 48), + (-180 56, -90 56, 0 56, 90 56, 180 56), + (-180 64, -90 64, 0 64, 90 64, 180 64), + (-180 72, -90 72, 0 72, 90 72, 180 72), + (-180 84, -90 84, 0 84, 90 84, 180 84), + + (-180 0, -90 0, 0 0, 90 0, 180 0), + (-180 -8, -90 -8, 0 -8, 90 -8, 180 -8), + (-180 -16, -90 -16, 0 -16, 90 -16, 180 -16), + (-180 -24, -90 -24, 0 -24, 90 -24, 180 -24), + (-180 -32, -90 -32, 0 -32, 90 -32, 180 -32), + (-180 -40, -90 -40, 0 -40, 90 -40, 180 -40), + (-180 -48, -90 -48, 0 -48, 90 -48, 180 -48), + (-180 -56, -90 -56, 0 -56, 90 -56, 180 -56), + (-180 -64, -90 -64, 0 -64, 90 -64, 180 -64), + (-180 -72, -90 -72, 0 -72, 90 -72, 180 -72), + (-180 -80, -90 -80, 0 -80, 90 -80, 180 -80), + + ( 0 84, 0 -80), + ( 6 84, 6 -80), ( -6 84, -6 -80), + ( 12 84, 12 -80), ( -12 84, -12 -80), + ( 18 84, 18 -80), ( -18 84, -18 -80), + ( 24 84, 24 -80), ( -24 84, -24 -80), + ( 30 84, 30 -80), ( -30 84, -30 -80), + ( 36 84, 36 -80), ( -36 84, -36 -80), + ( 42 84, 42 -80), ( -42 84, -42 -80), + ( 48 84, 48 -80), ( -48 84, -48 -80), + ( 54 84, 54 -80), ( -54 84, -54 -80), + ( 60 84, 60 -80), ( -60 84, -60 -80), + ( 66 84, 66 -80), ( -66 84, -66 -80), + ( 72 84, 72 -80), ( -72 84, -72 -80), + ( 78 84, 78 -80), ( -78 84, -78 -80), + ( 84 84, 84 -80), ( -84 84, -84 -80), + ( 90 84, 90 -80), ( -90 84, -90 -80), + + ( 96 84, 96 -80), ( -96 84, -96 -80), + ( 102 84, 102 -80), ( -102 84, -102 -80), + ( 108 84, 108 -80), ( -108 84, -108 -80), + ( 114 84, 114 -80), ( -114 84, -114 -80), + ( 120 84, 120 -80), ( -120 84, -120 -80), + ( 126 84, 126 -80), ( -126 84, -126 -80), + ( 132 84, 132 -80), ( -132 84, -132 -80), + ( 138 84, 138 -80), ( -138 84, -138 -80), + ( 144 84, 144 -80), ( -144 84, -144 -80), + ( 150 84, 150 -80), ( -150 84, -150 -80), + ( 156 84, 156 -80), ( -156 84, -156 -80), + ( 162 84, 162 -80), ( -162 84, -162 -80), + ( 168 84, 168 -80), ( -168 84, -168 -80), + ( 174 84, 174 -80), ( -174 84, -174 -80), + ( 180 84, 180 -80), + + ( 0 84, 0 90), ( 180 84, 180 90), + ( -90 84, -90 90), ( 90 84, 90 90), + + ( 0 -80, 0 -90), ( 180 -80, 180 -90), + ( -90 -80, -90 -90), ( 90 -80, 90 -90) + ) + + + + + + diff -Nru osgearth-2.0+dfsg/tests/min_max_level.earth osgearth-2.1.1+dfsg/tests/min_max_level.earth --- osgearth-2.0+dfsg/tests/min_max_level.earth 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/tests/min_max_level.earth 2011-11-04 19:44:43.000000000 +0000 @@ -1,6 +1,6 @@ diff -Nru osgearth-2.0+dfsg/tests/min_max_resolutions.earth osgearth-2.1.1+dfsg/tests/min_max_resolutions.earth --- osgearth-2.0+dfsg/tests/min_max_resolutions.earth 1970-01-01 00:00:00.000000000 +0000 +++ osgearth-2.1.1+dfsg/tests/min_max_resolutions.earth 2011-11-04 19:44:43.000000000 +0000 @@ -0,0 +1,25 @@ + + + + + + + + + satellite + 4891 + + + + + 2445 + + + + + diff -Nru osgearth-2.0+dfsg/tests/readymap.earth osgearth-2.1.1+dfsg/tests/readymap.earth --- osgearth-2.0+dfsg/tests/readymap.earth 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/tests/readymap.earth 2011-11-04 19:44:43.000000000 +0000 @@ -28,11 +28,16 @@ false - 6.0 + multitexture + 0.125 + - cache_readymap + osgearth_cache + + + diff -Nru osgearth-2.0+dfsg/tests/wms_nexrad.earth osgearth-2.1.1+dfsg/tests/wms_nexrad.earth --- osgearth-2.0+dfsg/tests/wms_nexrad.earth 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/tests/wms_nexrad.earth 2011-11-04 19:44:43.000000000 +0000 @@ -20,9 +20,6 @@ - - - false diff -Nru osgearth-2.0+dfsg/tests/wms-t_nexrad_animated.earth osgearth-2.1.1+dfsg/tests/wms-t_nexrad_animated.earth --- osgearth-2.0+dfsg/tests/wms-t_nexrad_animated.earth 2011-02-22 19:21:31.000000000 +0000 +++ osgearth-2.1.1+dfsg/tests/wms-t_nexrad_animated.earth 2011-11-04 19:44:43.000000000 +0000 @@ -16,6 +16,7 @@ nexrad-n0r-wmst 256 EPSG:4326 + global-geodetic true 2005-08-29T13:00:00Z, @@ -32,8 +33,5 @@ false - - -