Compare commits
122 Commits
version/3.
...
version/20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
68fd8e8dba | ||
|
|
807330a616 | ||
|
|
2deb361f0f | ||
|
|
63e7a1fbb4 | ||
|
|
951859d8a7 | ||
|
|
41f40a9a10 | ||
|
|
a6290e367a | ||
|
|
af0a51930e | ||
|
|
ccb5d05eb4 | ||
|
|
9d7402242a | ||
|
|
819833a560 | ||
|
|
576ff21488 | ||
|
|
b60f8b4989 | ||
|
|
096d625445 | ||
|
|
dad77b3983 | ||
|
|
598b64fa95 | ||
|
|
b5d6aa3fe4 | ||
|
|
f32063e6dd | ||
|
|
3d9d44cf73 | ||
|
|
ac84115ac3 | ||
|
|
d58607242e | ||
|
|
7afd2be652 | ||
|
|
f256d45b65 | ||
|
|
edf15e9f55 | ||
|
|
2bb24f43fb | ||
|
|
be0447d4c0 | ||
|
|
ef7a0dc5a3 | ||
|
|
5d754c0419 | ||
|
|
1f23fb89c0 | ||
|
|
584ee1364f | ||
|
|
1e32c24a17 | ||
|
|
20ea55bdbc | ||
|
|
c62b4467b4 | ||
|
|
31095c39cc | ||
|
|
05d9d7cae8 | ||
|
|
78a548b861 | ||
|
|
6be4ad27ee | ||
|
|
589c5ba35a | ||
|
|
81d668784a | ||
|
|
75ad5a7e5c | ||
|
|
3f20a3d2c6 | ||
|
|
a57e969639 | ||
|
|
23b8c86e78 | ||
|
|
5c9ca9cbe2 | ||
|
|
5676f96fbf | ||
|
|
4104f7d18f | ||
|
|
10e6bbc2c5 | ||
|
|
21e6dd34b2 | ||
|
|
319922f044 | ||
|
|
ff3efaee93 | ||
|
|
08fb433923 | ||
|
|
1a752d28a4 | ||
|
|
00a20409f7 | ||
|
|
3bfd0c872a | ||
|
|
945cf5d963 | ||
|
|
4e40913aef | ||
|
|
3bc53474ed | ||
|
|
28dff1d5ca | ||
|
|
81bfec336c | ||
|
|
60a0c51e2b | ||
|
|
70c5d60564 | ||
|
|
1b2247103a | ||
|
|
a49c3a49d3 | ||
|
|
105438fc58 | ||
|
|
2910c6a77b | ||
|
|
6dd859c75e | ||
|
|
b7c7f66bf3 | ||
|
|
c7a60d4dc4 | ||
|
|
a841fcc89e | ||
|
|
b91d1a3f6a | ||
|
|
1378511c13 | ||
|
|
9a9cd62957 | ||
|
|
a9742fba7c | ||
|
|
9ea772f938 | ||
|
|
e64902ae8c | ||
|
|
0b1399479a | ||
|
|
0369d1f506 | ||
|
|
f19cf6d56a | ||
|
|
d12d1b2c3a | ||
|
|
b7fbb79565 | ||
|
|
541239ceac | ||
|
|
fd74095939 | ||
|
|
b24d190e55 | ||
|
|
8b351520bd | ||
|
|
5bbdcfd240 | ||
|
|
51ff30f386 | ||
|
|
6683092bb2 | ||
|
|
2c6f9de020 | ||
|
|
686f095f1e | ||
|
|
089fc5ea0a | ||
|
|
72ae14227e | ||
|
|
0e9948f9d4 | ||
|
|
0b6738b616 | ||
|
|
3c204e84f4 | ||
|
|
7774b76ad2 | ||
|
|
acd655b37b | ||
|
|
1a09683351 | ||
|
|
181afb7fd0 | ||
|
|
9d1354f6bd | ||
|
|
596591bb64 | ||
|
|
a67a984d29 | ||
|
|
37a963da92 | ||
|
|
384a700e05 | ||
|
|
7d2ecef14a | ||
|
|
bc72c34d0f | ||
|
|
ca7acb1f2c | ||
|
|
1365a02aea | ||
|
|
611785c04b | ||
|
|
e5995208a9 | ||
|
|
6f15bb415f | ||
|
|
ce7f78e0ca | ||
|
|
6323477a35 | ||
|
|
8c38f799ad | ||
|
|
1bf0b7222d | ||
|
|
0cc5374fe7 | ||
|
|
b2ac9982d4 | ||
|
|
84cbfb2e98 | ||
|
|
7479cadbba | ||
|
|
f711306085 | ||
|
|
148640be34 | ||
|
|
27a91062bd | ||
|
|
dc1816bb08 |
122
CMakeLists.txt
122
CMakeLists.txt
@@ -1,8 +1,26 @@
|
||||
cmake_minimum_required (VERSION 2.6.4)
|
||||
cmake_minimum_required (VERSION 2.8.11)
|
||||
|
||||
if(COMMAND cmake_policy)
|
||||
if(POLICY CMP0054)
|
||||
cmake_policy(SET CMP0054 NEW)
|
||||
endif()
|
||||
if(POLICY CMP0042)
|
||||
cmake_policy(SET CMP0042 NEW)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include (CheckFunctionExists)
|
||||
include (CheckIncludeFile)
|
||||
include (CheckLibraryExists)
|
||||
include (CheckCXXSourceCompiles)
|
||||
include (CheckCXXCompilerFlag)
|
||||
include (GenerateExportHeader)
|
||||
|
||||
# using 10.7 because boost requires libc++ and 10.6 doesn't include it
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.7)
|
||||
|
||||
# only relevant for building shared libs but let's set it regardless
|
||||
set(CMAKE_OSX_RPATH 1)
|
||||
|
||||
project(SimGear)
|
||||
|
||||
@@ -34,7 +52,7 @@ SET(CPACK_INSTALL_CMAKE_PROJECTS ${CMAKE_CURRENT_BINARY_DIR};SimGear;ALL;/)
|
||||
|
||||
# split version string into components, note CMAKE_MATCH_0 is the entire regexp match
|
||||
string(REGEX MATCH "([0-9]+)\\.([0-9]+)\\.([0-9]+)" CPACK_PACKAGE_VERSION ${SIMGEAR_VERSION} )
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR ${CMAKE_MATCH_1})
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR ${CMAKE_MATCH_1})
|
||||
set(CPACK_PACKAGE_VERSION_MINOR ${CMAKE_MATCH_2})
|
||||
set(CPACK_PACKAGE_VERSION_PATCH ${CMAKE_MATCH_3})
|
||||
|
||||
@@ -62,12 +80,7 @@ endif(NOT CMAKE_BUILD_TYPE)
|
||||
# Determine name of library installation directory, i.e. "lib" vs "lib64", which
|
||||
# differs between all Debian-based vs all other Linux distros.
|
||||
# See cmake bug #11964, http://cmake.org/gitweb?p=cmake.git;a=commit;h=126c993d
|
||||
# GNUInstallDirs requires CMake >= 2.8.5, use own file for older cmake
|
||||
if(${CMAKE_VERSION} VERSION_GREATER 2.8.4)
|
||||
include(GNUInstallDirs)
|
||||
else(${CMAKE_VERSION} VERSION_GREATER 2.8.4)
|
||||
include(OldGNUInstallDirs)
|
||||
endif(${CMAKE_VERSION} VERSION_GREATER 2.8.4)
|
||||
include(GNUInstallDirs)
|
||||
message(STATUS "Library installation directory: ${CMAKE_INSTALL_LIBDIR}")
|
||||
|
||||
#####################################################################################
|
||||
@@ -104,6 +117,7 @@ option(ENABLE_RTI "Set to ON to build SimGear with RTI support" OFF)
|
||||
option(ENABLE_TESTS "Set to OFF to disable building SimGear's test applications" ON)
|
||||
option(ENABLE_SOUND "Set to OFF to disable building SimGear's sound support" ON)
|
||||
option(ENABLE_PKGUTIL "Set to ON to build the sg_pkgutil application (default)" ON)
|
||||
option(ENABLE_CURL "Set to ON to use libCurl as the HTTP client backend" OFF)
|
||||
|
||||
if (MSVC)
|
||||
GET_FILENAME_COMPONENT(PARENT_DIR ${PROJECT_BINARY_DIR} PATH)
|
||||
@@ -161,13 +175,23 @@ endif (MSVC AND MSVC_3RDPARTY_ROOT)
|
||||
|
||||
if(APPLE)
|
||||
find_library(COCOA_LIBRARY Cocoa)
|
||||
|
||||
# this should be handled by setting CMAKE_OSX_DEPLOYMENT_TARGET
|
||||
# but it's not working reliably, so forcing it for now
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.7")
|
||||
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmacosx-version-min=10.7")
|
||||
endif()
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux" OR
|
||||
${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
|
||||
find_package(Threads REQUIRED)
|
||||
endif()
|
||||
|
||||
# Somehow this only works if included before searching for Boost...
|
||||
include(BoostTestTargets)
|
||||
|
||||
find_package(Boost REQUIRED)
|
||||
set (BOOST_CXX_FLAGS "-DBOOST_MULTI_INDEX_DISABLE_SERIALIZATION -DBOOST_BIMAP_DISABLE_SERIALIZATION")
|
||||
set (BOOST_CXX_FLAGS "-DBOOST_BIMAP_DISABLE_SERIALIZATION")
|
||||
|
||||
if(SIMGEAR_HEADLESS)
|
||||
message(STATUS "SimGear mode: HEADLESS")
|
||||
@@ -175,17 +199,21 @@ if(SIMGEAR_HEADLESS)
|
||||
else()
|
||||
message(STATUS "SimGear mode: NORMAL")
|
||||
find_package(OpenGL REQUIRED)
|
||||
|
||||
|
||||
if (ENABLE_SOUND)
|
||||
find_package(OpenAL REQUIRED)
|
||||
message(STATUS "Sound support: ENABLED")
|
||||
endif(ENABLE_SOUND)
|
||||
|
||||
|
||||
find_package(OpenSceneGraph 3.2.0 REQUIRED osgText osgSim osgDB osgParticle osgGA osgViewer osgUtil)
|
||||
endif(SIMGEAR_HEADLESS)
|
||||
|
||||
find_package(ZLIB REQUIRED)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
if (ENABLE_CURL)
|
||||
find_package(CURL REQUIRED)
|
||||
message(STATUS "Curl HTTP client: ENABLED")
|
||||
endif()
|
||||
|
||||
if (SYSTEM_EXPAT)
|
||||
message(STATUS "Requested to use system Expat library, forcing SIMGEAR_SHARED to true")
|
||||
@@ -197,8 +225,8 @@ else()
|
||||
# XML_STATIC is important to avoid sg_expat_external.h
|
||||
# declaring symbols as declspec(import)
|
||||
add_definitions(-DHAVE_EXPAT_CONFIG_H -DXML_STATIC)
|
||||
set(EXPAT_INCLUDE_DIRS
|
||||
${PROJECT_SOURCE_DIR}/3rdparty/expat
|
||||
set(EXPAT_INCLUDE_DIRS
|
||||
${PROJECT_SOURCE_DIR}/3rdparty/expat
|
||||
${PROJECT_BINARY_DIR}/3rdparty/expat)
|
||||
endif(SYSTEM_EXPAT)
|
||||
|
||||
@@ -275,12 +303,12 @@ SET(CMAKE_MINSIZEREL_POSTFIX "" CACHE STRING "add a postfix, usually empty on wi
|
||||
|
||||
# isnan might not be real symbol, so can't check using function_exists
|
||||
check_cxx_source_compiles(
|
||||
"#include <cmath>
|
||||
"#include <cmath>
|
||||
int main() { return isnan(0.0);} "
|
||||
HAVE_ISNAN)
|
||||
|
||||
check_cxx_source_compiles(
|
||||
"#include <cmath>
|
||||
"#include <cmath>
|
||||
int main() { return std::isnan(0.0);} "
|
||||
HAVE_STD_ISNAN)
|
||||
|
||||
@@ -297,8 +325,12 @@ if(CMAKE_COMPILER_IS_GNUCXX)
|
||||
endif(CMAKE_COMPILER_IS_GNUCXX)
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
set(WARNING_FLAGS_CXX "-Wall -Wno-overloaded-virtual")
|
||||
set(WARNING_FLAGS_C "-Wall")
|
||||
# Boost redeclares class members
|
||||
set(WARNING_FLAGS_CXX "-Wall -Wno-overloaded-virtual -Wno-redeclared-class-member")
|
||||
set(WARNING_FLAGS_C "-Wall")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++11")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -stdlib=libc++")
|
||||
endif()
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
@@ -320,16 +352,16 @@ if(WIN32)
|
||||
# foreach(warning 4244 4251 4267 4275 4290 4786 4305 4996)
|
||||
# SET(WARNING_FLAGS "${WARNING_FLAGS} /wd${warning}")
|
||||
# endforeach(warning)
|
||||
|
||||
|
||||
set(MSVC_FLAGS "-DWIN32 -DNOMINMAX -D_USE_MATH_DEFINES -D_CRT_SECURE_NO_WARNINGS -D__CRT_NONSTDC_NO_WARNINGS /wd4996 /wd4250 -Dstrdup=_strdup")
|
||||
if (${MSVC_VERSION} GREATER 1599)
|
||||
set( MSVC_LD_FLAGS "/FORCE:MULTIPLE" )
|
||||
endif (${MSVC_VERSION} GREATER 1599)
|
||||
endif(MSVC)
|
||||
|
||||
|
||||
# assumed on Windows
|
||||
set(HAVE_GETLOCALTIME 1)
|
||||
|
||||
|
||||
set( WINSOCK_LIBRARY "ws2_32.lib" )
|
||||
set( RT_LIBRARY "winmm" )
|
||||
endif(WIN32)
|
||||
@@ -338,16 +370,17 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS_C} ${MSVC_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS_CXX} ${MSVC_FLAGS} ${BOOST_CXX_FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${MSVC_LD_FLAGS}")
|
||||
|
||||
# use BEFORE to ensure local directories are used first,
|
||||
# use BEFORE to ensure local directories are used first,
|
||||
# ahead of system-installed libs
|
||||
include_directories(BEFORE ${PROJECT_SOURCE_DIR})
|
||||
include_directories(BEFORE ${PROJECT_SOURCE_DIR}/simgear/canvas/ShivaVG/include)
|
||||
include_directories(BEFORE ${PROJECT_BINARY_DIR}/simgear)
|
||||
|
||||
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS}
|
||||
${Boost_INCLUDE_DIRS}
|
||||
${ZLIB_INCLUDE_DIR}
|
||||
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS}
|
||||
${Boost_INCLUDE_DIRS}
|
||||
${ZLIB_INCLUDE_DIR}
|
||||
${OPENAL_INCLUDE_DIR}
|
||||
${CURL_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
add_definitions(-DHAVE_CONFIG_H)
|
||||
@@ -376,7 +409,8 @@ set(TEST_LIBS_INTERNAL_CORE
|
||||
${WINSOCK_LIBRARY}
|
||||
${RT_LIBRARY}
|
||||
${DL_LIBRARY}
|
||||
${COCOA_LIBRARY})
|
||||
${COCOA_LIBRARY}
|
||||
${CURL_LIBRARIES})
|
||||
set(TEST_LIBS SimGearCore ${TEST_LIBS_INTERNAL_CORE})
|
||||
|
||||
if(NOT SIMGEAR_HEADLESS)
|
||||
@@ -390,6 +424,40 @@ include_directories(3rdparty/utf8/source)
|
||||
add_subdirectory(3rdparty)
|
||||
add_subdirectory(simgear)
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
### Export stuff, see https://cmake.org/cmake/help/v3.2/manual/cmake-packages.7.html#creating-packages
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
generate_export_header(SimGearCore)
|
||||
if(NOT SIMGEAR_HEADLESS)
|
||||
generate_export_header(SimGearScene)
|
||||
endif()
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
write_basic_package_version_file(
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/SimGear/SimGearConfigVersion.cmake"
|
||||
VERSION ${SIMGEAR_VERSION}
|
||||
COMPATIBILITY AnyNewerVersion
|
||||
)
|
||||
|
||||
configure_file(SimGearConfig.cmake.in
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/SimGear/SimGearConfig.cmake"
|
||||
@ONLY
|
||||
)
|
||||
|
||||
set(ConfigPackageLocation lib/cmake/SimGear)
|
||||
install(EXPORT SimGearTargets
|
||||
DESTINATION ${ConfigPackageLocation}
|
||||
)
|
||||
install(
|
||||
FILES
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/SimGear/SimGearConfig.cmake"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/SimGear/SimGearConfigVersion.cmake"
|
||||
DESTINATION ${ConfigPackageLocation}
|
||||
COMPONENT Devel
|
||||
)
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
### uninstall target
|
||||
#-----------------------------------------------------------------------------
|
||||
@@ -399,5 +467,3 @@ CONFIGURE_FILE(
|
||||
IMMEDIATE @ONLY)
|
||||
ADD_CUSTOM_TARGET(uninstall
|
||||
"${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
|
||||
|
||||
|
||||
|
||||
@@ -1,182 +0,0 @@
|
||||
# - Define GNU standard installation directories
|
||||
# Provides install directory variables as defined for GNU software:
|
||||
# http://www.gnu.org/prep/standards/html_node/Directory-Variables.html
|
||||
# Inclusion of this module defines the following variables:
|
||||
# CMAKE_INSTALL_<dir> - destination for files of a given type
|
||||
# CMAKE_INSTALL_FULL_<dir> - corresponding absolute path
|
||||
# where <dir> is one of:
|
||||
# BINDIR - user executables (bin)
|
||||
# SBINDIR - system admin executables (sbin)
|
||||
# LIBEXECDIR - program executables (libexec)
|
||||
# SYSCONFDIR - read-only single-machine data (etc)
|
||||
# SHAREDSTATEDIR - modifiable architecture-independent data (com)
|
||||
# LOCALSTATEDIR - modifiable single-machine data (var)
|
||||
# LIBDIR - object code libraries (lib or lib64)
|
||||
# INCLUDEDIR - C header files (include)
|
||||
# OLDINCLUDEDIR - C header files for non-gcc (/usr/include)
|
||||
# DATAROOTDIR - read-only architecture-independent data root (share)
|
||||
# DATADIR - read-only architecture-independent data (DATAROOTDIR)
|
||||
# INFODIR - info documentation (DATAROOTDIR/info)
|
||||
# LOCALEDIR - locale-dependent data (DATAROOTDIR/locale)
|
||||
# MANDIR - man documentation (DATAROOTDIR/man)
|
||||
# DOCDIR - documentation root (DATAROOTDIR/doc/PROJECT_NAME)
|
||||
# Each CMAKE_INSTALL_<dir> value may be passed to the DESTINATION options of
|
||||
# install() commands for the corresponding file type. If the includer does
|
||||
# not define a value the above-shown default will be used and the value will
|
||||
# appear in the cache for editing by the user.
|
||||
# Each CMAKE_INSTALL_FULL_<dir> value contains an absolute path constructed
|
||||
# from the corresponding destination by prepending (if necessary) the value
|
||||
# of CMAKE_INSTALL_PREFIX.
|
||||
|
||||
#=============================================================================
|
||||
# Copyright 2011 Nikita Krupen'ko <krnekit@gmail.com>
|
||||
# Copyright 2011 Kitware, Inc.
|
||||
#
|
||||
# Distributed under the OSI-approved BSD License (the "License");
|
||||
# see accompanying file Copyright.txt for details.
|
||||
#
|
||||
# This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
# See the License for more information.
|
||||
#=============================================================================
|
||||
# (To distribute this file outside of CMake, substitute the full
|
||||
# License text for the above reference.)
|
||||
|
||||
# Installation directories
|
||||
#
|
||||
if(NOT DEFINED CMAKE_INSTALL_BINDIR)
|
||||
set(CMAKE_INSTALL_BINDIR "bin" CACHE PATH "user executables (bin)")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED CMAKE_INSTALL_SBINDIR)
|
||||
set(CMAKE_INSTALL_SBINDIR "sbin" CACHE PATH "system admin executables (sbin)")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED CMAKE_INSTALL_LIBEXECDIR)
|
||||
set(CMAKE_INSTALL_LIBEXECDIR "libexec" CACHE PATH "program executables (libexec)")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED CMAKE_INSTALL_SYSCONFDIR)
|
||||
set(CMAKE_INSTALL_SYSCONFDIR "etc" CACHE PATH "read-only single-machine data (etc)")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED CMAKE_INSTALL_SHAREDSTATEDIR)
|
||||
set(CMAKE_INSTALL_SHAREDSTATEDIR "com" CACHE PATH "modifiable architecture-independent data (com)")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED CMAKE_INSTALL_LOCALSTATEDIR)
|
||||
set(CMAKE_INSTALL_LOCALSTATEDIR "var" CACHE PATH "modifiable single-machine data (var)")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED CMAKE_INSTALL_LIBDIR)
|
||||
set(_LIBDIR_DEFAULT "lib")
|
||||
# Override this default 'lib' with 'lib64' iff:
|
||||
# - we are on Linux system but NOT cross-compiling
|
||||
# - we are NOT on debian
|
||||
# - we are on a 64 bits system
|
||||
# reason is: amd64 ABI: http://www.x86-64.org/documentation/abi.pdf
|
||||
# Note that the future of multi-arch handling may be even
|
||||
# more complicated than that: http://wiki.debian.org/Multiarch
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "Linux"
|
||||
AND NOT CMAKE_CROSSCOMPILING
|
||||
AND NOT EXISTS "/etc/debian_version")
|
||||
if(NOT DEFINED CMAKE_SIZEOF_VOID_P)
|
||||
message(AUTHOR_WARNING
|
||||
"Unable to determine default CMAKE_INSTALL_LIBDIR directory because no target architecture is known. "
|
||||
"Please enable at least one language before including GNUInstallDirs.")
|
||||
else()
|
||||
if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||
set(_LIBDIR_DEFAULT "lib64")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
set(CMAKE_INSTALL_LIBDIR "${_LIBDIR_DEFAULT}" CACHE PATH "object code libraries (${_LIBDIR_DEFAULT})")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED CMAKE_INSTALL_INCLUDEDIR)
|
||||
set(CMAKE_INSTALL_INCLUDEDIR "include" CACHE PATH "C header files (include)")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED CMAKE_INSTALL_OLDINCLUDEDIR)
|
||||
set(CMAKE_INSTALL_OLDINCLUDEDIR "/usr/include" CACHE PATH "C header files for non-gcc (/usr/include)")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED CMAKE_INSTALL_DATAROOTDIR)
|
||||
set(CMAKE_INSTALL_DATAROOTDIR "share" CACHE PATH "read-only architecture-independent data root (share)")
|
||||
endif()
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Values whose defaults are relative to DATAROOTDIR. Store empty values in
|
||||
# the cache and store the defaults in local variables if the cache values are
|
||||
# not set explicitly. This auto-updates the defaults as DATAROOTDIR changes.
|
||||
|
||||
if(NOT CMAKE_INSTALL_DATADIR)
|
||||
set(CMAKE_INSTALL_DATADIR "" CACHE PATH "read-only architecture-independent data (DATAROOTDIR)")
|
||||
set(CMAKE_INSTALL_DATADIR "${CMAKE_INSTALL_DATAROOTDIR}")
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_INSTALL_INFODIR)
|
||||
set(CMAKE_INSTALL_INFODIR "" CACHE PATH "info documentation (DATAROOTDIR/info)")
|
||||
set(CMAKE_INSTALL_INFODIR "${CMAKE_INSTALL_DATAROOTDIR}/info")
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_INSTALL_LOCALEDIR)
|
||||
set(CMAKE_INSTALL_LOCALEDIR "" CACHE PATH "locale-dependent data (DATAROOTDIR/locale)")
|
||||
set(CMAKE_INSTALL_LOCALEDIR "${CMAKE_INSTALL_DATAROOTDIR}/locale")
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_INSTALL_MANDIR)
|
||||
set(CMAKE_INSTALL_MANDIR "" CACHE PATH "man documentation (DATAROOTDIR/man)")
|
||||
set(CMAKE_INSTALL_MANDIR "${CMAKE_INSTALL_DATAROOTDIR}/man")
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_INSTALL_DOCDIR)
|
||||
set(CMAKE_INSTALL_DOCDIR "" CACHE PATH "documentation root (DATAROOTDIR/doc/PROJECT_NAME)")
|
||||
set(CMAKE_INSTALL_DOCDIR "${CMAKE_INSTALL_DATAROOTDIR}/doc/${PROJECT_NAME}")
|
||||
endif()
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
mark_as_advanced(
|
||||
CMAKE_INSTALL_BINDIR
|
||||
CMAKE_INSTALL_SBINDIR
|
||||
CMAKE_INSTALL_LIBEXECDIR
|
||||
CMAKE_INSTALL_SYSCONFDIR
|
||||
CMAKE_INSTALL_SHAREDSTATEDIR
|
||||
CMAKE_INSTALL_LOCALSTATEDIR
|
||||
CMAKE_INSTALL_LIBDIR
|
||||
CMAKE_INSTALL_INCLUDEDIR
|
||||
CMAKE_INSTALL_OLDINCLUDEDIR
|
||||
CMAKE_INSTALL_DATAROOTDIR
|
||||
CMAKE_INSTALL_DATADIR
|
||||
CMAKE_INSTALL_INFODIR
|
||||
CMAKE_INSTALL_LOCALEDIR
|
||||
CMAKE_INSTALL_MANDIR
|
||||
CMAKE_INSTALL_DOCDIR
|
||||
)
|
||||
|
||||
# Result directories
|
||||
#
|
||||
foreach(dir
|
||||
BINDIR
|
||||
SBINDIR
|
||||
LIBEXECDIR
|
||||
SYSCONFDIR
|
||||
SHAREDSTATEDIR
|
||||
LOCALSTATEDIR
|
||||
LIBDIR
|
||||
INCLUDEDIR
|
||||
OLDINCLUDEDIR
|
||||
DATAROOTDIR
|
||||
DATADIR
|
||||
INFODIR
|
||||
LOCALEDIR
|
||||
MANDIR
|
||||
DOCDIR
|
||||
)
|
||||
if(NOT IS_ABSOLUTE ${CMAKE_INSTALL_${dir}})
|
||||
set(CMAKE_INSTALL_FULL_${dir} "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_${dir}}")
|
||||
else()
|
||||
set(CMAKE_INSTALL_FULL_${dir} "${CMAKE_INSTALL_${dir}}")
|
||||
endif()
|
||||
endforeach()
|
||||
16
SimGearConfig.cmake.in
Normal file
16
SimGearConfig.cmake.in
Normal file
@@ -0,0 +1,16 @@
|
||||
include(CMakeFindDependencyMacro)
|
||||
|
||||
find_dependency(ZLIB)
|
||||
find_dependency(Threads)
|
||||
|
||||
# OSG
|
||||
|
||||
set(SIMGEAR_HEADLESS @SIMGEAR_HEADLESS@)
|
||||
set(SIMGEAR_SOUND @ENABLE_SOUND@)
|
||||
|
||||
# OpenAL isn't a public dependency, so maybe not needed
|
||||
#if (SIMGEAR_SOUND)
|
||||
# find_dependency(OpenAL)
|
||||
#endif()
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/SimGearTargets.cmake")
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
file(WRITE ${PROJECT_BINARY_DIR}/simgear/version.h "#define SIMGEAR_VERSION ${SIMGEAR_VERSION}")
|
||||
|
||||
foreach( mylibfolder
|
||||
foreach( mylibfolder
|
||||
bucket
|
||||
bvh
|
||||
debug
|
||||
@@ -54,9 +54,11 @@ if(SIMGEAR_SHARED)
|
||||
set_property(TARGET SimGearCore PROPERTY LINKER_LANGUAGE CXX)
|
||||
set_property(TARGET SimGearCore PROPERTY VERSION ${SIMGEAR_VERSION})
|
||||
set_property(TARGET SimGearCore PROPERTY SOVERSION ${SIMGEAR_SOVERSION})
|
||||
install(TARGETS SimGearCore EXPORT SimGearCoreConfig LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
install(EXPORT SimGearCoreConfig DESTINATION share/SimGearCore)
|
||||
|
||||
install(TARGETS SimGearCore
|
||||
EXPORT SimGearTargets
|
||||
LIBRARY DESTINATION
|
||||
${CMAKE_INSTALL_LIBDIR})
|
||||
|
||||
if(NOT SIMGEAR_HEADLESS)
|
||||
add_library(SimGearScene SHARED ${sceneSources})
|
||||
set_property(TARGET SimGearScene PROPERTY LINKER_LANGUAGE CXX)
|
||||
@@ -64,10 +66,12 @@ if(SIMGEAR_SHARED)
|
||||
set_property(TARGET SimGearScene PROPERTY SOVERSION ${SIMGEAR_SOVERSION})
|
||||
|
||||
# EXPORT SimGearSceneConfig
|
||||
install(TARGETS SimGearScene LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} )
|
||||
# install(EXPORT SimGearSceneConfig DESTINATION share/SimGearScene)
|
||||
install(TARGETS SimGearScene
|
||||
EXPORT SimGearTargets
|
||||
LIBRARY
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR} )
|
||||
endif()
|
||||
|
||||
|
||||
else()
|
||||
message(STATUS "Library building mode: STATIC LIBRARIES")
|
||||
|
||||
@@ -90,8 +94,10 @@ else()
|
||||
endforeach()
|
||||
|
||||
add_library(SimGearCore STATIC ${coreSources} ${localExpatSources})
|
||||
install(TARGETS SimGearCore ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
|
||||
install(TARGETS SimGearCore
|
||||
EXPORT SimGearTargets
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
|
||||
if(NOT SIMGEAR_HEADLESS)
|
||||
get_property(FG_GROUPS_SCENE_SOURCES_C GLOBAL PROPERTY FG_GROUPS_SCENE_SOURCES_C)
|
||||
string(REPLACE "@" ";" groups ${FG_GROUPS_SCENE_SOURCES_C} )
|
||||
@@ -112,7 +118,9 @@ else()
|
||||
endforeach()
|
||||
|
||||
add_library(SimGearScene STATIC ${sceneSources})
|
||||
install(TARGETS SimGearScene ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
install(TARGETS SimGearScene
|
||||
EXPORT SimGearTargets
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
endif(NOT SIMGEAR_HEADLESS)
|
||||
endif(SIMGEAR_SHARED)
|
||||
|
||||
@@ -122,7 +130,8 @@ target_link_libraries(SimGearCore
|
||||
${DL_LIBRARY}
|
||||
${EXPAT_LIBRARIES}
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${COCOA_LIBRARY})
|
||||
${COCOA_LIBRARY}
|
||||
${CURL_LIBRARIES})
|
||||
|
||||
if(NOT SIMGEAR_HEADLESS)
|
||||
target_link_libraries(SimGearScene
|
||||
|
||||
@@ -43,7 +43,7 @@ public:
|
||||
void removeChild(BVHNode* child);
|
||||
|
||||
unsigned getNumChildren() const
|
||||
{ return _children.size(); }
|
||||
{ return static_cast<unsigned>(_children.size()); }
|
||||
const BVHNode* getChild(unsigned i) const
|
||||
{ if (_children.size() <= i) return 0; return _children[i]; }
|
||||
BVHNode* getChild(unsigned i)
|
||||
|
||||
@@ -31,13 +31,13 @@ public:
|
||||
virtual ~BVHStaticData() {}
|
||||
|
||||
unsigned addVertex(const SGVec3f& vertex)
|
||||
{ _vertices.push_back(vertex); return _vertices.size() - 1; }
|
||||
{ _vertices.push_back(vertex); return static_cast<unsigned>(_vertices.size() - 1); }
|
||||
const SGVec3f& getVertex(unsigned i) const
|
||||
{ return _vertices[i]; }
|
||||
|
||||
|
||||
unsigned addMaterial(const BVHMaterial* material)
|
||||
{ _materials.push_back(material); return _materials.size() - 1; }
|
||||
{ _materials.push_back(material); return static_cast<unsigned>(_materials.size() - 1); }
|
||||
const BVHMaterial* getMaterial(unsigned i) const
|
||||
{ if (_materials.size() <= i) return 0; return _materials[i]; }
|
||||
|
||||
|
||||
@@ -207,19 +207,6 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
Element::~Element()
|
||||
{
|
||||
if( !_transform.valid() )
|
||||
return;
|
||||
|
||||
for(unsigned int i = 0; i < _transform->getNumChildren(); ++i)
|
||||
{
|
||||
OSGUserData* ud =
|
||||
static_cast<OSGUserData*>(_transform->getChild(i)->getUserData());
|
||||
|
||||
if( ud )
|
||||
// Ensure parent is cleared to prevent accessing released memory if an
|
||||
// element somehow survives longer than his parent.
|
||||
ud->element->_parent = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -246,7 +233,7 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
ElementPtr Element::getParent() const
|
||||
{
|
||||
return _parent;
|
||||
return _parent.lock();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -324,8 +311,9 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
bool Element::ascend(EventVisitor& visitor)
|
||||
{
|
||||
if( _parent )
|
||||
return _parent->accept(visitor);
|
||||
ElementPtr parent = getParent();
|
||||
if( parent )
|
||||
return parent->accept(visitor);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -374,9 +362,9 @@ namespace canvas
|
||||
EventPropagationPath path;
|
||||
path.push_back( EventTarget(this) );
|
||||
|
||||
for( Element* parent = _parent;
|
||||
parent != NULL;
|
||||
parent = parent->_parent )
|
||||
for( ElementPtr parent = getParent();
|
||||
parent.valid();
|
||||
parent = parent->getParent() )
|
||||
path.push_front( EventTarget(parent) );
|
||||
|
||||
CanvasPtr canvas = _canvas.lock();
|
||||
@@ -772,7 +760,7 @@ namespace canvas
|
||||
Element::Element( const CanvasWeakPtr& canvas,
|
||||
const SGPropertyNode_ptr& node,
|
||||
const Style& parent_style,
|
||||
Element* parent ):
|
||||
ElementWeakPtr parent ):
|
||||
PropertyBasedElement(node),
|
||||
_canvas( canvas ),
|
||||
_parent( parent ),
|
||||
@@ -871,11 +859,12 @@ namespace canvas
|
||||
Element::getParentStyle(const SGPropertyNode* child) const
|
||||
{
|
||||
// Try to get value from parent...
|
||||
if( _parent )
|
||||
ElementPtr parent = getParent();
|
||||
if( parent )
|
||||
{
|
||||
Style::const_iterator style =
|
||||
_parent->_style.find(child->getNameString());
|
||||
if( style != _parent->_style.end() )
|
||||
parent->_style.find(child->getNameString());
|
||||
if( style != parent->_style.end() )
|
||||
return style->second;
|
||||
}
|
||||
|
||||
|
||||
@@ -232,8 +232,8 @@ namespace canvas
|
||||
|
||||
class RelativeScissor;
|
||||
|
||||
CanvasWeakPtr _canvas;
|
||||
Element *_parent;
|
||||
CanvasWeakPtr _canvas;
|
||||
ElementWeakPtr _parent;
|
||||
|
||||
mutable uint32_t _attributes_dirty;
|
||||
|
||||
@@ -256,7 +256,7 @@ namespace canvas
|
||||
Element( const CanvasWeakPtr& canvas,
|
||||
const SGPropertyNode_ptr& node,
|
||||
const Style& parent_style,
|
||||
Element* parent );
|
||||
ElementWeakPtr parent );
|
||||
|
||||
/**
|
||||
* Returns false on first call and true on any successive call. Use to
|
||||
|
||||
@@ -70,7 +70,7 @@ namespace canvas
|
||||
Group::Group( const CanvasWeakPtr& canvas,
|
||||
const SGPropertyNode_ptr& node,
|
||||
const Style& parent_style,
|
||||
Element* parent ):
|
||||
ElementWeakPtr parent ):
|
||||
Element(canvas, node, parent_style, parent)
|
||||
{
|
||||
staticInit();
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace canvas
|
||||
Group( const CanvasWeakPtr& canvas,
|
||||
const SGPropertyNode_ptr& node,
|
||||
const Style& parent_style = Style(),
|
||||
Element* parent = 0 );
|
||||
ElementWeakPtr parent = 0 );
|
||||
virtual ~Group();
|
||||
|
||||
ElementPtr createChild( const std::string& type,
|
||||
|
||||
@@ -112,7 +112,7 @@ namespace canvas
|
||||
Image::Image( const CanvasWeakPtr& canvas,
|
||||
const SGPropertyNode_ptr& node,
|
||||
const Style& parent_style,
|
||||
Element* parent ):
|
||||
ElementWeakPtr parent ):
|
||||
Element(canvas, node, parent_style, parent),
|
||||
_texture(new osg::Texture2D),
|
||||
_node_src_rect( node->getNode("source", 0, true) ),
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace canvas
|
||||
Image( const CanvasWeakPtr& canvas,
|
||||
const SGPropertyNode_ptr& node,
|
||||
const Style& parent_style = Style(),
|
||||
Element* parent = 0 );
|
||||
ElementWeakPtr parent = 0 );
|
||||
virtual ~Image();
|
||||
|
||||
virtual void update(double dt);
|
||||
|
||||
@@ -44,6 +44,7 @@ namespace canvas
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
const std::string GEO = "-geo";
|
||||
const std::string HDG = "hdg";
|
||||
const std::string Map::TYPE_NAME = "map";
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@@ -61,7 +62,7 @@ namespace canvas
|
||||
Map::Map( const CanvasWeakPtr& canvas,
|
||||
const SGPropertyNode_ptr& node,
|
||||
const Style& parent_style,
|
||||
Element* parent ):
|
||||
ElementWeakPtr parent ):
|
||||
Group(canvas, node, parent_style, parent),
|
||||
// TODO make projection configurable
|
||||
_projection(new SansonFlamsteedProjection),
|
||||
@@ -112,87 +113,50 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
void Map::childAdded(SGPropertyNode* parent, SGPropertyNode* child)
|
||||
{
|
||||
if( !boost::ends_with(child->getNameString(), GEO) )
|
||||
if( boost::ends_with(child->getNameString(), GEO) )
|
||||
_geo_nodes[child].reset(new GeoNodePair());
|
||||
else if( parent != _node && child->getNameString() == HDG )
|
||||
_hdg_nodes.insert(child);
|
||||
else
|
||||
return Element::childAdded(parent, child);
|
||||
|
||||
_geo_nodes[child].reset(new GeoNodePair());
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Map::childRemoved(SGPropertyNode* parent, SGPropertyNode* child)
|
||||
{
|
||||
if( !boost::ends_with(child->getNameString(), GEO) )
|
||||
return Element::childRemoved(parent, child);
|
||||
|
||||
// TODO remove from other node
|
||||
_geo_nodes.erase(child);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Map::valueChanged(SGPropertyNode * child)
|
||||
{
|
||||
const std::string& name = child->getNameString();
|
||||
|
||||
if( !boost::ends_with(name, GEO) )
|
||||
return Group::valueChanged(child);
|
||||
|
||||
GeoNodes::iterator it_geo_node = _geo_nodes.find(child);
|
||||
if( it_geo_node == _geo_nodes.end() )
|
||||
LOG_GEO_RET("geo node not found!")
|
||||
GeoNodePair* geo_node = it_geo_node->second.get();
|
||||
|
||||
geo_node->setDirty();
|
||||
|
||||
if( geo_node->getStatus() & GeoNodePair::INCOMPLETE )
|
||||
if( boost::ends_with(child->getNameString(), GEO) )
|
||||
// TODO remove from other node
|
||||
_geo_nodes.erase(child);
|
||||
else if( parent != _node && child->getName() == HDG )
|
||||
{
|
||||
// Detect lat, lon tuples...
|
||||
GeoCoord coord = parseGeoCoord(child->getStringValue());
|
||||
int index_other = -1;
|
||||
_hdg_nodes.erase(child);
|
||||
|
||||
switch( coord.type )
|
||||
{
|
||||
case GeoCoord::LATITUDE:
|
||||
index_other = child->getIndex() + 1;
|
||||
geo_node->setNodeLat(child);
|
||||
break;
|
||||
case GeoCoord::LONGITUDE:
|
||||
index_other = child->getIndex() - 1;
|
||||
geo_node->setNodeLon(child);
|
||||
break;
|
||||
default:
|
||||
LOG_GEO_RET("Invalid geo coord")
|
||||
}
|
||||
|
||||
SGPropertyNode *other = child->getParent()->getChild(name, index_other);
|
||||
if( !other )
|
||||
return;
|
||||
|
||||
GeoCoord coord_other = parseGeoCoord(other->getStringValue());
|
||||
if( coord_other.type == GeoCoord::INVALID
|
||||
|| coord_other.type == coord.type )
|
||||
return;
|
||||
|
||||
GeoNodes::iterator it_geo_node_other = _geo_nodes.find(other);
|
||||
if( it_geo_node_other == _geo_nodes.end() )
|
||||
LOG_GEO_RET("other geo node not found!")
|
||||
GeoNodePair* geo_node_other = it_geo_node_other->second.get();
|
||||
|
||||
// Let use both nodes use the same GeoNodePair instance
|
||||
if( geo_node_other != geo_node )
|
||||
it_geo_node_other->second = it_geo_node->second;
|
||||
|
||||
if( coord_other.type == GeoCoord::LATITUDE )
|
||||
geo_node->setNodeLat(other);
|
||||
else
|
||||
geo_node->setNodeLon(other);
|
||||
|
||||
// Set name for resulting screen coordinate nodes
|
||||
geo_node->setTargetName( name.substr(0, name.length() - GEO.length()) );
|
||||
// Remove rotation matrix (tf[0]) and return to element always being
|
||||
// oriented upwards (or any orientation inside other matrices).
|
||||
child->getParent()->removeChild("tf", 0);
|
||||
}
|
||||
else
|
||||
return Element::childRemoved(parent, child);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Map::childChanged(SGPropertyNode * child)
|
||||
void Map::valueChanged(SGPropertyNode* child)
|
||||
{
|
||||
if( child->getParent() != _node )
|
||||
{
|
||||
const std::string& name = child->getNameString();
|
||||
|
||||
if( boost::ends_with(name, GEO) )
|
||||
return geoNodeChanged(child);
|
||||
else if( name == HDG )
|
||||
return hdgNodeChanged(child);
|
||||
}
|
||||
|
||||
return Group::valueChanged(child);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Map::childChanged(SGPropertyNode* child)
|
||||
{
|
||||
if( child->getParent() != _node )
|
||||
return Group::childChanged(child);
|
||||
@@ -201,8 +165,14 @@ namespace canvas
|
||||
|| child->getNameString() == "ref-lon" )
|
||||
_projection->setWorldPosition( _node->getDoubleValue("ref-lat"),
|
||||
_node->getDoubleValue("ref-lon") );
|
||||
else if( child->getNameString() == "hdg" )
|
||||
else if( child->getNameString() == HDG )
|
||||
{
|
||||
_projection->setOrientation(child->getFloatValue());
|
||||
for( NodeSet::iterator it = _hdg_nodes.begin();
|
||||
it != _hdg_nodes.end();
|
||||
++it )
|
||||
hdgNodeChanged(*it);
|
||||
}
|
||||
else if( child->getNameString() == "range" )
|
||||
_projection->setRange(child->getDoubleValue());
|
||||
else if( child->getNameString() == "screen-range" )
|
||||
@@ -213,6 +183,74 @@ namespace canvas
|
||||
_projection_dirty = true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Map::geoNodeChanged(SGPropertyNode* child)
|
||||
{
|
||||
GeoNodes::iterator it_geo_node = _geo_nodes.find(child);
|
||||
if( it_geo_node == _geo_nodes.end() )
|
||||
LOG_GEO_RET("GeoNode not found!")
|
||||
GeoNodePair* geo_node = it_geo_node->second.get();
|
||||
|
||||
geo_node->setDirty();
|
||||
|
||||
if( !(geo_node->getStatus() & GeoNodePair::INCOMPLETE) )
|
||||
return;
|
||||
|
||||
// Detect lat, lon tuples...
|
||||
GeoCoord coord = parseGeoCoord(child->getStringValue());
|
||||
int index_other = -1;
|
||||
|
||||
switch( coord.type )
|
||||
{
|
||||
case GeoCoord::LATITUDE:
|
||||
index_other = child->getIndex() + 1;
|
||||
geo_node->setNodeLat(child);
|
||||
break;
|
||||
case GeoCoord::LONGITUDE:
|
||||
index_other = child->getIndex() - 1;
|
||||
geo_node->setNodeLon(child);
|
||||
break;
|
||||
default:
|
||||
LOG_GEO_RET("Invalid geo coord")
|
||||
}
|
||||
|
||||
const std::string& name = child->getNameString();
|
||||
SGPropertyNode *other = child->getParent()->getChild(name, index_other);
|
||||
if( !other )
|
||||
return;
|
||||
|
||||
GeoCoord coord_other = parseGeoCoord(other->getStringValue());
|
||||
if( coord_other.type == GeoCoord::INVALID
|
||||
|| coord_other.type == coord.type )
|
||||
return;
|
||||
|
||||
GeoNodes::iterator it_geo_node_other = _geo_nodes.find(other);
|
||||
if( it_geo_node_other == _geo_nodes.end() )
|
||||
LOG_GEO_RET("other geo node not found!")
|
||||
GeoNodePair* geo_node_other = it_geo_node_other->second.get();
|
||||
|
||||
// Let use both nodes use the same GeoNodePair instance
|
||||
if( geo_node_other != geo_node )
|
||||
it_geo_node_other->second = it_geo_node->second;
|
||||
|
||||
if( coord_other.type == GeoCoord::LATITUDE )
|
||||
geo_node->setNodeLat(other);
|
||||
else
|
||||
geo_node->setNodeLon(other);
|
||||
|
||||
// Set name for resulting screen coordinate nodes
|
||||
geo_node->setTargetName( name.substr(0, name.length() - GEO.length()) );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Map::hdgNodeChanged(SGPropertyNode* child)
|
||||
{
|
||||
child->getParent()->setFloatValue(
|
||||
"tf[0]/rot",
|
||||
SGMiscf::deg2rad(child->getFloatValue() - _projection->orientation())
|
||||
);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Map::GeoCoord Map::parseGeoCoord(const std::string& val) const
|
||||
{
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/unordered_set.hpp>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
@@ -41,7 +42,7 @@ namespace canvas
|
||||
Map( const CanvasWeakPtr& canvas,
|
||||
const SGPropertyNode_ptr& node,
|
||||
const Style& parent_style,
|
||||
Element* parent = 0 );
|
||||
ElementWeakPtr parent = 0 );
|
||||
virtual ~Map();
|
||||
|
||||
virtual void update(double dt);
|
||||
@@ -59,14 +60,18 @@ namespace canvas
|
||||
typedef boost::unordered_map< SGPropertyNode*,
|
||||
boost::shared_ptr<GeoNodePair>
|
||||
> GeoNodes;
|
||||
typedef boost::unordered_set<SGPropertyNode*> NodeSet;
|
||||
|
||||
GeoNodes _geo_nodes;
|
||||
NodeSet _hdg_nodes;
|
||||
boost::shared_ptr<HorizontalProjection> _projection;
|
||||
bool _projection_dirty;
|
||||
|
||||
struct GeoCoord
|
||||
{
|
||||
GeoCoord():
|
||||
type(INVALID)
|
||||
type(INVALID),
|
||||
value(0)
|
||||
{}
|
||||
enum
|
||||
{
|
||||
@@ -77,6 +82,9 @@ namespace canvas
|
||||
double value;
|
||||
};
|
||||
|
||||
void geoNodeChanged(SGPropertyNode * child);
|
||||
void hdgNodeChanged(SGPropertyNode * child);
|
||||
|
||||
GeoCoord parseGeoCoord(const std::string& val) const;
|
||||
};
|
||||
|
||||
|
||||
@@ -531,7 +531,7 @@ namespace canvas
|
||||
Path::Path( const CanvasWeakPtr& canvas,
|
||||
const SGPropertyNode_ptr& node,
|
||||
const Style& parent_style,
|
||||
Element* parent ):
|
||||
ElementWeakPtr parent ):
|
||||
Element(canvas, node, parent_style, parent),
|
||||
_path( new PathDrawable(this) )
|
||||
{
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace canvas
|
||||
Path( const CanvasWeakPtr& canvas,
|
||||
const SGPropertyNode_ptr& node,
|
||||
const Style& parent_style,
|
||||
Element* parent = 0 );
|
||||
ElementWeakPtr parent = 0 );
|
||||
virtual ~Path();
|
||||
|
||||
virtual void update(double dt);
|
||||
|
||||
@@ -159,8 +159,12 @@ namespace canvas
|
||||
|
||||
if( empty() )
|
||||
return pos;
|
||||
|
||||
#if OSG_VERSION_LESS_THAN(3,3,5)
|
||||
GlyphQuads::Coords2 const& coords = _quads->_coords;
|
||||
#else
|
||||
GlyphQuads::Coords2 refCoords = _quads->_coords;
|
||||
GlyphQuads::Coords2::element_type &coords = *refCoords.get();
|
||||
#endif
|
||||
size_t global_i = _begin + i;
|
||||
|
||||
if( global_i == _begin )
|
||||
@@ -194,7 +198,12 @@ namespace canvas
|
||||
return cursorPos(0);
|
||||
|
||||
GlyphQuads::Glyphs const& glyphs = _quads->_glyphs;
|
||||
#if OSG_VERSION_LESS_THAN(3,3,5)
|
||||
GlyphQuads::Coords2 const& coords = _quads->_coords;
|
||||
#else
|
||||
GlyphQuads::Coords2 refCoords = _quads->_coords;
|
||||
GlyphQuads::Coords2::element_type &coords = *refCoords.get();
|
||||
#endif
|
||||
|
||||
float const HIT_FRACTION = 0.6;
|
||||
float const character_width = _text->getCharacterHeight()
|
||||
@@ -627,7 +636,13 @@ namespace canvas
|
||||
|
||||
const GlyphQuads& quads = _textureGlyphQuadMap.begin()->second;
|
||||
const GlyphQuads::Glyphs& glyphs = quads._glyphs;
|
||||
const GlyphQuads::Coords2& coords = quads._coords;
|
||||
#if OSG_VERSION_LESS_THAN(3,3,5)
|
||||
GlyphQuads::Coords2 const& coords = quads._coords;
|
||||
#else
|
||||
GlyphQuads::Coords2 refCoords = quads._coords;
|
||||
GlyphQuads::Coords2::element_type &coords = *refCoords.get();
|
||||
#endif
|
||||
|
||||
const GlyphQuads::LineNumbers& line_numbers = quads._lineNumbers;
|
||||
|
||||
float wr = _characterHeight / getCharacterAspectRatio();
|
||||
@@ -725,7 +740,7 @@ namespace canvas
|
||||
Text::Text( const CanvasWeakPtr& canvas,
|
||||
const SGPropertyNode_ptr& node,
|
||||
const Style& parent_style,
|
||||
Element* parent ):
|
||||
ElementWeakPtr parent ):
|
||||
Element(canvas, node, parent_style, parent),
|
||||
_text( new Text::TextOSG(this) )
|
||||
{
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace canvas
|
||||
Text( const CanvasWeakPtr& canvas,
|
||||
const SGPropertyNode_ptr& node,
|
||||
const Style& parent_style,
|
||||
Element* parent = 0 );
|
||||
ElementWeakPtr parent = 0 );
|
||||
~Text();
|
||||
|
||||
void setText(const char* text);
|
||||
|
||||
@@ -34,7 +34,10 @@ namespace canvas
|
||||
public:
|
||||
struct ScreenPosition
|
||||
{
|
||||
ScreenPosition() {}
|
||||
ScreenPosition():
|
||||
x(0),
|
||||
y(0)
|
||||
{}
|
||||
|
||||
ScreenPosition(double x, double y):
|
||||
x(x),
|
||||
@@ -67,8 +70,11 @@ namespace canvas
|
||||
public:
|
||||
|
||||
HorizontalProjection():
|
||||
_cos_rot(1),
|
||||
_sin_rot(0),
|
||||
_ref_lat(0),
|
||||
_ref_lon(0),
|
||||
_angle(0),
|
||||
_cos_angle(1),
|
||||
_sin_angle(0),
|
||||
_range(5)
|
||||
{
|
||||
setScreenRange(200);
|
||||
@@ -88,9 +94,19 @@ namespace canvas
|
||||
*/
|
||||
void setOrientation(float hdg)
|
||||
{
|
||||
_angle = hdg;
|
||||
|
||||
hdg = SGMiscf::deg2rad(hdg);
|
||||
_sin_rot = sin(hdg);
|
||||
_cos_rot = cos(hdg);
|
||||
_sin_angle = sin(hdg);
|
||||
_cos_angle = cos(hdg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get orientation/heading of the projection (in degree)
|
||||
*/
|
||||
float orientation() const
|
||||
{
|
||||
return _angle;
|
||||
}
|
||||
|
||||
void setRange(double range)
|
||||
@@ -114,8 +130,8 @@ namespace canvas
|
||||
pos.y *= scale;
|
||||
return ScreenPosition
|
||||
(
|
||||
_cos_rot * pos.x - _sin_rot * pos.y,
|
||||
-_sin_rot * pos.x - _cos_rot * pos.y
|
||||
_cos_angle * pos.x - _sin_angle * pos.y,
|
||||
-_sin_angle * pos.x - _cos_angle * pos.y
|
||||
);
|
||||
}
|
||||
|
||||
@@ -129,10 +145,11 @@ namespace canvas
|
||||
*/
|
||||
virtual ScreenPosition project(double lat, double lon) const = 0;
|
||||
|
||||
double _ref_lat,
|
||||
_ref_lon,
|
||||
_cos_rot,
|
||||
_sin_rot,
|
||||
double _ref_lat, ///<! Reference latitude (radian)
|
||||
_ref_lon, ///<! Reference latitude (radian)
|
||||
_angle, ///<! Map rotation angle (degree)
|
||||
_cos_angle,
|
||||
_sin_angle,
|
||||
_range;
|
||||
};
|
||||
|
||||
|
||||
@@ -190,6 +190,12 @@ inline int (isnan)(double r) { return !(r <= 0 || r >= 0); }
|
||||
# define DEPRECATED
|
||||
#endif
|
||||
|
||||
#if defined(__clang__)
|
||||
# define SG_NO_RETURN [[noreturn]]
|
||||
#else
|
||||
# define SG_NO_RETURN
|
||||
#endif
|
||||
|
||||
//
|
||||
// No user modifiable definitions beyond here.
|
||||
//
|
||||
|
||||
@@ -32,7 +32,8 @@ typedef enum {
|
||||
SG_SOUND = 0x00200000,
|
||||
SG_NAVAID = 0x00400000,
|
||||
SG_GUI = 0x00800000,
|
||||
SG_UNDEFD = 0x01000000, // For range checking
|
||||
SG_TERRASYNC = 0x01000000,
|
||||
SG_UNDEFD = 0x02000000, // For range checking
|
||||
|
||||
SG_ALL = 0xFFFFFFFF
|
||||
} sgDebugClass;
|
||||
|
||||
@@ -71,6 +71,7 @@ const char* debugClassToString(sgDebugClass c)
|
||||
case SG_SOUND: return "sound";
|
||||
case SG_NAVAID: return "navaid";
|
||||
case SG_GUI: return "gui";
|
||||
case SG_TERRASYNC: return "terrasync";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
* $Id$
|
||||
**************************************************************************/
|
||||
|
||||
#include <math.h>
|
||||
#include <cmath>
|
||||
|
||||
#include "jupiter.hxx"
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
* $Id$
|
||||
**************************************************************************/
|
||||
|
||||
#include <math.h>
|
||||
#include <cmath>
|
||||
|
||||
#include "mars.hxx"
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
* $Id$
|
||||
**************************************************************************/
|
||||
|
||||
#include <math.h>
|
||||
#include <cmath>
|
||||
|
||||
#include "mercury.hxx"
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#ifdef __BORLANDC__
|
||||
# define exception c_exception
|
||||
#endif
|
||||
#include <math.h>
|
||||
#include <cmath>
|
||||
|
||||
#include "neptune.hxx"
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
* $Id$
|
||||
**************************************************************************/
|
||||
|
||||
#include <math.h>
|
||||
#include <cmath>
|
||||
|
||||
#include "saturn.hxx"
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
* $Id$
|
||||
**************************************************************************/
|
||||
|
||||
#include <math.h>
|
||||
#include <cmath>
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ public:
|
||||
bool load( const SGPath& path );
|
||||
|
||||
// stars
|
||||
inline int getNumStars() const { return _stars.size(); }
|
||||
inline int getNumStars() const { return static_cast<int>(_stars.size()); }
|
||||
inline SGVec3d *getStars() { return &(_stars[0]); }
|
||||
|
||||
private:
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
* $Id$
|
||||
**************************************************************************/
|
||||
|
||||
#include <math.h>
|
||||
#include <cmath>
|
||||
|
||||
#include "uranus.hxx"
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
* $Id$
|
||||
**************************************************************************/
|
||||
|
||||
#include <math.h>
|
||||
#include <cmath>
|
||||
|
||||
#include "venus.hxx"
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ set(HEADERS
|
||||
HTTPFileRequest.hxx
|
||||
HTTPMemoryRequest.hxx
|
||||
HTTPRequest.hxx
|
||||
HTTPContentDecode.hxx
|
||||
DAVMultiStatus.hxx
|
||||
SVNRepository.hxx
|
||||
SVNDirectory.hxx
|
||||
@@ -41,13 +40,17 @@ set(SOURCES
|
||||
HTTPFileRequest.cxx
|
||||
HTTPMemoryRequest.cxx
|
||||
HTTPRequest.cxx
|
||||
HTTPContentDecode.cxx
|
||||
DAVMultiStatus.cxx
|
||||
SVNRepository.cxx
|
||||
SVNDirectory.cxx
|
||||
SVNReportParser.cxx
|
||||
)
|
||||
|
||||
if (NOT ENABLE_CURL)
|
||||
list(APPEND SOURCES HTTPContentDecode.cxx)
|
||||
list(APPEND HEADERS HTTPContentDecode.hxx)
|
||||
endif()
|
||||
|
||||
simgear_component(io io "${SOURCES}" "${HEADERS}")
|
||||
|
||||
if(ENABLE_TESTS)
|
||||
@@ -70,8 +73,8 @@ add_executable(decode_binobj decode_binobj.cxx)
|
||||
target_link_libraries(decode_binobj ${TEST_LIBS})
|
||||
|
||||
add_executable(test_binobj test_binobj.cxx)
|
||||
target_link_libraries(test_binobj ${TEST_LIBS})
|
||||
|
||||
target_link_libraries(test_binobj ${TEST_LIBS})
|
||||
|
||||
add_test(binobj ${EXECUTABLE_OUTPUT_PATH}/test_binobj)
|
||||
|
||||
endif(ENABLE_TESTS)
|
||||
|
||||
@@ -193,7 +193,7 @@ public:
|
||||
{
|
||||
if (tagStack.empty()) {
|
||||
if (strcmp(name, DAV_MULTISTATUS_TAG)) {
|
||||
SG_LOG(SG_IO, SG_WARN, "root element is not " <<
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "root element is not " <<
|
||||
DAV_MULTISTATUS_TAG << ", got:" << name);
|
||||
} else {
|
||||
|
||||
@@ -202,7 +202,7 @@ public:
|
||||
// not at the root element
|
||||
if (tagStack.back() == DAV_MULTISTATUS_TAG) {
|
||||
if (strcmp(name, DAV_RESPONSE_TAG)) {
|
||||
SG_LOG(SG_IO, SG_WARN, "multistatus child is not response: saw:"
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "multistatus child is not response: saw:"
|
||||
<< name);
|
||||
}
|
||||
}
|
||||
@@ -362,7 +362,7 @@ void DAVMultiStatus::parseXML(const char* data, int size)
|
||||
}
|
||||
|
||||
if (!XML_Parse(_d->xmlParser, data, size, false)) {
|
||||
SG_LOG(SG_IO, SG_WARN, "DAV parse error:" << XML_ErrorString(XML_GetErrorCode(_d->xmlParser))
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "DAV parse error:" << XML_ErrorString(XML_GetErrorCode(_d->xmlParser))
|
||||
<< " at line:" << XML_GetCurrentLineNumber(_d->xmlParser)
|
||||
<< " column " << XML_GetCurrentColumnNumber(_d->xmlParser));
|
||||
|
||||
@@ -376,7 +376,7 @@ void DAVMultiStatus::finishParse()
|
||||
{
|
||||
if (_d->parserInited) {
|
||||
if (!XML_Parse(_d->xmlParser, NULL, 0, true)) {
|
||||
SG_LOG(SG_IO, SG_WARN, "DAV parse error:" << XML_ErrorString(XML_GetErrorCode(_d->xmlParser))
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "DAV parse error:" << XML_ErrorString(XML_GetErrorCode(_d->xmlParser))
|
||||
<< " at line:" << XML_GetCurrentLineNumber(_d->xmlParser)
|
||||
<< " column " << XML_GetCurrentColumnNumber(_d->xmlParser));
|
||||
_d->valid = false;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -38,15 +38,15 @@ namespace HTTP
|
||||
|
||||
// forward decls
|
||||
class Connection;
|
||||
|
||||
|
||||
class Client
|
||||
{
|
||||
public:
|
||||
Client();
|
||||
~Client();
|
||||
|
||||
|
||||
void update(int waitTimeout = 0);
|
||||
|
||||
|
||||
void makeRequest(const Request_ptr& r);
|
||||
|
||||
/**
|
||||
@@ -68,44 +68,51 @@ public:
|
||||
|
||||
void setUserAgent(const std::string& ua);
|
||||
void setProxy(const std::string& proxy, int port, const std::string& auth = "");
|
||||
|
||||
|
||||
/**
|
||||
* Specify the maximum permitted simultaneous connections
|
||||
* (default value is 1)
|
||||
*/
|
||||
void setMaxConnections(unsigned int maxCons);
|
||||
|
||||
|
||||
const std::string& userAgent() const;
|
||||
|
||||
|
||||
const std::string& proxyHost() const;
|
||||
|
||||
|
||||
const std::string& proxyAuth() const;
|
||||
|
||||
|
||||
/**
|
||||
* predicate, check if at least one connection is active, with at
|
||||
* least one request active or queued.
|
||||
*/
|
||||
bool hasActiveRequests() const;
|
||||
|
||||
|
||||
/**
|
||||
* crude tracking of bytes-per-second transferred over the socket.
|
||||
* suitable for user feedback and rough profiling, nothing more.
|
||||
*/
|
||||
unsigned int transferRateBytesPerSec() const;
|
||||
|
||||
|
||||
/**
|
||||
* total bytes downloaded by this HTTP client, for bandwidth usage
|
||||
* monitoring
|
||||
*/
|
||||
uint64_t totalBytesDownloaded() const;
|
||||
|
||||
void debugDumpRequests();
|
||||
private:
|
||||
// libCurl callbacks
|
||||
static size_t requestWriteCallback(char *ptr, size_t size, size_t nmemb, void *userdata);
|
||||
static size_t requestReadCallback(char *ptr, size_t size, size_t nmemb, void *userdata);
|
||||
static size_t requestHeaderCallback(char *buffer, size_t size, size_t nitems, void *userdata);
|
||||
|
||||
void requestFinished(Connection* con);
|
||||
|
||||
|
||||
void receivedBytes(unsigned int count);
|
||||
|
||||
|
||||
friend class Connection;
|
||||
friend class Request;
|
||||
|
||||
|
||||
class ClientPrivate;
|
||||
std::auto_ptr<ClientPrivate> d;
|
||||
};
|
||||
|
||||
@@ -133,19 +133,25 @@ void Request::responseStart(const std::string& r)
|
||||
const int maxSplit = 2; // HTTP/1.1 nnn reason-string
|
||||
string_list parts = strutils::split(r, NULL, maxSplit);
|
||||
if (parts.size() != 3) {
|
||||
throw sg_io_exception("bad HTTP response");
|
||||
throw sg_io_exception("bad HTTP response:" + r);
|
||||
}
|
||||
|
||||
|
||||
_responseVersion = decodeHTTPVersion(parts[0]);
|
||||
_responseStatus = strutils::to_int(parts[1]);
|
||||
_responseReason = parts[2];
|
||||
|
||||
setReadyState(STATUS_RECEIVED);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Request::responseHeader(const std::string& key, const std::string& value)
|
||||
{
|
||||
if( key == "connection" )
|
||||
if( key == "connection" ) {
|
||||
_willClose = (value.find("close") != std::string::npos);
|
||||
} else if (key == "content-length") {
|
||||
int sz = strutils::to_int(value);
|
||||
setResponseLength(sz);
|
||||
}
|
||||
|
||||
_responseHeaders[key] = value;
|
||||
}
|
||||
@@ -207,7 +213,7 @@ std::string Request::scheme() const
|
||||
if (firstColon > 0) {
|
||||
return url().substr(0, firstColon);
|
||||
}
|
||||
|
||||
|
||||
return ""; // couldn't parse scheme
|
||||
}
|
||||
|
||||
@@ -219,20 +225,20 @@ std::string Request::path() const
|
||||
if (schemeEnd < 0) {
|
||||
return ""; // couldn't parse scheme
|
||||
}
|
||||
|
||||
|
||||
int hostEnd = u.find('/', schemeEnd + 3);
|
||||
if (hostEnd < 0) {
|
||||
// couldn't parse host, or URL looks like 'http://foo.com' (no trailing '/')
|
||||
// fixup to root resource path: '/'
|
||||
return "/";
|
||||
// couldn't parse host, or URL looks like 'http://foo.com' (no trailing '/')
|
||||
// fixup to root resource path: '/'
|
||||
return "/";
|
||||
}
|
||||
|
||||
|
||||
int query = u.find('?', hostEnd + 1);
|
||||
if (query < 0) {
|
||||
// all remainder of URL is path
|
||||
return u.substr(hostEnd);
|
||||
}
|
||||
|
||||
|
||||
return u.substr(hostEnd, query - hostEnd);
|
||||
}
|
||||
|
||||
@@ -244,7 +250,7 @@ std::string Request::query() const
|
||||
if (query < 0) {
|
||||
return ""; //no query string found
|
||||
}
|
||||
|
||||
|
||||
return u.substr(query); //includes question mark
|
||||
}
|
||||
|
||||
@@ -319,6 +325,7 @@ unsigned int Request::responseLength() const
|
||||
//------------------------------------------------------------------------------
|
||||
void Request::setFailure(int code, const std::string& reason)
|
||||
{
|
||||
SG_LOG(SG_IO, SG_WARN, "HTTP request: set failure:" << code << " reason " << reason);
|
||||
_responseStatus = code;
|
||||
_responseReason = reason;
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ public:
|
||||
{
|
||||
UNSENT = 0,
|
||||
OPENED,
|
||||
STATUS_RECEIVED,
|
||||
HEADERS_RECEIVED,
|
||||
LOADING,
|
||||
DONE,
|
||||
@@ -124,19 +125,19 @@ public:
|
||||
void setBodyData( const SGPropertyNode* data );
|
||||
|
||||
virtual void setUrl(const std::string& url);
|
||||
|
||||
|
||||
virtual std::string method() const
|
||||
{ return _method; }
|
||||
virtual std::string url() const
|
||||
{ return _url; }
|
||||
|
||||
|
||||
virtual std::string scheme() const;
|
||||
virtual std::string path() const;
|
||||
virtual std::string host() const;
|
||||
virtual std::string hostAndPort() const;
|
||||
virtual unsigned short port() const;
|
||||
virtual std::string query() const;
|
||||
|
||||
|
||||
StringMap const& responseHeaders() const
|
||||
{ return _responseHeaders; }
|
||||
|
||||
@@ -144,13 +145,13 @@ public:
|
||||
|
||||
virtual int responseCode() const
|
||||
{ return _responseStatus; }
|
||||
|
||||
|
||||
virtual std::string responseReason() const
|
||||
{ return _responseReason; }
|
||||
|
||||
|
||||
void setResponseLength(unsigned int l);
|
||||
virtual unsigned int responseLength() const;
|
||||
|
||||
|
||||
/**
|
||||
* Check if request contains body data.
|
||||
*/
|
||||
@@ -165,32 +166,32 @@ public:
|
||||
* Retrieve the size of the request body.
|
||||
*/
|
||||
virtual size_t bodyLength() const;
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the body data bytes. Will be passed the maximum body bytes
|
||||
* to return in the buffer, and must return the actual number
|
||||
* of bytes written.
|
||||
* of bytes written.
|
||||
*/
|
||||
virtual size_t getBodyData(char* s, size_t offset, size_t max_count) const;
|
||||
|
||||
|
||||
/**
|
||||
* running total of body bytes received so far. Can be used
|
||||
* to generate a completion percentage, if the response length is
|
||||
* known.
|
||||
* known.
|
||||
*/
|
||||
unsigned int responseBytesReceived() const
|
||||
{ return _receivedBodyBytes; }
|
||||
|
||||
|
||||
enum HTTPVersion {
|
||||
HTTP_VERSION_UNKNOWN = 0,
|
||||
HTTP_0_x, // 0.9 or similar
|
||||
HTTP_1_0,
|
||||
HTTP_1_1
|
||||
};
|
||||
|
||||
|
||||
HTTPVersion responseVersion() const
|
||||
{ return _responseVersion; }
|
||||
|
||||
|
||||
ReadyState readyState() const { return _ready_state; }
|
||||
|
||||
/**
|
||||
@@ -226,7 +227,7 @@ private:
|
||||
friend class Client;
|
||||
friend class Connection;
|
||||
friend class ContentDecoder;
|
||||
|
||||
|
||||
Request(const Request&); // = delete;
|
||||
Request& operator=(const Request&); // = delete;
|
||||
|
||||
|
||||
@@ -96,14 +96,14 @@ void SVNDirectory::parseCache()
|
||||
LineState lineState = LINESTATE_HREF;
|
||||
std::ifstream file(p.c_str());
|
||||
if (!file.is_open()) {
|
||||
SG_LOG(SG_IO, SG_WARN, "unable to open cache file for reading:" << p);
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "unable to open cache file for reading:" << p);
|
||||
return;
|
||||
}
|
||||
bool doneSelf = false;
|
||||
|
||||
file.getline(href, 1024);
|
||||
if (strcmp(CACHE_VERSION_4_TOKEN, href)) {
|
||||
SG_LOG(SG_IO, SG_WARN, "invalid cache file [missing header token]:" << p << " '" << href << "'");
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "invalid cache file [missing header token]:" << p << " '" << href << "'");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -183,7 +183,7 @@ void SVNDirectory::writeCache()
|
||||
void SVNDirectory::setBaseUrl(const string& url)
|
||||
{
|
||||
if (_parent) {
|
||||
SG_LOG(SG_IO, SG_ALERT, "setting base URL on non-root directory " << url);
|
||||
SG_LOG(SG_TERRASYNC, SG_ALERT, "setting base URL on non-root directory " << url);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -239,7 +239,6 @@ void SVNDirectory::deleteChildByName(const std::string& nm)
|
||||
{
|
||||
DAVResource* child = dav->childWithName(nm);
|
||||
if (!child) {
|
||||
// std::cerr << "ZZZ: deleteChildByName: unknown:" << nm << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -247,17 +246,24 @@ void SVNDirectory::deleteChildByName(const std::string& nm)
|
||||
|
||||
if (child->isCollection()) {
|
||||
Dir d(path);
|
||||
d.remove(true);
|
||||
|
||||
bool ok = d.remove(true);
|
||||
if (!ok) {
|
||||
SG_LOG(SG_TERRASYNC, SG_ALERT, "SVNDirectory::deleteChildByName: failed to remove dir:"
|
||||
<< nm << " at path:\n\t" << path);
|
||||
}
|
||||
|
||||
DirectoryList::iterator it = findChildDir(nm);
|
||||
if (it != _children.end()) {
|
||||
SVNDirectory* c = *it;
|
||||
// std::cout << "YYY: deleting SVNDirectory for:" << nm << std::endl;
|
||||
delete c;
|
||||
_children.erase(it);
|
||||
}
|
||||
} else {
|
||||
path.remove();
|
||||
bool ok = path.remove();
|
||||
if (!ok) {
|
||||
SG_LOG(SG_TERRASYNC, SG_ALERT, "SVNDirectory::deleteChildByName: failed to remove path:" << nm
|
||||
<< " at path:\n\t" << path);
|
||||
}
|
||||
}
|
||||
|
||||
dav->removeChild(child);
|
||||
|
||||
@@ -73,7 +73,7 @@ public:
|
||||
|
||||
void updateFailed(HTTP::Request* req, SVNRepository::ResultCode err)
|
||||
{
|
||||
SG_LOG(SG_IO, SG_WARN, "SVN: failed to update from:" << req->url()
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "SVN: failed to update from:" << req->url()
|
||||
<< "\n(repository:" << p->baseUrl() << ")");
|
||||
isUpdating = false;
|
||||
status = err;
|
||||
@@ -120,6 +120,7 @@ namespace { // anonmouse
|
||||
Request(repo->baseUrl, "PROPFIND"),
|
||||
_repo(repo)
|
||||
{
|
||||
assert(repo);
|
||||
requestHeader("Depth") = "0";
|
||||
setBodyData( PROPFIND_REQUEST_BODY,
|
||||
"application/xml; charset=\"utf-8\"" );
|
||||
@@ -133,11 +134,13 @@ namespace { // anonmouse
|
||||
} else if (responseCode() == 404) {
|
||||
_repo->propFindFailed(this, SVNRepository::SVN_ERROR_NOT_FOUND);
|
||||
} else {
|
||||
SG_LOG(SG_IO, SG_WARN, "request for:" << url() <<
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "request for:" << url() <<
|
||||
" return code " << responseCode());
|
||||
_repo->propFindFailed(this, SVNRepository::SVN_ERROR_SOCKET);
|
||||
_repo = NULL;
|
||||
}
|
||||
|
||||
Request::responseHeadersComplete();
|
||||
}
|
||||
|
||||
virtual void onDone()
|
||||
@@ -228,7 +231,7 @@ protected:
|
||||
_repo->updateFailed(this, SVNRepository::SVN_ERROR_NOT_FOUND);
|
||||
_failed = true;
|
||||
} else {
|
||||
SG_LOG(SG_IO, SG_WARN, "SVN: request for:" << url() <<
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "SVN: request for:" << url() <<
|
||||
" got HTTP status " << responseCode());
|
||||
_repo->updateFailed(this, SVNRepository::SVN_ERROR_HTTP);
|
||||
_failed = true;
|
||||
@@ -328,7 +331,7 @@ void SVNRepository::update()
|
||||
}
|
||||
|
||||
if (_d->targetRevision == rootDir()->cachedRevision()) {
|
||||
SG_LOG(SG_IO, SG_DEBUG, baseUrl() << " in sync at version " << _d->targetRevision);
|
||||
SG_LOG(SG_TERRASYNC, SG_DEBUG, baseUrl() << " in sync at version " << _d->targetRevision);
|
||||
_d->isUpdating = false;
|
||||
return;
|
||||
}
|
||||
@@ -372,7 +375,7 @@ void SVNRepoPrivate::propFindComplete(HTTP::Request* req, DAVCollection* c)
|
||||
void SVNRepoPrivate::propFindFailed(HTTP::Request *req, SVNRepository::ResultCode err)
|
||||
{
|
||||
if (err != SVNRepository::SVN_ERROR_NOT_FOUND) {
|
||||
SG_LOG(SG_IO, SG_WARN, "PropFind failed for:" << req->url());
|
||||
SG_LOG(SG_TERRASYNC, SG_WARN, "PropFind failed for:" << req->url());
|
||||
}
|
||||
|
||||
isUpdating = false;
|
||||
|
||||
@@ -227,6 +227,13 @@ void NetChannel::handleAccept (void) {
|
||||
|
||||
void NetChannel::handleError (int error)
|
||||
{
|
||||
if (error == EINPROGRESS) {
|
||||
// this shoudl never happen, because we should use isNonBlocking to check
|
||||
// such error codes.
|
||||
SG_LOG(SG_IO, SG_WARN, "Got EINPROGRESS at NetChannel::handleError: suggests broken logic somewhere else");
|
||||
return; // not an actual error, don't warn
|
||||
}
|
||||
|
||||
// warn about address lookup failures seperately, don't warn again.
|
||||
// (and we (ab-)use ENOENT to mean 'name not found'.
|
||||
if (error != ENOENT) {
|
||||
@@ -251,6 +258,11 @@ NetChannelPoller::removeChannel(NetChannel* channel)
|
||||
assert(channel->poller == this);
|
||||
channel->poller = NULL;
|
||||
|
||||
// portability: MSVC throws assertion failure when empty
|
||||
if (channels.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ChannelList::iterator it = channels.begin();
|
||||
for (; it != channels.end(); ++it) {
|
||||
if (*it == channel) {
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <algorithm>
|
||||
|
||||
namespace simgear {
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdlib> // for atoi
|
||||
#include <algorithm>
|
||||
|
||||
using std::string;
|
||||
|
||||
|
||||
@@ -3,15 +3,23 @@
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <errno.h>
|
||||
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
|
||||
#include <simgear/simgear_config.h>
|
||||
|
||||
#include "HTTPClient.hxx"
|
||||
#include "HTTPRequest.hxx"
|
||||
|
||||
#include <simgear/io/sg_netChat.hxx>
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
#include <simgear/timing/timestamp.hxx>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
|
||||
#if defined(ENABLE_CURL)
|
||||
#include <curl/multi.h>
|
||||
#endif
|
||||
|
||||
using std::cout;
|
||||
using std::cerr;
|
||||
@@ -46,7 +54,7 @@ char body2[body2Size];
|
||||
cerr << "failed:" << #a << endl; \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
|
||||
class TestRequest : public HTTP::Request
|
||||
{
|
||||
public:
|
||||
@@ -54,41 +62,43 @@ public:
|
||||
bool failed;
|
||||
string bodyData;
|
||||
|
||||
TestRequest(const std::string& url, const std::string method = "GET") :
|
||||
TestRequest(const std::string& url, const std::string method = "GET") :
|
||||
HTTP::Request(url, method),
|
||||
complete(false)
|
||||
complete(false),
|
||||
failed(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
std::map<string, string> headers;
|
||||
protected:
|
||||
|
||||
|
||||
virtual void onDone()
|
||||
{
|
||||
complete = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
virtual void onFail()
|
||||
{
|
||||
failed = true;
|
||||
}
|
||||
|
||||
|
||||
virtual void gotBodyData(const char* s, int n)
|
||||
{
|
||||
//std::cout << "got body data:'" << string(s, n) << "'" <<std::endl;
|
||||
bodyData += string(s, n);
|
||||
}
|
||||
|
||||
|
||||
virtual void responseHeader(const string& header, const string& value)
|
||||
{
|
||||
Request::responseHeader(header, value);
|
||||
headers[header] = value;
|
||||
}
|
||||
};
|
||||
|
||||
class TestServerChannel : public NetChat
|
||||
{
|
||||
public:
|
||||
public:
|
||||
enum State
|
||||
{
|
||||
STATE_IDLE = 0,
|
||||
@@ -96,19 +106,19 @@ public:
|
||||
STATE_CLOSING,
|
||||
STATE_REQUEST_BODY
|
||||
};
|
||||
|
||||
|
||||
TestServerChannel()
|
||||
{
|
||||
state = STATE_IDLE;
|
||||
setTerminator("\r\n");
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
virtual void collectIncomingData(const char* s, int n)
|
||||
{
|
||||
buffer += string(s, n);
|
||||
}
|
||||
|
||||
|
||||
virtual void foundTerminator(void)
|
||||
{
|
||||
if (state == STATE_IDLE) {
|
||||
@@ -118,16 +128,16 @@ public:
|
||||
cerr << "malformed request:" << buffer << endl;
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
|
||||
method = line[0];
|
||||
path = line[1];
|
||||
|
||||
|
||||
string::size_type queryPos = path.find('?');
|
||||
if (queryPos != string::npos) {
|
||||
parseArgs(path.substr(queryPos + 1));
|
||||
path = path.substr(0, queryPos);
|
||||
}
|
||||
|
||||
|
||||
httpVersion = line[2];
|
||||
requestHeaders.clear();
|
||||
buffer.clear();
|
||||
@@ -138,10 +148,10 @@ public:
|
||||
receivedRequestHeaders();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
string::size_type colonPos = buffer.find(':');
|
||||
if (colonPos == string::npos) {
|
||||
cerr << "malformed HTTP response header:" << buffer << endl;
|
||||
cerr << "test malformed HTTP response header:" << buffer << endl;
|
||||
buffer.clear();
|
||||
return;
|
||||
}
|
||||
@@ -156,8 +166,8 @@ public:
|
||||
} else if (state == STATE_CLOSING) {
|
||||
// ignore!
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void parseArgs(const string& argData)
|
||||
{
|
||||
string_list argv = strutils::split(argData, "&");
|
||||
@@ -173,11 +183,10 @@ public:
|
||||
args[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void receivedRequestHeaders()
|
||||
{
|
||||
state = STATE_IDLE;
|
||||
|
||||
if (path == "/test1") {
|
||||
string contentStr(BODY1);
|
||||
stringstream d;
|
||||
@@ -205,7 +214,7 @@ public:
|
||||
} else if (path == "/test_headers") {
|
||||
COMPARE(requestHeaders["X-Foo"], string("Bar"));
|
||||
COMPARE(requestHeaders["X-AnotherHeader"], string("A longer value"));
|
||||
|
||||
|
||||
string contentStr(BODY1);
|
||||
stringstream d;
|
||||
d << "HTTP/1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
|
||||
@@ -235,11 +244,11 @@ public:
|
||||
if (requestHeaders["Host"] != "www.google.com") {
|
||||
sendErrorResponse(400, true, "bad destination");
|
||||
}
|
||||
|
||||
|
||||
if (requestHeaders["Proxy-Authorization"] != string()) {
|
||||
sendErrorResponse(401, false, "bad auth"); // shouldn't supply auth
|
||||
sendErrorResponse(401, false, "bad auth, not empty"); // shouldn't supply auth
|
||||
}
|
||||
|
||||
|
||||
sendBody2();
|
||||
} else if (path == "http://www.google.com/test3") {
|
||||
// proxy test
|
||||
@@ -247,8 +256,28 @@ public:
|
||||
sendErrorResponse(400, true, "bad destination");
|
||||
}
|
||||
|
||||
if (requestHeaders["Proxy-Authorization"] != "ABCDEF") {
|
||||
sendErrorResponse(401, false, "bad auth"); // forbidden
|
||||
string credentials = requestHeaders["Proxy-Authorization"];
|
||||
if (credentials.substr(0, 5) != "Basic") {
|
||||
// request basic auth
|
||||
stringstream d;
|
||||
d << "HTTP/1.1 " << 407 << " " << reasonForCode(407) << "\r\n";
|
||||
d << "WWW-Authenticate: Basic real=\"simgear\"\r\n";
|
||||
d << "\r\n"; // final CRLF to terminate the headers
|
||||
push(d.str().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<unsigned char> userAndPass;
|
||||
strutils::decodeBase64(credentials.substr(6), userAndPass);
|
||||
std::string decodedUserPass((char*) userAndPass.data(), userAndPass.size());
|
||||
|
||||
if (decodedUserPass != "johndoe:swordfish") {
|
||||
std::map<string, string>::const_iterator it;
|
||||
for (it = requestHeaders.begin(); it != requestHeaders.end(); ++it) {
|
||||
cerr << "header:" << it->first << " = " << it->second << endl;
|
||||
}
|
||||
|
||||
sendErrorResponse(401, false, "bad auth, not as set"); // forbidden
|
||||
}
|
||||
|
||||
sendBody2();
|
||||
@@ -272,6 +301,12 @@ public:
|
||||
d << contentStr;
|
||||
push(d.str().c_str());
|
||||
closeAfterSending();
|
||||
} else if (path == "/test_abrupt_close") {
|
||||
// simulate server doing socket close before sending any
|
||||
// response - this used to cause a TerraSync failure since we
|
||||
// would get stuck restarting the request
|
||||
closeAfterSending();
|
||||
|
||||
} else if (path == "/test_args") {
|
||||
if ((args["foo"] != "abc") || (args["bar"] != "1234") || (args["username"] != "johndoe")) {
|
||||
sendErrorResponse(400, true, "bad arguments");
|
||||
@@ -291,43 +326,78 @@ public:
|
||||
sendErrorResponse(400, true, "bad content type");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
requestContentLength = strutils::to_int(requestHeaders["Content-Length"]);
|
||||
setByteCount(requestContentLength);
|
||||
state = STATE_REQUEST_BODY;
|
||||
} else if ((path == "/test_put") || (path == "/test_create")) {
|
||||
if (requestHeaders["Content-Type"] != "x-application/foobar") {
|
||||
cerr << "bad content type: '" << requestHeaders["Content-Type"] << "'" << endl;
|
||||
sendErrorResponse(400, true, "bad content type");
|
||||
return;
|
||||
}
|
||||
|
||||
requestContentLength = strutils::to_int(requestHeaders["Content-Length"]);
|
||||
setByteCount(requestContentLength);
|
||||
state = STATE_REQUEST_BODY;
|
||||
} else {
|
||||
sendErrorResponse(404, false, "");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void closeAfterSending()
|
||||
{
|
||||
state = STATE_CLOSING;
|
||||
closeWhenDone();
|
||||
}
|
||||
|
||||
|
||||
void receivedBody()
|
||||
{
|
||||
state = STATE_IDLE;
|
||||
if (method == "POST") {
|
||||
parseArgs(buffer);
|
||||
}
|
||||
|
||||
|
||||
if (path == "/test_post") {
|
||||
if ((args["foo"] != "abc") || (args["bar"] != "1234") || (args["username"] != "johndoe")) {
|
||||
sendErrorResponse(400, true, "bad arguments");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
stringstream d;
|
||||
d << "HTTP/1.1 " << 204 << " " << reasonForCode(204) << "\r\n";
|
||||
d << "\r\n"; // final CRLF to terminate the headers
|
||||
push(d.str().c_str());
|
||||
|
||||
cerr << "sent 204 response ok" << endl;
|
||||
} else if (path == "/test_put") {
|
||||
std::cerr << "sending PUT response" << std::endl;
|
||||
|
||||
COMPARE(buffer, BODY3);
|
||||
stringstream d;
|
||||
d << "HTTP/1.1 " << 204 << " " << reasonForCode(204) << "\r\n";
|
||||
d << "\r\n"; // final CRLF to terminate the headers
|
||||
push(d.str().c_str());
|
||||
} else if (path == "/test_create") {
|
||||
std::cerr << "sending create response" << std::endl;
|
||||
|
||||
std::string entityStr = "http://localhost:2000/something.txt";
|
||||
|
||||
COMPARE(buffer, BODY3);
|
||||
stringstream d;
|
||||
d << "HTTP/1.1 " << 201 << " " << reasonForCode(201) << "\r\n";
|
||||
d << "Location:" << entityStr << "\r\n";
|
||||
d << "Content-Length:" << entityStr.size() << "\r\n";
|
||||
d << "\r\n"; // final CRLF to terminate the headers
|
||||
d << entityStr;
|
||||
|
||||
push(d.str().c_str());
|
||||
} else {
|
||||
std::cerr << "weird URL " << path << std::endl;
|
||||
sendErrorResponse(400, true, "bad URL:" + path);
|
||||
}
|
||||
|
||||
buffer.clear();
|
||||
}
|
||||
|
||||
|
||||
void sendBody2()
|
||||
{
|
||||
stringstream d;
|
||||
@@ -337,32 +407,36 @@ public:
|
||||
push(d.str().c_str());
|
||||
bufferSend(body2, body2Size);
|
||||
}
|
||||
|
||||
|
||||
void sendErrorResponse(int code, bool close, string content)
|
||||
{
|
||||
cerr << "sending error " << code << " for " << path << endl;
|
||||
cerr << "\tcontent:" << content << endl;
|
||||
|
||||
stringstream headerData;
|
||||
headerData << "HTTP/1.1 " << code << " " << reasonForCode(code) << "\r\n";
|
||||
headerData << "Content-Length:" << content.size() << "\r\n";
|
||||
headerData << "\r\n"; // final CRLF to terminate the headers
|
||||
push(headerData.str().c_str());
|
||||
push(content.c_str());
|
||||
|
||||
|
||||
if (close) {
|
||||
closeWhenDone();
|
||||
}
|
||||
}
|
||||
|
||||
string reasonForCode(int code)
|
||||
|
||||
string reasonForCode(int code)
|
||||
{
|
||||
switch (code) {
|
||||
case 200: return "OK";
|
||||
case 201: return "Created";
|
||||
case 204: return "no content";
|
||||
case 404: return "not found";
|
||||
case 407: return "proxy authentication required";
|
||||
default: return "unknown code";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
State state;
|
||||
string buffer;
|
||||
string method;
|
||||
@@ -376,20 +450,22 @@ public:
|
||||
class TestServer : public NetChannel
|
||||
{
|
||||
simgear::NetChannelPoller _poller;
|
||||
public:
|
||||
public:
|
||||
TestServer()
|
||||
{
|
||||
Socket::initSockets();
|
||||
|
||||
open();
|
||||
bind(NULL, 2000); // localhost, any port
|
||||
listen(5);
|
||||
|
||||
|
||||
_poller.addChannel(this);
|
||||
}
|
||||
|
||||
|
||||
virtual ~TestServer()
|
||||
{
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
virtual bool writable (void) { return false ; }
|
||||
|
||||
virtual void handleAccept (void)
|
||||
@@ -399,10 +475,10 @@ public:
|
||||
//cout << "did accept from " << addr.getHost() << ":" << addr.getPort() << endl;
|
||||
TestServerChannel* chan = new TestServerChannel();
|
||||
chan->setHandle(handle);
|
||||
|
||||
|
||||
_poller.addChannel(chan);
|
||||
}
|
||||
|
||||
|
||||
void poll()
|
||||
{
|
||||
_poller.poll();
|
||||
@@ -414,42 +490,43 @@ TestServer testServer;
|
||||
void waitForComplete(HTTP::Client* cl, TestRequest* tr)
|
||||
{
|
||||
SGTimeStamp start(SGTimeStamp::now());
|
||||
while (start.elapsedMSec() < 1000) {
|
||||
while (start.elapsedMSec() < 10000) {
|
||||
cl->update();
|
||||
testServer.poll();
|
||||
|
||||
|
||||
if (tr->complete) {
|
||||
return;
|
||||
}
|
||||
SGTimeStamp::sleepForMSec(1);
|
||||
SGTimeStamp::sleepForMSec(15);
|
||||
}
|
||||
|
||||
|
||||
cerr << "timed out" << endl;
|
||||
}
|
||||
|
||||
void waitForFailed(HTTP::Client* cl, TestRequest* tr)
|
||||
{
|
||||
SGTimeStamp start(SGTimeStamp::now());
|
||||
while (start.elapsedMSec() < 1000) {
|
||||
while (start.elapsedMSec() < 10000) {
|
||||
cl->update();
|
||||
testServer.poll();
|
||||
|
||||
|
||||
if (tr->failed) {
|
||||
return;
|
||||
}
|
||||
SGTimeStamp::sleepForMSec(1);
|
||||
SGTimeStamp::sleepForMSec(15);
|
||||
}
|
||||
|
||||
|
||||
cerr << "timed out waiting for failure" << endl;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
|
||||
sglog().setLogLevels( SG_ALL, SG_INFO );
|
||||
|
||||
HTTP::Client cl;
|
||||
// force all requests to use the same connection for this test
|
||||
cl.setMaxConnections(1);
|
||||
|
||||
cl.setMaxConnections(1);
|
||||
|
||||
// test URL parsing
|
||||
TestRequest* tr1 = new TestRequest("http://localhost.woo.zar:2000/test1?foo=bar");
|
||||
COMPARE(tr1->scheme(), "http");
|
||||
@@ -457,14 +534,14 @@ int main(int argc, char* argv[])
|
||||
COMPARE(tr1->host(), "localhost.woo.zar");
|
||||
COMPARE(tr1->port(), 2000);
|
||||
COMPARE(tr1->path(), "/test1");
|
||||
|
||||
|
||||
TestRequest* tr2 = new TestRequest("http://192.168.1.1/test1/dir/thing/file.png");
|
||||
COMPARE(tr2->scheme(), "http");
|
||||
COMPARE(tr2->hostAndPort(), "192.168.1.1");
|
||||
COMPARE(tr2->host(), "192.168.1.1");
|
||||
COMPARE(tr2->port(), 80);
|
||||
COMPARE(tr2->path(), "/test1/dir/thing/file.png");
|
||||
|
||||
|
||||
// basic get request
|
||||
{
|
||||
TestRequest* tr = new TestRequest("http://localhost:2000/test1");
|
||||
@@ -478,12 +555,12 @@ int main(int argc, char* argv[])
|
||||
COMPARE(tr->responseBytesReceived(), strlen(BODY1));
|
||||
COMPARE(tr->bodyData, string(BODY1));
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
TestRequest* tr = new TestRequest("http://localhost:2000/testLorem");
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
|
||||
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->responseReason(), string("OK"));
|
||||
@@ -491,7 +568,7 @@ int main(int argc, char* argv[])
|
||||
COMPARE(tr->responseBytesReceived(), strlen(BODY3));
|
||||
COMPARE(tr->bodyData, string(BODY3));
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
TestRequest* tr = new TestRequest("http://localhost:2000/test_args?foo=abc&bar=1234&username=johndoe");
|
||||
HTTP::Request_ptr own(tr);
|
||||
@@ -499,9 +576,7 @@ int main(int argc, char* argv[])
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
}
|
||||
|
||||
cerr << "done args" << endl;
|
||||
|
||||
|
||||
{
|
||||
TestRequest* tr = new TestRequest("http://localhost:2000/test_headers");
|
||||
HTTP::Request_ptr own(tr);
|
||||
@@ -516,12 +591,12 @@ int main(int argc, char* argv[])
|
||||
COMPARE(tr->responseBytesReceived(), strlen(BODY1));
|
||||
COMPARE(tr->bodyData, string(BODY1));
|
||||
}
|
||||
|
||||
|
||||
// larger get request
|
||||
for (unsigned int i=0; i<body2Size; ++i) {
|
||||
body2[i] = (i << 4) | (i >> 2);
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
TestRequest* tr = new TestRequest("http://localhost:2000/test2");
|
||||
HTTP::Request_ptr own(tr);
|
||||
@@ -531,8 +606,8 @@ int main(int argc, char* argv[])
|
||||
COMPARE(tr->responseBytesReceived(), body2Size);
|
||||
COMPARE(tr->bodyData, string(body2, body2Size));
|
||||
}
|
||||
|
||||
cerr << "testing chunked" << endl;
|
||||
|
||||
cerr << "testing chunked transfer encoding" << endl;
|
||||
{
|
||||
TestRequest* tr = new TestRequest("http://localhost:2000/testchunked");
|
||||
HTTP::Request_ptr own(tr);
|
||||
@@ -546,7 +621,7 @@ int main(int argc, char* argv[])
|
||||
// check trailers made it too
|
||||
COMPARE(tr->headers["x-foobar"], string("wibble"));
|
||||
}
|
||||
|
||||
|
||||
// test 404
|
||||
{
|
||||
TestRequest* tr = new TestRequest("http://localhost:2000/not-found");
|
||||
@@ -593,15 +668,40 @@ int main(int argc, char* argv[])
|
||||
}
|
||||
cout << "done3" << endl;
|
||||
// test connectToHost failure
|
||||
/*
|
||||
|
||||
{
|
||||
TestRequest* tr = new TestRequest("http://not.found/something");
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForFailed(tr);
|
||||
COMPARE(tr->responseCode(), -1);
|
||||
waitForFailed(&cl, tr);
|
||||
|
||||
|
||||
|
||||
#if defined(ENABLE_CURL)
|
||||
const int HOST_NOT_FOUND_CODE = CURLE_COULDNT_RESOLVE_HOST;
|
||||
#else
|
||||
const int HOST_NOT_FOUND_CODE = ENOENT;
|
||||
#endif
|
||||
COMPARE(tr->responseCode(), HOST_NOT_FOUND_CODE);
|
||||
}
|
||||
*/
|
||||
|
||||
cout << "testing abrupt close" << endl;
|
||||
// test server-side abrupt close
|
||||
{
|
||||
TestRequest* tr = new TestRequest("http://localhost:2000/test_abrupt_close");
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForFailed(&cl, tr);
|
||||
|
||||
#if defined(ENABLE_CURL)
|
||||
const int SERVER_NO_DATA_CODE = CURLE_GOT_NOTHING;
|
||||
#else
|
||||
const int SERVER_NO_DATA_CODE = 500;
|
||||
#endif
|
||||
COMPARE(tr->responseCode(), SERVER_NO_DATA_CODE);
|
||||
}
|
||||
|
||||
cout << "testing proxy close" << endl;
|
||||
// test proxy
|
||||
{
|
||||
cl.setProxy("localhost", 2000);
|
||||
@@ -613,9 +713,10 @@ int main(int argc, char* argv[])
|
||||
COMPARE(tr->responseLength(), body2Size);
|
||||
COMPARE(tr->bodyData, string(body2, body2Size));
|
||||
}
|
||||
|
||||
|
||||
#if defined(ENABLE_CURL)
|
||||
{
|
||||
cl.setProxy("localhost", 2000, "ABCDEF");
|
||||
cl.setProxy("localhost", 2000, "johndoe:swordfish");
|
||||
TestRequest* tr = new TestRequest("http://www.google.com/test3");
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
@@ -624,81 +725,105 @@ int main(int argc, char* argv[])
|
||||
COMPARE(tr->responseBytesReceived(), body2Size);
|
||||
COMPARE(tr->bodyData, string(body2, body2Size));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// pipelining
|
||||
cout << "testing HTTP 1.1 pipelineing" << endl;
|
||||
|
||||
cout << "testing HTTP 1.1 pipelining" << endl;
|
||||
|
||||
{
|
||||
|
||||
|
||||
cl.setProxy("", 80);
|
||||
TestRequest* tr = new TestRequest("http://localhost:2000/test1");
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
|
||||
|
||||
|
||||
|
||||
TestRequest* tr2 = new TestRequest("http://localhost:2000/testLorem");
|
||||
HTTP::Request_ptr own2(tr2);
|
||||
cl.makeRequest(tr2);
|
||||
|
||||
|
||||
TestRequest* tr3 = new TestRequest("http://localhost:2000/test1");
|
||||
HTTP::Request_ptr own3(tr3);
|
||||
cl.makeRequest(tr3);
|
||||
|
||||
|
||||
waitForComplete(&cl, tr3);
|
||||
VERIFY(tr->complete);
|
||||
VERIFY(tr2->complete);
|
||||
COMPARE(tr->bodyData, string(BODY1));
|
||||
|
||||
|
||||
COMPARE(tr2->responseLength(), strlen(BODY3));
|
||||
COMPARE(tr2->responseBytesReceived(), strlen(BODY3));
|
||||
COMPARE(tr2->bodyData, string(BODY3));
|
||||
|
||||
|
||||
COMPARE(tr3->bodyData, string(BODY1));
|
||||
}
|
||||
|
||||
|
||||
// multiple requests with an HTTP 1.0 server
|
||||
{
|
||||
cout << "http 1.0 multiple requests" << endl;
|
||||
|
||||
|
||||
cl.setProxy("", 80);
|
||||
TestRequest* tr = new TestRequest("http://localhost:2000/test_1_0/A");
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
|
||||
|
||||
TestRequest* tr2 = new TestRequest("http://localhost:2000/test_1_0/B");
|
||||
HTTP::Request_ptr own2(tr2);
|
||||
cl.makeRequest(tr2);
|
||||
|
||||
|
||||
TestRequest* tr3 = new TestRequest("http://localhost:2000/test_1_0/C");
|
||||
HTTP::Request_ptr own3(tr3);
|
||||
cl.makeRequest(tr3);
|
||||
|
||||
|
||||
waitForComplete(&cl, tr3);
|
||||
VERIFY(tr->complete);
|
||||
VERIFY(tr2->complete);
|
||||
|
||||
|
||||
COMPARE(tr->responseLength(), strlen(BODY1));
|
||||
COMPARE(tr->responseBytesReceived(), strlen(BODY1));
|
||||
COMPARE(tr->bodyData, string(BODY1));
|
||||
|
||||
|
||||
COMPARE(tr2->responseLength(), strlen(BODY3));
|
||||
COMPARE(tr2->responseBytesReceived(), strlen(BODY3));
|
||||
COMPARE(tr2->bodyData, string(BODY3));
|
||||
COMPARE(tr3->bodyData, string(BODY1));
|
||||
}
|
||||
|
||||
|
||||
// POST
|
||||
{
|
||||
cout << "POST" << endl;
|
||||
cout << "testing POST" << endl;
|
||||
TestRequest* tr = new TestRequest("http://localhost:2000/test_post?foo=abc&bar=1234&username=johndoe", "POST");
|
||||
tr->setBodyData("", "application/x-www-form-urlencoded");
|
||||
|
||||
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 204);
|
||||
}
|
||||
|
||||
|
||||
// PUT
|
||||
{
|
||||
cout << "testing PUT" << endl;
|
||||
TestRequest* tr = new TestRequest("http://localhost:2000/test_put", "PUT");
|
||||
tr->setBodyData(BODY3, "x-application/foobar");
|
||||
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 204);
|
||||
}
|
||||
|
||||
{
|
||||
cout << "testing PUT create" << endl;
|
||||
TestRequest* tr = new TestRequest("http://localhost:2000/test_create", "PUT");
|
||||
tr->setBodyData(BODY3, "x-application/foobar");
|
||||
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 201);
|
||||
}
|
||||
|
||||
// test_zero_length_content
|
||||
{
|
||||
cout << "zero-length-content-response" << endl;
|
||||
@@ -710,8 +835,8 @@ int main(int argc, char* argv[])
|
||||
COMPARE(tr->bodyData, string());
|
||||
COMPARE(tr->responseBytesReceived(), 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
cout << "all tests passed ok" << endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
// Put in some bullet-proofing to handle magnetic and geographic poles.
|
||||
// 3/28/2000 EAW
|
||||
//
|
||||
// Updated coefficient arrays to use the current WMM2005 model,
|
||||
// Updated coefficient arrays to use the current wmm2005 model,
|
||||
// (valid between 2005.0 and 2010.0)
|
||||
// Also removed unused variables and corrected earth radii constants
|
||||
// to the values for WGS84 and WMM2005.
|
||||
@@ -36,6 +36,20 @@
|
||||
//
|
||||
// 25/10/2006 Wim Van Hoydonck -- wim.van.hoydonck@gmail.com
|
||||
//
|
||||
//
|
||||
// Updated coefficient arrays to use the current WMM2015 model,
|
||||
// (valid between 2015.0 and 2020.0)
|
||||
// Also removed unused variables and corrected earth radii constants
|
||||
// to the values for WGS84 and WMM2015.
|
||||
// Reference:
|
||||
// A. Chulliat , S. Macmillan, P. Alken, C. Beggan, M.
|
||||
// Nair, B. Hamilton, A. Woods, V. Ridley,
|
||||
// S Maus, and A Thomson, December 2014, The
|
||||
// US/UK World Magnetic Model for 2015-2020,
|
||||
// NOAA Technical Report WMM2015_Report.pdf
|
||||
//
|
||||
// 18/06/2015 Jean-Paul Anceaux -- j.p.r.anceaux@gmail.com
|
||||
|
||||
|
||||
// The routine uses a spherical harmonic expansion of the magnetic
|
||||
// potential up to twelfth order, together with its time variation, as
|
||||
@@ -52,7 +66,7 @@
|
||||
// the magnetic poles (where it is ill-defined) where the error may reach
|
||||
// 4 degrees or more.
|
||||
//
|
||||
// Variation is undefined at both the geographic and
|
||||
// Variation is undefined at both the geographic and
|
||||
// magnetic poles, even though the field itself is well-behaved. To
|
||||
// avoid the routine blowing up, latitude entries corresponding to
|
||||
// the geographic poles are slightly offset. At the magnetic poles,
|
||||
@@ -79,7 +93,7 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <cmath>
|
||||
|
||||
#include <simgear/constants.h>
|
||||
#include <simgear/sg_inlines.h>
|
||||
@@ -90,72 +104,72 @@ static const double a = 6378.137; /* semi-major axis (equatorial radius) o
|
||||
static const double b = 6356.7523142; /* semi-minor axis referenced to the WGS84 ellipsoid */
|
||||
static const double r_0 = 6371.2; /* standard Earth magnetic reference radius */
|
||||
|
||||
static double gnm_wmm2005[13][13] =
|
||||
static double gnm_wmm2015[13][13] =
|
||||
{
|
||||
{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{-29556.8, -1671.7, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{-2340.6, 3046.9, 1657.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{1335.4, -2305.1, 1246.7, 674.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{919.8, 798.1, 211.3, -379.4, 100.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{-227.4, 354.6, 208.7, -136.5, -168.3, -14.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{73.2, 69.7, 76.7, -151.2, -14.9, 14.6, -86.3, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{80.1, -74.5, -1.4, 38.5, 12.4, 9.5, 5.7, 1.8, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{24.9, 7.7, -11.6, -6.9, -18.2, 10.0, 9.2, -11.6, -5.2, 0.0, 0.0, 0.0, 0.0},
|
||||
{5.6, 9.9, 3.5, -7.0, 5.1, -10.8, -1.3, 8.8, -6.7, -9.1, 0.0, 0.0, 0.0},
|
||||
{-2.3, -6.3, 1.6, -2.6, 0.0, 3.1, 0.4, 2.1, 3.9, -0.1, -2.3, 0.0, 0.0},
|
||||
{2.8, -1.6, -1.7, 1.7, -0.1, 0.1, -0.7, 0.7, 1.8, 0.0, 1.1, 4.1, 0.0},
|
||||
{-2.4, -0.4, 0.2, 0.8, -0.3, 1.1, -0.5, 0.4, -0.3, -0.3, -0.1, -0.3, -0.1},
|
||||
{-29438.5, -1501.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{-2445.3, 3012.5, 1676.6, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{1351.1, -2352.3, 1225.6, 581.9, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{907.2, 813.7, 120.3, -335.0, 70.3, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{-232.6, 360.1, 192.4, -141.0, -157.4, 4.3, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{69.5, 67.4, 72.8, -129.8, -29.0, 13.2, -70.9, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{81.6, -76.1, -6.8, 51.9, 15.0, 9.3, -2.8, 6.7, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{24.0, 8.6, -16.9, -3.2, -20.6, 13.3, 11.7, -16.0, -2.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{5.4, 8.8, 3.1, -3.1, 0.6, -13.3, -0.1, 8.7, -9.1, -10.5, 0.0, 0.0, 0.0},
|
||||
{-1.9, -6.5, 0.2, 0.6, -0.6, 1.7, -0.7, 2.1, 2.3, -1.8, -3.6, 0.0, 0.0},
|
||||
{3.1, -1.5, -2.3, 2.1, -0.9, 0.6, -0.7, 0.2, 1.7, -0.2, 0.4, 3.5, 0.0},
|
||||
{-2.0, -0.3, 0.4, 1.3, -0.9, 0.9, 0.1, 0.5, -0.4, -0.4, 0.2, -0.9, 0.0},
|
||||
};
|
||||
|
||||
static double hnm_wmm2005[13][13]=
|
||||
static double hnm_wmm2015[13][13]=
|
||||
{
|
||||
{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, 5079.8, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, -2594.7, -516.7, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, -199.9, 269.3, -524.2, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, 281.5, -226.0, 145.8, -304.7, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, 42.4, 179.8, -123.0, -19.5, 103.6, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, -20.3, 54.7, 63.6, -63.4, -0.1, 50.4, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, -61.5, -22.4, 7.2, 25.4, 11.0, -26.4, -5.1, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, 11.2, -21.0, 9.6, -19.8, 16.1, 7.7, -12.9, -0.2, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, -20.1, 12.9, 12.6, -6.7, -8.1, 8.0, 2.9, -7.9, 6.0, 0.0, 0.0, 0.0},
|
||||
{0.0, 2.4, 0.2, 4.4, 4.8, -6.5, -1.1, -3.4, -0.8, -2.3, -7.9, 0.0, 0.0},
|
||||
{0.0, 0.3, 1.2, -0.8, -2.5, 0.9, -0.6, -2.7, -0.9, -1.3, -2.0, -1.2, 0.0},
|
||||
{0.0, -0.4, 0.3, 2.4, -2.6, 0.6, 0.3, 0.0, 0.0, 0.3, -0.9, -0.4, 0.8},
|
||||
{0.0, 4796.2, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, -2845.6, -642.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, -115.3, 245.0, -538.3, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, 283.4, -188.6, 180.9, -329.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, 47.4, 196.9, -119.4, 16.1, 100.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, -20.7, 33.2, 58.8, -66.5, 7.3, 62.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, -54.1, -19.4, 5.6, 24.4, 3.3, -27.5, -2.3, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, 10.2, -18.1, 13.2, -14.6, 16.2, 5.7, -9.1, 2.2, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, -21.6, 10.8, 11.7, -6.8, -6.9, 7.8, 1.0, -3.9, 8.5, 0.0, 0.0, 0.0},
|
||||
{0.0, 3.3, -0.3, 4.6, 4.4, -7.9, -0.6, -4.1, -2.8, -1.1, -8.7, 0.0, 0.0},
|
||||
{0.0, -0.1, 2.1, -0.7, -1.1, 0.7, -0.2, -2.1, -1.5, -2.5, -2.0, -2.3, 0.0},
|
||||
{0.0, -1.0, 0.5, 1.8, -2.2, 0.3, 0.7, -0.1, 0.3, 0.2, -0.9, -0.2, 0.7},
|
||||
};
|
||||
|
||||
static double gtnm_wmm2005[13][13]=
|
||||
static double gtnm_wmm2015[13][13]=
|
||||
{
|
||||
{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{8.0, 10.6, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{-15.1, -7.8, -0.8, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.4, -2.6, -1.2, -6.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{-2.5, 2.8, -7.0, 6.2, -3.8, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{-2.8, 0.7, -3.2, -1.1, 0.1, -0.8, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{-0.7, 0.4, -0.3, 2.3, -2.1, -0.6, 1.4, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.2, -0.1, -0.3, 1.1, 0.6, 0.5, -0.4, 0.6, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.1, 0.3, -0.4, 0.3, -0.3, 0.2, 0.4, -0.7, 0.4, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{10.7, 17.9, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{-8.6, -3.3, 2.4, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{3.1, -6.2, -0.4, -10.4, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{-0.4, 0.8, -9.2, 4.0, -4.2, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{-0.2, 0.1, -1.4, 0.0, 1.3, 3.8, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{-0.5, -0.2, -0.6, 2.4, -1.1, 0.3, 1.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.2, -0.2, -0.4, 1.3, 0.2, -0.4, -0.9, 0.3, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, 0.1, -0.5, 0.5, -0.2, 0.4, 0.2, -0.4, 0.3, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, -0.1, -0.1, 0.4, -0.5, -0.2, 0.1, 0.0, -0.2, -0.1, 0.0, 0.0, 0.0},
|
||||
{0.0, 0.0, -0.1, 0.3, -0.1, -0.1, -0.1, 0.0, -0.2, -0.1, -0.2, 0.0, 0.0},
|
||||
{0.0, 0.0, -0.1, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.1, -0.1, 0.0},
|
||||
{0.1, 0.0, 0.0, 0.1, -0.1, 0.0, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
};
|
||||
|
||||
static double htnm_wmm2005[13][13]=
|
||||
static double htnm_wmm2015[13][13]=
|
||||
{
|
||||
{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, -20.9, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, -23.2, -14.6, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, 5.0, -7.0, -0.6, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, 2.2, 1.6, 5.8, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, 0.0, 1.7, 2.1, 4.8, -1.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, -0.6, -1.9, -0.4, -0.5, -0.3, 0.7, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, 0.6, 0.4, 0.2, 0.3, -0.8, -0.2, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, -0.2, 0.1, 0.3, 0.4, 0.1, -0.2, 0.4, 0.4, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, -26.8, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, -27.1, -13.3, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, 8.4, -0.4, 2.3, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, -0.6, 5.3, 3.0, -5.3, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, 0.4, 1.6, -1.1, 3.3, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, 0.0, -2.2, -0.7, 0.1, 1.0, 1.3, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, 0.7, 0.5, -0.2, -0.1, -0.7, 0.1, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, -0.3, 0.3, 0.3, 0.6, -0.1, -0.2, 0.3, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
{0.0, -0.2, -0.1, -0.2, 0.1, 0.1, 0.0, -0.2, 0.4, 0.3, 0.0, 0.0, 0.0},
|
||||
{0.0, 0.1, -0.1, 0.0, 0.0, -0.2, 0.1, -0.1, -0.2, 0.1, -0.1, 0.0, 0.0},
|
||||
{0.0, 0.0, 0.1, 0.0, 0.1, 0.0, 0.0, 0.1, 0.0, -0.1, 0.0, -0.1, 0.0},
|
||||
{0.0, 0.0, 0.0, -0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
|
||||
};
|
||||
|
||||
static const int nmax = 12;
|
||||
@@ -174,7 +188,7 @@ static double roots[13][13][2];
|
||||
unsigned long int yymmdd_to_julian_days( int yy, int mm, int dd )
|
||||
{
|
||||
unsigned long jd;
|
||||
|
||||
|
||||
yy = (yy < 50) ? (2000 + yy) : (1900 + yy);
|
||||
jd = dd - 32075L + 1461L * (yy + 4800L + (mm - 14) / 12 ) / 4;
|
||||
jd = jd + 367L * (mm - 2 - (mm - 14) / 12*12) / 12;
|
||||
@@ -182,7 +196,7 @@ unsigned long int yymmdd_to_julian_days( int yy, int mm, int dd )
|
||||
|
||||
/* printf("julian date = %d\n", jd ); */
|
||||
return jd;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
@@ -195,8 +209,8 @@ double calc_magvar( double lat, double lon, double h, long dat, double* field )
|
||||
{
|
||||
/* output field B_r,B_th,B_phi,B_x,B_y,B_z */
|
||||
int n,m;
|
||||
/* reference date for current model is 1 januari 2005 */
|
||||
long date0_wmm2005 = yymmdd_to_julian_days(5,1,1);
|
||||
/* reference date for current model is 1 januari 2015 */
|
||||
long date0_wmm2015 = yymmdd_to_julian_days(15,1,1);
|
||||
|
||||
double yearfrac,sr,r,theta,c,s,psi,fn,fn_0,B_r,B_theta,B_phi,X,Y,Z;
|
||||
double sinpsi, cospsi, inv_s;
|
||||
@@ -215,7 +229,7 @@ double calc_magvar( double lat, double lon, double h, long dat, double* field )
|
||||
/* theta is geocentric co-latitude */
|
||||
|
||||
r = h*h + 2.0*h * sr +
|
||||
(a*a*a*a - ( a*a*a*a - b*b*b*b ) * sinlat*sinlat ) /
|
||||
(a*a*a*a - ( a*a*a*a - b*b*b*b ) * sinlat*sinlat ) /
|
||||
(a*a - (a*a - b*b) * sinlat*sinlat );
|
||||
|
||||
r = sqrt(r);
|
||||
@@ -224,7 +238,7 @@ double calc_magvar( double lat, double lon, double h, long dat, double* field )
|
||||
c = cos(theta);
|
||||
s = sin(theta);
|
||||
/* protect against zero divide at geographic poles */
|
||||
inv_s = 1.0 / (s + (s == 0.)*1.0e-8);
|
||||
inv_s = 1.0 / (s + (s == 0.)*1.0e-8);
|
||||
|
||||
/* zero out arrays */
|
||||
for ( n = 0; n <= nmax; n++ ) {
|
||||
@@ -283,12 +297,12 @@ double calc_magvar( double lat, double lon, double h, long dat, double* field )
|
||||
|
||||
/* compute Gauss coefficients gnm and hnm of degree n and order m for the desired time
|
||||
achieved by adjusting the coefficients at time t0 for linear secular variation */
|
||||
/* WMM2005 */
|
||||
yearfrac = (dat - date0_wmm2005) / 365.25;
|
||||
/* WMM2015 */
|
||||
yearfrac = (dat - date0_wmm2015) / 365.25;
|
||||
for ( n = 1; n <= nmax; n++ ) {
|
||||
for ( m = 0; m <= nmax; m++ ) {
|
||||
gnm[n][m] = gnm_wmm2005[n][m] + yearfrac * gtnm_wmm2005[n][m];
|
||||
hnm[n][m] = hnm_wmm2005[n][m] + yearfrac * htnm_wmm2005[n][m];
|
||||
gnm[n][m] = gnm_wmm2015[n][m] + yearfrac * gtnm_wmm2015[n][m];
|
||||
hnm[n][m] = hnm_wmm2015[n][m] + yearfrac * htnm_wmm2015[n][m];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,7 +324,7 @@ double calc_magvar( double lat, double lon, double h, long dat, double* field )
|
||||
double c2_n=0;
|
||||
double c3_n=0;
|
||||
for ( m = 0; m <= n; m++ ) {
|
||||
double tmp = (gnm[n][m] * cm[m] + hnm[n][m] * sm[m]);
|
||||
double tmp = (gnm[n][m] * cm[m] + hnm[n][m] * sm[m]);
|
||||
c1_n=c1_n + tmp * P[n][m];
|
||||
c2_n=c2_n + tmp * DP[n][m];
|
||||
c3_n=c3_n + m * (gnm[n][m] * sm[m] - hnm[n][m] * cm[m]) * P[n][m];
|
||||
@@ -340,7 +354,7 @@ double calc_magvar( double lat, double lon, double h, long dat, double* field )
|
||||
/* find variation in radians */
|
||||
/* return zero variation at magnetic pole X=Y=0. */
|
||||
/* E is positive */
|
||||
return (X != 0. || Y != 0.) ? atan2(Y, X) : (double) 0.;
|
||||
return (X != 0. || Y != 0.) ? atan2(Y, X) : (double) 0.;
|
||||
}
|
||||
|
||||
|
||||
@@ -350,7 +364,7 @@ double SGMagVarOrig( double lat, double lon, double h, long dat, double* field )
|
||||
/* output field B_r,B_th,B_phi,B_x,B_y,B_z */
|
||||
int n,m;
|
||||
/* reference dates */
|
||||
long date0_wmm2005 = yymmdd_to_julian_days(5,1,1);
|
||||
long date0_wmm2015 = yymmdd_to_julian_days(5,1,1);
|
||||
|
||||
double yearfrac,sr,r,theta,c,s,psi,fn,B_r,B_theta,B_phi,X,Y,Z;
|
||||
|
||||
@@ -362,7 +376,7 @@ double SGMagVarOrig( double lat, double lon, double h, long dat, double* field )
|
||||
/* theta is geocentric co-latitude */
|
||||
|
||||
r = h * h + 2.0*h * sr +
|
||||
(pow(a,4.0) - (pow(a,4.0) - pow(b,4.0)) * pow(sin(lat),2.0)) /
|
||||
(pow(a,4.0) - (pow(a,4.0) - pow(b,4.0)) * pow(sin(lat),2.0)) /
|
||||
(a * a - (a * a - b * b) * pow(sin(lat),2.0));
|
||||
|
||||
r = sqrt(r);
|
||||
@@ -407,12 +421,12 @@ double SGMagVarOrig( double lat, double lon, double h, long dat, double* field )
|
||||
}
|
||||
|
||||
/* compute gnm, hnm at dat */
|
||||
/* WMM2005 */
|
||||
yearfrac = (dat - date0_wmm2005) / 365.25;
|
||||
/* WMM2015 */
|
||||
yearfrac = (dat - date0_wmm2015) / 365.25;
|
||||
for ( n = 1; n <= nmax; n++ ) {
|
||||
for ( m = 0; m <= nmax; m++ ) {
|
||||
gnm[n][m] = gnm_wmm2005[n][m] + yearfrac * gtnm_wmm2005[n][m];
|
||||
hnm[n][m] = hnm_wmm2005[n][m] + yearfrac * htnm_wmm2005[n][m];
|
||||
gnm[n][m] = gnm_wmm2015[n][m] + yearfrac * gtnm_wmm2015[n][m];
|
||||
hnm[n][m] = hnm_wmm2015[n][m] + yearfrac * htnm_wmm2015[n][m];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#endif
|
||||
|
||||
|
||||
#include <math.h>
|
||||
#include <cmath>
|
||||
|
||||
#include <simgear/magvar/magvar.hxx>
|
||||
#include <simgear/math/SGMath.hxx>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/* 2/14/00 fixed help message- dip angle (down positive), variation (E positive) */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <cmath>
|
||||
|
||||
#include <simgear/constants.h>
|
||||
|
||||
|
||||
@@ -154,7 +154,7 @@ public:
|
||||
static bool isNaN(const T& v)
|
||||
{
|
||||
#ifdef HAVE_ISNAN
|
||||
return isnan(v);
|
||||
return (isnan(v) != 0);
|
||||
#elif defined HAVE_STD_ISNAN
|
||||
return std::isnan(v);
|
||||
#else
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace simgear
|
||||
}
|
||||
|
||||
void setMaxSubdiv( size_t aMaxSubdiv ) { mMaxSubdiv = aMaxSubdiv; }
|
||||
void getMaxSubdiv() const { return mMaxSubdiv; }
|
||||
size_t getMaxSubdiv() const { return mMaxSubdiv; }
|
||||
PointList &pointList() { return mPointList; }
|
||||
const PointList &pointList() const { return mPointList; }
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ SGInterpTable::SGInterpTable( const std::string& file )
|
||||
while ( in ) {
|
||||
double ind, dep;
|
||||
in >> ind >> dep;
|
||||
in >> skipws;
|
||||
in >> std::skipws;
|
||||
_table[ind] = dep;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
include (SimGearComponent)
|
||||
|
||||
set(HEADERS
|
||||
set(HEADERS
|
||||
CSSBorder.hxx
|
||||
ListDiff.hxx
|
||||
ResourceManager.hxx
|
||||
@@ -37,6 +37,10 @@ set(SOURCES
|
||||
gzcontainerfile.cxx
|
||||
)
|
||||
|
||||
if (WINDOWS)
|
||||
list(APPEND HEADERS sgversionhelpers.hxx)
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
list(APPEND SOURCES CocoaHelpers.mm)
|
||||
endif()
|
||||
|
||||
@@ -28,11 +28,12 @@ SGPath appleSpecialFolder(int dirType, int domainMask, const SGPath& def)
|
||||
{
|
||||
CocoaAutoreleasePool ap;
|
||||
NSFileManager* fm = [NSFileManager defaultManager];
|
||||
NSURL* pathUrl = [fm URLForDirectory:dirType
|
||||
inDomain:domainMask
|
||||
appropriateForURL:Nil
|
||||
create:YES
|
||||
error:nil];
|
||||
NSSearchPathDirectory d = static_cast<NSSearchPathDirectory>(dirType);
|
||||
NSURL* pathUrl = [fm URLForDirectory:d
|
||||
inDomain:domainMask
|
||||
appropriateForURL:Nil
|
||||
create:YES
|
||||
error:nil];
|
||||
if (!pathUrl) {
|
||||
return def;;
|
||||
}
|
||||
|
||||
@@ -141,8 +141,12 @@ int main(int argc, char* argv[])
|
||||
|
||||
// add
|
||||
pc.add("/opt/local");
|
||||
#ifdef _WIN32
|
||||
COMPARE(pc.str(), std::string("/usr/local/include/;/opt/local"));
|
||||
#else
|
||||
COMPARE(pc.str(), std::string("/usr/local/include/:/opt/local"));
|
||||
|
||||
#endif
|
||||
|
||||
// concat
|
||||
SGPath pd = pb;
|
||||
pd.concat("-1");
|
||||
|
||||
@@ -382,6 +382,7 @@ bool Dir::remove(bool recursive)
|
||||
|
||||
if (recursive) {
|
||||
if (!removeChildren()) {
|
||||
SG_LOG(SG_IO, SG_WARN, "Dir at:" << _path << " failed to remove children");
|
||||
return false;
|
||||
}
|
||||
} // of recursive deletion
|
||||
|
||||
@@ -56,7 +56,10 @@ static const char sgSearchPathSep = ':';
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <ShlObj.h> // for CSIDL
|
||||
#include <ShlObj.h> // for CSIDL
|
||||
// TODO: replace this include file with the official <versionhelpers.h> header
|
||||
// included in the Windows 8.1 SDK
|
||||
#include "sgversionhelpers.hxx"
|
||||
|
||||
static SGPath pathForCSIDL(int csidl, const SGPath& def)
|
||||
{
|
||||
@@ -80,6 +83,42 @@ static SGPath pathForCSIDL(int csidl, const SGPath& def)
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
static SGPath pathForKnownFolder(REFKNOWNFOLDERID folderId, const SGPath& def)
|
||||
{
|
||||
typedef HRESULT (WINAPI*PSHGKFP)(REFKNOWNFOLDERID, DWORD, HANDLE, PWSTR*);
|
||||
|
||||
HINSTANCE shellDll = LoadLibrary(TEXT("shell32"));
|
||||
if (shellDll != NULL) {
|
||||
PSHGKFP pSHGetKnownFolderPath = (PSHGKFP) GetProcAddress(shellDll, "SHGetKnownFolderPath");
|
||||
if (pSHGetKnownFolderPath != NULL) {
|
||||
// system call will allocate dynamic memory... which we must release when done
|
||||
wchar_t* localFolder = 0;
|
||||
|
||||
if (pSHGetKnownFolderPath(folderId, KF_FLAG_DEFAULT_PATH, NULL, &localFolder) == S_OK) {
|
||||
// copy into local memory
|
||||
char path[MAX_PATH];
|
||||
size_t len;
|
||||
if (wcstombs_s(&len, path, localFolder, MAX_PATH) != S_OK) {
|
||||
path[0] = '\0';
|
||||
SG_LOG(SG_GENERAL, SG_WARN, "WCS to MBS failed");
|
||||
}
|
||||
|
||||
SGPath folder_path = SGPath(path, def.getPermissionChecker());
|
||||
|
||||
// release dynamic memory
|
||||
CoTaskMemFree(static_cast<void*>(localFolder));
|
||||
|
||||
return folder_path;
|
||||
}
|
||||
}
|
||||
|
||||
FreeLibrary(shellDll);
|
||||
}
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
#elif __APPLE__
|
||||
|
||||
// defined in CocoaHelpers.mm
|
||||
@@ -704,20 +743,35 @@ SGPath SGPath::standardLocation(StandardLocation type, const SGPath& def)
|
||||
{
|
||||
case HOME:
|
||||
return home(def);
|
||||
|
||||
#ifdef _WIN32
|
||||
case DESKTOP:
|
||||
return pathForCSIDL(CSIDL_DESKTOPDIRECTORY, def);
|
||||
case DOWNLOADS:
|
||||
// TODO use KnownFolders
|
||||
// http://msdn.microsoft.com/en-us/library/bb776911%28v=vs.85%29.aspx
|
||||
if( !def.isNull() )
|
||||
return def;
|
||||
if (IsWindowsVistaOrGreater())
|
||||
return pathForKnownFolder(FOLDERID_Desktop, def);
|
||||
|
||||
return pathForCSIDL(CSIDL_DESKTOPDIRECTORY, def);
|
||||
|
||||
case DOWNLOADS:
|
||||
if (IsWindowsVistaOrGreater())
|
||||
return pathForKnownFolder(FOLDERID_Downloads, def);
|
||||
|
||||
if (!def.isNull())
|
||||
return def;
|
||||
|
||||
return pathForCSIDL(CSIDL_DESKTOPDIRECTORY, def);
|
||||
|
||||
return pathForCSIDL(CSIDL_DESKTOPDIRECTORY, def);
|
||||
case DOCUMENTS:
|
||||
return pathForCSIDL(CSIDL_MYDOCUMENTS, def);
|
||||
if (IsWindowsVistaOrGreater())
|
||||
return pathForKnownFolder(FOLDERID_Documents, def);
|
||||
|
||||
return pathForCSIDL(CSIDL_MYDOCUMENTS, def);
|
||||
|
||||
case PICTURES:
|
||||
return pathForCSIDL(CSIDL_MYPICTURES, def);
|
||||
if (IsWindowsVistaOrGreater())
|
||||
return pathForKnownFolder(FOLDERID_Pictures, def);
|
||||
|
||||
return pathForCSIDL(CSIDL_MYPICTURES, def);
|
||||
|
||||
#elif __APPLE__
|
||||
// since this is C++, we can't include NSPathUtilities.h to access the enum
|
||||
// values, so hard-coding them here (they are stable, don't worry)
|
||||
@@ -756,20 +810,15 @@ SGPath SGPath::fromEnv(const char* name, const SGPath& def)
|
||||
return def;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
SGPath SGPath::home(const SGPath& def)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
//------------------------------------------------------------------------------
|
||||
SGPath SGPath::home(const SGPath& def)
|
||||
{
|
||||
// TODO
|
||||
return def;
|
||||
}
|
||||
return fromEnv("USERPROFILE", def);
|
||||
#else
|
||||
//------------------------------------------------------------------------------
|
||||
SGPath SGPath::home(const SGPath& def)
|
||||
{
|
||||
return fromEnv("HOME", def);
|
||||
}
|
||||
return fromEnv("HOME", def);
|
||||
#endif
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
SGPath SGPath::desktop(const SGPath& def)
|
||||
@@ -786,25 +835,39 @@ SGPath SGPath::documents(const SGPath& def)
|
||||
//------------------------------------------------------------------------------
|
||||
std::string SGPath::realpath() const
|
||||
{
|
||||
#if (defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED <= 1050)
|
||||
// Workaround for Mac OS 10.5. Somehow fgfs crashes on Mac at ::realpath.
|
||||
// This means relative paths cannot be used on Mac OS <= 10.5
|
||||
return path;
|
||||
#else
|
||||
#if defined(_MSC_VER) /*for MS compilers */ || defined(_WIN32) /*needed for non MS windows compilers like MingW*/
|
||||
#if defined(_MSC_VER) /*for MS compilers */ || defined(_WIN32) /*needed for non MS windows compilers like MingW*/
|
||||
// with absPath NULL, will allocate, and ignore length
|
||||
char *buf = _fullpath( NULL, path.c_str(), _MAX_PATH );
|
||||
#else
|
||||
#else
|
||||
// POSIX
|
||||
char* buf = ::realpath(path.c_str(), NULL);
|
||||
#endif
|
||||
if (!buf)
|
||||
#endif
|
||||
if (!buf) // File does not exist: return the realpath it would have if created now
|
||||
// (needed for fgValidatePath security)
|
||||
{
|
||||
SG_LOG(SG_IO, SG_ALERT, "ERROR: The path '" << path << "' does not exist in the file system.");
|
||||
return path;
|
||||
if (path.empty()) {
|
||||
return SGPath(".").realpath(); // current directory
|
||||
}
|
||||
std::string this_dir = dir();
|
||||
if (isAbsolute() && this_dir.empty()) { // top level
|
||||
this_dir = "/";
|
||||
}
|
||||
if (file() == "..") {
|
||||
this_dir = SGPath(SGPath(this_dir).realpath()).dir();
|
||||
if (this_dir.empty()) { // invalid path: .. above root
|
||||
return "";
|
||||
}
|
||||
return SGPath(this_dir).realpath(); // use native path separator,
|
||||
// and handle 'existing/nonexisting/../symlink' paths
|
||||
}
|
||||
return SGPath(this_dir).realpath() +
|
||||
#if defined(_MSC_VER) || defined(_WIN32)
|
||||
"\\" + file();
|
||||
#else
|
||||
"/" + file();
|
||||
#endif
|
||||
}
|
||||
std::string p(buf);
|
||||
free(buf);
|
||||
return p;
|
||||
#endif
|
||||
}
|
||||
|
||||
122
simgear/misc/sgversionhelpers.hxx
Normal file
122
simgear/misc/sgversionhelpers.hxx
Normal file
@@ -0,0 +1,122 @@
|
||||
// Clean drop-in replacement for the versionhelpers.h header
|
||||
//
|
||||
// Copyright (C) 2015 Alessandro Menti <alessandro.menti@hotmail.it>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#ifndef SG_VERSIONHELPERS_HXX_
|
||||
#define SG_VERSIONHELPERS_HXX_
|
||||
|
||||
#include <sdkddkver.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define VERSIONHELPERAPI inline bool
|
||||
#else
|
||||
#define VERSIONHELPERAPI FORCEINLINE BOOL
|
||||
#endif // __cplusplus
|
||||
|
||||
/* Windows 8/8.1 version numbers, not defined in the Windows 7 SDK. */
|
||||
#ifndef _WIN32_WINNT_WIN8
|
||||
#define _WIN32_WINNT_WIN8 0x0602
|
||||
#endif
|
||||
#ifndef _WIN32_WINNT_WINBLUE
|
||||
#define _WIN32_WINNT_WINBLUE 0x0603
|
||||
#endif
|
||||
|
||||
VERSIONHELPERAPI IsWindowsVersionOrGreater(WORD wMajorVersion,
|
||||
WORD wMinorVersion, WORD wServicePackMajor) {
|
||||
OSVERSIONINFOEXW osVersionInfo;
|
||||
DWORDLONG dwlConditionMask = 0;
|
||||
ZeroMemory(&osVersionInfo, sizeof(osVersionInfo));
|
||||
osVersionInfo.dwOSVersionInfoSize = sizeof(osVersionInfo);
|
||||
osVersionInfo.dwMajorVersion = wMajorVersion;
|
||||
osVersionInfo.dwMinorVersion = wMinorVersion;
|
||||
osVersionInfo.wServicePackMajor = wServicePackMajor;
|
||||
VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION,
|
||||
VER_GREATER_EQUAL);
|
||||
VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION,
|
||||
VER_GREATER_EQUAL);
|
||||
VER_SET_CONDITION(dwlConditionMask, VER_SERVICEPACKMAJOR,
|
||||
VER_GREATER_EQUAL);
|
||||
return VerifyVersionInfoW(&osVersionInfo, VER_MAJORVERSION
|
||||
| VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask);
|
||||
}
|
||||
|
||||
VERSIONHELPERAPI IsWindowsXPOrGreater() {
|
||||
return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP),
|
||||
LOBYTE(_WIN32_WINNT_WINXP), 0);
|
||||
}
|
||||
|
||||
VERSIONHELPERAPI IsWindowsXPSP1OrGreater() {
|
||||
return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP),
|
||||
LOBYTE(_WIN32_WINNT_WINXP), 1);
|
||||
}
|
||||
|
||||
VERSIONHELPERAPI IsWindowsXPSP2OrGreater() {
|
||||
return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP),
|
||||
LOBYTE(_WIN32_WINNT_WINXP), 2);
|
||||
}
|
||||
|
||||
VERSIONHELPERAPI IsWindowsXPSP3OrGreater() {
|
||||
return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP),
|
||||
LOBYTE(_WIN32_WINNT_WINXP), 3);
|
||||
}
|
||||
|
||||
VERSIONHELPERAPI IsWindowsVistaOrGreater() {
|
||||
return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA),
|
||||
LOBYTE(_WIN32_WINNT_VISTA), 0);
|
||||
}
|
||||
|
||||
VERSIONHELPERAPI IsWindowsVistaSP1OrGreater() {
|
||||
return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA),
|
||||
LOBYTE(_WIN32_WINNT_VISTA), 1);
|
||||
}
|
||||
|
||||
VERSIONHELPERAPI IsWindowsVistaSP2OrGreater() {
|
||||
return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA),
|
||||
LOBYTE(_WIN32_WINNT_VISTA), 2);
|
||||
}
|
||||
|
||||
VERSIONHELPERAPI IsWindows7OrGreater() {
|
||||
return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN7),
|
||||
LOBYTE(_WIN32_WINNT_WIN7), 0);
|
||||
}
|
||||
|
||||
VERSIONHELPERAPI IsWindows7SP1OrGreater() {
|
||||
return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN7),
|
||||
LOBYTE(_WIN32_WINNT_WIN7), 1);
|
||||
}
|
||||
|
||||
VERSIONHELPERAPI IsWindows8OrGreater() {
|
||||
return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN8),
|
||||
LOBYTE(_WIN32_WINNT_WIN8), 0);
|
||||
}
|
||||
|
||||
VERSIONHELPERAPI IsWindows8Point1OrGreater() {
|
||||
return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINBLUE),
|
||||
LOBYTE(_WIN32_WINNT_WINBLUE), 0);
|
||||
}
|
||||
|
||||
VERSIONHELPERAPI IsWindowsServer() {
|
||||
OSVERSIONINFOEXW osVersionInfo;
|
||||
DWORDLONG dwlConditionMask = 0;
|
||||
ZeroMemory(&osVersionInfo, sizeof(osVersionInfo));
|
||||
osVersionInfo.dwOSVersionInfoSize = sizeof(osVersionInfo);
|
||||
osVersionInfo.wProductType = VER_NT_WORKSTATION;
|
||||
VER_SET_CONDITION(dwlConditionMask, VER_PRODUCT_TYPE, VER_EQUAL);
|
||||
return !VerifyVersionInfoW(&osVersionInfo, VER_PRODUCT_TYPE,
|
||||
dwlConditionMask);
|
||||
}
|
||||
#endif // SG_VERSIONHELPERS_HXX_
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <ctype.h>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
|
||||
#include "strutils.hxx"
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ include (SimGearComponent)
|
||||
set(HEADERS
|
||||
naref.h
|
||||
nasal.h
|
||||
iolib.h
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
@@ -26,8 +27,7 @@ set(SOURCES
|
||||
vector.c
|
||||
code.h
|
||||
data.h
|
||||
iolib.h
|
||||
parse.h
|
||||
)
|
||||
|
||||
simgear_component(nasal nasal "${SOURCES}" "${HEADERS}")
|
||||
simgear_component(nasal nasal "${SOURCES}" "${HEADERS}")
|
||||
|
||||
@@ -66,14 +66,6 @@ namespace nasal
|
||||
void GhostMetadata::addDerived(const GhostMetadata* derived)
|
||||
{
|
||||
assert(derived);
|
||||
|
||||
SG_LOG
|
||||
(
|
||||
SG_NASAL,
|
||||
SG_INFO,
|
||||
"Ghost::addDerived: " << _name_strong << " -> "
|
||||
<< derived->_name_strong
|
||||
);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
@@ -39,6 +39,12 @@ static void runNumTests( double (TestContext::*test_double)(const std::string&),
|
||||
BOOST_CHECK_EQUAL((c.*test_int)("0x755"), 0x755);
|
||||
BOOST_CHECK_EQUAL((c.*test_int)("0x055"), 0x55);
|
||||
BOOST_CHECK_EQUAL((c.*test_int)("-0x155"), -0x155);
|
||||
|
||||
BOOST_CHECK_CLOSE((c.*test_double)("2.000000953656983160"),
|
||||
2.000000953656983160, 1e-5);
|
||||
/* this value has bit pattern 0x400000007fff6789L,
|
||||
* so will look like a pointer if the endianness is set wrong
|
||||
* (see naref.h, data.h)*/
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( parse_num )
|
||||
|
||||
@@ -142,6 +142,9 @@ naRef naIOGhost(naContext c, FILE* f)
|
||||
return naNewGhost(c, &naIOGhostType, ghost);
|
||||
}
|
||||
|
||||
#if SG_NASAL_UNRESTRICTED_OPEN
|
||||
// Allows unrestricted file access, which would be a security hole
|
||||
// Replaced by the one in flightgear src/Scripting/NasalSys.cxx
|
||||
static naRef f_open(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
FILE* f;
|
||||
@@ -152,6 +155,7 @@ static naRef f_open(naContext c, naRef me, int argc, naRef* args)
|
||||
if(!f) naRuntimeError(c, strerror(errno));
|
||||
return naIOGhost(c, f);
|
||||
}
|
||||
#endif
|
||||
|
||||
// frees buffer before tossing an error
|
||||
static int getcguard(naContext ctx, FILE* f, void* buf)
|
||||
@@ -244,7 +248,9 @@ static naCFuncItem funcs[] = {
|
||||
{ "seek", f_seek },
|
||||
{ "tell", f_tell },
|
||||
{ "flush", f_flush },
|
||||
#if SG_NASAL_UNRESTRICTED_OPEN
|
||||
{ "open", f_open },
|
||||
#endif
|
||||
{ "readln", f_readln },
|
||||
{ "stat", f_stat },
|
||||
{ 0 }
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#ifndef _IOLIB_H
|
||||
#define _IOLIB_H
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "nasal.h"
|
||||
|
||||
@@ -33,5 +36,7 @@ extern struct naIOType naStdIOType;
|
||||
|
||||
// Defined in iolib.c, there is no "library" header to put this in
|
||||
naRef naIOGhost(naContext c, FILE* f);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
#endif // _IOLIB_H
|
||||
|
||||
@@ -1,28 +1,31 @@
|
||||
#ifndef _NAREF_H
|
||||
#define _NAREF_H
|
||||
|
||||
/* Rather than play elaborate and complicated games with
|
||||
* platform-dependent endianness headers, just detect the platforms we
|
||||
* support. This list is simpler and smaller, yet still quite
|
||||
* complete. */
|
||||
#if (defined(__x86_64) && defined(__linux__)) || defined(__sparcv9) || \
|
||||
defined(__powerpc64__)
|
||||
/* Win64 and Irix should work with this too, but have not been
|
||||
* tested */
|
||||
/* NASAL_NAN64 mode requires 64 bit pointers that only use the
|
||||
* lower 48 bits; Win64 and Irix should work with this too, but
|
||||
* have not been tested */
|
||||
# define NASAL_NAN64
|
||||
#elif defined(_M_IX86) || defined(i386) || defined(__x86_64) || \
|
||||
#elif defined(__BYTE_ORDER__)
|
||||
/* GCC and Clang define these (as a builtin, while
|
||||
* __LITTLE_ENDIAN__ requires a header), MSVC doesn't */
|
||||
# if __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__
|
||||
# define NASAL_LE
|
||||
# elif __BYTE_ORDER__==__ORDER_BIG_ENDIAN__
|
||||
# define NASAL_BE
|
||||
# else
|
||||
# error Unrecognized endianness
|
||||
# endif
|
||||
#elif defined(_M_IX86) || defined(__i386) || defined(__x86_64) || \
|
||||
defined(__ia64__) || defined(_M_IA64) || defined(__ARMEL__) || \
|
||||
defined(_M_X64) || defined(__alpha__) || \
|
||||
(defined(__sh__) && defined(__LITTLE_ENDIAN__))
|
||||
defined(_M_X64) || defined(_M_ARM)
|
||||
# define NASAL_LE
|
||||
#elif defined(__sparc) || defined(__ppc__) || defined(__PPC) || \
|
||||
defined (__powerpc__) || defined (__powerpc64__) || defined (__alpha__) || \
|
||||
defined(__mips) || defined(__ARMEB__) || \
|
||||
defined(__hppa__) || defined(__s390__) || defined(__s390x__) || \
|
||||
(defined(__sh__) && !defined(__LITTLE_ENDIAN__))
|
||||
#elif defined(__sparc) || defined(__ARMEB__) || \
|
||||
defined(__hppa__) || defined(__s390__) || defined(__s390x__)
|
||||
# define NASAL_BE
|
||||
#else
|
||||
# error Unrecognized CPU architecture
|
||||
# error Unknown endianness
|
||||
#endif
|
||||
|
||||
typedef union {
|
||||
|
||||
@@ -26,3 +26,15 @@ if(ENABLE_PKGUTIL)
|
||||
add_executable(sg_pkgutil pkgutil.cxx)
|
||||
target_link_libraries(sg_pkgutil ${TEST_LIBS})
|
||||
endif()
|
||||
|
||||
if(ENABLE_TESTS)
|
||||
|
||||
add_executable(catalog_test CatalogTest.cxx)
|
||||
target_link_libraries(catalog_test ${TEST_LIBS})
|
||||
|
||||
set_target_properties(catalog_test PROPERTIES
|
||||
COMPILE_DEFINITIONS "SRC_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}\"" )
|
||||
|
||||
add_test(catalog_test ${EXECUTABLE_OUTPUT_PATH}/catalog_test)
|
||||
|
||||
endif(ENABLE_TESTS)
|
||||
@@ -31,12 +31,56 @@
|
||||
#include <simgear/package/Package.hxx>
|
||||
#include <simgear/package/Root.hxx>
|
||||
#include <simgear/package/Install.hxx>
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
|
||||
namespace simgear {
|
||||
|
||||
|
||||
namespace pkg {
|
||||
|
||||
CatalogList static_catalogs;
|
||||
bool checkVersion(const std::string& aVersion, SGPropertyNode_ptr props)
|
||||
{
|
||||
BOOST_FOREACH(SGPropertyNode* v, props->getChildren("version")) {
|
||||
std::string s(v->getStringValue());
|
||||
if (s == aVersion) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// examine each dot-seperated component in turn, supporting a wildcard
|
||||
// in the versions from the catalog.
|
||||
string_list appVersionParts = simgear::strutils::split(aVersion, ".");
|
||||
string_list catVersionParts = simgear::strutils::split(s, ".");
|
||||
|
||||
size_t partCount = appVersionParts.size();
|
||||
if (partCount != catVersionParts.size()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool ok = true;
|
||||
for (unsigned int p=0; p < partCount; ++p) {
|
||||
if (catVersionParts[p] == "*") {
|
||||
// always passes
|
||||
} else if (appVersionParts[p] != catVersionParts[p]) {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string redirectUrlForVersion(const std::string& aVersion, SGPropertyNode_ptr props)
|
||||
{
|
||||
BOOST_FOREACH(SGPropertyNode* v, props->getChildren("alternate-version")) {
|
||||
if (v->getStringValue("version") == aVersion) {
|
||||
return v->getStringValue("url");
|
||||
}
|
||||
}
|
||||
|
||||
return std::string();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -46,25 +90,32 @@ public:
|
||||
Downloader(CatalogRef aOwner, const std::string& aUrl) :
|
||||
HTTP::Request(aUrl),
|
||||
m_owner(aOwner)
|
||||
{
|
||||
{
|
||||
// refreshing
|
||||
m_owner->changeStatus(Delegate::STATUS_IN_PROGRESS);
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
virtual void gotBodyData(const char* s, int n)
|
||||
{
|
||||
m_buffer += std::string(s, n);
|
||||
}
|
||||
|
||||
|
||||
virtual void onDone()
|
||||
{
|
||||
{
|
||||
if (responseCode() != 200) {
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "catalog download failure:" << m_owner->url());
|
||||
m_owner->refreshComplete(Delegate::FAIL_DOWNLOAD);
|
||||
Delegate::StatusCode code = Delegate::FAIL_DOWNLOAD;
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "catalog download failure:" << m_owner->url()
|
||||
<< "\n\t" << responseCode());
|
||||
if (responseCode() == 404) {
|
||||
code = Delegate::FAIL_NOT_FOUND;
|
||||
}
|
||||
m_owner->refreshComplete(code);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
SGPropertyNode* props = new SGPropertyNode;
|
||||
|
||||
|
||||
try {
|
||||
readProperties(m_buffer.data(), m_buffer.size(), props);
|
||||
m_owner->parseProps(props);
|
||||
@@ -73,15 +124,37 @@ protected:
|
||||
m_owner->refreshComplete(Delegate::FAIL_EXTRACT);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string ver(m_owner->root()->catalogVersion());
|
||||
if (!checkVersion(ver, props)) {
|
||||
SG_LOG(SG_GENERAL, SG_WARN, "downloaded catalog " << m_owner->url() << ", version mismatch:\n\t"
|
||||
<< props->getStringValue("version") << " vs required " << ver);
|
||||
|
||||
if (m_owner->root()->catalogVersion() != props->getIntValue("catalog-version")) {
|
||||
SG_LOG(SG_GENERAL, SG_WARN, "catalog:" << m_owner->url() << " is not version "
|
||||
<< m_owner->root()->catalogVersion());
|
||||
m_owner->refreshComplete(Delegate::FAIL_VERSION);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::string ver(m_owner->root()->applicationVersion());
|
||||
if (!checkVersion(ver, props)) {
|
||||
SG_LOG(SG_GENERAL, SG_WARN, "downloaded catalog " << m_owner->url() << ", version mismatch:\n\t"
|
||||
<< props->getStringValue("version") << " vs required " << ver);
|
||||
|
||||
// check for a version redirect entry
|
||||
std::string url = redirectUrlForVersion(ver, props);
|
||||
if (!url.empty()) {
|
||||
SG_LOG(SG_GENERAL, SG_WARN, "redirecting from " << m_owner->url() <<
|
||||
" to \n\t" << url);
|
||||
|
||||
// update the URL and kick off a new request
|
||||
m_owner->m_url = url;
|
||||
Downloader* dl = new Downloader(m_owner, url);
|
||||
m_owner->root()->makeHTTPRequest(dl);
|
||||
} else {
|
||||
m_owner->refreshComplete(Delegate::FAIL_VERSION);
|
||||
}
|
||||
|
||||
return;
|
||||
} // of version check failed
|
||||
|
||||
// cache the catalog data, now we have a valid install root
|
||||
Dir d(m_owner->installRoot());
|
||||
SGPath p = d.file("catalog.xml");
|
||||
@@ -89,57 +162,39 @@ protected:
|
||||
std::ofstream f(p.c_str(), std::ios::out | std::ios::trunc);
|
||||
f.write(m_buffer.data(), m_buffer.size());
|
||||
f.close();
|
||||
|
||||
|
||||
time(&m_owner->m_retrievedTime);
|
||||
m_owner->writeTimestamp();
|
||||
m_owner->refreshComplete(Delegate::FAIL_SUCCESS);
|
||||
m_owner->refreshComplete(Delegate::STATUS_REFRESHED);
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
bool checkVersion(const std::string& aVersion, SGPropertyNode* aProps)
|
||||
{
|
||||
BOOST_FOREACH(SGPropertyNode* v, aProps->getChildren("version")) {
|
||||
if (v->getStringValue() == aVersion) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
CatalogRef m_owner;
|
||||
std::string m_buffer;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CatalogList Catalog::allCatalogs()
|
||||
{
|
||||
return static_catalogs;
|
||||
}
|
||||
|
||||
Catalog::Catalog(Root *aRoot) :
|
||||
m_root(aRoot),
|
||||
m_status(Delegate::FAIL_UNKNOWN),
|
||||
m_retrievedTime(0)
|
||||
{
|
||||
static_catalogs.push_back(this);
|
||||
}
|
||||
|
||||
Catalog::~Catalog()
|
||||
{
|
||||
CatalogList::iterator it = std::find(static_catalogs.begin(), static_catalogs.end(), this);
|
||||
static_catalogs.erase(it);
|
||||
}
|
||||
|
||||
CatalogRef Catalog::createFromUrl(Root* aRoot, const std::string& aUrl)
|
||||
{
|
||||
CatalogRef c = new Catalog(aRoot);
|
||||
c->m_url = aUrl;
|
||||
Downloader* dl = new Downloader(c, aUrl);
|
||||
aRoot->makeHTTPRequest(dl);
|
||||
|
||||
c->refresh();
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
CatalogRef Catalog::createFromPath(Root* aRoot, const SGPath& aPath)
|
||||
{
|
||||
SGPath xml = aPath;
|
||||
@@ -147,29 +202,66 @@ CatalogRef Catalog::createFromPath(Root* aRoot, const SGPath& aPath)
|
||||
if (!xml.exists()) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
SGPropertyNode_ptr props;
|
||||
try {
|
||||
props = new SGPropertyNode;
|
||||
readProperties(xml.str(), props);
|
||||
} catch (sg_exception& e) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (props->getStringValue("version") != aRoot->catalogVersion()) {
|
||||
SG_LOG(SG_GENERAL, SG_WARN, "skipping catalog at " << aPath << ", version mismatch:\n\t"
|
||||
<< props->getStringValue("version") << " vs required " << aRoot->catalogVersion());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
bool versionCheckOk = checkVersion(aRoot->applicationVersion(), props);
|
||||
|
||||
if (!versionCheckOk) {
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "catalog at:" << aPath << " failed version check: need" << aRoot->applicationVersion());
|
||||
// keep the catalog but mark it as needing an update
|
||||
} else {
|
||||
SG_LOG(SG_GENERAL, SG_DEBUG, "creating catalog from:" << aPath);
|
||||
}
|
||||
|
||||
CatalogRef c = new Catalog(aRoot);
|
||||
c->m_installRoot = aPath;
|
||||
c->parseProps(props);
|
||||
c->parseTimestamp();
|
||||
|
||||
|
||||
if (versionCheckOk) {
|
||||
// parsed XML ok, mark status as valid
|
||||
c->changeStatus(Delegate::STATUS_SUCCESS);
|
||||
} else {
|
||||
c->changeStatus(Delegate::FAIL_VERSION);
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
bool Catalog::uninstall()
|
||||
{
|
||||
bool ok;
|
||||
bool atLeastOneFailure = false;
|
||||
|
||||
BOOST_FOREACH(PackageRef p, installedPackages()) {
|
||||
ok = p->existingInstall()->uninstall();
|
||||
if (!ok) {
|
||||
SG_LOG(SG_GENERAL, SG_WARN, "uninstall of package " <<
|
||||
p->id() << " failed");
|
||||
// continue trying other packages, bailing out here
|
||||
// gains us nothing
|
||||
atLeastOneFailure = true;
|
||||
}
|
||||
}
|
||||
|
||||
Dir d(m_installRoot);
|
||||
ok = d.remove(true /* recursive */);
|
||||
if (!ok) {
|
||||
atLeastOneFailure = true;
|
||||
}
|
||||
|
||||
changeStatus(atLeastOneFailure ? Delegate::FAIL_FILESYSTEM
|
||||
: Delegate::STATUS_SUCCESS);
|
||||
return ok;
|
||||
}
|
||||
|
||||
PackageList const&
|
||||
Catalog::packages() const
|
||||
{
|
||||
@@ -196,7 +288,7 @@ Catalog::packagesNeedingUpdate() const
|
||||
if (!p->isInstalled()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (p->install()->hasUpdate()) {
|
||||
r.push_back(p);
|
||||
}
|
||||
@@ -215,31 +307,32 @@ Catalog::installedPackages() const
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
InstallRef Catalog::installForPackage(PackageRef pkg) const
|
||||
{
|
||||
PackageInstallDict::const_iterator it = m_installed.find(pkg);
|
||||
if (it == m_installed.end()) {
|
||||
// check if it exists on disk, create
|
||||
|
||||
SGPath p(pkg->pathOnDisk());
|
||||
if (p.exists()) {
|
||||
return Install::createFromPath(p, CatalogRef(const_cast<Catalog*>(this)));
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void Catalog::refresh()
|
||||
{
|
||||
if (m_refreshRequest.valid()) {
|
||||
// refresh in progress
|
||||
return;
|
||||
}
|
||||
|
||||
Downloader* dl = new Downloader(this, url());
|
||||
m_refreshRequest = dl;
|
||||
// will update status to IN_PROGRESS
|
||||
m_root->makeHTTPRequest(dl);
|
||||
m_root->catalogRefreshBegin(this);
|
||||
}
|
||||
|
||||
struct FindById
|
||||
{
|
||||
FindById(const std::string &id) : m_id(id) {}
|
||||
|
||||
bool operator()(const PackageRef& ref) const
|
||||
{
|
||||
return ref->id() == m_id;
|
||||
}
|
||||
|
||||
std::string m_id;
|
||||
};
|
||||
|
||||
void Catalog::parseProps(const SGPropertyNode* aProps)
|
||||
{
|
||||
// copy everything except package children?
|
||||
@@ -253,8 +346,14 @@ void Catalog::parseProps(const SGPropertyNode* aProps)
|
||||
for (int i = 0; i < nChildren; i++) {
|
||||
const SGPropertyNode* pkgProps = aProps->getChild(i);
|
||||
if (strcmp(pkgProps->getName(), "package") == 0) {
|
||||
PackageRef p = getPackageById(pkgProps->getStringValue("id"));
|
||||
if (p) {
|
||||
// can't use getPackageById here becuase the variant dict isn't
|
||||
// built yet. Instead we need to look at m_packages directly.
|
||||
|
||||
PackageList::iterator pit = std::find_if(m_packages.begin(), m_packages.end(),
|
||||
FindById(pkgProps->getStringValue("id")));
|
||||
PackageRef p;
|
||||
if (pit != m_packages.end()) {
|
||||
p = *pit;
|
||||
// existing package
|
||||
p->updateFromProps(pkgProps);
|
||||
orphans.erase(p); // not an orphan
|
||||
@@ -294,13 +393,13 @@ void Catalog::parseProps(const SGPropertyNode* aProps)
|
||||
<< " is now at: " << m_props->getStringValue("url"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
m_url = m_props->getStringValue("url");
|
||||
|
||||
if (m_installRoot.isNull()) {
|
||||
m_installRoot = m_root->path();
|
||||
m_installRoot.append(id());
|
||||
|
||||
|
||||
Dir d(m_installRoot);
|
||||
d.create(0755);
|
||||
}
|
||||
@@ -312,11 +411,23 @@ PackageRef Catalog::getPackageById(const std::string& aId) const
|
||||
// works as expected.
|
||||
PackageWeakMap::const_iterator it = m_variantDict.find(aId);
|
||||
if (it == m_variantDict.end())
|
||||
return NULL;
|
||||
return PackageRef();
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
PackageRef Catalog::getPackageByPath(const std::string& aPath) const
|
||||
{
|
||||
PackageList::const_iterator it;
|
||||
for (it = m_packages.begin(); it != m_packages.end(); ++it) {
|
||||
if ((*it)->dirName() == aPath) {
|
||||
return *it;
|
||||
}
|
||||
}
|
||||
|
||||
return PackageRef();
|
||||
}
|
||||
|
||||
std::string Catalog::id() const
|
||||
{
|
||||
return m_props->getStringValue("id");
|
||||
@@ -327,11 +438,16 @@ std::string Catalog::url() const
|
||||
return m_url;
|
||||
}
|
||||
|
||||
std::string Catalog::name() const
|
||||
{
|
||||
return getLocalisedString(m_props, "name");
|
||||
}
|
||||
|
||||
std::string Catalog::description() const
|
||||
{
|
||||
return getLocalisedString(m_props, "description");
|
||||
}
|
||||
|
||||
|
||||
SGPropertyNode* Catalog::properties() const
|
||||
{
|
||||
return m_props.ptr();
|
||||
@@ -363,43 +479,56 @@ unsigned int Catalog::ageInSeconds() const
|
||||
|
||||
bool Catalog::needsRefresh() const
|
||||
{
|
||||
// always refresh in these cases
|
||||
if ((m_status == Delegate::FAIL_VERSION) || (m_status == Delegate::FAIL_DOWNLOAD)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned int maxAge = m_props->getIntValue("max-age-sec", m_root->maxAgeSeconds());
|
||||
return (ageInSeconds() > maxAge);
|
||||
}
|
||||
|
||||
|
||||
std::string Catalog::getLocalisedString(const SGPropertyNode* aRoot, const char* aName) const
|
||||
{
|
||||
if (!aRoot) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
if (aRoot->hasChild(m_root->getLocale())) {
|
||||
const SGPropertyNode* localeRoot = aRoot->getChild(m_root->getLocale().c_str());
|
||||
if (localeRoot->hasChild(aName)) {
|
||||
return localeRoot->getStringValue(aName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return aRoot->getStringValue(aName);
|
||||
}
|
||||
|
||||
void Catalog::refreshComplete(Delegate::FailureCode aReason)
|
||||
void Catalog::refreshComplete(Delegate::StatusCode aReason)
|
||||
{
|
||||
m_root->catalogRefreshComplete(this, aReason);
|
||||
changeStatus(aReason);
|
||||
m_refreshRequest.reset();
|
||||
}
|
||||
|
||||
void Catalog::registerInstall(Install* ins)
|
||||
void Catalog::changeStatus(Delegate::StatusCode newStatus)
|
||||
{
|
||||
if (!ins || ins->package()->catalog() != this) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_installed[ins->package()] = ins;
|
||||
if (m_status == newStatus) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_status = newStatus;
|
||||
m_root->catalogRefreshStatus(this, newStatus);
|
||||
m_statusCallbacks(this);
|
||||
}
|
||||
|
||||
void Catalog::unregisterInstall(Install* ins)
|
||||
void Catalog::addStatusCallback(const Callback& cb)
|
||||
{
|
||||
if (!ins || ins->package()->catalog() != this) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_installed.erase(ins->package());
|
||||
m_statusCallbacks.push_back(cb);
|
||||
}
|
||||
|
||||
Delegate::StatusCode Catalog::status() const
|
||||
{
|
||||
return m_status;
|
||||
}
|
||||
|
||||
} // of namespace pkg
|
||||
|
||||
@@ -22,19 +22,23 @@
|
||||
#include <ctime>
|
||||
#include <map>
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/props/props.hxx>
|
||||
|
||||
#include <simgear/structure/SGReferenced.hxx>
|
||||
#include <simgear/structure/SGSharedPtr.hxx>
|
||||
#include <simgear/structure/function_list.hxx>
|
||||
#include <simgear/io/HTTPRequest.hxx>
|
||||
|
||||
#include <simgear/package/Delegate.hxx>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
|
||||
|
||||
namespace HTTP { class Client; }
|
||||
|
||||
|
||||
namespace pkg
|
||||
{
|
||||
|
||||
@@ -43,11 +47,11 @@ class Package;
|
||||
class Catalog;
|
||||
class Root;
|
||||
class Install;
|
||||
|
||||
|
||||
typedef SGSharedPtr<Package> PackageRef;
|
||||
typedef SGSharedPtr<Catalog> CatalogRef;
|
||||
typedef SGSharedPtr<Install> InstallRef;
|
||||
|
||||
|
||||
typedef std::vector<PackageRef> PackageList;
|
||||
typedef std::vector<CatalogRef> CatalogList;
|
||||
|
||||
@@ -55,16 +59,19 @@ class Catalog : public SGReferenced
|
||||
{
|
||||
public:
|
||||
virtual ~Catalog();
|
||||
|
||||
|
||||
static CatalogRef createFromUrl(Root* aRoot, const std::string& aUrl);
|
||||
|
||||
|
||||
static CatalogRef createFromPath(Root* aRoot, const SGPath& aPath);
|
||||
|
||||
static CatalogList allCatalogs();
|
||||
|
||||
|
||||
Root* root() const
|
||||
{ return m_root;};
|
||||
|
||||
|
||||
/**
|
||||
* uninstall this catalog entirely, including all installed packages
|
||||
*/
|
||||
bool uninstall();
|
||||
|
||||
/**
|
||||
* perform a refresh of the catalog contents
|
||||
*/
|
||||
@@ -89,70 +96,81 @@ public:
|
||||
/**
|
||||
* retrieve all the packages in the catalog which are installed
|
||||
* and have a pendig update
|
||||
*/
|
||||
*/
|
||||
PackageList packagesNeedingUpdate() const;
|
||||
|
||||
|
||||
SGPath installRoot() const
|
||||
{ return m_installRoot; }
|
||||
|
||||
|
||||
std::string id() const;
|
||||
|
||||
|
||||
std::string url() const;
|
||||
|
||||
|
||||
std::string name() const;
|
||||
|
||||
std::string description() const;
|
||||
|
||||
|
||||
PackageRef getPackageById(const std::string& aId) const;
|
||||
|
||||
InstallRef installForPackage(PackageRef pkg) const;
|
||||
|
||||
|
||||
PackageRef getPackageByPath(const std::string& aPath) const;
|
||||
|
||||
/**
|
||||
* test if the catalog data was retrieved longer ago than the
|
||||
* maximum permitted age for this catalog.
|
||||
*/
|
||||
bool needsRefresh() const;
|
||||
|
||||
|
||||
unsigned int ageInSeconds() const;
|
||||
|
||||
|
||||
/**
|
||||
* access the raw property data in the catalog
|
||||
*/
|
||||
SGPropertyNode* properties() const;
|
||||
|
||||
Delegate::StatusCode status() const;
|
||||
|
||||
typedef boost::function<void(Catalog*)> Callback;
|
||||
|
||||
void addStatusCallback(const Callback& cb);
|
||||
|
||||
template<class C>
|
||||
void addStatusCallback(C* instance, void (C::*mem_func)(Catalog*))
|
||||
{
|
||||
return addStatusCallback(boost::bind(mem_func, instance, _1));
|
||||
}
|
||||
private:
|
||||
Catalog(Root* aRoot);
|
||||
|
||||
|
||||
class Downloader;
|
||||
friend class Downloader;
|
||||
|
||||
friend class Install;
|
||||
void registerInstall(Install* ins);
|
||||
void unregisterInstall(Install* ins);
|
||||
|
||||
|
||||
void parseProps(const SGPropertyNode* aProps);
|
||||
|
||||
void refreshComplete(Delegate::FailureCode aReason);
|
||||
|
||||
|
||||
void refreshComplete(Delegate::StatusCode aReason);
|
||||
|
||||
void parseTimestamp();
|
||||
void writeTimestamp();
|
||||
|
||||
|
||||
std::string getLocalisedString(const SGPropertyNode* aRoot, const char* aName) const;
|
||||
|
||||
|
||||
void changeStatus(Delegate::StatusCode newStatus);
|
||||
|
||||
Root* m_root;
|
||||
SGPropertyNode_ptr m_props;
|
||||
SGPath m_installRoot;
|
||||
std::string m_url;
|
||||
Delegate::StatusCode m_status;
|
||||
HTTP::Request_ptr m_refreshRequest;
|
||||
|
||||
PackageList m_packages;
|
||||
time_t m_retrievedTime;
|
||||
|
||||
typedef std::map<std::string, Package*> PackageWeakMap;
|
||||
PackageWeakMap m_variantDict;
|
||||
|
||||
// important that this is a weak-ref to Installs,
|
||||
// since it is only cleaned up in the Install destructor
|
||||
typedef std::map<PackageRef, Install*> PackageInstallDict;
|
||||
PackageInstallDict m_installed;
|
||||
|
||||
function_list<Callback> m_statusCallbacks;
|
||||
};
|
||||
|
||||
|
||||
} // of namespace pkg
|
||||
|
||||
} // of namespace simgear
|
||||
|
||||
107
simgear/package/CatalogTest.cxx
Normal file
107
simgear/package/CatalogTest.cxx
Normal file
@@ -0,0 +1,107 @@
|
||||
// Copyright (C) 2015 James Turner - zakalawe@mac.com
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <simgear_config.h>
|
||||
#endif
|
||||
|
||||
#include <simgear/misc/test_macros.hxx>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
|
||||
#include <simgear/package/Catalog.hxx>
|
||||
#include <simgear/package/Root.hxx>
|
||||
#include <simgear/package/Package.hxx>
|
||||
|
||||
#include <simgear/misc/sg_dir.hxx>
|
||||
|
||||
using namespace simgear;
|
||||
|
||||
int parseTest()
|
||||
{
|
||||
SGPath rootPath = simgear::Dir::current().path();
|
||||
rootPath.append("testRoot");
|
||||
pkg::Root* root = new pkg::Root(rootPath, "8.1.12");
|
||||
pkg::CatalogRef cat = pkg::Catalog::createFromPath(root, SGPath(SRC_DIR "/catalogTest1"));
|
||||
|
||||
VERIFY(cat.valid());
|
||||
|
||||
COMPARE(cat->id(), "org.flightgear.test.catalog1");
|
||||
COMPARE(cat->url(), "http://download.flightgear.org/catalog1/catalog.xml");
|
||||
COMPARE(cat->description(), "First test catalog");
|
||||
|
||||
// check the packages too
|
||||
COMPARE(cat->packages().size(), 3);
|
||||
|
||||
pkg::PackageRef p1 = cat->packages().front();
|
||||
COMPARE(p1->catalog(), cat.ptr());
|
||||
|
||||
COMPARE(p1->id(), "alpha");
|
||||
COMPARE(p1->qualifiedId(), "org.flightgear.test.catalog1.alpha");
|
||||
COMPARE(p1->name(), "Alpha package");
|
||||
COMPARE(p1->revision(), 8);
|
||||
COMPARE(p1->fileSizeBytes(), 1234567);
|
||||
|
||||
|
||||
pkg::PackageRef p2 = cat->getPackageById("c172p");
|
||||
VERIFY(p2.valid());
|
||||
COMPARE(p2->qualifiedId(), "org.flightgear.test.catalog1.c172p");
|
||||
COMPARE(p2->description(), "A plane made by Cessna");
|
||||
|
||||
|
||||
|
||||
// test filtering / searching too
|
||||
string_set tags(p2->tags());
|
||||
COMPARE(tags.size(), 4);
|
||||
VERIFY(tags.find("ifr") != tags.end());
|
||||
VERIFY(tags.find("cessna") != tags.end());
|
||||
VERIFY(tags.find("jet") == tags.end());
|
||||
|
||||
|
||||
SGPropertyNode_ptr queryA(new SGPropertyNode);
|
||||
queryA->setStringValue("tag", "ifr");
|
||||
VERIFY(p2->matches(queryA.ptr()));
|
||||
|
||||
SGPropertyNode_ptr queryB(new SGPropertyNode);
|
||||
queryB->setStringValue("name", "ces");
|
||||
VERIFY(p2->matches(queryB.ptr()));
|
||||
|
||||
SGPropertyNode_ptr queryC(new SGPropertyNode);
|
||||
queryC->setStringValue("name", "foo");
|
||||
VERIFY(!p2->matches(queryC.ptr()));
|
||||
|
||||
SGPropertyNode_ptr queryD(new SGPropertyNode);
|
||||
queryD->setIntValue("rating-FDM", 3);
|
||||
VERIFY(p2->matches(queryD.ptr()));
|
||||
|
||||
SGPropertyNode_ptr queryE(new SGPropertyNode);
|
||||
queryE->setIntValue("rating-model", 5);
|
||||
queryE->setStringValue("description", "cessna");
|
||||
VERIFY(p2->matches(queryE.ptr()));
|
||||
|
||||
|
||||
delete root;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
parseTest();
|
||||
std::cout << "Successfully passed all tests!" << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -18,15 +18,21 @@
|
||||
#ifndef SG_PACKAGE_DELEGATE_HXX
|
||||
#define SG_PACKAGE_DELEGATE_HXX
|
||||
|
||||
#include <simgear/misc/stdint.hxx>
|
||||
#include <simgear/structure/SGSharedPtr.hxx>
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
|
||||
|
||||
namespace pkg
|
||||
{
|
||||
|
||||
|
||||
class Install;
|
||||
class Catalog;
|
||||
|
||||
|
||||
typedef SGSharedPtr<Catalog> CatalogRef;
|
||||
typedef SGSharedPtr<Install> InstallRef;
|
||||
|
||||
/**
|
||||
* package delegate is the mechanism to discover progress / completion /
|
||||
* errors in packaging steps asynchronously.
|
||||
@@ -35,40 +41,42 @@ class Delegate
|
||||
{
|
||||
public:
|
||||
typedef enum {
|
||||
FAIL_SUCCESS = 0, ///< not a failure :)
|
||||
STATUS_SUCCESS = 0,
|
||||
FAIL_UNKNOWN = 1,
|
||||
FAIL_IN_PROGRESS, ///< downloading/installation in progress (not a failure :P)
|
||||
FAIL_CHECKSUM, ///< package MD5 verificstion failed
|
||||
FAIL_DOWNLOAD, ///< network issue
|
||||
FAIL_EXTRACT, ///< package archive failed to extract cleanly
|
||||
STATUS_IN_PROGRESS, ///< downloading/installation in progress
|
||||
FAIL_CHECKSUM, ///< package MD5 verificstion failed
|
||||
FAIL_DOWNLOAD, ///< network issue
|
||||
FAIL_EXTRACT, ///< package archive failed to extract cleanly
|
||||
FAIL_FILESYSTEM, ///< unknown filesystem error occurred
|
||||
FAIL_VERSION ///< version check mismatch
|
||||
} FailureCode;
|
||||
|
||||
|
||||
FAIL_VERSION, ///< version check mismatch
|
||||
FAIL_NOT_FOUND, ///< package URL returned a 404
|
||||
STATUS_REFRESHED,
|
||||
USER_CANCELLED
|
||||
} StatusCode;
|
||||
|
||||
|
||||
virtual ~Delegate() { }
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* invoked when all catalogs have finished refreshing - either successfully
|
||||
* or with a failure.
|
||||
* emitted when a catalog refesh completes, either success or failure
|
||||
* If catalog is null, this means /all/ catalogs have been refreshed
|
||||
*/
|
||||
virtual void refreshComplete() = 0;
|
||||
|
||||
virtual void catalogRefreshed(CatalogRef, StatusCode aReason) = 0;
|
||||
|
||||
virtual void startInstall(InstallRef aInstall) = 0;
|
||||
virtual void installProgress(InstallRef aInstall, unsigned int aBytes, unsigned int aTotal) = 0;
|
||||
virtual void finishInstall(InstallRef aInstall, StatusCode aReason) = 0;
|
||||
|
||||
/**
|
||||
* emitted when a catalog fails to refresh, due to a network issue or
|
||||
* some other failure.
|
||||
* Notification when catalogs/packages are added or removed
|
||||
*/
|
||||
virtual void failedRefresh(Catalog*, FailureCode aReason) = 0;
|
||||
|
||||
virtual void startInstall(Install* aInstall) = 0;
|
||||
virtual void installProgress(Install* aInstall, unsigned int aBytes, unsigned int aTotal) = 0;
|
||||
virtual void finishInstall(Install* aInstall) = 0;
|
||||
|
||||
|
||||
virtual void failedInstall(Install* aInstall, FailureCode aReason) = 0;
|
||||
|
||||
};
|
||||
|
||||
virtual void availablePackagesChanged() {}
|
||||
|
||||
virtual void dataForThumbnail(const std::string& aThumbnailUrl,
|
||||
size_t lenth, const uint8_t* bytes) {}
|
||||
};
|
||||
|
||||
} // of namespace pkg
|
||||
|
||||
} // of namespace simgear
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <simgear/package/md5.h>
|
||||
|
||||
#include <simgear/structure/exception.hxx>
|
||||
#include <simgear/props/props_io.hxx>
|
||||
#include <simgear/package/Catalog.hxx>
|
||||
#include <simgear/package/Package.hxx>
|
||||
#include <simgear/package/Root.hxx>
|
||||
@@ -37,7 +38,7 @@ extern "C" {
|
||||
}
|
||||
|
||||
namespace simgear {
|
||||
|
||||
|
||||
namespace pkg {
|
||||
|
||||
class Install::PackageArchiveDownloader : public HTTP::Request
|
||||
@@ -45,48 +46,87 @@ class Install::PackageArchiveDownloader : public HTTP::Request
|
||||
public:
|
||||
PackageArchiveDownloader(InstallRef aOwner) :
|
||||
HTTP::Request("" /* dummy URL */),
|
||||
m_owner(aOwner)
|
||||
m_owner(aOwner),
|
||||
m_downloaded(0)
|
||||
{
|
||||
m_urls = m_owner->package()->downloadUrls();
|
||||
if (m_urls.empty()) {
|
||||
throw sg_exception("no package download URLs");
|
||||
}
|
||||
|
||||
|
||||
// TODO randomise order of m_urls
|
||||
|
||||
|
||||
m_extractPath = aOwner->path().dir();
|
||||
m_extractPath.append("_DOWNLOAD"); // add some temporary value
|
||||
|
||||
m_extractPath.append("_extract_" + aOwner->package()->md5());
|
||||
|
||||
// clean up any existing files
|
||||
Dir d(m_extractPath);
|
||||
if (d.exists()) {
|
||||
d.remove(true /* recursive */);
|
||||
}
|
||||
}
|
||||
|
||||
~PackageArchiveDownloader()
|
||||
{
|
||||
// always clean up our extraction dir: if we successfully downloaded
|
||||
// and installed it will be an empty dir, if we failed it might contain
|
||||
// (some) of the package files.
|
||||
Dir d(m_extractPath);
|
||||
if (d.exists()) {
|
||||
d.remove(true /* recursive */);
|
||||
}
|
||||
}
|
||||
|
||||
size_t downloadedBytes() const
|
||||
{
|
||||
return m_downloaded;
|
||||
}
|
||||
|
||||
int percentDownloaded() const
|
||||
{
|
||||
if (responseLength() <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (m_downloaded * 100) / responseLength();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual std::string url() const
|
||||
{
|
||||
return m_urls.front();
|
||||
}
|
||||
|
||||
|
||||
virtual void responseHeadersComplete()
|
||||
{
|
||||
Request::responseHeadersComplete();
|
||||
|
||||
Dir d(m_extractPath);
|
||||
d.create(0755);
|
||||
|
||||
d.create(0755);
|
||||
|
||||
memset(&m_md5, 0, sizeof(SG_MD5_CTX));
|
||||
SG_MD5Init(&m_md5);
|
||||
}
|
||||
|
||||
|
||||
virtual void gotBodyData(const char* s, int n)
|
||||
{
|
||||
m_buffer += std::string(s, n);
|
||||
SG_MD5Update(&m_md5, (unsigned char*) s, n);
|
||||
|
||||
|
||||
m_downloaded = m_buffer.size();
|
||||
m_owner->installProgress(m_buffer.size(), responseLength());
|
||||
}
|
||||
|
||||
|
||||
virtual void onDone()
|
||||
{
|
||||
if (responseCode() != 200) {
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "download failure");
|
||||
doFailure(Delegate::FAIL_DOWNLOAD);
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "download failure:" << responseCode() <<
|
||||
"\n\t" << url());
|
||||
Delegate::StatusCode code = Delegate::FAIL_DOWNLOAD;
|
||||
if (responseCode() == 404) {
|
||||
code = Delegate::FAIL_NOT_FOUND;
|
||||
}
|
||||
|
||||
doFailure(code);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -103,65 +143,79 @@ protected:
|
||||
doFailure(Delegate::FAIL_CHECKSUM);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!extractUnzip()) {
|
||||
SG_LOG(SG_GENERAL, SG_WARN, "zip extraction failed");
|
||||
doFailure(Delegate::FAIL_EXTRACT);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (m_owner->path().exists()) {
|
||||
//std::cout << "removing existing path" << std::endl;
|
||||
Dir destDir(m_owner->path());
|
||||
destDir.remove(true /* recursive */);
|
||||
}
|
||||
|
||||
m_extractPath.append(m_owner->package()->id());
|
||||
bool ok = m_extractPath.rename(m_owner->path());
|
||||
|
||||
// build a path like /path/to/packages/org.some.catalog/Aircraft/extract_xxxx/MyAircraftDir
|
||||
SGPath extractedPath = m_extractPath;
|
||||
extractedPath.append(m_owner->package()->dirName());
|
||||
|
||||
// rename it to path/to/packages/org.some.catalog/Aircraft/MyAircraftDir
|
||||
bool ok = extractedPath.rename(m_owner->path());
|
||||
if (!ok) {
|
||||
doFailure(Delegate::FAIL_FILESYSTEM);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
m_owner->m_revision = m_owner->package()->revision();
|
||||
m_owner->writeRevisionFile();
|
||||
m_owner->installResult(Delegate::FAIL_SUCCESS);
|
||||
m_owner->m_download.reset(); // so isDownloading reports false
|
||||
|
||||
m_owner->installResult(Delegate::STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
virtual void onFail()
|
||||
{
|
||||
if (responseCode() == -1) {
|
||||
doFailure(Delegate::USER_CANCELLED);
|
||||
} else {
|
||||
doFailure(Delegate::FAIL_DOWNLOAD);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void extractCurrentFile(unzFile zip, char* buffer, size_t bufferSize)
|
||||
{
|
||||
unz_file_info fileInfo;
|
||||
unzGetCurrentFileInfo(zip, &fileInfo,
|
||||
buffer, bufferSize,
|
||||
unzGetCurrentFileInfo(zip, &fileInfo,
|
||||
buffer, bufferSize,
|
||||
NULL, 0, /* extra field */
|
||||
NULL, 0 /* comment field */);
|
||||
|
||||
|
||||
std::string name(buffer);
|
||||
// no absolute paths, no 'up' traversals
|
||||
// we could also look for suspicious file extensions here (forbid .dll, .exe, .so)
|
||||
if ((name[0] == '/') || (name.find("../") != std::string::npos) || (name.find("..\\") != std::string::npos)) {
|
||||
throw sg_format_exception("Bad zip path", name);
|
||||
}
|
||||
|
||||
|
||||
if (fileInfo.uncompressed_size == 0) {
|
||||
// assume it's a directory for now
|
||||
// since we create parent directories when extracting
|
||||
// a path, we're done here
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
int result = unzOpenCurrentFile(zip);
|
||||
if (result != UNZ_OK) {
|
||||
throw sg_io_exception("opening current zip file failed", sg_location(name));
|
||||
}
|
||||
|
||||
|
||||
std::ofstream outFile;
|
||||
bool eof = false;
|
||||
SGPath path(m_extractPath);
|
||||
path.append(name);
|
||||
|
||||
|
||||
// create enclosing directory heirarchy as required
|
||||
Dir parentDir(path.dir());
|
||||
if (!parentDir.exists()) {
|
||||
@@ -170,12 +224,12 @@ private:
|
||||
throw sg_io_exception("failed to create directory heirarchy for extraction", path.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
outFile.open(path.c_str(), std::ios::binary | std::ios::trunc | std::ios::out);
|
||||
if (outFile.fail()) {
|
||||
throw sg_io_exception("failed to open output file for writing", path.c_str());
|
||||
}
|
||||
|
||||
|
||||
while (!eof) {
|
||||
int bytes = unzReadCurrentFile(zip, buffer, bufferSize);
|
||||
if (bytes < 0) {
|
||||
@@ -186,30 +240,30 @@ private:
|
||||
outFile.write(buffer, bytes);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
outFile.close();
|
||||
unzCloseCurrentFile(zip);
|
||||
}
|
||||
|
||||
|
||||
bool extractUnzip()
|
||||
{
|
||||
bool result = true;
|
||||
zlib_filefunc_def memoryAccessFuncs;
|
||||
fill_memory_filefunc(&memoryAccessFuncs);
|
||||
|
||||
|
||||
char bufferName[128];
|
||||
snprintf(bufferName, 128, "%p+%lx", m_buffer.data(), m_buffer.size());
|
||||
unzFile zip = unzOpen2(bufferName, &memoryAccessFuncs);
|
||||
|
||||
|
||||
const size_t BUFFER_SIZE = 32 * 1024;
|
||||
void* buf = malloc(BUFFER_SIZE);
|
||||
|
||||
|
||||
try {
|
||||
int result = unzGoToFirstFile(zip);
|
||||
if (result != UNZ_OK) {
|
||||
throw sg_exception("failed to go to first file in archive");
|
||||
}
|
||||
|
||||
|
||||
while (true) {
|
||||
extractCurrentFile(zip, (char*) buf, BUFFER_SIZE);
|
||||
result = unzGoToNextFile(zip);
|
||||
@@ -222,57 +276,53 @@ private:
|
||||
} catch (sg_exception& e) {
|
||||
result = false;
|
||||
}
|
||||
|
||||
|
||||
free(buf);
|
||||
unzClose(zip);
|
||||
return result;
|
||||
}
|
||||
|
||||
void doFailure(Delegate::FailureCode aReason)
|
||||
|
||||
void doFailure(Delegate::StatusCode aReason)
|
||||
{
|
||||
Dir dir(m_extractPath);
|
||||
dir.remove(true /* recursive */);
|
||||
|
||||
if (m_urls.size() == 1) {
|
||||
std::cout << "failure:" << aReason << std::endl;
|
||||
m_owner->installResult(aReason);
|
||||
return;
|
||||
if (dir.exists()) {
|
||||
dir.remove(true /* recursive */);
|
||||
}
|
||||
|
||||
std::cout << "retrying download" << std::endl;
|
||||
m_urls.erase(m_urls.begin()); // pop first URL
|
||||
|
||||
// TODO - try other mirrors
|
||||
m_owner->m_download.reset(); // ensure we get cleaned up
|
||||
m_owner->installResult(aReason);
|
||||
}
|
||||
|
||||
|
||||
InstallRef m_owner;
|
||||
string_list m_urls;
|
||||
SG_MD5_CTX m_md5;
|
||||
std::string m_buffer;
|
||||
SGPath m_extractPath;
|
||||
size_t m_downloaded;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
Install::Install(PackageRef aPkg, const SGPath& aPath) :
|
||||
m_package(aPkg),
|
||||
m_path(aPath),
|
||||
m_download(NULL),
|
||||
_status(Delegate::FAIL_IN_PROGRESS)
|
||||
m_status(Delegate::STATUS_IN_PROGRESS)
|
||||
{
|
||||
parseRevision();
|
||||
m_package->catalog()->registerInstall(this);
|
||||
m_package->catalog()->root()->registerInstall(this);
|
||||
}
|
||||
|
||||
|
||||
Install::~Install()
|
||||
{
|
||||
m_package->catalog()->unregisterInstall(this);
|
||||
}
|
||||
|
||||
InstallRef Install::createFromPath(const SGPath& aPath, CatalogRef aCat)
|
||||
{
|
||||
std::string id = aPath.file();
|
||||
PackageRef pkg = aCat->getPackageById(id);
|
||||
std::string path = aPath.file();
|
||||
PackageRef pkg = aCat->getPackageByPath(path);
|
||||
if (!pkg)
|
||||
throw sg_exception("no package with id:" + id);
|
||||
|
||||
throw sg_exception("no package with path:" + path);
|
||||
|
||||
return new Install(pkg, aPath);
|
||||
}
|
||||
|
||||
@@ -284,7 +334,7 @@ void Install::parseRevision()
|
||||
m_revision = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
std::ifstream f(revisionFile.c_str(), std::ios::in);
|
||||
f >> m_revision;
|
||||
}
|
||||
@@ -307,23 +357,108 @@ void Install::startUpdate()
|
||||
if (m_download) {
|
||||
return; // already active
|
||||
}
|
||||
|
||||
|
||||
m_download = new PackageArchiveDownloader(this);
|
||||
m_package->catalog()->root()->makeHTTPRequest(m_download);
|
||||
m_package->catalog()->root()->startInstall(this);
|
||||
}
|
||||
|
||||
void Install::uninstall()
|
||||
bool Install::uninstall()
|
||||
{
|
||||
Dir d(m_path);
|
||||
d.remove(true);
|
||||
m_package->catalog()->unregisterInstall(this);
|
||||
if (!d.remove(true)) {
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "package uninstall failed: couldn't remove path " << m_path);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_package->catalog()->root()->unregisterInstall(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Install::isDownloading() const
|
||||
{
|
||||
return (m_download.valid());
|
||||
}
|
||||
|
||||
bool Install::isQueued() const
|
||||
{
|
||||
return m_package->catalog()->root()->isInstallQueued(const_cast<Install*>(this));
|
||||
}
|
||||
|
||||
int Install::downloadedPercent() const
|
||||
{
|
||||
if (!m_download.valid()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
PackageArchiveDownloader* dl = static_cast<PackageArchiveDownloader*>(m_download.get());
|
||||
return dl->percentDownloaded();
|
||||
}
|
||||
|
||||
size_t Install::downloadedBytes() const
|
||||
{
|
||||
if (!m_download.valid()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
PackageArchiveDownloader* dl = static_cast<PackageArchiveDownloader*>(m_download.get());
|
||||
return dl->downloadedBytes();
|
||||
|
||||
}
|
||||
|
||||
void Install::cancelDownload()
|
||||
{
|
||||
if (m_download.valid()) {
|
||||
m_download->abort("User cancelled download");
|
||||
}
|
||||
|
||||
if (m_revision == 0) {
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "cancel install of package, will unregister");
|
||||
m_package->catalog()->root()->unregisterInstall(this);
|
||||
}
|
||||
|
||||
m_package->catalog()->root()->cancelDownload(this);
|
||||
}
|
||||
|
||||
struct PathAppender
|
||||
{
|
||||
PathAppender(const SGPath& p) : m_path(p) {}
|
||||
|
||||
SGPath operator()(const std::string& s) const
|
||||
{
|
||||
SGPath p(m_path);
|
||||
p.append(s);
|
||||
return p;
|
||||
}
|
||||
|
||||
SGPath m_path;
|
||||
};
|
||||
|
||||
PathList Install::thumbnailPaths() const
|
||||
{
|
||||
const string_list& thumbs(m_package->thumbnails());
|
||||
PathList result;
|
||||
if (thumbs.empty())
|
||||
return result;
|
||||
|
||||
std::transform(thumbs.begin(), thumbs.end(),
|
||||
std::back_inserter(result),
|
||||
PathAppender(m_path));
|
||||
return result;
|
||||
}
|
||||
|
||||
SGPath Install::primarySetPath() const
|
||||
{
|
||||
SGPath setPath(m_path);
|
||||
std::string ps(m_package->id());
|
||||
setPath.append(ps + "-set.xml");
|
||||
return setPath;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Install* Install::done(const Callback& cb)
|
||||
{
|
||||
if( _status == Delegate::FAIL_SUCCESS )
|
||||
if( m_status == Delegate::STATUS_SUCCESS )
|
||||
cb(this);
|
||||
else
|
||||
_cb_done.push_back(cb);
|
||||
@@ -334,8 +469,8 @@ Install* Install::done(const Callback& cb)
|
||||
//------------------------------------------------------------------------------
|
||||
Install* Install::fail(const Callback& cb)
|
||||
{
|
||||
if( _status != Delegate::FAIL_SUCCESS
|
||||
&& _status != Delegate::FAIL_IN_PROGRESS )
|
||||
if( m_status != Delegate::STATUS_SUCCESS
|
||||
&& m_status != Delegate::STATUS_IN_PROGRESS )
|
||||
cb(this);
|
||||
else
|
||||
_cb_fail.push_back(cb);
|
||||
@@ -346,7 +481,7 @@ Install* Install::fail(const Callback& cb)
|
||||
//------------------------------------------------------------------------------
|
||||
Install* Install::always(const Callback& cb)
|
||||
{
|
||||
if( _status != Delegate::FAIL_IN_PROGRESS )
|
||||
if( m_status != Delegate::STATUS_IN_PROGRESS )
|
||||
cb(this);
|
||||
else
|
||||
_cb_always.push_back(cb);
|
||||
@@ -362,13 +497,12 @@ Install* Install::progress(const ProgressCallback& cb)
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Install::installResult(Delegate::FailureCode aReason)
|
||||
void Install::installResult(Delegate::StatusCode aReason)
|
||||
{
|
||||
if (aReason == Delegate::FAIL_SUCCESS) {
|
||||
m_package->catalog()->root()->finishInstall(this);
|
||||
m_package->catalog()->root()->finishInstall(this, aReason);
|
||||
if (aReason == Delegate::STATUS_SUCCESS) {
|
||||
_cb_done(this);
|
||||
} else {
|
||||
m_package->catalog()->root()->failedInstall(this, aReason);
|
||||
_cb_fail(this);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,11 +21,13 @@
|
||||
#include <vector>
|
||||
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/misc/sg_dir.hxx>
|
||||
#include <simgear/package/Delegate.hxx>
|
||||
|
||||
#include <simgear/structure/function_list.hxx>
|
||||
#include <simgear/structure/SGReferenced.hxx>
|
||||
#include <simgear/structure/SGSharedPtr.hxx>
|
||||
#include <simgear/io/HTTPRequest.hxx>
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
@@ -73,9 +75,36 @@ public:
|
||||
bool hasUpdate() const;
|
||||
|
||||
void startUpdate();
|
||||
|
||||
void uninstall();
|
||||
|
||||
bool uninstall();
|
||||
|
||||
bool isDownloading() const;
|
||||
|
||||
bool isQueued() const;
|
||||
|
||||
int downloadedPercent() const;
|
||||
|
||||
size_t downloadedBytes() const;
|
||||
|
||||
/**
|
||||
* full path to the primary -set.xml file for this install
|
||||
*/
|
||||
SGPath primarySetPath() const;
|
||||
|
||||
/**
|
||||
* if a download is in progress, cancel it. If this is the first install
|
||||
* of the package (as opposed to an update), the install will be cleaned
|
||||
* up once the last reference is gone.
|
||||
*/
|
||||
void cancelDownload();
|
||||
|
||||
/**
|
||||
* return the thumbnails associated with this install, but as locations
|
||||
* on the file system, not URLs. It is assumed the order of thumbnails
|
||||
* is consistent with the URLs returned from Package::thumbnailUrls()
|
||||
*/
|
||||
PathList thumbnailPaths() const;
|
||||
|
||||
/**
|
||||
* Set the handler to be called when the installation successfully
|
||||
* completes.
|
||||
@@ -145,16 +174,16 @@ private:
|
||||
void parseRevision();
|
||||
void writeRevisionFile();
|
||||
|
||||
void installResult(Delegate::FailureCode aReason);
|
||||
void installResult(Delegate::StatusCode aReason);
|
||||
void installProgress(unsigned int aBytes, unsigned int aTotal);
|
||||
|
||||
PackageRef m_package;
|
||||
unsigned int m_revision; ///< revision on disk
|
||||
SGPath m_path; ///< installation point on disk
|
||||
|
||||
PackageArchiveDownloader* m_download;
|
||||
HTTP::Request_ptr m_download;
|
||||
|
||||
Delegate::FailureCode _status;
|
||||
Delegate::StatusCode m_status;
|
||||
|
||||
function_list<Callback> _cb_done,
|
||||
_cb_fail,
|
||||
|
||||
@@ -117,7 +117,7 @@ SGPath Package::pathOnDisk() const
|
||||
{
|
||||
SGPath p(m_catalog->installRoot());
|
||||
p.append("Aircraft");
|
||||
p.append(id());
|
||||
p.append(dirName());
|
||||
return p;
|
||||
}
|
||||
|
||||
@@ -139,7 +139,12 @@ InstallRef Package::install()
|
||||
|
||||
InstallRef Package::existingInstall(const InstallCallback& cb) const
|
||||
{
|
||||
InstallRef install = m_catalog->installForPackage(const_cast<Package*>(this));
|
||||
InstallRef install;
|
||||
try {
|
||||
install = m_catalog->root()->existingInstallForPackage(const_cast<Package*>(this));
|
||||
} catch (std::exception& e) {
|
||||
return InstallRef();
|
||||
}
|
||||
|
||||
if( cb )
|
||||
{
|
||||
@@ -162,13 +167,30 @@ std::string Package::qualifiedId() const
|
||||
return m_catalog->id() + "." + id();
|
||||
}
|
||||
|
||||
std::string Package::qualifiedVariantId(const unsigned int variantIndex) const
|
||||
{
|
||||
return m_catalog->id() + "." + variants()[variantIndex];
|
||||
}
|
||||
|
||||
std::string Package::md5() const
|
||||
{
|
||||
return m_props->getStringValue("md5");
|
||||
}
|
||||
|
||||
std::string Package::dirName() const
|
||||
{
|
||||
std::string r(m_props->getStringValue("dir"));
|
||||
if (r.empty())
|
||||
throw sg_exception("missing dir property on catalog package entry for " + m_id);
|
||||
return r;
|
||||
}
|
||||
|
||||
unsigned int Package::revision() const
|
||||
{
|
||||
if (!m_props) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return m_props->getIntValue("revision");
|
||||
}
|
||||
|
||||
@@ -186,6 +208,11 @@ std::string Package::description() const
|
||||
{
|
||||
return getLocalisedProp("description");
|
||||
}
|
||||
|
||||
string_set Package::tags() const
|
||||
{
|
||||
return m_tags;
|
||||
}
|
||||
|
||||
SGPropertyNode* Package::properties() const
|
||||
{
|
||||
@@ -195,15 +222,36 @@ SGPropertyNode* Package::properties() const
|
||||
string_list Package::thumbnailUrls() const
|
||||
{
|
||||
string_list r;
|
||||
if (!m_props) {
|
||||
return r;
|
||||
}
|
||||
|
||||
BOOST_FOREACH(SGPropertyNode* dl, m_props->getChildren("thumbnail")) {
|
||||
r.push_back(dl->getStringValue());
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
string_list Package::thumbnails() const
|
||||
{
|
||||
string_list r;
|
||||
if (!m_props) {
|
||||
return r;
|
||||
}
|
||||
|
||||
BOOST_FOREACH(SGPropertyNode* dl, m_props->getChildren("thumbnail-path")) {
|
||||
r.push_back(dl->getStringValue());
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
string_list Package::downloadUrls() const
|
||||
{
|
||||
string_list r;
|
||||
if (!m_props) {
|
||||
return r;
|
||||
}
|
||||
|
||||
BOOST_FOREACH(SGPropertyNode* dl, m_props->getChildren("url")) {
|
||||
r.push_back(dl->getStringValue());
|
||||
}
|
||||
@@ -238,7 +286,7 @@ PackageList Package::dependencies() const
|
||||
|
||||
// prefer local hangar package if possible, in case someone does something
|
||||
// silly with naming. Of course flightgear's aircraft search doesn't know
|
||||
// about hanagrs, so names still need to be unique.
|
||||
// about hangars, so names still need to be unique.
|
||||
PackageRef depPkg = m_catalog->getPackageById(depName);
|
||||
if (!depPkg) {
|
||||
Root* rt = m_catalog->root();
|
||||
@@ -290,6 +338,20 @@ std::string Package::nameForVariant(const std::string& vid) const
|
||||
throw sg_exception("Unknow variant +" + vid + " in package " + id());
|
||||
}
|
||||
|
||||
std::string Package::nameForVariant(const unsigned int vIndex) const
|
||||
{
|
||||
if (vIndex == 0)
|
||||
return name();
|
||||
|
||||
// offset by minus one to allow for index 0 being the primary
|
||||
SGPropertyNode_ptr var = m_props->getChild("variant", vIndex - 1);
|
||||
if (var)
|
||||
return var->getStringValue("name");
|
||||
|
||||
throw sg_exception("Unknow variant in package " + id());
|
||||
}
|
||||
|
||||
|
||||
} // of namespace pkg
|
||||
|
||||
} // of namespace simgear
|
||||
|
||||
@@ -75,6 +75,11 @@ public:
|
||||
* Fully-qualified ID, including our catalog'd ID
|
||||
*/
|
||||
std::string qualifiedId() const;
|
||||
|
||||
/**
|
||||
* Fully-qualified ID, including our catalog'd ID
|
||||
*/
|
||||
std::string qualifiedVariantId(const unsigned int variantIndex) const;
|
||||
|
||||
/**
|
||||
* human-readable name - note this is probably not localised,
|
||||
@@ -87,6 +92,8 @@ public:
|
||||
*/
|
||||
std::string nameForVariant(const std::string& vid) const;
|
||||
|
||||
std::string nameForVariant(const unsigned int vIndex) const;
|
||||
|
||||
/**
|
||||
* syntactic sugar to get the localised description
|
||||
*/
|
||||
@@ -112,7 +119,9 @@ public:
|
||||
{ return m_catalog; }
|
||||
|
||||
bool matches(const SGPropertyNode* aFilter) const;
|
||||
|
||||
|
||||
string_set tags() const;
|
||||
|
||||
/**
|
||||
* download URLs for the package
|
||||
*/
|
||||
@@ -120,16 +129,28 @@ public:
|
||||
|
||||
string_list thumbnailUrls() const;
|
||||
|
||||
/**
|
||||
* thumbnail file paths within the package on disk
|
||||
*/
|
||||
string_list thumbnails() const;
|
||||
|
||||
/**
|
||||
* Packages we depend upon.
|
||||
* If the dependency list cannot be satisifed for some reason,
|
||||
* this will raise an sg_exception.
|
||||
*/
|
||||
PackageList dependencies() const;
|
||||
|
||||
/**
|
||||
* Name of the package directory on disk. This may or may not be the
|
||||
* same as the primary ID, depending on the aircraft author
|
||||
*/
|
||||
std::string dirName() const;
|
||||
private:
|
||||
SGPath pathOnDisk() const;
|
||||
|
||||
friend class Catalog;
|
||||
friend class Root;
|
||||
|
||||
Package(const SGPropertyNode* aProps, CatalogRef aCatalog);
|
||||
|
||||
|
||||
@@ -34,45 +34,173 @@
|
||||
#include <simgear/package/Catalog.hxx>
|
||||
|
||||
namespace simgear {
|
||||
|
||||
|
||||
namespace pkg {
|
||||
|
||||
typedef std::map<std::string, CatalogRef> CatalogDict;
|
||||
|
||||
typedef std::vector<Delegate*> DelegateVec;
|
||||
typedef std::map<std::string, std::string> MemThumbnailCache;
|
||||
typedef std::deque<std::string> StringDeque;
|
||||
|
||||
class Root::ThumbnailDownloader : public HTTP::Request
|
||||
{
|
||||
public:
|
||||
ThumbnailDownloader(Root::RootPrivate* aOwner, const std::string& aUrl) :
|
||||
HTTP::Request(aUrl),
|
||||
m_owner(aOwner)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void gotBodyData(const char* s, int n)
|
||||
{
|
||||
m_buffer += std::string(s, n);
|
||||
}
|
||||
|
||||
virtual void onDone();
|
||||
|
||||
private:
|
||||
Root::RootPrivate* m_owner;
|
||||
std::string m_buffer;
|
||||
};
|
||||
|
||||
class Root::RootPrivate
|
||||
{
|
||||
public:
|
||||
RootPrivate() :
|
||||
http(NULL),
|
||||
maxAgeSeconds(60 * 60 * 24),
|
||||
delegate(NULL)
|
||||
maxAgeSeconds(60 * 60 * 24)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
void fireStartInstall(InstallRef install)
|
||||
{
|
||||
DelegateVec::const_iterator it;
|
||||
for (it = delegates.begin(); it != delegates.end(); ++it) {
|
||||
(*it)->startInstall(install);
|
||||
}
|
||||
}
|
||||
|
||||
void fireInstallProgress(InstallRef install,
|
||||
unsigned int aBytes, unsigned int aTotal)
|
||||
{
|
||||
DelegateVec::const_iterator it;
|
||||
for (it = delegates.begin(); it != delegates.end(); ++it) {
|
||||
(*it)->installProgress(install, aBytes, aTotal);
|
||||
}
|
||||
}
|
||||
|
||||
void fireFinishInstall(InstallRef install, Delegate::StatusCode status)
|
||||
{
|
||||
DelegateVec::const_iterator it;
|
||||
for (it = delegates.begin(); it != delegates.end(); ++it) {
|
||||
(*it)->finishInstall(install, status);
|
||||
}
|
||||
}
|
||||
|
||||
void fireRefreshStatus(CatalogRef catalog, Delegate::StatusCode status)
|
||||
{
|
||||
DelegateVec::const_iterator it;
|
||||
for (it = delegates.begin(); it != delegates.end(); ++it) {
|
||||
(*it)->catalogRefreshed(catalog, status);
|
||||
}
|
||||
}
|
||||
|
||||
void firePackagesChanged()
|
||||
{
|
||||
DelegateVec::const_iterator it;
|
||||
for (it = delegates.begin(); it != delegates.end(); ++it) {
|
||||
(*it)->availablePackagesChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void thumbnailDownloadComplete(HTTP::Request_ptr request,
|
||||
Delegate::StatusCode status, const std::string& bytes)
|
||||
{
|
||||
std::string u(request->url());
|
||||
if (status == Delegate::STATUS_SUCCESS) {
|
||||
thumbnailCache[u] = bytes;
|
||||
fireDataForThumbnail(u, bytes);
|
||||
}
|
||||
|
||||
downloadNextPendingThumbnail();
|
||||
}
|
||||
|
||||
void fireDataForThumbnail(const std::string& aUrl, const std::string& bytes)
|
||||
{
|
||||
DelegateVec::const_iterator it;
|
||||
const uint8_t* data = reinterpret_cast<const uint8_t*>(bytes.data());
|
||||
for (it = delegates.begin(); it != delegates.end(); ++it) {
|
||||
(*it)->dataForThumbnail(aUrl, bytes.size(), data);
|
||||
}
|
||||
}
|
||||
|
||||
void downloadNextPendingThumbnail()
|
||||
{
|
||||
thumbnailDownloadRequest.clear();
|
||||
if (pendingThumbnails.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string u = pendingThumbnails.front();
|
||||
pendingThumbnails.pop_front();
|
||||
thumbnailDownloadRequest = new Root::ThumbnailDownloader(this, u);
|
||||
|
||||
if (http) {
|
||||
http->makeRequest(thumbnailDownloadRequest);
|
||||
} else {
|
||||
httpPendingRequests.push_back(thumbnailDownloadRequest);
|
||||
}
|
||||
}
|
||||
|
||||
DelegateVec delegates;
|
||||
|
||||
SGPath path;
|
||||
std::string locale;
|
||||
HTTP::Client* http;
|
||||
CatalogDict catalogs;
|
||||
CatalogList disabledCatalogs;
|
||||
unsigned int maxAgeSeconds;
|
||||
Delegate* delegate;
|
||||
std::string version;
|
||||
|
||||
|
||||
std::set<CatalogRef> refreshing;
|
||||
std::deque<InstallRef> updateDeque;
|
||||
typedef std::deque<InstallRef> UpdateDeque;
|
||||
UpdateDeque updateDeque;
|
||||
std::deque<HTTP::Request_ptr> httpPendingRequests;
|
||||
|
||||
HTTP::Request_ptr thumbnailDownloadRequest;
|
||||
StringDeque pendingThumbnails;
|
||||
MemThumbnailCache thumbnailCache;
|
||||
|
||||
typedef std::map<PackageRef, InstallRef> InstallCache;
|
||||
InstallCache m_installs;
|
||||
};
|
||||
|
||||
|
||||
|
||||
void Root::ThumbnailDownloader::onDone()
|
||||
{
|
||||
if (responseCode() != 200) {
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "thumbnail download failure:" << url());
|
||||
m_owner->thumbnailDownloadComplete(this, Delegate::FAIL_DOWNLOAD, std::string());
|
||||
return;
|
||||
}
|
||||
|
||||
m_owner->thumbnailDownloadComplete(this, Delegate::STATUS_SUCCESS, m_buffer);
|
||||
//time(&m_owner->m_retrievedTime);
|
||||
//m_owner->writeTimestamp();
|
||||
//m_owner->refreshComplete(Delegate::STATUS_REFRESHED);
|
||||
}
|
||||
|
||||
SGPath Root::path() const
|
||||
{
|
||||
return d->path;
|
||||
}
|
||||
|
||||
|
||||
void Root::setMaxAgeSeconds(unsigned int seconds)
|
||||
{
|
||||
d->maxAgeSeconds = seconds;
|
||||
}
|
||||
|
||||
|
||||
unsigned int Root::maxAgeSeconds() const
|
||||
{
|
||||
return d->maxAgeSeconds;
|
||||
@@ -94,10 +222,10 @@ void Root::makeHTTPRequest(HTTP::Request *req)
|
||||
d->http->makeRequest(req);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
d->httpPendingRequests.push_back(req);
|
||||
}
|
||||
|
||||
|
||||
Root::Root(const SGPath& aPath, const std::string& aVersion) :
|
||||
d(new RootPrivate)
|
||||
{
|
||||
@@ -106,45 +234,56 @@ Root::Root(const SGPath& aPath, const std::string& aVersion) :
|
||||
if (getenv("LOCALE")) {
|
||||
d->locale = getenv("LOCALE");
|
||||
}
|
||||
|
||||
|
||||
Dir dir(aPath);
|
||||
if (!dir.exists()) {
|
||||
dir.create(0755);
|
||||
return;
|
||||
}
|
||||
|
||||
BOOST_FOREACH(SGPath c, dir.children(Dir::TYPE_DIR)) {
|
||||
|
||||
BOOST_FOREACH(SGPath c, dir.children(Dir::TYPE_DIR | Dir::NO_DOT_OR_DOTDOT)) {
|
||||
CatalogRef cat = Catalog::createFromPath(this, c);
|
||||
if (cat) {
|
||||
d->catalogs[cat->id()] = cat;
|
||||
if (cat->status() == Delegate::STATUS_SUCCESS) {
|
||||
d->catalogs[cat->id()] = cat;
|
||||
} else {
|
||||
// catalog has problems, such as needing an update
|
||||
// keep it out of the main collection for now
|
||||
d->disabledCatalogs.push_back(cat);
|
||||
}
|
||||
}
|
||||
} // of child directories iteration
|
||||
}
|
||||
|
||||
Root::~Root()
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
std::string Root::catalogVersion() const
|
||||
|
||||
int Root::catalogVersion() const
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
|
||||
std::string Root::applicationVersion() const
|
||||
{
|
||||
return d->version;
|
||||
}
|
||||
|
||||
|
||||
CatalogRef Root::getCatalogById(const std::string& aId) const
|
||||
{
|
||||
CatalogDict::const_iterator it = d->catalogs.find(aId);
|
||||
if (it == d->catalogs.end()) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
PackageRef Root::getPackageById(const std::string& aName) const
|
||||
{
|
||||
size_t lastDot = aName.rfind('.');
|
||||
|
||||
|
||||
PackageRef pkg = NULL;
|
||||
if (lastDot == std::string::npos) {
|
||||
// naked package ID
|
||||
@@ -155,17 +294,17 @@ PackageRef Root::getPackageById(const std::string& aName) const
|
||||
return pkg;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
std::string catalogId = aName.substr(0, lastDot);
|
||||
std::string id = aName.substr(lastDot + 1);
|
||||
std::string id = aName.substr(lastDot + 1);
|
||||
CatalogRef catalog = getCatalogById(catalogId);
|
||||
if (!catalog) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
return catalog->getPackageById(id);
|
||||
}
|
||||
|
||||
@@ -176,7 +315,21 @@ CatalogList Root::catalogs() const
|
||||
for (; it != d->catalogs.end(); ++it) {
|
||||
r.push_back(it->second);
|
||||
}
|
||||
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
PackageList
|
||||
Root::allPackages() const
|
||||
{
|
||||
PackageList r;
|
||||
|
||||
CatalogDict::const_iterator it = d->catalogs.begin();
|
||||
for (; it != d->catalogs.end(); ++it) {
|
||||
const PackageList& r2(it->second->packages());
|
||||
r.insert(r.end(), r2.begin(), r2.end());
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -184,13 +337,13 @@ PackageList
|
||||
Root::packagesMatching(const SGPropertyNode* aFilter) const
|
||||
{
|
||||
PackageList r;
|
||||
|
||||
|
||||
CatalogDict::const_iterator it = d->catalogs.begin();
|
||||
for (; it != d->catalogs.end(); ++it) {
|
||||
PackageList r2(it->second->packagesMatching(aFilter));
|
||||
r.insert(r.end(), r2.begin(), r2.end());
|
||||
}
|
||||
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -198,31 +351,59 @@ PackageList
|
||||
Root::packagesNeedingUpdate() const
|
||||
{
|
||||
PackageList r;
|
||||
|
||||
|
||||
CatalogDict::const_iterator it = d->catalogs.begin();
|
||||
for (; it != d->catalogs.end(); ++it) {
|
||||
PackageList r2(it->second->packagesNeedingUpdate());
|
||||
r.insert(r.end(), r2.begin(), r2.end());
|
||||
}
|
||||
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void Root::refresh(bool aForce)
|
||||
{
|
||||
bool didStartAny = false;
|
||||
|
||||
// copy all candidate ctalogs to a seperate list, since refreshing
|
||||
// can modify both the main collection and/or the disabled list
|
||||
CatalogList toRefresh;
|
||||
CatalogDict::iterator it = d->catalogs.begin();
|
||||
for (; it != d->catalogs.end(); ++it) {
|
||||
if (aForce || it->second->needsRefresh()) {
|
||||
it->second->refresh();
|
||||
}
|
||||
toRefresh.push_back(it->second);
|
||||
}
|
||||
|
||||
toRefresh.insert(toRefresh.end(), d->disabledCatalogs.begin(),
|
||||
d->disabledCatalogs.end());
|
||||
|
||||
|
||||
CatalogList::iterator j = toRefresh.begin();
|
||||
for (; j != toRefresh.end(); ++j) {
|
||||
(*j)->refresh();
|
||||
didStartAny = true;
|
||||
}
|
||||
|
||||
if (!didStartAny) {
|
||||
// signal refresh complete to the delegate already
|
||||
d->fireRefreshStatus(CatalogRef(), Delegate::STATUS_REFRESHED);
|
||||
}
|
||||
}
|
||||
|
||||
void Root::setDelegate(simgear::pkg::Delegate *aDelegate)
|
||||
void Root::addDelegate(simgear::pkg::Delegate *aDelegate)
|
||||
{
|
||||
d->delegate = aDelegate;
|
||||
d->delegates.push_back(aDelegate);
|
||||
}
|
||||
|
||||
|
||||
void Root::removeDelegate(simgear::pkg::Delegate *aDelegate)
|
||||
{
|
||||
DelegateVec::iterator it = std::find(d->delegates.begin(),
|
||||
d->delegates.end(), aDelegate);
|
||||
if (it == d->delegates.end()) {
|
||||
throw sg_exception("unknown delegate in removeDelegate");
|
||||
}
|
||||
d->delegates.erase(it);
|
||||
}
|
||||
|
||||
void Root::setLocale(const std::string& aLocale)
|
||||
{
|
||||
d->locale = aLocale;
|
||||
@@ -238,7 +419,7 @@ void Root::scheduleToUpdate(InstallRef aInstall)
|
||||
if (!aInstall) {
|
||||
throw sg_exception("missing argument to scheduleToUpdate");
|
||||
}
|
||||
|
||||
|
||||
PackageList deps = aInstall->package()->dependencies();
|
||||
BOOST_FOREACH(Package* dep, deps) {
|
||||
// will internally schedule for update if required
|
||||
@@ -248,24 +429,27 @@ void Root::scheduleToUpdate(InstallRef aInstall)
|
||||
|
||||
bool wasEmpty = d->updateDeque.empty();
|
||||
d->updateDeque.push_back(aInstall);
|
||||
|
||||
|
||||
if (wasEmpty) {
|
||||
aInstall->startUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
bool Root::isInstallQueued(InstallRef aInstall) const
|
||||
{
|
||||
RootPrivate::UpdateDeque::const_iterator it =
|
||||
std::find(d->updateDeque.begin(), d->updateDeque.end(), aInstall);
|
||||
return (it != d->updateDeque.end());
|
||||
}
|
||||
|
||||
void Root::startInstall(InstallRef aInstall)
|
||||
{
|
||||
if (d->delegate) {
|
||||
d->delegate->startInstall(aInstall.ptr());
|
||||
}
|
||||
d->fireStartInstall(aInstall);
|
||||
}
|
||||
|
||||
void Root::installProgress(InstallRef aInstall, unsigned int aBytes, unsigned int aTotal)
|
||||
{
|
||||
if (d->delegate) {
|
||||
d->delegate->installProgress(aInstall.ptr(), aBytes, aTotal);
|
||||
}
|
||||
d->fireInstallProgress(aInstall, aBytes, aTotal);
|
||||
}
|
||||
|
||||
void Root::startNext(InstallRef aCurrent)
|
||||
@@ -275,65 +459,182 @@ void Root::startNext(InstallRef aCurrent)
|
||||
} else {
|
||||
d->updateDeque.pop_front();
|
||||
}
|
||||
|
||||
|
||||
if (!d->updateDeque.empty()) {
|
||||
d->updateDeque.front()->startUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void Root::finishInstall(InstallRef aInstall)
|
||||
void Root::finishInstall(InstallRef aInstall, Delegate::StatusCode aReason)
|
||||
{
|
||||
if (d->delegate) {
|
||||
d->delegate->finishInstall(aInstall.ptr());
|
||||
if (aReason != Delegate::STATUS_SUCCESS) {
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "failed to install package:"
|
||||
<< aInstall->package()->id() << ":" << aReason);
|
||||
}
|
||||
|
||||
|
||||
// order matters here, so a call to 'isQueued' from a finish-install
|
||||
// callback returns false, not true
|
||||
startNext(aInstall);
|
||||
d->fireFinishInstall(aInstall, aReason);
|
||||
}
|
||||
|
||||
void Root::failedInstall(InstallRef aInstall, Delegate::FailureCode aReason)
|
||||
void Root::cancelDownload(InstallRef aInstall)
|
||||
{
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "failed to install package:"
|
||||
<< aInstall->package()->id() << ":" << aReason);
|
||||
if (d->delegate) {
|
||||
d->delegate->failedInstall(aInstall.ptr(), aReason);
|
||||
}
|
||||
|
||||
startNext(aInstall);
|
||||
RootPrivate::UpdateDeque::iterator it =
|
||||
std::find(d->updateDeque.begin(), d->updateDeque.end(), aInstall);
|
||||
if (it != d->updateDeque.end()) {
|
||||
bool startNext = (aInstall == d->updateDeque.front());
|
||||
d->updateDeque.erase(it);
|
||||
if (startNext) {
|
||||
if (!d->updateDeque.empty()) {
|
||||
d->updateDeque.front()->startUpdate();
|
||||
}
|
||||
} // of install was front item
|
||||
} // of found install in queue
|
||||
}
|
||||
|
||||
void Root::catalogRefreshBegin(CatalogRef aCat)
|
||||
{
|
||||
d->refreshing.insert(aCat);
|
||||
}
|
||||
|
||||
void Root::catalogRefreshComplete(CatalogRef aCat, Delegate::FailureCode aReason)
|
||||
void Root::catalogRefreshStatus(CatalogRef aCat, Delegate::StatusCode aReason)
|
||||
{
|
||||
CatalogDict::iterator catIt = d->catalogs.find(aCat->id());
|
||||
if (aReason != Delegate::FAIL_SUCCESS) {
|
||||
if (d->delegate) {
|
||||
d->delegate->failedRefresh(aCat, aReason);
|
||||
d->fireRefreshStatus(aCat, aReason);
|
||||
|
||||
if (aReason == Delegate::STATUS_IN_PROGRESS) {
|
||||
d->refreshing.insert(aCat);
|
||||
} else {
|
||||
d->refreshing.erase(aCat);
|
||||
}
|
||||
|
||||
if ((aReason == Delegate::STATUS_REFRESHED) && (catIt == d->catalogs.end())) {
|
||||
assert(!aCat->id().empty());
|
||||
d->catalogs.insert(catIt, CatalogDict::value_type(aCat->id(), aCat));
|
||||
|
||||
// catalog might have been previously disabled, let's remove in that case
|
||||
CatalogList::iterator j = std::find(d->disabledCatalogs.begin(),
|
||||
d->disabledCatalogs.end(),
|
||||
aCat);
|
||||
if (j != d->disabledCatalogs.end()) {
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "re-enabling disabled catalog:" << aCat->id());
|
||||
d->disabledCatalogs.erase(j);
|
||||
}
|
||||
|
||||
// if the failure is permanent, delete the catalog from our
|
||||
// list (don't touch it on disk)
|
||||
bool isPermanentFailure = (aReason == Delegate::FAIL_VERSION);
|
||||
if (isPermanentFailure) {
|
||||
SG_LOG(SG_GENERAL, SG_WARN, "permanent failure for catalog:" << aCat->id());
|
||||
if (catIt != d->catalogs.end()) {
|
||||
d->catalogs.erase(catIt);
|
||||
}
|
||||
|
||||
if ((aReason != Delegate::STATUS_REFRESHED) &&
|
||||
(aReason != Delegate::STATUS_IN_PROGRESS) &&
|
||||
(aReason != Delegate::STATUS_SUCCESS))
|
||||
{
|
||||
// catalog has errors, disable it
|
||||
CatalogList::iterator j = std::find(d->disabledCatalogs.begin(),
|
||||
d->disabledCatalogs.end(),
|
||||
aCat);
|
||||
if (j == d->disabledCatalogs.end()) {
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "disabling catalog:" << aCat->id());
|
||||
d->disabledCatalogs.push_back(aCat);
|
||||
}
|
||||
|
||||
// and remove it from the active collection
|
||||
if (catIt != d->catalogs.end()) {
|
||||
d->catalogs.erase(catIt);
|
||||
}
|
||||
} // of catalog has errors case
|
||||
|
||||
if (d->refreshing.empty()) {
|
||||
d->fireRefreshStatus(CatalogRef(), Delegate::STATUS_REFRESHED);
|
||||
d->firePackagesChanged();
|
||||
}
|
||||
}
|
||||
|
||||
bool Root::removeCatalogById(const std::string& aId)
|
||||
{
|
||||
CatalogRef cat;
|
||||
|
||||
CatalogDict::iterator catIt = d->catalogs.find(aId);
|
||||
if (catIt == d->catalogs.end()) {
|
||||
// check the disabled list
|
||||
CatalogList::iterator j = d->disabledCatalogs.begin();
|
||||
for (; j != d->disabledCatalogs.end(); ++j) {
|
||||
if ((*j)->id() == aId) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (catIt == d->catalogs.end()) {
|
||||
// first fresh, add to our storage now
|
||||
d->catalogs.insert(catIt, CatalogDict::value_type(aCat->id(), aCat));
|
||||
}
|
||||
|
||||
d->refreshing.erase(aCat);
|
||||
if (d->refreshing.empty()) {
|
||||
if (d->delegate) {
|
||||
d->delegate->refreshComplete();
|
||||
|
||||
if (j == d->disabledCatalogs.end()) {
|
||||
SG_LOG(SG_GENERAL, SG_WARN, "removeCatalogById: no catalog with id:" << aId);
|
||||
return false;
|
||||
}
|
||||
|
||||
cat = *j;
|
||||
d->disabledCatalogs.erase(j);
|
||||
} else {
|
||||
cat = catIt->second;
|
||||
// drop the reference
|
||||
d->catalogs.erase(catIt);
|
||||
}
|
||||
|
||||
bool ok = cat->uninstall();
|
||||
if (!ok) {
|
||||
SG_LOG(SG_GENERAL, SG_WARN, "removeCatalogById: catalog :" << aId
|
||||
<< "failed to uninstall");
|
||||
}
|
||||
|
||||
// notify that a catalog is being removed
|
||||
d->firePackagesChanged();
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
void Root::requestThumbnailData(const std::string& aUrl)
|
||||
{
|
||||
MemThumbnailCache::iterator it = d->thumbnailCache.find(aUrl);
|
||||
if (it == d->thumbnailCache.end()) {
|
||||
// insert into cache to mark as pending
|
||||
d->pendingThumbnails.push_front(aUrl);
|
||||
d->thumbnailCache[aUrl] = std::string();
|
||||
d->downloadNextPendingThumbnail();
|
||||
} else if (!it->second.empty()) {
|
||||
// already loaded, fire data synchronously
|
||||
d->fireDataForThumbnail(aUrl, it->second);
|
||||
} else {
|
||||
// in cache but empty data, still fetching
|
||||
}
|
||||
}
|
||||
|
||||
InstallRef Root::existingInstallForPackage(PackageRef p) const
|
||||
{
|
||||
RootPrivate::InstallCache::const_iterator it =
|
||||
d->m_installs.find(p);
|
||||
if (it == d->m_installs.end()) {
|
||||
// check if it exists on disk, create
|
||||
SGPath path(p->pathOnDisk());
|
||||
if (path.exists()) {
|
||||
// this will add to our cache, and hence, modify m_installs
|
||||
return Install::createFromPath(path, p->catalog());
|
||||
}
|
||||
|
||||
// insert a null reference into the dictionary, so we don't call
|
||||
// the pathOnDisk -> exists codepath repeatedley
|
||||
d->m_installs[p] = InstallRef();
|
||||
return InstallRef();
|
||||
}
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void Root::registerInstall(InstallRef ins)
|
||||
{
|
||||
if (!ins.valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
d->m_installs[ins->package()] = ins;
|
||||
}
|
||||
|
||||
void Root::unregisterInstall(InstallRef ins)
|
||||
{
|
||||
if (!ins .valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
d->m_installs.erase(ins->package());
|
||||
}
|
||||
|
||||
} // of namespace pkg
|
||||
|
||||
@@ -62,8 +62,10 @@ public:
|
||||
|
||||
void setLocale(const std::string& aLocale);
|
||||
|
||||
void setDelegate(Delegate* aDelegate);
|
||||
|
||||
void addDelegate(Delegate* aDelegate);
|
||||
|
||||
void removeDelegate(Delegate* aDelegate);
|
||||
|
||||
std::string getLocale() const;
|
||||
|
||||
CatalogList catalogs() const;
|
||||
@@ -81,17 +83,28 @@ public:
|
||||
void makeHTTPRequest(HTTP::Request* req);
|
||||
|
||||
/**
|
||||
* the version string of the root. Catalogs must match this version,
|
||||
* The catalog XML/property version in use. This is used to make incomaptible
|
||||
* changes to the package/catalog syntax
|
||||
*/
|
||||
int catalogVersion() const;
|
||||
|
||||
/**
|
||||
* the version string of the application. Catalogs must match this version,
|
||||
* or they will be ignored / rejected.
|
||||
*/
|
||||
std::string catalogVersion() const;
|
||||
|
||||
std::string applicationVersion() const;
|
||||
|
||||
/**
|
||||
* refresh catalogs which are more than the maximum age (24 hours by default)
|
||||
* set force to true, to download all catalogs regardless of age.
|
||||
*/
|
||||
void refresh(bool aForce = false);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
PackageList allPackages() const;
|
||||
|
||||
/**
|
||||
* retrieve packages matching a filter.
|
||||
* filter consists of required / minimum values, AND-ed together.
|
||||
@@ -109,21 +122,39 @@ public:
|
||||
CatalogRef getCatalogById(const std::string& aId) const;
|
||||
|
||||
void scheduleToUpdate(InstallRef aInstall);
|
||||
|
||||
/**
|
||||
* remove a catalog. Will uninstall all packages originating
|
||||
* from the catalog too.
|
||||
*/
|
||||
bool removeCatalogById(const std::string& aId);
|
||||
|
||||
/**
|
||||
* request thumbnail data from the cache / network
|
||||
*/
|
||||
void requestThumbnailData(const std::string& aUrl);
|
||||
|
||||
bool isInstallQueued(InstallRef aInstall) const;
|
||||
private:
|
||||
friend class Install;
|
||||
friend class Catalog;
|
||||
friend class Package;
|
||||
|
||||
|
||||
void catalogRefreshBegin(CatalogRef aCat);
|
||||
void catalogRefreshComplete(CatalogRef aCat, Delegate::FailureCode aReason);
|
||||
InstallRef existingInstallForPackage(PackageRef p) const;
|
||||
|
||||
void catalogRefreshStatus(CatalogRef aCat, Delegate::StatusCode aReason);
|
||||
|
||||
void startNext(InstallRef aCurrent);
|
||||
|
||||
void startInstall(InstallRef aInstall);
|
||||
void installProgress(InstallRef aInstall, unsigned int aBytes, unsigned int aTotal);
|
||||
void finishInstall(InstallRef aInstall);
|
||||
void failedInstall(InstallRef aInstall, Delegate::FailureCode aReason);
|
||||
|
||||
void finishInstall(InstallRef aInstall, Delegate::StatusCode aReason);
|
||||
void cancelDownload(InstallRef aInstall);
|
||||
|
||||
void registerInstall(InstallRef ins);
|
||||
void unregisterInstall(InstallRef ins);
|
||||
|
||||
class ThumbnailDownloader;
|
||||
class RootPrivate;
|
||||
std::auto_ptr<RootPrivate> d;
|
||||
};
|
||||
|
||||
64
simgear/package/catalogTest1/catalog.xml
Normal file
64
simgear/package/catalogTest1/catalog.xml
Normal file
@@ -0,0 +1,64 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<PropertyList>
|
||||
<id>org.flightgear.test.catalog1</id>
|
||||
<description>First test catalog</description>
|
||||
<url>http://download.flightgear.org/catalog1/catalog.xml</url>
|
||||
<version>8.1.*</version>
|
||||
<version>8.0.0</version>
|
||||
<version>8.2.0</version>
|
||||
|
||||
<package>
|
||||
<id>alpha</id>
|
||||
<name>Alpha package</name>
|
||||
<revision type="int">8</revision>
|
||||
<file-size-bytes type="int">1234567</file-size-bytes>
|
||||
</package>
|
||||
|
||||
<package>
|
||||
<id>c172p</id>
|
||||
<name>Cessna 172-P</name>
|
||||
<description>A plane made by Cessna</description>
|
||||
<revision type="int">42</revision>
|
||||
<file-size-bytes type="int">34567</file-size-bytes>
|
||||
|
||||
<tag>cessna</tag>
|
||||
<tag>ga</tag>
|
||||
<tag>piston</tag>
|
||||
<tag>ifr</tag>
|
||||
|
||||
<rating>
|
||||
<FDM type="int">3</FDM>
|
||||
<systems type="int">4</systems>
|
||||
<model type="int">5</model>
|
||||
<cockpit type="int">4</cockpit>
|
||||
</rating>
|
||||
|
||||
<!-- local dependency -->
|
||||
<depends>
|
||||
<id>org.flightgear.test.catalog1.common-sounds</id>
|
||||
<revision>10</revision>
|
||||
</depends>
|
||||
|
||||
<variant>
|
||||
<id>c172p-2d-panel</id>
|
||||
<name>C172 with 2d panel only</name>
|
||||
</variant>
|
||||
|
||||
<variant>
|
||||
<id>c172p-floats</id>
|
||||
<name>C172 with floats</name>
|
||||
</variant>
|
||||
|
||||
<variant>
|
||||
<id>c172p-skis</id>
|
||||
<name>C172 with skis</name>
|
||||
</variant>
|
||||
</package>
|
||||
|
||||
<package>
|
||||
<id>common-sounds</id>
|
||||
<name>Common sound files for test catalog aircraft</name>
|
||||
<revision>10</revision>
|
||||
</package>
|
||||
</PropertyList>
|
||||
@@ -158,7 +158,8 @@ SG_MD5Transform(u_int32_t state[4], const u_int8_t block[MD5_BLOCK_LENGTH])
|
||||
{
|
||||
u_int32_t a, b, c, d, in[MD5_BLOCK_LENGTH / 4];
|
||||
|
||||
#ifndef WORDS_BIGENDIAN
|
||||
#if ((defined(__BYTE_ORDER__) && __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__) || \
|
||||
defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM) )
|
||||
memcpy(in, block, sizeof(in));
|
||||
#else
|
||||
for (a = 0; a < MD5_BLOCK_LENGTH / 4; a++) {
|
||||
@@ -247,4 +248,4 @@ SG_MD5Transform(u_int32_t state[4], const u_int8_t block[MD5_BLOCK_LENGTH])
|
||||
state[1] += b;
|
||||
state[2] += c;
|
||||
state[3] += d;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
|
||||
using namespace simgear;
|
||||
using namespace simgear;
|
||||
using namespace std;
|
||||
|
||||
bool keepRunning = true;
|
||||
@@ -34,44 +34,50 @@ bool keepRunning = true;
|
||||
class MyDelegate : public pkg::Delegate
|
||||
{
|
||||
public:
|
||||
virtual void refreshComplete()
|
||||
virtual void catalogRefreshed(pkg::CatalogRef aCatalog, StatusCode aReason)
|
||||
{
|
||||
if (aReason == STATUS_REFRESHED) {
|
||||
if (aCatalog.ptr() == NULL) {
|
||||
cout << "refreshed all catalogs" << endl;
|
||||
} else {
|
||||
cout << "refreshed catalog " << aCatalog->url() << endl;
|
||||
}
|
||||
} else if (aReason == STATUS_IN_PROGRESS) {
|
||||
cout << "started refresh of " << aCatalog->url() << endl;
|
||||
} else {
|
||||
cerr << "failed refresh of " << aCatalog->url() << ":" << aReason << endl;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void failedRefresh(pkg::Catalog* aCatalog, FailureCode aReason)
|
||||
{
|
||||
cerr << "failed refresh of " << aCatalog->description() << ":" << aReason << endl;
|
||||
}
|
||||
|
||||
virtual void startInstall(pkg::Install* aInstall)
|
||||
|
||||
virtual void startInstall(pkg::InstallRef aInstall)
|
||||
{
|
||||
_lastPercent = 999;
|
||||
cout << "starting install of " << aInstall->package()->name() << endl;
|
||||
}
|
||||
|
||||
virtual void installProgress(pkg::Install* aInstall, unsigned int bytes, unsigned int total)
|
||||
|
||||
virtual void installProgress(pkg::InstallRef aInstall, unsigned int bytes, unsigned int total)
|
||||
{
|
||||
unsigned int percent = (bytes * 100) / total;
|
||||
if (percent == _lastPercent) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
_lastPercent = percent;
|
||||
cout << percent << "%" << endl;
|
||||
}
|
||||
|
||||
virtual void finishInstall(pkg::Install* aInstall)
|
||||
|
||||
virtual void finishInstall(pkg::InstallRef aInstall, StatusCode aReason)
|
||||
{
|
||||
cout << "done install of " << aInstall->package()->name() << endl;
|
||||
if (aReason == STATUS_SUCCESS) {
|
||||
cout << "done install of " << aInstall->package()->name() << endl;
|
||||
} else {
|
||||
cerr << "failed install of " << aInstall->package()->name() << endl;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void failedInstall(pkg::Install* aInstall, FailureCode aReason)
|
||||
{
|
||||
cerr << "failed install of " << aInstall->package()->name() << endl;
|
||||
}
|
||||
private:
|
||||
unsigned int _lastPercent;
|
||||
|
||||
|
||||
};
|
||||
|
||||
void printRating(pkg::Package* pkg, const std::string& aRating, const std::string& aLabel)
|
||||
@@ -87,24 +93,24 @@ void printPackageInfo(pkg::Package* pkg)
|
||||
cout << "Name:" << pkg->name() << endl;
|
||||
cout << "Description:" << pkg->description() << endl;
|
||||
cout << "Long description:\n" << pkg->getLocalisedProp("long-description") << endl << endl;
|
||||
|
||||
|
||||
if (pkg->properties()->hasChild("author")) {
|
||||
cout << "Authors:" << endl;
|
||||
BOOST_FOREACH(SGPropertyNode* author, pkg->properties()->getChildren("author")) {
|
||||
if (author->hasChild("name")) {
|
||||
cout << "\t" << author->getStringValue("name") << endl;
|
||||
|
||||
|
||||
} else {
|
||||
// simple author structure
|
||||
cout << "\t" << author->getStringValue() << endl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
cout << endl;
|
||||
}
|
||||
|
||||
|
||||
cout << "Ratings:" << endl;
|
||||
printRating(pkg, "fdm", "Flight-model ");
|
||||
printRating(pkg, "cockpit", "Cockpit ");
|
||||
@@ -117,15 +123,15 @@ int main(int argc, char** argv)
|
||||
|
||||
HTTP::Client* http = new HTTP::Client();
|
||||
pkg::Root* root = new pkg::Root(Dir::current().path(), "");
|
||||
|
||||
|
||||
MyDelegate dlg;
|
||||
root->setDelegate(&dlg);
|
||||
|
||||
root->addDelegate(&dlg);
|
||||
|
||||
cout << "Package root is:" << Dir::current().path() << endl;
|
||||
cout << "have " << pkg::Catalog::allCatalogs().size() << " catalog(s)" << endl;
|
||||
|
||||
cout << "have " << root->catalogs().size() << " catalog(s)" << endl;
|
||||
|
||||
root->setHTTPClient(http);
|
||||
|
||||
|
||||
if (!strcmp(argv[1], "add")) {
|
||||
std::string url(argv[2]);
|
||||
pkg::Catalog::createFromUrl(root, url);
|
||||
@@ -137,12 +143,12 @@ int main(int argc, char** argv)
|
||||
cerr << "unknown package:" << argv[2] << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
if (pkg->isInstalled()) {
|
||||
cout << "package " << pkg->id() << " is already installed at " << pkg->install()->path() << endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
pkg::CatalogRef catalog = pkg->catalog();
|
||||
cout << "Will install:" << pkg->id() << " from " << catalog->id() <<
|
||||
"(" << catalog->description() << ")" << endl;
|
||||
@@ -153,12 +159,12 @@ int main(int argc, char** argv)
|
||||
cerr << "unknown package:" << argv[2] << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
if (!pkg->isInstalled()) {
|
||||
cerr << "package " << argv[2] << " not installed" << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
cout << "Will uninstall:" << pkg->id() << endl;
|
||||
pkg->install()->uninstall();
|
||||
} else if (!strcmp(argv[1], "update-all")) {
|
||||
@@ -172,7 +178,7 @@ int main(int argc, char** argv)
|
||||
cout << "no packages with updates" << endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
cout << updates.size() << " packages have updates" << endl;
|
||||
BOOST_FOREACH(pkg::Package* p, updates) {
|
||||
cout << "\t" << p->id() << " " << p->getLocalisedProp("name") << endl;
|
||||
@@ -183,13 +189,13 @@ int main(int argc, char** argv)
|
||||
cerr << "unknown package:" << argv[2] << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
printPackageInfo(pkg);
|
||||
} else {
|
||||
cerr << "unknown command:" << argv[1] << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
while (http->hasActiveRequests()) {
|
||||
http->update();
|
||||
}
|
||||
|
||||
@@ -144,9 +144,9 @@ public:
|
||||
|
||||
#undef SG_DEF_ASSIGN_OP
|
||||
|
||||
SGPropertyNode* node() const
|
||||
SGPropertyNode* node(bool aCreate = false) const
|
||||
{
|
||||
return PropertyObjectBase::node(false);
|
||||
return PropertyObjectBase::node(aCreate);
|
||||
}
|
||||
}; // of template PropertyObject
|
||||
|
||||
@@ -237,9 +237,9 @@ public:
|
||||
return (s == value);
|
||||
}
|
||||
|
||||
SGPropertyNode* node() const
|
||||
SGPropertyNode* node(bool aCreate = false) const
|
||||
{
|
||||
return PropertyObjectBase::node(false);
|
||||
return PropertyObjectBase::node(aCreate);
|
||||
}
|
||||
private:
|
||||
};
|
||||
|
||||
@@ -11,8 +11,6 @@
|
||||
#endif
|
||||
|
||||
#include "props.hxx"
|
||||
#include "PropertyInterpolationMgr.hxx"
|
||||
#include "vectorPropTemplates.hxx"
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
@@ -24,31 +22,30 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <boost/algorithm/string/find_iterator.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/algorithm/string/classification.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/functional/hash.hpp>
|
||||
#include <boost/range.hpp>
|
||||
|
||||
#if PROPS_STANDALONE
|
||||
#include <iostream>
|
||||
# include <iostream>
|
||||
using std::cerr;
|
||||
#else
|
||||
# include <boost/algorithm/string/find_iterator.hpp>
|
||||
# include <boost/algorithm/string/predicate.hpp>
|
||||
# include <boost/algorithm/string/classification.hpp>
|
||||
# include <boost/bind.hpp>
|
||||
# include <boost/functional/hash.hpp>
|
||||
# include <boost/range.hpp>
|
||||
# include <simgear/compiler.h>
|
||||
# include <simgear/debug/logstream.hxx>
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
# include "PropertyInterpolationMgr.hxx"
|
||||
# include "vectorPropTemplates.hxx"
|
||||
|
||||
#if ( _MSC_VER == 1200 )
|
||||
# if ( _MSC_VER == 1200 )
|
||||
// MSVC 6 is buggy, and needs something strange here
|
||||
using std::vector<SGPropertyNode_ptr>;
|
||||
using std::vector<SGPropertyChangeListener *>;
|
||||
using std::vector<SGPropertyNode *>;
|
||||
#endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if PROPS_STANDALONE
|
||||
using std::cerr;
|
||||
#endif
|
||||
using std::endl;
|
||||
using std::find;
|
||||
using std::sort;
|
||||
@@ -84,6 +81,14 @@ public:
|
||||
// Local path normalization code.
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if PROPS_STANDALONE
|
||||
struct PathComponent
|
||||
{
|
||||
string name;
|
||||
int index;
|
||||
};
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Parse the name for a path component.
|
||||
*
|
||||
@@ -146,10 +151,149 @@ inline bool validateName(const std::string& name)
|
||||
return false;
|
||||
if (!isalpha(name[0]) && name[0] != '_')
|
||||
return false;
|
||||
#if PROPS_STANDALONE
|
||||
std::string is_any_of("_-.");
|
||||
bool rv = true;
|
||||
for(unsigned i=1; i<name.length(); ++i) {
|
||||
if (!isalnum(name[i]) && is_any_of.find(name[i]) == std::string::npos) {
|
||||
rv = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
#else
|
||||
return all(make_iterator_range(name.begin(), name.end()),
|
||||
is_alnum() || is_any_of("_-."));
|
||||
#endif
|
||||
}
|
||||
|
||||
#if PROPS_STANDALONE
|
||||
/**
|
||||
* Parse the name for a path component.
|
||||
*
|
||||
* Name: [_a-zA-Z][-._a-zA-Z0-9]*
|
||||
*/
|
||||
static inline const string
|
||||
parse_name (const string &path, int &i)
|
||||
{
|
||||
string name = "";
|
||||
int max = (int)path.size();
|
||||
|
||||
if (path[i] == '.') {
|
||||
i++;
|
||||
if (i < max && path[i] == '.') {
|
||||
i++;
|
||||
name = "..";
|
||||
} else {
|
||||
name = ".";
|
||||
}
|
||||
if (i < max && path[i] != '/')
|
||||
throw string("Illegal character after " + name);
|
||||
}
|
||||
|
||||
else if (isalpha(path[i]) || path[i] == '_') {
|
||||
name += path[i];
|
||||
i++;
|
||||
|
||||
// The rules inside a name are a little
|
||||
// less restrictive.
|
||||
while (i < max) {
|
||||
if (isalpha(path[i]) || isdigit(path[i]) || path[i] == '_' ||
|
||||
path[i] == '-' || path[i] == '.') {
|
||||
name += path[i];
|
||||
} else if (path[i] == '[' || path[i] == '/') {
|
||||
break;
|
||||
} else {
|
||||
throw string("name may contain only ._- and alphanumeric characters");
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
if (name.size() == 0)
|
||||
throw string("name must begin with alpha or '_'");
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse the optional integer index for a path component.
|
||||
*
|
||||
* Index: "[" [0-9]+ "]"
|
||||
*/
|
||||
static inline int
|
||||
parse_index (const string &path, int &i)
|
||||
{
|
||||
int index = 0;
|
||||
|
||||
if (path[i] != '[')
|
||||
return 0;
|
||||
else
|
||||
i++;
|
||||
|
||||
for (int max = (int)path.size(); i < max; i++) {
|
||||
if (isdigit(path[i])) {
|
||||
index = (index * 10) + (path[i] - '0');
|
||||
} else if (path[i] == ']') {
|
||||
i++;
|
||||
return index;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
throw string("unterminated index (looking for ']')");
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a single path component.
|
||||
*
|
||||
* Component: Name Index?
|
||||
*/
|
||||
static inline PathComponent
|
||||
parse_component (const string &path, int &i)
|
||||
{
|
||||
PathComponent component;
|
||||
component.name = parse_name(path, i);
|
||||
if (component.name[0] != '.')
|
||||
component.index = parse_index(path, i);
|
||||
else
|
||||
component.index = -1;
|
||||
return component;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a path into its components.
|
||||
*/
|
||||
static void
|
||||
parse_path (const string &path, vector<PathComponent> &components)
|
||||
{
|
||||
int pos = 0;
|
||||
int max = (int)path.size();
|
||||
|
||||
// Check for initial '/'
|
||||
if (path[pos] == '/') {
|
||||
PathComponent root;
|
||||
root.name = "";
|
||||
root.index = -1;
|
||||
components.push_back(root);
|
||||
pos++;
|
||||
while (pos < max && path[pos] == '/')
|
||||
pos++;
|
||||
}
|
||||
|
||||
while (pos < max) {
|
||||
components.push_back(parse_component(path, pos));
|
||||
while (pos < max && path[pos] == '/')
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Other static utility functions.
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
@@ -182,6 +326,13 @@ static int
|
||||
find_child (Itr begin, Itr end, int index, const PropertyList& nodes)
|
||||
{
|
||||
size_t nNodes = nodes.size();
|
||||
#if PROPS_STANDALONE
|
||||
for (int i = 0; i < nNodes; i++) {
|
||||
SGPropertyNode * node = nodes[i];
|
||||
if (node->getIndex() == index && compare_strings(node->getName(), begin))
|
||||
return i;
|
||||
}
|
||||
#else
|
||||
boost::iterator_range<Itr> name(begin, end);
|
||||
for (size_t i = 0; i < nNodes; i++) {
|
||||
SGPropertyNode * node = nodes[i];
|
||||
@@ -191,6 +342,7 @@ find_child (Itr begin, Itr end, int index, const PropertyList& nodes)
|
||||
if (node->getIndex() == index && boost::equals(node->getName(), name))
|
||||
return static_cast<int>(i);
|
||||
}
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -333,11 +485,57 @@ find_node_aux(SGPropertyNode * current, SplitItr& itr, bool create,
|
||||
|
||||
// Internal function for parsing property paths. last_index provides
|
||||
// and index value for the last node name token, if supplied.
|
||||
#if PROPS_STANDALONE
|
||||
static SGPropertyNode *
|
||||
find_node (SGPropertyNode * current,
|
||||
const vector<PathComponent> &components,
|
||||
int position,
|
||||
bool create)
|
||||
{
|
||||
// Run off the end of the list
|
||||
if (current == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Success! This is the one we want.
|
||||
else if (position >= (int)components.size()) {
|
||||
return (current->getAttribute(SGPropertyNode::REMOVED) ? 0 : current);
|
||||
}
|
||||
|
||||
// Empty component means root.
|
||||
else if (components[position].name == "") {
|
||||
return find_node(current->getRootNode(), components, position + 1, create);
|
||||
}
|
||||
|
||||
// . means current directory
|
||||
else if (components[position].name == ".") {
|
||||
return find_node(current, components, position + 1, create);
|
||||
}
|
||||
|
||||
// .. means parent directory
|
||||
else if (components[position].name == "..") {
|
||||
SGPropertyNode * parent = current->getParent();
|
||||
if (parent == 0)
|
||||
throw string("Attempt to move past root with '..'");
|
||||
else
|
||||
return find_node(parent, components, position + 1, create);
|
||||
}
|
||||
|
||||
// Otherwise, a child name
|
||||
else {
|
||||
SGPropertyNode * child =
|
||||
current->getChild(components[position].name.c_str(),
|
||||
components[position].index,
|
||||
create);
|
||||
return find_node(child, components, position + 1, create);
|
||||
}
|
||||
}
|
||||
#else
|
||||
template<typename Range>
|
||||
SGPropertyNode*
|
||||
find_node (SGPropertyNode * current,
|
||||
const Range& path,
|
||||
bool create,
|
||||
bool create,
|
||||
int last_index = -1)
|
||||
{
|
||||
using namespace boost;
|
||||
@@ -351,6 +549,7 @@ find_node (SGPropertyNode * current,
|
||||
else
|
||||
return find_node_aux(current, itr, create, last_index);
|
||||
}
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Private methods from SGPropertyNode (may be inlined for speed).
|
||||
@@ -611,13 +810,8 @@ SGPropertyNode::make_string () const
|
||||
void
|
||||
SGPropertyNode::trace_write () const
|
||||
{
|
||||
#if PROPS_STANDALONE
|
||||
cerr << "TRACE: Write node " << getPath () << ", value \""
|
||||
<< make_string() << '"' << endl;
|
||||
#else
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "TRACE: Write node " << getPath()
|
||||
<< ", value \"" << make_string() << '"');
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -626,13 +820,8 @@ SGPropertyNode::trace_write () const
|
||||
void
|
||||
SGPropertyNode::trace_read () const
|
||||
{
|
||||
#if PROPS_STANDALONE
|
||||
cerr << "TRACE: Write node " << getPath () << ", value \""
|
||||
<< make_string() << '"' << endl;
|
||||
#else
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "TRACE: Read node " << getPath()
|
||||
<< ", value \"" << make_string() << '"');
|
||||
#endif
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
@@ -786,8 +975,6 @@ SGPropertyNode::alias (SGPropertyNode * target)
|
||||
return true;
|
||||
}
|
||||
|
||||
#if PROPS_STANDALONE
|
||||
#else
|
||||
if (!target)
|
||||
{
|
||||
SG_LOG(SG_GENERAL, SG_ALERT,
|
||||
@@ -809,7 +996,6 @@ SGPropertyNode::alias (SGPropertyNode * target)
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "Failed to create alias at " << target->getPath() << ". "
|
||||
"Source " << getPath() << " is a tied property.");
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -957,11 +1143,18 @@ SGPropertyNode::getChild (const char * name, int index, bool create)
|
||||
SGPropertyNode *
|
||||
SGPropertyNode::getChild (const std::string& name, int index, bool create)
|
||||
{
|
||||
#if PROPS_STANDALONE
|
||||
const char *n = name.c_str();
|
||||
int pos = find_child(n, n + strlen(n), index, _children);
|
||||
if (pos >= 0) {
|
||||
return _children[pos];
|
||||
#else
|
||||
SGPropertyNode* node = getExistingChild(name.begin(), name.end(), index);
|
||||
if (node) {
|
||||
return node;
|
||||
#endif
|
||||
} else if (create) {
|
||||
node = new SGPropertyNode(name, index, this);
|
||||
SGPropertyNode* node = new SGPropertyNode(name, index, this);
|
||||
_children.push_back(node);
|
||||
fireChildAdded(node);
|
||||
return node;
|
||||
@@ -1635,12 +1828,14 @@ SGPropertyNode::setUnspecifiedValue (const char * value)
|
||||
case props::UNSPECIFIED:
|
||||
result = set_string(value);
|
||||
break;
|
||||
#if !PROPS_STANDALONE
|
||||
case props::VEC3D:
|
||||
result = static_cast<SGRawValue<SGVec3d>*>(_value.val)->setValue(parseString<SGVec3d>(value));
|
||||
break;
|
||||
case props::VEC4D:
|
||||
result = static_cast<SGRawValue<SGVec4d>*>(_value.val)->setValue(parseString<SGVec4d>(value));
|
||||
break;
|
||||
#endif
|
||||
case props::NONE:
|
||||
default:
|
||||
break;
|
||||
@@ -1652,6 +1847,7 @@ SGPropertyNode::setUnspecifiedValue (const char * value)
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
#if !PROPS_STANDALONE
|
||||
bool SGPropertyNode::interpolate( const std::string& type,
|
||||
const SGPropertyNode& target,
|
||||
double duration,
|
||||
@@ -1701,6 +1897,7 @@ simgear::PropertyInterpolationMgr* SGPropertyNode::getInterpolationMgr()
|
||||
}
|
||||
|
||||
simgear::PropertyInterpolationMgr* SGPropertyNode::_interpolation_mgr = 0;
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
std::ostream& SGPropertyNode::printOn(std::ostream& stream) const
|
||||
@@ -1854,20 +2051,37 @@ SGPropertyNode::getRootNode () const
|
||||
SGPropertyNode *
|
||||
SGPropertyNode::getNode (const char * relative_path, bool create)
|
||||
{
|
||||
#if PROPS_STANDALONE
|
||||
vector<PathComponent> components;
|
||||
parse_path(relative_path, components);
|
||||
return find_node(this, components, 0, create);
|
||||
|
||||
#else
|
||||
using namespace boost;
|
||||
|
||||
return find_node(this, make_iterator_range(relative_path, relative_path
|
||||
+ strlen(relative_path)),
|
||||
create);
|
||||
#endif
|
||||
}
|
||||
|
||||
SGPropertyNode *
|
||||
SGPropertyNode::getNode (const char * relative_path, int index, bool create)
|
||||
{
|
||||
#if PROPS_STANDALONE
|
||||
vector<PathComponent> components;
|
||||
parse_path(relative_path, components);
|
||||
if (components.size() > 0)
|
||||
components.back().index = index;
|
||||
return find_node(this, components, 0, create);
|
||||
|
||||
#else
|
||||
using namespace boost;
|
||||
|
||||
return find_node(this, make_iterator_range(relative_path, relative_path
|
||||
+ strlen(relative_path)),
|
||||
create, index);
|
||||
#endif
|
||||
}
|
||||
|
||||
const SGPropertyNode *
|
||||
@@ -2315,6 +2529,7 @@ SGPropertyChangeListener::unregister_property (SGPropertyNode * node)
|
||||
_properties.erase(it);
|
||||
}
|
||||
|
||||
#if !PROPS_STANDALONE
|
||||
template<>
|
||||
std::ostream& SGRawBase<SGVec3d>::printOn(std::ostream& stream) const
|
||||
{
|
||||
@@ -2351,9 +2566,11 @@ std::ostream& SGRawBase<SGVec4d>::printOn(std::ostream& stream) const
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
#if !PROPS_STANDALONE
|
||||
template<>
|
||||
std::istream& readFrom<SGVec4d>(std::istream& stream, SGVec4d& result)
|
||||
{
|
||||
@@ -2362,6 +2579,7 @@ std::istream& readFrom<SGVec4d>(std::istream& stream, SGVec4d& result)
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -2389,10 +2607,12 @@ bool compareNodeValue(const SGPropertyNode& lhs, const SGPropertyNode& rhs)
|
||||
case props::STRING:
|
||||
case props::UNSPECIFIED:
|
||||
return !strcmp(lhs.getStringValue(), rhs.getStringValue());
|
||||
#if !PROPS_STANDALONE
|
||||
case props::VEC3D:
|
||||
return lhs.getValue<SGVec3d>() == rhs.getValue<SGVec3d>();
|
||||
case props::VEC4D:
|
||||
return lhs.getValue<SGVec4d>() == rhs.getValue<SGVec4d>();
|
||||
#endif
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -2449,10 +2669,10 @@ struct PropertyPlaceLess {
|
||||
}
|
||||
};
|
||||
|
||||
#if !PROPS_STANDALONE
|
||||
size_t hash_value(const SGPropertyNode& node)
|
||||
{
|
||||
using namespace boost;
|
||||
|
||||
if (node.nChildren() == 0) {
|
||||
switch (node.getType()) {
|
||||
case props::NONE:
|
||||
@@ -2503,5 +2723,6 @@ size_t hash_value(const SGPropertyNode& node)
|
||||
return seed;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// end of props.cxx
|
||||
|
||||
@@ -22,24 +22,54 @@
|
||||
#include <sstream>
|
||||
#include <typeinfo>
|
||||
|
||||
#include <boost/utility.hpp>
|
||||
#include <boost/type_traits/is_enum.hpp>
|
||||
|
||||
#if PROPS_STANDALONE
|
||||
#else
|
||||
#include <simgear/compiler.h>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#if PROPS_STANDALONE
|
||||
// taken from: boost/utility/enable_if.hpp
|
||||
#ifndef SG_LOG
|
||||
# define SG_GENERAL 0
|
||||
# define SG_ALERT 0
|
||||
# define SG_WARN 1
|
||||
# define SG_LOG(type, level, message) (type) ? (std::cerr <<message << endl) : (std::cout <<message << endl)
|
||||
#endif
|
||||
namespace boost {
|
||||
template <bool B, class T = void>
|
||||
struct enable_if_c {
|
||||
typedef T type;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct enable_if_c<false, T> {};
|
||||
|
||||
#include <simgear/math/SGMathFwd.hxx>
|
||||
#include <simgear/math/sg_types.hxx>
|
||||
template <class Cond, class T = void>
|
||||
struct enable_if : public enable_if_c<Cond::value, T> {};
|
||||
|
||||
template <bool B, class T = void>
|
||||
struct disable_if_c {
|
||||
typedef T type;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct disable_if_c<true, T> {};
|
||||
|
||||
template <class Cond, class T = void>
|
||||
struct disable_if : public disable_if_c<Cond::value, T> {};
|
||||
}
|
||||
#else
|
||||
# include <boost/utility.hpp>
|
||||
# include <boost/type_traits/is_enum.hpp>
|
||||
|
||||
# include <simgear/debug/logstream.hxx>
|
||||
# include <simgear/math/SGMathFwd.hxx>
|
||||
# include <simgear/math/sg_types.hxx>
|
||||
#endif
|
||||
#include <simgear/structure/SGReferenced.hxx>
|
||||
#include <simgear/structure/SGSharedPtr.hxx>
|
||||
|
||||
// XXX This whole file should be in the simgear namespace, but I don't
|
||||
// have the guts yet...
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
|
||||
@@ -1275,6 +1305,7 @@ public:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if !PROPS_STANDALONE
|
||||
/**
|
||||
* Interpolate current value to target value within given time.
|
||||
*
|
||||
@@ -1310,6 +1341,7 @@ public:
|
||||
* Get the interpolation manager
|
||||
*/
|
||||
static simgear::PropertyInterpolationMgr* getInterpolationMgr();
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Print the value of the property to a stream.
|
||||
@@ -1807,7 +1839,11 @@ private:
|
||||
|
||||
// Convenience functions for use in templates
|
||||
template<typename T>
|
||||
#if PROPS_STANDALONE
|
||||
T
|
||||
#else
|
||||
typename boost::disable_if<boost::is_enum<T>, T>::type
|
||||
#endif
|
||||
getValue(const SGPropertyNode*);
|
||||
|
||||
template<>
|
||||
@@ -1871,7 +1907,11 @@ namespace simgear
|
||||
|
||||
/** Extract enum from SGPropertyNode */
|
||||
template<typename T>
|
||||
#if PROPS_STANDALONE
|
||||
inline T
|
||||
#else
|
||||
inline typename boost::enable_if<boost::is_enum<T>, T>::type
|
||||
#endif
|
||||
getValue(const SGPropertyNode* node)
|
||||
{
|
||||
typedef simgear::enum_traits<T> Traits;
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <boost/iterator/iterator_adaptor.hpp>
|
||||
|
||||
#include "Effect.hxx"
|
||||
#include "mat.hxx"
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
@@ -69,6 +70,8 @@ class EffectGeode : public osg::Geode
|
||||
META_Node(simgear,EffectGeode);
|
||||
Effect* getEffect() const { return _effect.get(); }
|
||||
void setEffect(Effect* effect);
|
||||
SGMaterial* getMaterial() const { return _material; }
|
||||
void setMaterial(SGMaterial* mat) { _material = mat; }
|
||||
virtual void resizeGLObjectBuffers(unsigned int maxSize);
|
||||
virtual void releaseGLObjects(osg::State* = 0) const;
|
||||
|
||||
@@ -83,6 +86,7 @@ class EffectGeode : public osg::Geode
|
||||
void runGenerators(osg::Geometry *geometry);
|
||||
private:
|
||||
osg::ref_ptr<Effect> _effect;
|
||||
SGMaterial* _material;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -326,9 +326,15 @@ public:
|
||||
= GL2Extensions::Get(static_cast<unsigned>(contextId), true);
|
||||
if (!extensions)
|
||||
return;
|
||||
#if OSG_VERSION_LESS_THAN(3,3,4)
|
||||
if (!extensions->isGlslSupported())
|
||||
return;
|
||||
value = extensions->getLanguageVersion();
|
||||
#else
|
||||
if (!extensions->isGlslSupported)
|
||||
return;
|
||||
value = extensions->glslLanguageVersion;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
@@ -357,7 +363,11 @@ public:
|
||||
= GL2Extensions::Get(static_cast<unsigned>(contextId), true);
|
||||
if (!extensions)
|
||||
return;
|
||||
#if OSG_VERSION_LESS_THAN(3,3,4)
|
||||
value = extensions->isGlslSupported();
|
||||
#else
|
||||
value = extensions->isGlslSupported;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -323,6 +323,7 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options,
|
||||
|
||||
// Random vegetation properties
|
||||
wood_coverage = props->getDoubleValue("wood-coverage", 0.0);
|
||||
tree_effect = props->getStringValue("tree-effect", "Effects/tree");
|
||||
tree_height = props->getDoubleValue("tree-height-m", 0.0);
|
||||
tree_width = props->getDoubleValue("tree-width-m", 0.0);
|
||||
tree_range = props->getDoubleValue("tree-range-m", 0.0);
|
||||
@@ -441,7 +442,7 @@ Effect* SGMaterial::get_effect(int i)
|
||||
return _status[i].effect.get();
|
||||
}
|
||||
|
||||
Effect* SGMaterial::get_effect(const SGTexturedTriangleBin& triangleBin)
|
||||
Effect* SGMaterial::get_one_effect(int texIndex)
|
||||
{
|
||||
SGGuard<SGMutex> g(_lock);
|
||||
if (_status.empty()) {
|
||||
@@ -449,7 +450,7 @@ Effect* SGMaterial::get_effect(const SGTexturedTriangleBin& triangleBin)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i = triangleBin.getTextureIndex() % _status.size();
|
||||
int i = texIndex % _status.size();
|
||||
return get_effect(i);
|
||||
}
|
||||
|
||||
@@ -460,7 +461,7 @@ Effect* SGMaterial::get_effect()
|
||||
}
|
||||
|
||||
|
||||
osg::Texture2D* SGMaterial::get_object_mask(const SGTexturedTriangleBin& triangleBin)
|
||||
osg::Texture2D* SGMaterial::get_one_object_mask(int texIndex)
|
||||
{
|
||||
if (_status.empty()) {
|
||||
SG_LOG( SG_GENERAL, SG_WARN, "No mask available.");
|
||||
@@ -469,7 +470,7 @@ osg::Texture2D* SGMaterial::get_object_mask(const SGTexturedTriangleBin& triangl
|
||||
|
||||
// Note that the object mask is closely linked to the texture/effect
|
||||
// so we index based on the texture index,
|
||||
unsigned int i = triangleBin.getTextureIndex() % _status.size();
|
||||
unsigned int i = texIndex % _status.size();
|
||||
if (i < _masks.size()) {
|
||||
return _masks[i].get();
|
||||
} else {
|
||||
|
||||
@@ -119,13 +119,13 @@ public:
|
||||
/**
|
||||
* Get the textured state.
|
||||
*/
|
||||
simgear::Effect* get_effect(const SGTexturedTriangleBin& triangleBin);
|
||||
simgear::Effect* get_one_effect(int texIndex);
|
||||
simgear::Effect* get_effect();
|
||||
|
||||
/**
|
||||
* Get the textured state.
|
||||
*/
|
||||
osg::Texture2D* get_object_mask(const SGTexturedTriangleBin& triangleBin);
|
||||
osg::Texture2D* get_one_object_mask(int texIndex);
|
||||
|
||||
|
||||
/**
|
||||
@@ -273,6 +273,14 @@ public:
|
||||
* @return the texture to use for trees.
|
||||
*/
|
||||
inline std::string get_tree_texture () const { return tree_texture; }
|
||||
|
||||
|
||||
/**
|
||||
* Get the effect file name to use for trees
|
||||
*
|
||||
* @return the effect to use for this set of trees.
|
||||
*/
|
||||
inline std::string get_tree_effect () const { return tree_effect; }
|
||||
|
||||
/**
|
||||
* Get the cosine of the maximum tree density slope angle. We
|
||||
@@ -476,6 +484,9 @@ private:
|
||||
|
||||
// Tree texture, typically a strip of applicable tree textures
|
||||
std::string tree_texture;
|
||||
|
||||
// Tree effect to be used for a particular material
|
||||
std::string tree_effect;
|
||||
|
||||
// Object mask, a simple RGB texture used as a mask when placing
|
||||
// random vegetation, objects and buildings
|
||||
|
||||
@@ -201,7 +201,7 @@ SGMaterialCache *SGMaterialLib::generateMatCache(SGGeod center)
|
||||
|
||||
// Destructor
|
||||
SGMaterialLib::~SGMaterialLib ( void ) {
|
||||
SG_LOG( SG_GENERAL, SG_INFO, "SGMaterialLib::~SGMaterialLib() size=" << matlib.size());
|
||||
SG_LOG( SG_TERRAIN, SG_DEBUG, "SGMaterialLib::~SGMaterialLib() size=" << matlib.size());
|
||||
}
|
||||
|
||||
const SGMaterial *SGMaterialLib::findMaterial(const osg::Geode* geode)
|
||||
@@ -244,5 +244,5 @@ SGMaterial *SGMaterialCache::find(const string& material) const
|
||||
|
||||
// Destructor
|
||||
SGMaterialCache::~SGMaterialCache ( void ) {
|
||||
SG_LOG( SG_GENERAL, SG_INFO, "SGMaterialCache::~SGMaterialCache() size=" << cache.size());
|
||||
SG_LOG( SG_TERRAIN, SG_DEBUG, "SGMaterialCache::~SGMaterialCache() size=" << cache.size());
|
||||
}
|
||||
|
||||
@@ -364,6 +364,8 @@ public:
|
||||
if (_textureProp) {
|
||||
std::string textureName = _textureProp->getStringValue();
|
||||
if (_textureName != textureName) {
|
||||
_textureName = textureName;
|
||||
|
||||
while (stateSet->getTextureAttribute(0,
|
||||
osg::StateAttribute::TEXTURE)) {
|
||||
stateSet->removeTextureAttribute(0, osg::StateAttribute::TEXTURE);
|
||||
@@ -377,8 +379,12 @@ public:
|
||||
osg::StateAttribute::OVERRIDE);
|
||||
stateSet->setTextureMode(0, GL_TEXTURE_2D,
|
||||
osg::StateAttribute::ON);
|
||||
_textureName = textureName;
|
||||
}
|
||||
} else {
|
||||
SG_LOG(SG_IO, SG_WARN, "texture animation: requested texture : " << textureName << " not found. Searched paths:" );
|
||||
for( osgDB::FilePathList::iterator it = _texturePathList.begin(); it != _texturePathList.end(); ++it ) {
|
||||
SG_LOG(SG_IO, SG_WARN, " - " << *it );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ SGModelLib::panel_func SGModelLib::static_panelFunc = NULL;
|
||||
void SGModelLib::init(const string &root_dir, SGPropertyNode* root)
|
||||
{
|
||||
osgDB::Registry::instance()->getDataFilePathList().push_front(root_dir);
|
||||
osgDB::Registry::instance()->getOptions()->getDatabasePathList().push_front(root_dir);
|
||||
static_propRoot = root;
|
||||
}
|
||||
|
||||
@@ -114,6 +115,7 @@ SGModelLib::loadModel(const string &path,
|
||||
{
|
||||
osg::ref_ptr<SGReaderWriterOptions> opt;
|
||||
opt = SGReaderWriterOptions::copyOrCreate(osgDB::Registry::instance()->getOptions());
|
||||
opt->getDatabasePathList().push_front( osgDB::getFilePath(path) );
|
||||
opt->setPropertyNode(prop_root ? prop_root: static_propRoot.get());
|
||||
opt->setModelData(data);
|
||||
|
||||
@@ -136,8 +138,10 @@ SGModelLib::loadDeferredModel(const string &path, SGPropertyNode *prop_root,
|
||||
proxyNode->setLoadingExternalReferenceMode(osg::ProxyNode::DEFER_LOADING_TO_DATABASE_PAGER);
|
||||
proxyNode->setFileName(0, path);
|
||||
|
||||
|
||||
osg::ref_ptr<SGReaderWriterOptions> opt;
|
||||
opt = SGReaderWriterOptions::copyOrCreate(osgDB::Registry::instance()->getOptions());
|
||||
opt->getDatabasePathList().push_front( osgDB::getFilePath(path) );
|
||||
opt->setPropertyNode(prop_root ? prop_root: static_propRoot.get());
|
||||
opt->setModelData(data);
|
||||
opt->setLoadPanel(static_panelFunc);
|
||||
|
||||
@@ -127,8 +127,12 @@ void CloudShaderGeometry::drawImplementation(RenderInfo& renderInfo) const
|
||||
|
||||
sortData.frameSorted = frameNumber;
|
||||
}
|
||||
|
||||
|
||||
#if OSG_VERSION_LESS_THAN(3,3,4)
|
||||
const Extensions* extensions = getExtensions(state.getContextID(),true);
|
||||
#else
|
||||
const GLExtensions* extensions = GLExtensions::Get(state.getContextID(), true);
|
||||
#endif
|
||||
GLfloat ua1[3] = { (GLfloat) alpha_factor,
|
||||
(GLfloat) shade_factor,
|
||||
(GLfloat) cloud_height };
|
||||
|
||||
@@ -216,6 +216,46 @@ SGSun::build( SGPath path, double sun_size, SGPropertyNode *property_tree_Node )
|
||||
|
||||
sun_transform->addChild( geode );
|
||||
|
||||
|
||||
// set up the brilliance state
|
||||
|
||||
geode = new osg::Geode;
|
||||
stateSet = geode->getOrCreateStateSet();
|
||||
stateSet->setRenderBinDetails(-9, "RenderBin");
|
||||
|
||||
|
||||
texture = SGLoadTexture2D("brilliance.png", options.get());
|
||||
stateSet->setTextureAttributeAndModes(0, texture);
|
||||
|
||||
|
||||
// Build ssg structure
|
||||
brilliance_cl = new osg::Vec4Array;
|
||||
brilliance_cl->push_back(osg::Vec4(1, 1, 1, 1));
|
||||
|
||||
double brilliance_size = sun_size * 12.0;
|
||||
osg::Vec3Array* brilliance_vl = new osg::Vec3Array;
|
||||
brilliance_vl->push_back(osg::Vec3(-brilliance_size, 0, -brilliance_size));
|
||||
brilliance_vl->push_back(osg::Vec3(brilliance_size, 0, -brilliance_size));
|
||||
brilliance_vl->push_back(osg::Vec3(-brilliance_size, 0, brilliance_size));
|
||||
brilliance_vl->push_back(osg::Vec3(brilliance_size, 0, brilliance_size));
|
||||
|
||||
osg::Vec2Array* brilliance_tl = new osg::Vec2Array;
|
||||
brilliance_tl->push_back(osg::Vec2(0, 0));
|
||||
brilliance_tl->push_back(osg::Vec2(1, 0));
|
||||
brilliance_tl->push_back(osg::Vec2(0, 1));
|
||||
brilliance_tl->push_back(osg::Vec2(1, 1));
|
||||
|
||||
geometry = new osg::Geometry;
|
||||
geometry->setUseDisplayList(false);
|
||||
geometry->setVertexArray(brilliance_vl);
|
||||
geometry->setColorArray(brilliance_cl.get(), osg::Array::BIND_OVERALL);
|
||||
geometry->setNormalBinding(osg::Geometry::BIND_OFF);
|
||||
geometry->setTexCoordArray(0, brilliance_tl, osg::Array::BIND_PER_VERTEX);
|
||||
geometry->addPrimitiveSet(new osg::DrawArrays(GL_TRIANGLE_STRIP, 0, 4));
|
||||
geode->addDrawable(geometry);
|
||||
|
||||
sun_transform->addChild( geode );
|
||||
|
||||
// force a repaint of the sun colors with arbitrary defaults
|
||||
repaint( 0.0, 1.0 );
|
||||
|
||||
@@ -262,7 +302,7 @@ bool SGSun::repaint( double sun_angle, double new_visibility ) {
|
||||
}
|
||||
|
||||
// ok, now let's go and generate the sun and scene color
|
||||
osg::Vec4 i_halo_color, o_halo_color, scene_color, sun_color;
|
||||
osg::Vec4 i_halo_color, o_halo_color, scene_color, sun_color, brilliance_color;
|
||||
|
||||
// Some comments:
|
||||
// * When the sunangle changes, light has to travel a longer
|
||||
@@ -336,16 +376,29 @@ bool SGSun::repaint( double sun_angle, double new_visibility ) {
|
||||
if (o_halo_color[3] > 1) o_halo_color[3] = 1;
|
||||
if (o_halo_color[3] < 0) o_halo_color[3] = 0;
|
||||
|
||||
brilliance_color[0] = i_halo_color[0];
|
||||
brilliance_color[1] = i_halo_color[1];
|
||||
brilliance_color[2] = i_halo_color[2];
|
||||
|
||||
double norm = (i_halo_color[0] * i_halo_color[0] + i_halo_color[1] * i_halo_color[1] + i_halo_color[2] * i_halo_color[2])/1.732;
|
||||
|
||||
brilliance_color[3] = pow(norm, 6.0);
|
||||
if (brilliance_color[3] < 0.0) {brilliance_color[3] = 0.0;}
|
||||
|
||||
|
||||
|
||||
gamma_correct_rgb( i_halo_color._v );
|
||||
gamma_correct_rgb( o_halo_color._v );
|
||||
gamma_correct_rgb( scene_color._v );
|
||||
gamma_correct_rgb( sun_color._v );
|
||||
gamma_correct_rgb( brilliance_color._v );
|
||||
|
||||
if (sun_angle >91.0 * 3.1415/180.0 + horizon_angle)
|
||||
{
|
||||
sun_color[3] = 0;
|
||||
o_halo_color[3]=0;
|
||||
i_halo_color[3]=0;
|
||||
brilliance_color[3]=0;
|
||||
}
|
||||
|
||||
(*sun_cl)[0] = sun_color;
|
||||
@@ -356,6 +409,8 @@ bool SGSun::repaint( double sun_angle, double new_visibility ) {
|
||||
ihalo_cl->dirty();
|
||||
(*ohalo_cl)[0] = o_halo_color;
|
||||
ohalo_cl->dirty();
|
||||
(*brilliance_cl)[0] = brilliance_color;
|
||||
brilliance_cl->dirty();
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -372,19 +427,14 @@ bool SGSun::reposition( double rightAscension, double declination,
|
||||
{
|
||||
// GST - GMT sidereal time
|
||||
osg::Matrix T2, RA, DEC;
|
||||
|
||||
// xglRotatef( ((SGD_RADIANS_TO_DEGREES * rightAscension)- 90.0),
|
||||
// 0.0, 0.0, 1.0);
|
||||
RA.makeRotate(rightAscension - 90*SGD_DEGREES_TO_RADIANS, osg::Vec3(0, 0, 1));
|
||||
|
||||
// xglRotatef((SGD_RADIANS_TO_DEGREES * declination), 1.0, 0.0, 0.0);
|
||||
DEC.makeRotate(declination, osg::Vec3(1, 0, 0));
|
||||
|
||||
// xglTranslatef(0,sun_dist);
|
||||
T2.makeTranslate(osg::Vec3(0, sun_dist, 0));
|
||||
|
||||
sun_transform->setMatrix(T2*DEC*RA);
|
||||
|
||||
// Suncolor related things:
|
||||
if ( prev_sun_angle != sun_angle ) {
|
||||
if ( sun_angle == 0 ) sun_angle = 0.1;
|
||||
@@ -412,7 +462,7 @@ bool SGSun::reposition( double rightAscension, double declination,
|
||||
if ( alt_half < 0.0 ) alt_half = 0.0;
|
||||
|
||||
//angle at which the sun is visible below the horizon
|
||||
horizon_angle = acos(r_earth/position_radius);
|
||||
horizon_angle = acos(min(r_earth/position_radius, 1.0));
|
||||
|
||||
// Push the data to the property tree, so it can be used in the enviromental code
|
||||
if ( env_node ){
|
||||
|
||||
@@ -45,6 +45,7 @@ class SGSun : public SGReferenced {
|
||||
osg::ref_ptr<osg::Vec4Array> scene_cl;
|
||||
osg::ref_ptr<osg::Vec4Array> ihalo_cl;
|
||||
osg::ref_ptr<osg::Vec4Array> ohalo_cl;
|
||||
osg::ref_ptr<osg::Vec4Array> brilliance_cl;
|
||||
|
||||
double visibility;
|
||||
double prev_sun_angle;
|
||||
|
||||
@@ -8,9 +8,12 @@ set(HEADERS
|
||||
SGDirectionalLightBin.hxx
|
||||
SGLightBin.hxx
|
||||
SGModelBin.hxx
|
||||
SGNodeTriangles.hxx
|
||||
SGOceanTile.hxx
|
||||
SGReaderWriterBTG.hxx
|
||||
SGTexturedTriangleBin.hxx
|
||||
SGTileDetailsCallback.hxx
|
||||
SGTileGeometryBin.hxx
|
||||
SGTriangleBin.hxx
|
||||
SGVasiDrawable.hxx
|
||||
SGVertexArrayBin.hxx
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#include <simgear/misc/sgstream.hxx>
|
||||
#include <simgear/scene/util/OptionsReadFileCallback.hxx>
|
||||
#include <simgear/scene/util/OsgMath.hxx>
|
||||
#include <simgear/scene/util/QuadTreeBuilder.hxx>
|
||||
#include <simgear/scene/util/RenderConstants.hxx>
|
||||
#include <simgear/scene/util/SGReaderWriterOptions.hxx>
|
||||
#include <simgear/scene/tgdb/apt_signs.hxx>
|
||||
@@ -113,48 +114,79 @@ struct ReaderWriterSTG::_ModelBin {
|
||||
};
|
||||
|
||||
class DelayLoadReadFileCallback : public OptionsReadFileCallback {
|
||||
|
||||
private:
|
||||
// QuadTreeBuilder for structuring static objects
|
||||
struct MakeQuadLeaf {
|
||||
osg::LOD* operator() () const { return new osg::LOD; }
|
||||
};
|
||||
struct AddModelLOD {
|
||||
void operator() (osg::LOD* leaf, _ObjectStatic o) const
|
||||
{
|
||||
osg::ref_ptr<osg::Node> node;
|
||||
if (o._proxy) {
|
||||
osg::ref_ptr<osg::ProxyNode> proxy = new osg::ProxyNode;
|
||||
proxy->setName("proxyNode");
|
||||
proxy->setLoadingExternalReferenceMode(osg::ProxyNode::DEFER_LOADING_TO_DATABASE_PAGER);
|
||||
proxy->setFileName(0, o._name);
|
||||
proxy->setDatabaseOptions(o._options.get());
|
||||
|
||||
// Give the node some values so the Quadtree builder has
|
||||
// a BoundingBox to work with prior to the model being loaded.
|
||||
proxy->setCenter(osg::Vec3f(0.0f,0.0f,0.0f));
|
||||
proxy->setRadius(10);
|
||||
proxy->setCenterMode(osg::ProxyNode::UNION_OF_BOUNDING_SPHERE_AND_USER_DEFINED);
|
||||
node = proxy;
|
||||
} else {
|
||||
node = osgDB::readRefNodeFile(o._name, o._options.get());
|
||||
if (!node.valid()) {
|
||||
SG_LOG(SG_TERRAIN, SG_ALERT, o._errorLocation << ": Failed to load "
|
||||
<< o._token << " '" << o._name << "'");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (SGPath(o._name).lower_extension() == "ac")
|
||||
node->setNodeMask(~simgear::MODELLIGHT_BIT);
|
||||
|
||||
osg::Matrix matrix;
|
||||
matrix = makeZUpFrame(SGGeod::fromDegM(o._lon, o._lat, o._elev));
|
||||
matrix.preMultRotate(osg::Quat(SGMiscd::deg2rad(o._hdg), osg::Vec3(0, 0, 1)));
|
||||
matrix.preMultRotate(osg::Quat(SGMiscd::deg2rad(o._pitch), osg::Vec3(0, 1, 0)));
|
||||
matrix.preMultRotate(osg::Quat(SGMiscd::deg2rad(o._roll), osg::Vec3(1, 0, 0)));
|
||||
|
||||
osg::MatrixTransform* matrixTransform;
|
||||
matrixTransform = new osg::MatrixTransform(matrix);
|
||||
matrixTransform->setName("rotateStaticObject");
|
||||
matrixTransform->setDataVariance(osg::Object::STATIC);
|
||||
matrixTransform->addChild(node.get());
|
||||
leaf->addChild(matrixTransform, 0, 20000); //TODO: Make configurable?
|
||||
}
|
||||
};
|
||||
struct GetModelLODCoord {
|
||||
GetModelLODCoord() {}
|
||||
GetModelLODCoord(const GetModelLODCoord& rhs)
|
||||
{}
|
||||
osg::Vec3 operator() (const _ObjectStatic& o) const
|
||||
{
|
||||
SGVec3d coord;
|
||||
SGGeodesy::SGGeodToCart(SGGeod::fromDegM(o._lon, o._lat, o._elev), coord);
|
||||
return toOsg(coord);
|
||||
}
|
||||
};
|
||||
typedef QuadTreeBuilder<osg::LOD*, _ObjectStatic, MakeQuadLeaf, AddModelLOD,
|
||||
GetModelLODCoord> STGObjectsQuadtree;
|
||||
|
||||
|
||||
public:
|
||||
virtual osgDB::ReaderWriter::ReadResult
|
||||
readNode(const std::string&, const osgDB::Options*)
|
||||
{
|
||||
osg::ref_ptr<osg::Group> group = new osg::Group;
|
||||
STGObjectsQuadtree quadtree((GetModelLODCoord()), (AddModelLOD()));
|
||||
quadtree.buildQuadTree(_objectStaticList.begin(), _objectStaticList.end());
|
||||
osg::ref_ptr<osg::Group> group = quadtree.getRoot();
|
||||
group->setName("STG-group-A");
|
||||
group->setDataVariance(osg::Object::STATIC);
|
||||
|
||||
for (std::list<_ObjectStatic>::iterator i = _objectStaticList.begin(); i != _objectStaticList.end(); ++i) {
|
||||
osg::ref_ptr<osg::Node> node;
|
||||
if (i->_proxy) {
|
||||
osg::ref_ptr<osg::ProxyNode> proxy = new osg::ProxyNode;
|
||||
proxy->setName("proxyNode");
|
||||
proxy->setLoadingExternalReferenceMode(osg::ProxyNode::DEFER_LOADING_TO_DATABASE_PAGER);
|
||||
proxy->setFileName(0, i->_name);
|
||||
proxy->setDatabaseOptions(i->_options.get());
|
||||
node = proxy;
|
||||
} else {
|
||||
node = osgDB::readRefNodeFile(i->_name, i->_options.get());
|
||||
if (!node.valid()) {
|
||||
SG_LOG(SG_TERRAIN, SG_ALERT, i->_errorLocation << ": Failed to load "
|
||||
<< i->_token << " '" << i->_name << "'");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (SGPath(i->_name).lower_extension() == "ac")
|
||||
node->setNodeMask(~simgear::MODELLIGHT_BIT);
|
||||
|
||||
osg::Matrix matrix;
|
||||
matrix = makeZUpFrame(SGGeod::fromDegM(i->_lon, i->_lat, i->_elev));
|
||||
matrix.preMultRotate(osg::Quat(SGMiscd::deg2rad(i->_hdg), osg::Vec3(0, 0, 1)));
|
||||
matrix.preMultRotate(osg::Quat(SGMiscd::deg2rad(i->_pitch), osg::Vec3(0, 1, 0)));
|
||||
matrix.preMultRotate(osg::Quat(SGMiscd::deg2rad(i->_roll), osg::Vec3(1, 0, 0)));
|
||||
|
||||
osg::MatrixTransform* matrixTransform;
|
||||
matrixTransform = new osg::MatrixTransform(matrix);
|
||||
matrixTransform->setName("positionStaticObject");
|
||||
matrixTransform->setDataVariance(osg::Object::STATIC);
|
||||
matrixTransform->addChild(node.get());
|
||||
group->addChild(matrixTransform);
|
||||
}
|
||||
|
||||
|
||||
simgear::AirportSignBuilder signBuilder(_options->getMaterialLib(), _bucket.get_center());
|
||||
for (std::list<_Sign>::iterator i = _signList.begin(); i != _signList.end(); ++i)
|
||||
signBuilder.addSign(SGGeod::fromDegM(i->_lon, i->_lat, i->_elev), i->_hdg, i->_name, i->_size);
|
||||
@@ -266,7 +298,7 @@ struct ReaderWriterSTG::_ModelBin {
|
||||
// read a line
|
||||
std::string line;
|
||||
std::getline(stream, line);
|
||||
|
||||
|
||||
// strip comments
|
||||
std::string::size_type hash_pos = line.find('#');
|
||||
if (hash_pos != std::string::npos)
|
||||
|
||||
@@ -386,10 +386,10 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const
|
||||
int row = ((int) (mt_rand(&seed) * 1000)) % 5;
|
||||
float base_y = (float) row * 16.0 * 3.0 / 1024.0;
|
||||
float top_y = base_y + 16.0 * (float) building.floors / 1024.0;
|
||||
float left_x = 32.0 / 1024.0 * round((float) building.width / 6.0f);
|
||||
float left_x = 32.0 / 1024.0 * SGMisc<double>::round((float)building.width / 6.0f);
|
||||
float right_x = 0.0f;
|
||||
float front_x = 384.0/1024.0;
|
||||
float back_x = 384.0/1024.0 + 32.0 / 1024.0 * round((float) building.depth/ 6.0f);
|
||||
float back_x = 384.0 / 1024.0 + 32.0 / 1024.0 * SGMisc<double>::round((float)building.depth / 6.0f);
|
||||
|
||||
// BASEMENT - uses the baseline texture
|
||||
for (unsigned int i = 0; i < 16; i++) {
|
||||
@@ -424,7 +424,7 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const
|
||||
if (building.pitched) {
|
||||
// Use the entire height of the roof texture
|
||||
top_y = base_y + 16.0 * 3.0 / 1024.0;
|
||||
left_x = 512/1024.0 + 32.0 / 1024.0 * round(building.width / 6.0f);
|
||||
left_x = 512 / 1024.0 + 32.0 / 1024.0 * SGMisc<double>::round(building.width / 6.0f);
|
||||
right_x = 512/1024.0;
|
||||
front_x = 480.0/1024.0;
|
||||
back_x = 512.0/1024.0;
|
||||
@@ -475,7 +475,7 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const
|
||||
int column = ((int) (mt_rand(&seed) * 1000)) % 5;
|
||||
float base_y = 288 / 1024.0;
|
||||
float top_y = base_y + 16.0 * (float) building.floors / 1024.0;
|
||||
float left_x = column * 192.0 /1024.0 + 32.0 / 1024.0 * round((float) building.width / 10.0f);
|
||||
float left_x = column * 192.0 / 1024.0 + 32.0 / 1024.0 * SGMisc<double>::round((float)building.width / 10.0f);
|
||||
float right_x = column * 192.0 /1024.0;
|
||||
|
||||
// BASEMENT - uses the baseline texture
|
||||
@@ -560,7 +560,7 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const
|
||||
int column = ((int) (mt_rand(&seed) * 1000)) % 8;
|
||||
float base_y = 576 / 1024.0;
|
||||
float top_y = base_y + 16.0 * (float) building.floors / 1024.0;
|
||||
float left_x = column * 128.0 /1024.0 + 32.0 / 1024.0 * round((float) building.width / 20.0f);
|
||||
float left_x = column * 128.0 / 1024.0 + 32.0 / 1024.0 * SGMisc<double>::round((float)building.width / 20.0f);
|
||||
float right_x = column * 128.0 /1024.0;
|
||||
|
||||
// BASEMENT - uses the baseline texture
|
||||
|
||||
485
simgear/scene/tgdb/SGNodeTriangles.hxx
Normal file
485
simgear/scene/tgdb/SGNodeTriangles.hxx
Normal file
@@ -0,0 +1,485 @@
|
||||
// future API - just run through once to convert from OSG to SG
|
||||
// then we can use these triangle lists for random
|
||||
// trees/lights/buildings/objects
|
||||
struct SGTexturedTriangle
|
||||
{
|
||||
public:
|
||||
std::vector<SGVec3f> vertices;
|
||||
std::vector<SGVec2f> texcoords;
|
||||
};
|
||||
|
||||
struct SGBorderContour
|
||||
{
|
||||
public:
|
||||
SGVec3d start;
|
||||
SGVec3d end;
|
||||
};
|
||||
|
||||
class SGTriangleInfo
|
||||
{
|
||||
public:
|
||||
SGTriangleInfo( const SGVec3d& center ) {
|
||||
gbs_center = center;
|
||||
mt_init(&seed, 123);
|
||||
}
|
||||
|
||||
// API used to build the Info by the visitor
|
||||
void addGeometry( osg::Geometry* g ) {
|
||||
geometries.push_back(g);
|
||||
}
|
||||
|
||||
void setMaterial( SGMaterial* m ) {
|
||||
mat = m;
|
||||
}
|
||||
|
||||
SGMaterial* getMaterial( void ) const {
|
||||
return mat;
|
||||
}
|
||||
|
||||
// API used to get a specific texture or effect from a material. Materials can have
|
||||
// multiple textures - use the floor of the x coordinate of the first vertes to select it.
|
||||
// This will be constant, and give the same result each time to select one effect/texture per drawable.
|
||||
int getTextureIndex( void ) const {
|
||||
int texInfo = 0;
|
||||
const osg::Vec3Array* vertices = dynamic_cast<osg::Vec3Array*>(geometries[0]->getVertexArray());
|
||||
if ( vertices ) {
|
||||
const osg::Vec3 *v0 = &vertices->operator[](0);
|
||||
texInfo = floor(v0->x());
|
||||
}
|
||||
return texInfo;
|
||||
}
|
||||
|
||||
// new API - TODO
|
||||
void getTriangles( std::vector<SGTexturedTriangle>& tris )
|
||||
{
|
||||
const osg::Vec3Array* vertices = dynamic_cast<osg::Vec3Array*>(geometries[0]->getVertexArray());
|
||||
const osg::Vec2Array* texcoords = dynamic_cast<osg::Vec2Array*>(geometries[0]->getTexCoordArray(0));
|
||||
|
||||
int numPrimitiveSets = geometries[0]->getNumPrimitiveSets();
|
||||
if ( numPrimitiveSets > 0 ) {
|
||||
const osg::PrimitiveSet* ps = geometries[0]->getPrimitiveSet(0);
|
||||
unsigned int numIndices = ps->getNumIndices();
|
||||
|
||||
for ( unsigned int i=2; i<numIndices; i+= 3 ) {
|
||||
SGTexturedTriangle tri;
|
||||
|
||||
tri.vertices.push_back( toSG(vertices->operator[](ps->index(i-2))) );
|
||||
tri.vertices.push_back( toSG(vertices->operator[](ps->index(i-1))) );
|
||||
tri.vertices.push_back( toSG(vertices->operator[](ps->index(i-0))) );
|
||||
|
||||
tri.texcoords.push_back( toSG(texcoords->operator[](ps->index(i-2))) );
|
||||
tri.texcoords.push_back( toSG(texcoords->operator[](ps->index(i-1))) );
|
||||
tri.texcoords.push_back( toSG(texcoords->operator[](ps->index(i-0))) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void getBorderContours( std::vector<SGBorderContour>& border )
|
||||
{
|
||||
// each structure contains a list of target indexes and a count
|
||||
int numPrimitiveSets = geometries[0]->getNumPrimitiveSets();
|
||||
if ( numPrimitiveSets > 0 ) {
|
||||
const osg::Vec3Array* vertices = dynamic_cast<osg::Vec3Array*>(geometries[0]->getVertexArray());
|
||||
|
||||
const osg::PrimitiveSet* ps = geometries[0]->getPrimitiveSet(0);
|
||||
unsigned int numTriangles = ps->getNumIndices()/3;
|
||||
|
||||
// use a map for fast lookup map the segment as a 64 bit int
|
||||
std::map<uint64_t, int> segCounter;
|
||||
uint32_t idx1, idx2;
|
||||
uint64_t key;
|
||||
|
||||
for ( unsigned int i=0; i<numTriangles; i+= 3 ) {
|
||||
// first seg
|
||||
if ( ps->index(i+0) < ps->index(i+1) ) {
|
||||
idx1 = ps->index(i+0);
|
||||
idx2 = ps->index(i+1);
|
||||
} else {
|
||||
idx1 = ps->index(i+1);
|
||||
idx2 = ps->index(i+0);
|
||||
}
|
||||
|
||||
key=( (uint64_t)idx1<<32) | (uint64_t)idx2;
|
||||
SG_LOG(SG_TERRAIN, SG_ALERT, "key " << std::hex << key << std::dec << " count is " << segCounter[key] );
|
||||
segCounter[key]++;
|
||||
SG_LOG(SG_TERRAIN, SG_ALERT, "after increment key " << std::hex << key << std::dec << " count is " << segCounter[key] );
|
||||
|
||||
// second seg
|
||||
if ( ps->index(i+1) < ps->index(i+2) ) {
|
||||
idx1 = ps->index(i+1);
|
||||
idx2 = ps->index(i+2);
|
||||
} else {
|
||||
idx1 = ps->index(i+2);
|
||||
idx2 = ps->index(i+1);
|
||||
}
|
||||
|
||||
key=( (uint64_t)idx1<<32) | (uint64_t)idx2;
|
||||
SG_LOG(SG_TERRAIN, SG_ALERT, "key " << std::hex << key << std::dec << " count is " << segCounter[key] );
|
||||
segCounter[key]++;
|
||||
SG_LOG(SG_TERRAIN, SG_ALERT, "after increment key " << std::hex << key << std::dec << " count is " << segCounter[key] );
|
||||
|
||||
// third seg
|
||||
if ( ps->index(i+2) < ps->index(i+0) ) {
|
||||
idx1 = ps->index(i+2);
|
||||
idx2 = ps->index(i+0);
|
||||
} else {
|
||||
idx1 = ps->index(i+0);
|
||||
idx2 = ps->index(i+2);
|
||||
}
|
||||
|
||||
key=( (uint64_t)idx1<<32) | (uint64_t)idx2;
|
||||
SG_LOG(SG_TERRAIN, SG_ALERT, "key " << std::hex << key << std::dec << " count is " << segCounter[key] );
|
||||
segCounter[key]++;
|
||||
SG_LOG(SG_TERRAIN, SG_ALERT, "after increment key " << std::hex << key << std::dec << " count is " << segCounter[key] );
|
||||
}
|
||||
|
||||
// return all segments with count = 1 ( border )
|
||||
std::map<uint64_t, int>::iterator segIt = segCounter.begin();
|
||||
while ( segIt != segCounter.end() ) {
|
||||
if ( segIt->second == 1 ) {
|
||||
SG_LOG(SG_TERRAIN, SG_ALERT, "key " << std::hex << segIt->first << std::dec << " count is " << segIt->second );
|
||||
|
||||
unsigned int iStart = segIt->first >> 32;
|
||||
unsigned int iEnd = segIt->first & 0x00000000FFFFFFFF;
|
||||
|
||||
SGBorderContour bc;
|
||||
|
||||
bc.start = toVec3d(toSG(vertices->operator[](iStart)));
|
||||
bc.end = toVec3d(toSG(vertices->operator[](iEnd)));
|
||||
border.push_back( bc );
|
||||
}
|
||||
segIt++;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// debug out - requires GDAL
|
||||
//
|
||||
//
|
||||
//
|
||||
SGGeod geodPos = SGGeod::fromCart(gbs_center);
|
||||
SGQuatd hlOr = SGQuatd::fromLonLat(geodPos)*SGQuatd::fromEulerDeg(0, 0, 180);
|
||||
|
||||
for ( unsigned int i=0; i<border.size(); i++ ){
|
||||
// de-rotate and translate : todo - create a paralell vertex list so we just do this
|
||||
// once per vertex, not for every triangle's use of the vertex
|
||||
SGVec3d sgVStart = hlOr.backTransform( border[i].start) + gbs_center;
|
||||
SGVec3d sgVEnd = hlOr.backTransform( border[i].end) + gbs_center;
|
||||
|
||||
// convert from cartesian to Geodetic, and save as a list of Geods for output
|
||||
SGGeod gStart = SGGeod::fromCart(sgVStart);
|
||||
SGGeod gEnd = SGGeod::fromCart(sgVEnd);
|
||||
|
||||
SGShapefile::FromSegment( gStart, gEnd, true, "./borders", mat->get_names()[0], "border" );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Random buildings API - get num triangles, then get a triangle at index
|
||||
unsigned int getNumTriangles( void ) const {
|
||||
unsigned int num_triangles = 0;
|
||||
|
||||
if ( !geometries.empty() ) {
|
||||
int numPrimitiveSets = geometries[0]->getNumPrimitiveSets();
|
||||
if ( numPrimitiveSets > 0 ) {
|
||||
const osg::PrimitiveSet* ps = geometries[0]->getPrimitiveSet(0);
|
||||
unsigned int numIndices = ps->getNumIndices();
|
||||
num_triangles = numIndices/3;
|
||||
}
|
||||
}
|
||||
|
||||
return num_triangles;
|
||||
}
|
||||
|
||||
void getTriangle(unsigned int i, std::vector<SGVec3f>& triVerts, std::vector<SGVec2f>& triTCs) const {
|
||||
const osg::Vec3Array* vertices = dynamic_cast<osg::Vec3Array*>(geometries[0]->getVertexArray());
|
||||
const osg::Vec2Array* texcoords = dynamic_cast<osg::Vec2Array*>(geometries[0]->getTexCoordArray(0));
|
||||
|
||||
if ( !geometries.empty() ) {
|
||||
int numPrimitiveSets = geometries[0]->getNumPrimitiveSets();
|
||||
if ( numPrimitiveSets > 0 ) {
|
||||
const osg::PrimitiveSet* ps = geometries[0]->getPrimitiveSet(0);
|
||||
int idxStart = i*3;
|
||||
|
||||
triVerts.push_back( toSG(vertices->operator[](ps->index(idxStart+0))) );
|
||||
triVerts.push_back( toSG(vertices->operator[](ps->index(idxStart+1))) );
|
||||
triVerts.push_back( toSG(vertices->operator[](ps->index(idxStart+2))) );
|
||||
|
||||
triTCs.push_back( toSG(texcoords->operator[](ps->index(idxStart+0))) );
|
||||
triTCs.push_back( toSG(texcoords->operator[](ps->index(idxStart+1))) );
|
||||
triTCs.push_back( toSG(texcoords->operator[](ps->index(idxStart+2))) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// random lights and trees - just get a list of points on where to add the light / tree
|
||||
// TODO move this out - and handle in the random light / tree code
|
||||
// just use generic triangle API.
|
||||
void addRandomSurfacePoints(float coverage, float offset,
|
||||
osg::Texture2D* object_mask,
|
||||
std::vector<SGVec3f>& points)
|
||||
{
|
||||
if ( !geometries.empty() ) {
|
||||
const osg::Vec3Array* vertices = dynamic_cast<osg::Vec3Array*>(geometries[0]->getVertexArray());
|
||||
const osg::Vec2Array* texcoords = dynamic_cast<osg::Vec2Array*>(geometries[0]->getTexCoordArray(0));
|
||||
|
||||
int numPrimitiveSets = geometries[0]->getNumPrimitiveSets();
|
||||
if ( numPrimitiveSets > 0 ) {
|
||||
const osg::PrimitiveSet* ps = geometries[0]->getPrimitiveSet(0);
|
||||
unsigned int numIndices = ps->getNumIndices();
|
||||
|
||||
for ( unsigned int i=2; i<numIndices; i+= 3 ) {
|
||||
SGVec3f v0 = toSG(vertices->operator[](ps->index(i-2)));
|
||||
SGVec3f v1 = toSG(vertices->operator[](ps->index(i-1)));
|
||||
SGVec3f v2 = toSG(vertices->operator[](ps->index(i-0)));
|
||||
|
||||
SGVec2f t0 = toSG(texcoords->operator[](ps->index(i-2)));
|
||||
SGVec2f t1 = toSG(texcoords->operator[](ps->index(i-1)));
|
||||
SGVec2f t2 = toSG(texcoords->operator[](ps->index(i-0)));
|
||||
|
||||
SGVec3f normal = cross(v1 - v0, v2 - v0);
|
||||
|
||||
// Compute the area
|
||||
float area = 0.5f*length(normal);
|
||||
if (area <= SGLimitsf::min())
|
||||
continue;
|
||||
|
||||
// For partial units of area, use a zombie door method to
|
||||
// create the proper random chance of a light being created
|
||||
// for this triangle
|
||||
float unit = area + mt_rand(&seed)*coverage;
|
||||
|
||||
SGVec3f offsetVector = offset*normalize(normal);
|
||||
// generate a light point for each unit of area
|
||||
|
||||
while ( coverage < unit ) {
|
||||
float a = mt_rand(&seed);
|
||||
float b = mt_rand(&seed);
|
||||
|
||||
if ( a + b > 1 ) {
|
||||
a = 1 - a;
|
||||
b = 1 - b;
|
||||
}
|
||||
float c = 1 - a - b;
|
||||
SGVec3f randomPoint = offsetVector + a*v0 + b*v1 + c*v2;
|
||||
|
||||
if (object_mask != NULL) {
|
||||
SGVec2f texCoord = a*t0 + b*t1 + c*t2;
|
||||
|
||||
// Check this random point against the object mask
|
||||
// red channel.
|
||||
osg::Image* img = object_mask->getImage();
|
||||
unsigned int x = (int) (img->s() * texCoord.x()) % img->s();
|
||||
unsigned int y = (int) (img->t() * texCoord.y()) % img->t();
|
||||
|
||||
if (mt_rand(&seed) < img->getColor(x, y).r()) {
|
||||
points.push_back(randomPoint);
|
||||
}
|
||||
} else {
|
||||
// No object mask, so simply place the object
|
||||
points.push_back(randomPoint);
|
||||
}
|
||||
unit -= coverage;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void addRandomTreePoints(float wood_coverage,
|
||||
osg::Texture2D* object_mask,
|
||||
float vegetation_density,
|
||||
float cos_max_density_angle,
|
||||
float cos_zero_density_angle,
|
||||
std::vector<SGVec3f>& points,
|
||||
std::vector<SGVec3f>& normals)
|
||||
{
|
||||
if ( !geometries.empty() ) {
|
||||
const osg::Vec3Array* vertices = dynamic_cast<osg::Vec3Array*>(geometries[0]->getVertexArray());
|
||||
const osg::Vec2Array* texcoords = dynamic_cast<osg::Vec2Array*>(geometries[0]->getTexCoordArray(0));
|
||||
|
||||
int numPrimitiveSets = geometries[0]->getNumPrimitiveSets();
|
||||
if ( numPrimitiveSets > 0 ) {
|
||||
const osg::PrimitiveSet* ps = geometries[0]->getPrimitiveSet(0);
|
||||
unsigned int numIndices = ps->getNumIndices();
|
||||
|
||||
for ( unsigned int i=2; i<numIndices; i+= 3 ) {
|
||||
SGVec3f v0 = toSG(vertices->operator[](ps->index(i-2)));
|
||||
SGVec3f v1 = toSG(vertices->operator[](ps->index(i-1)));
|
||||
SGVec3f v2 = toSG(vertices->operator[](ps->index(i-0)));
|
||||
|
||||
SGVec2f t0 = toSG(texcoords->operator[](ps->index(i-2)));
|
||||
SGVec2f t1 = toSG(texcoords->operator[](ps->index(i-1)));
|
||||
SGVec2f t2 = toSG(texcoords->operator[](ps->index(i-0)));
|
||||
|
||||
SGVec3f normal = cross(v1 - v0, v2 - v0);
|
||||
|
||||
// Ensure the slope isn't too steep by checking the
|
||||
// cos of the angle between the slope normal and the
|
||||
// vertical (conveniently the z-component of the normalized
|
||||
// normal) and values passed in.
|
||||
float alpha = normalize(normal).z();
|
||||
float slope_density = 1.0;
|
||||
|
||||
if (alpha < cos_zero_density_angle)
|
||||
continue; // Too steep for any vegetation
|
||||
|
||||
if (alpha < cos_max_density_angle) {
|
||||
slope_density =
|
||||
(alpha - cos_zero_density_angle) / (cos_max_density_angle - cos_zero_density_angle);
|
||||
}
|
||||
|
||||
// Compute the area
|
||||
float area = 0.5f*length(normal);
|
||||
if (area <= SGLimitsf::min())
|
||||
continue;
|
||||
|
||||
// Determine the number of trees, taking into account vegetation
|
||||
// density (which is linear) and the slope density factor.
|
||||
// Use a zombie door method to create the proper random chance
|
||||
// of a tree being created for partial values.
|
||||
int woodcount = (int) (vegetation_density * vegetation_density *
|
||||
slope_density *
|
||||
area / wood_coverage + mt_rand(&seed));
|
||||
|
||||
for (int j = 0; j < woodcount; j++) {
|
||||
float a = mt_rand(&seed);
|
||||
float b = mt_rand(&seed);
|
||||
|
||||
if ( a + b > 1.0f ) {
|
||||
a = 1.0f - a;
|
||||
b = 1.0f - b;
|
||||
}
|
||||
|
||||
float c = 1.0f - a - b;
|
||||
|
||||
SGVec3f randomPoint = a*v0 + b*v1 + c*v2;
|
||||
|
||||
if (object_mask != NULL) {
|
||||
SGVec2f texCoord = a*t0 + b*t1 + c*t2;
|
||||
|
||||
// Check this random point against the object mask
|
||||
// green (for trees) channel.
|
||||
osg::Image* img = object_mask->getImage();
|
||||
unsigned int x = (int) (img->s() * texCoord.x()) % img->s();
|
||||
unsigned int y = (int) (img->t() * texCoord.y()) % img->t();
|
||||
|
||||
if (mt_rand(&seed) < img->getColor(x, y).g()) {
|
||||
// The red channel contains the rotation for this object
|
||||
points.push_back(randomPoint);
|
||||
normals.push_back(normalize(normal));
|
||||
}
|
||||
} else {
|
||||
points.push_back(randomPoint);
|
||||
normals.push_back(normalize(normal));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
// debug : this will save the tile as a shapefile that can be viewed in QGIS.
|
||||
// NOTE: this is really slow....
|
||||
// remember - we need to de-rotate the tile, then translate back to gbs_center.
|
||||
void dumpBorder() {
|
||||
//dump the first triangle only of the first geometry, for now...
|
||||
SG_LOG(SG_TERRAIN, SG_ALERT, "effect geode has " << geometries.size() << " geometries" );
|
||||
|
||||
const osg::Vec3Array* vertices = dynamic_cast<osg::Vec3Array*>(geometries[0]->getVertexArray());
|
||||
if ( vertices ) {
|
||||
SG_LOG(SG_TERRAIN, SG_ALERT, " geometry has " << vertices->getNumElements() << " vertices" );
|
||||
}
|
||||
|
||||
if ( !geometries.empty() ) {
|
||||
int numPrimitiveSets = geometries[0]->getNumPrimitiveSets();
|
||||
SG_LOG(SG_TERRAIN, SG_ALERT, " geometry has " << numPrimitiveSets << " primitive sets" );
|
||||
|
||||
if ( numPrimitiveSets > 0 ) {
|
||||
const osg::PrimitiveSet* ps = geometries[0]->getPrimitiveSet(0);
|
||||
unsigned int numIndices = ps->getNumIndices();
|
||||
|
||||
// create the same quat we used to rotate here
|
||||
// - use backTransform to go back to original node location
|
||||
SGGeod geodPos = SGGeod::fromCart(gbs_center);
|
||||
SGQuatd hlOr = SGQuatd::fromLonLat(geodPos)*SGQuatd::fromEulerDeg(0, 0, 180);
|
||||
|
||||
SG_LOG(SG_TERRAIN, SG_ALERT, " primitive set has has " << numIndices << " indices" );
|
||||
for ( unsigned int i=2; i<numIndices; i+= 3 ) {
|
||||
if ( numIndices >= 3 ) {
|
||||
unsigned int v0i = ps->index(i-2);
|
||||
unsigned int v1i = ps->index(i-1);
|
||||
unsigned int v2i = ps->index(i-0);
|
||||
|
||||
const osg::Vec3 *v0 = &vertices->operator[](v0i);
|
||||
const osg::Vec3 *v1 = &vertices->operator[](v1i);
|
||||
const osg::Vec3 *v2 = &vertices->operator[](v2i);
|
||||
|
||||
// de-rotate and translate : todo - create a paralell vertex list so we just do this
|
||||
// once per vertex, not for every triangle's use of the vertex
|
||||
SGVec3d vec0 = hlOr.backTransform( toVec3d(toSG(*v0))) + gbs_center;
|
||||
SGVec3d vec1 = hlOr.backTransform( toVec3d(toSG(*v1))) + gbs_center;
|
||||
SGVec3d vec2 = hlOr.backTransform( toVec3d(toSG(*v2))) + gbs_center;
|
||||
|
||||
// convert from cartesian to Geodetic, and save as a list of Geods for output
|
||||
std::vector<SGGeod> triangle;
|
||||
triangle.push_back( SGGeod::fromCart(vec0) );
|
||||
triangle.push_back( SGGeod::fromCart(vec1) );
|
||||
triangle.push_back( SGGeod::fromCart(vec2) );
|
||||
|
||||
SGShapefile::FromGeodList( triangle, true, "./triangles", mat->get_names()[0], "tri" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
mt seed;
|
||||
SGMaterial* mat;
|
||||
SGVec3d gbs_center;
|
||||
std::vector<osg::Geometry*> geometries;
|
||||
std::vector<int> polygon_border; // TODO
|
||||
};
|
||||
|
||||
// This visitor will generate an SGTriangleInfo.
|
||||
// currently, it looks like it could save multiple lists, which could be the case
|
||||
// if multiple osg::geods are found with osg::Geometry.
|
||||
// But right now, we store a single PrimitiveSet under a single EffectGeod.
|
||||
// so the traversal should only find a single EffectGeod - building a single SGTriangleInfo
|
||||
class GetNodeTriangles : public osg::NodeVisitor
|
||||
{
|
||||
public:
|
||||
GetNodeTriangles(const SGVec3d& c, std::vector<SGTriangleInfo>* nt) : osg::NodeVisitor( osg::NodeVisitor::TRAVERSE_ALL_CHILDREN ), center(c), nodeTris(nt) {}
|
||||
|
||||
// This method gets called for every node in the scene
|
||||
// graph. Check each node to see if it has user
|
||||
// out target. If so, save the node's address.
|
||||
virtual void apply( osg::Node& node )
|
||||
{
|
||||
EffectGeode* eg = dynamic_cast<EffectGeode*>(&node);
|
||||
if ( eg ) {
|
||||
// get the material from the user info
|
||||
SGTriangleInfo triInfo( center );
|
||||
triInfo.setMaterial( eg->getMaterial() );
|
||||
|
||||
// let's find the drawables for this node
|
||||
int numDrawables = eg->getNumDrawables();
|
||||
for ( int i=0; i<numDrawables; i++ ) {
|
||||
triInfo.addGeometry( eg->getDrawable(i)->asGeometry() );
|
||||
}
|
||||
|
||||
nodeTris->push_back( triInfo );
|
||||
}
|
||||
|
||||
// Keep traversing the rest of the scene graph.
|
||||
traverse( node );
|
||||
}
|
||||
|
||||
protected:
|
||||
SGVec3d center;
|
||||
std::vector<SGTriangleInfo>* nodeTris;
|
||||
};
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <osg/Geometry>
|
||||
#include <osg/PrimitiveSet>
|
||||
#include <osg/Texture2D>
|
||||
#include <osg/ref_ptr>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <simgear/math/sg_random.h>
|
||||
@@ -122,16 +123,16 @@ public:
|
||||
osg::DrawElements* getDrawElements()
|
||||
{
|
||||
if (count > 65535) {
|
||||
free (_ushortElements);
|
||||
_ushortElements = 0;
|
||||
return _uintElements;
|
||||
} else {
|
||||
free (_uintElements);
|
||||
_uintElements = 0;
|
||||
return _ushortElements;
|
||||
}
|
||||
}
|
||||
protected:
|
||||
osg::DrawElementsUShort* _ushortElements;
|
||||
osg::DrawElementsUInt* _uintElements;
|
||||
osg::ref_ptr<osg::DrawElementsUShort> _ushortElements;
|
||||
osg::ref_ptr<osg::DrawElementsUInt> _uintElements;
|
||||
unsigned count;
|
||||
};
|
||||
|
||||
@@ -215,7 +216,8 @@ public:
|
||||
float vegetation_density,
|
||||
float cos_max_density_angle,
|
||||
float cos_zero_density_angle,
|
||||
std::vector<SGVec3f>& points)
|
||||
std::vector<SGVec3f>& points,
|
||||
std::vector<SGVec3f>& normals)
|
||||
{
|
||||
unsigned num = getNumTriangles();
|
||||
for (unsigned i = 0; i < num; ++i) {
|
||||
@@ -281,9 +283,11 @@ public:
|
||||
if (mt_rand(&seed) < img->getColor(x, y).g()) {
|
||||
// The red channel contains the rotation for this object
|
||||
points.push_back(randomPoint);
|
||||
normals.push_back(normalize(normal));
|
||||
}
|
||||
} else {
|
||||
points.push_back(randomPoint);
|
||||
normals.push_back(normalize(normal));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1141
simgear/scene/tgdb/SGTileDetailsCallback.hxx
Normal file
1141
simgear/scene/tgdb/SGTileDetailsCallback.hxx
Normal file
File diff suppressed because it is too large
Load Diff
304
simgear/scene/tgdb/SGTileGeometryBin.hxx
Normal file
304
simgear/scene/tgdb/SGTileGeometryBin.hxx
Normal file
@@ -0,0 +1,304 @@
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <simgear_config.h>
|
||||
#endif
|
||||
|
||||
#include "obj.hxx"
|
||||
|
||||
#include <simgear/scene/material/EffectGeode.hxx>
|
||||
#include <simgear/scene/material/matlib.hxx>
|
||||
#include <simgear/scene/material/mat.hxx>
|
||||
|
||||
#include "SGTexturedTriangleBin.hxx"
|
||||
|
||||
using namespace simgear;
|
||||
|
||||
typedef std::map<std::string,SGTexturedTriangleBin> SGMaterialTriangleMap;
|
||||
|
||||
// Class handling the initial BTG loading : should probably be in its own file
|
||||
// it is very closely coupled with SGTexturedTriangleBin.hxx
|
||||
// it was used to load fans, strips, and triangles.
|
||||
// WS2.0 no longer uses fans or strips, but people still use ws1.0, so we need
|
||||
// to keep this functionality.
|
||||
class SGTileGeometryBin : public osg::Referenced {
|
||||
public:
|
||||
SGMaterialTriangleMap materialTriangleMap;
|
||||
|
||||
SGTileGeometryBin() {}
|
||||
|
||||
static SGVec2f
|
||||
getTexCoord(const std::vector<SGVec2f>& texCoords, const int_list& tc,
|
||||
const SGVec2f& tcScale, unsigned i)
|
||||
{
|
||||
if (tc.empty())
|
||||
return tcScale;
|
||||
else if (tc.size() == 1)
|
||||
return mult(texCoords[tc[0]], tcScale);
|
||||
else
|
||||
return mult(texCoords[tc[i]], tcScale);
|
||||
}
|
||||
|
||||
SGVec2f getTexCoordScale(const std::string& name, SGMaterialCache* matcache)
|
||||
{
|
||||
if (!matcache)
|
||||
return SGVec2f(1, 1);
|
||||
SGMaterial* material = matcache->find(name);
|
||||
if (!material)
|
||||
return SGVec2f(1, 1);
|
||||
|
||||
return material->get_tex_coord_scale();
|
||||
}
|
||||
|
||||
static void
|
||||
addTriangleGeometry(SGTexturedTriangleBin& triangles,
|
||||
const SGBinObject& obj, unsigned grp,
|
||||
const SGVec2f& tc0Scale,
|
||||
const SGVec2f& tc1Scale)
|
||||
{
|
||||
const std::vector<SGVec3d>& vertices(obj.get_wgs84_nodes());
|
||||
const std::vector<SGVec3f>& normals(obj.get_normals());
|
||||
const std::vector<SGVec2f>& texCoords(obj.get_texcoords());
|
||||
const int_list& tris_v(obj.get_tris_v()[grp]);
|
||||
const int_list& tris_n(obj.get_tris_n()[grp]);
|
||||
const tci_list& tris_tc(obj.get_tris_tcs()[grp]);
|
||||
bool num_norms_is_num_verts = true;
|
||||
|
||||
if (tris_v.size() != tris_n.size()) {
|
||||
// If the normal indices do not match, they should be inmplicitly
|
||||
// the same than the vertex indices.
|
||||
num_norms_is_num_verts = false;
|
||||
}
|
||||
|
||||
if ( !tris_tc[1].empty() ) {
|
||||
triangles.hasSecondaryTexCoord(true);
|
||||
}
|
||||
|
||||
for (unsigned i = 2; i < tris_v.size(); i += 3) {
|
||||
SGVertNormTex v0;
|
||||
v0.SetVertex( toVec3f(vertices[tris_v[i-2]]) );
|
||||
v0.SetNormal( num_norms_is_num_verts ? normals[tris_n[i-2]] :
|
||||
normals[tris_v[i-2]] );
|
||||
v0.SetTexCoord( 0, getTexCoord(texCoords, tris_tc[0], tc0Scale, i-2) );
|
||||
if (!tris_tc[1].empty()) {
|
||||
v0.SetTexCoord( 1, getTexCoord(texCoords, tris_tc[1], tc1Scale, i-2) );
|
||||
}
|
||||
SGVertNormTex v1;
|
||||
v1.SetVertex( toVec3f(vertices[tris_v[i-1]]) );
|
||||
v1.SetNormal( num_norms_is_num_verts ? normals[tris_n[i-1]] :
|
||||
normals[tris_v[i-1]] );
|
||||
v1.SetTexCoord( 0, getTexCoord(texCoords, tris_tc[0], tc0Scale, i-1) );
|
||||
if (!tris_tc[1].empty()) {
|
||||
v1.SetTexCoord( 1, getTexCoord(texCoords, tris_tc[1], tc1Scale, i-1) );
|
||||
}
|
||||
SGVertNormTex v2;
|
||||
v2.SetVertex( toVec3f(vertices[tris_v[i]]) );
|
||||
v2.SetNormal( num_norms_is_num_verts ? normals[tris_n[i]] :
|
||||
normals[tris_v[i]] );
|
||||
v2.SetTexCoord( 0, getTexCoord(texCoords, tris_tc[0], tc0Scale, i) );
|
||||
if (!tris_tc[1].empty()) {
|
||||
v2.SetTexCoord( 1, getTexCoord(texCoords, tris_tc[1], tc1Scale, i) );
|
||||
}
|
||||
|
||||
triangles.insert(v0, v1, v2);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
addStripGeometry(SGTexturedTriangleBin& triangles,
|
||||
const SGBinObject& obj, unsigned grp,
|
||||
const SGVec2f& tc0Scale,
|
||||
const SGVec2f& tc1Scale)
|
||||
{
|
||||
const std::vector<SGVec3d>& vertices(obj.get_wgs84_nodes());
|
||||
const std::vector<SGVec3f>& normals(obj.get_normals());
|
||||
const std::vector<SGVec2f>& texCoords(obj.get_texcoords());
|
||||
const int_list& strips_v(obj.get_strips_v()[grp]);
|
||||
const int_list& strips_n(obj.get_strips_n()[grp]);
|
||||
const tci_list& strips_tc(obj.get_strips_tcs()[grp]);
|
||||
bool num_norms_is_num_verts = true;
|
||||
|
||||
if (strips_v.size() != strips_n.size()) {
|
||||
// If the normal indices do not match, they should be inmplicitly
|
||||
// the same than the vertex indices.
|
||||
num_norms_is_num_verts = false;
|
||||
}
|
||||
|
||||
if ( !strips_tc[1].empty() ) {
|
||||
triangles.hasSecondaryTexCoord(true);
|
||||
}
|
||||
|
||||
for (unsigned i = 2; i < strips_v.size(); ++i) {
|
||||
SGVertNormTex v0;
|
||||
v0.SetVertex( toVec3f(vertices[strips_v[i-2]]) );
|
||||
v0.SetNormal( num_norms_is_num_verts ? normals[strips_n[i-2]] :
|
||||
normals[strips_v[i-2]] );
|
||||
v0.SetTexCoord( 0, getTexCoord(texCoords, strips_tc[0], tc0Scale, i-2) );
|
||||
if (!strips_tc[1].empty()) {
|
||||
v0.SetTexCoord( 1, getTexCoord(texCoords, strips_tc[1], tc1Scale, i-2) );
|
||||
}
|
||||
SGVertNormTex v1;
|
||||
v1.SetVertex( toVec3f(vertices[strips_v[i-1]]) );
|
||||
v1.SetNormal( num_norms_is_num_verts ? normals[strips_n[i-1]] :
|
||||
normals[strips_v[i-1]] );
|
||||
v1.SetTexCoord( 0, getTexCoord(texCoords, strips_tc[1], tc0Scale, i-1) );
|
||||
if (!strips_tc[1].empty()) {
|
||||
v1.SetTexCoord( 1, getTexCoord(texCoords, strips_tc[1], tc1Scale, i-1) );
|
||||
}
|
||||
SGVertNormTex v2;
|
||||
v2.SetVertex( toVec3f(vertices[strips_v[i]]) );
|
||||
v2.SetNormal( num_norms_is_num_verts ? normals[strips_n[i]] :
|
||||
normals[strips_v[i]] );
|
||||
v2.SetTexCoord( 0, getTexCoord(texCoords, strips_tc[0], tc0Scale, i) );
|
||||
if (!strips_tc[1].empty()) {
|
||||
v2.SetTexCoord( 1, getTexCoord(texCoords, strips_tc[1], tc1Scale, i) );
|
||||
}
|
||||
if (i%2)
|
||||
triangles.insert(v1, v0, v2);
|
||||
else
|
||||
triangles.insert(v0, v1, v2);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
addFanGeometry(SGTexturedTriangleBin& triangles,
|
||||
const SGBinObject& obj, unsigned grp,
|
||||
const SGVec2f& tc0Scale,
|
||||
const SGVec2f& tc1Scale)
|
||||
{
|
||||
const std::vector<SGVec3d>& vertices(obj.get_wgs84_nodes());
|
||||
const std::vector<SGVec3f>& normals(obj.get_normals());
|
||||
const std::vector<SGVec2f>& texCoords(obj.get_texcoords());
|
||||
const int_list& fans_v(obj.get_fans_v()[grp]);
|
||||
const int_list& fans_n(obj.get_fans_n()[grp]);
|
||||
const tci_list& fans_tc(obj.get_fans_tcs()[grp]);
|
||||
bool num_norms_is_num_verts = true;
|
||||
|
||||
if (fans_v.size() != fans_n.size()) {
|
||||
// If the normal indices do not match, they should be inmplicitly
|
||||
// the same than the vertex indices.
|
||||
num_norms_is_num_verts = false;
|
||||
}
|
||||
|
||||
if ( !fans_tc[1].empty() ) {
|
||||
triangles.hasSecondaryTexCoord(true);
|
||||
}
|
||||
|
||||
SGVertNormTex v0;
|
||||
v0.SetVertex( toVec3f(vertices[fans_v[0]]) );
|
||||
v0.SetNormal( num_norms_is_num_verts ? normals[fans_n[0]] :
|
||||
normals[fans_v[0]] );
|
||||
v0.SetTexCoord( 0, getTexCoord(texCoords, fans_tc[0], tc0Scale, 0) );
|
||||
if (!fans_tc[1].empty()) {
|
||||
v0.SetTexCoord( 1, getTexCoord(texCoords, fans_tc[1], tc1Scale, 0) );
|
||||
}
|
||||
SGVertNormTex v1;
|
||||
v1.SetVertex( toVec3f(vertices[fans_v[1]]) );
|
||||
v1.SetNormal( num_norms_is_num_verts ? normals[fans_n[1]] :
|
||||
normals[fans_v[1]] );
|
||||
v1.SetTexCoord( 0, getTexCoord(texCoords, fans_tc[0], tc0Scale, 1) );
|
||||
if (!fans_tc[1].empty()) {
|
||||
v1.SetTexCoord( 1, getTexCoord(texCoords, fans_tc[1], tc1Scale, 1) );
|
||||
}
|
||||
for (unsigned i = 2; i < fans_v.size(); ++i) {
|
||||
SGVertNormTex v2;
|
||||
v2.SetVertex( toVec3f(vertices[fans_v[i]]) );
|
||||
v2.SetNormal( num_norms_is_num_verts ? normals[fans_n[i]] :
|
||||
normals[fans_v[i]] );
|
||||
v2.SetTexCoord( 0, getTexCoord(texCoords, fans_tc[0], tc0Scale, i) );
|
||||
if (!fans_tc[1].empty()) {
|
||||
v2.SetTexCoord( 1, getTexCoord(texCoords, fans_tc[1], tc1Scale, i) );
|
||||
}
|
||||
triangles.insert(v0, v1, v2);
|
||||
v1 = v2;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
insertSurfaceGeometry(const SGBinObject& obj, SGMaterialCache* matcache)
|
||||
{
|
||||
if (obj.get_tris_n().size() < obj.get_tris_v().size() ||
|
||||
obj.get_tris_tcs().size() < obj.get_tris_v().size()) {
|
||||
SG_LOG(SG_TERRAIN, SG_ALERT,
|
||||
"Group list sizes for triangles do not match!");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (unsigned grp = 0; grp < obj.get_tris_v().size(); ++grp) {
|
||||
std::string materialName = obj.get_tri_materials()[grp];
|
||||
SGVec2f tc0Scale = getTexCoordScale(materialName, matcache);
|
||||
SGVec2f tc1Scale(1.0, 1.0);
|
||||
addTriangleGeometry(materialTriangleMap[materialName],
|
||||
obj, grp, tc0Scale, tc1Scale );
|
||||
}
|
||||
|
||||
if (obj.get_strips_n().size() < obj.get_strips_v().size() ||
|
||||
obj.get_strips_tcs().size() < obj.get_strips_v().size()) {
|
||||
SG_LOG(SG_TERRAIN, SG_ALERT,
|
||||
"Group list sizes for strips do not match!");
|
||||
return false;
|
||||
}
|
||||
for (unsigned grp = 0; grp < obj.get_strips_v().size(); ++grp) {
|
||||
std::string materialName = obj.get_strip_materials()[grp];
|
||||
SGVec2f tc0Scale = getTexCoordScale(materialName, matcache);
|
||||
SGVec2f tc1Scale(1.0, 1.0);
|
||||
addStripGeometry(materialTriangleMap[materialName],
|
||||
obj, grp, tc0Scale, tc1Scale);
|
||||
}
|
||||
|
||||
if (obj.get_fans_n().size() < obj.get_fans_v().size() ||
|
||||
obj.get_fans_tcs().size() < obj.get_fans_v().size()) {
|
||||
SG_LOG(SG_TERRAIN, SG_ALERT,
|
||||
"Group list sizes for fans do not match!");
|
||||
return false;
|
||||
}
|
||||
for (unsigned grp = 0; grp < obj.get_fans_v().size(); ++grp) {
|
||||
std::string materialName = obj.get_fan_materials()[grp];
|
||||
SGVec2f tc0Scale = getTexCoordScale(materialName, matcache);
|
||||
SGVec2f tc1Scale(1.0, 1.0);
|
||||
addFanGeometry(materialTriangleMap[materialName],
|
||||
obj, grp, tc0Scale, tc1Scale );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
osg::Node* getSurfaceGeometry(SGMaterialCache* matcache, bool useVBOs) const
|
||||
{
|
||||
if (materialTriangleMap.empty())
|
||||
return 0;
|
||||
|
||||
EffectGeode* eg = NULL;
|
||||
osg::Group* group = (materialTriangleMap.size() > 1 ? new osg::Group : NULL);
|
||||
if (group) {
|
||||
group->setName("surfaceGeometryGroup");
|
||||
}
|
||||
|
||||
//osg::Geode* geode = new osg::Geode;
|
||||
SGMaterialTriangleMap::const_iterator i;
|
||||
for (i = materialTriangleMap.begin(); i != materialTriangleMap.end(); ++i) {
|
||||
osg::Geometry* geometry = i->second.buildGeometry(useVBOs);
|
||||
SGMaterial *mat = NULL;
|
||||
if (matcache) {
|
||||
mat = matcache->find(i->first);
|
||||
}
|
||||
eg = new EffectGeode;
|
||||
eg->setName("EffectGeode");
|
||||
if (mat) {
|
||||
eg->setMaterial(mat);
|
||||
eg->setEffect(mat->get_one_effect(i->second.getTextureIndex()));
|
||||
} else {
|
||||
eg->setMaterial(NULL);
|
||||
}
|
||||
eg->addDrawable(geometry);
|
||||
eg->runGenerators(geometry); // Generate extra data needed by effect
|
||||
if (group) {
|
||||
group->addChild(eg);
|
||||
}
|
||||
}
|
||||
|
||||
if (group) {
|
||||
return group;
|
||||
} else {
|
||||
return eg;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -47,7 +47,11 @@ void ShaderGeometry::addObject(const Vec3& position, float scale,
|
||||
void ShaderGeometry::drawImplementation(osg::RenderInfo& renderInfo) const
|
||||
{
|
||||
State& state = *renderInfo.getState();
|
||||
#if OSG_VERSION_LESS_THAN(3,3,4)
|
||||
const Extensions* extensions = getExtensions(state.getContextID(), true);
|
||||
#else
|
||||
const GLExtensions* extensions = GLExtensions::Get(state.getContextID(), true);
|
||||
#endif
|
||||
Vec4Array::const_iterator citer = _posScaleArray->begin();
|
||||
Vec4Array::const_iterator cend = _posScaleArray->end();
|
||||
FloatArray::const_iterator viter = _vertexAttribArray->begin();
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
#include <simgear/scene/util/QuadTreeBuilder.hxx>
|
||||
#include <simgear/scene/util/RenderConstants.hxx>
|
||||
#include <simgear/scene/util/StateAttributeFactory.hxx>
|
||||
#include <simgear/scene/util/SGReaderWriterOptions.hxx>
|
||||
#include <simgear/structure/OSGUtils.hxx>
|
||||
|
||||
#include "ShaderGeometry.hxx"
|
||||
@@ -62,6 +63,9 @@ using namespace osg;
|
||||
namespace simgear
|
||||
{
|
||||
|
||||
bool use_tree_shadows;
|
||||
bool use_tree_normals;
|
||||
|
||||
// Tree instance scheme:
|
||||
// vertex - local position of quad vertex.
|
||||
// normal - x y scaling, z number of varieties
|
||||
@@ -164,15 +168,24 @@ Geometry* createTreeGeometry(float width, float height, int varieties)
|
||||
// Positions
|
||||
quadGeom->setColorArray(new Vec3Array);
|
||||
quadGeom->setColorBinding(Geometry::BIND_PER_VERTEX);
|
||||
FloatArray* rotation = new FloatArray(2);
|
||||
// Normals
|
||||
if (use_tree_shadows || use_tree_normals)
|
||||
{
|
||||
quadGeom->setSecondaryColorArray(new Vec3Array);
|
||||
quadGeom->setSecondaryColorBinding(Geometry::BIND_PER_VERTEX);
|
||||
}
|
||||
FloatArray* rotation = new FloatArray(3);
|
||||
(*rotation)[0] = 0.0;
|
||||
(*rotation)[1] = PI_2;
|
||||
if (use_tree_shadows) {(*rotation)[2] = -1.0;}
|
||||
quadGeom->setFogCoordArray(rotation);
|
||||
quadGeom->setFogCoordBinding(Geometry::BIND_PER_PRIMITIVE_SET);
|
||||
// The primitive sets render the same geometry, but the second
|
||||
// will rotated 90 degrees by the vertex shader, which uses the
|
||||
// fog coordinate as a rotation.
|
||||
for (int i = 0; i < 2; ++i)
|
||||
int imax = 2;
|
||||
if (use_tree_shadows) {imax = 3;}
|
||||
for (int i = 0; i < imax; ++i)
|
||||
quadGeom->addPrimitiveSet(new DrawArrays(PrimitiveSet::QUADS));
|
||||
return quadGeom;
|
||||
}
|
||||
@@ -184,13 +197,17 @@ EffectGeode* createTreeGeode(float width, float height, int varieties)
|
||||
return result;
|
||||
}
|
||||
|
||||
void addTreeToLeafGeode(Geode* geode, const SGVec3f& p)
|
||||
void addTreeToLeafGeode(Geode* geode, const SGVec3f& p, const SGVec3f& t)
|
||||
{
|
||||
Vec3 pos = toOsg(p);
|
||||
Vec3 ter = toOsg(t);
|
||||
unsigned int numDrawables = geode->getNumDrawables();
|
||||
Geometry* geom
|
||||
= static_cast<Geometry*>(geode->getDrawable(numDrawables - 1));
|
||||
Vec3Array* posArray = static_cast<Vec3Array*>(geom->getColorArray());
|
||||
Vec3Array* tnormalArray;
|
||||
if (use_tree_shadows || use_tree_normals)
|
||||
{tnormalArray = static_cast<Vec3Array*>(geom->getSecondaryColorArray());}
|
||||
if (posArray->size()
|
||||
>= static_cast<Vec3Array*>(geom->getVertexArray())->size()) {
|
||||
Vec3Array* paramsArray
|
||||
@@ -198,11 +215,17 @@ void addTreeToLeafGeode(Geode* geode, const SGVec3f& p)
|
||||
Vec3 params = (*paramsArray)[0];
|
||||
geom = createTreeGeometry(params.x(), params.y(), params.z());
|
||||
posArray = static_cast<Vec3Array*>(geom->getColorArray());
|
||||
if (use_tree_shadows || use_tree_normals)
|
||||
{tnormalArray = static_cast<Vec3Array*>(geom->getSecondaryColorArray());}
|
||||
geode->addDrawable(geom);
|
||||
}
|
||||
posArray->insert(posArray->end(), 4, pos);
|
||||
if (use_tree_shadows || use_tree_normals)
|
||||
{tnormalArray->insert(tnormalArray->end(),4,ter);}
|
||||
size_t numVerts = posArray->size();
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
int imax = 2;
|
||||
if (use_tree_shadows) {imax = 3;}
|
||||
for (int i = 0; i < imax; ++i) {
|
||||
DrawArrays* primSet
|
||||
= static_cast<DrawArrays*>(geom->getPrimitiveSet(i));
|
||||
primSet->setCount(numVerts);
|
||||
@@ -218,14 +241,14 @@ namespace
|
||||
{
|
||||
struct MakeTreesLeaf
|
||||
{
|
||||
MakeTreesLeaf(float range, int varieties, float width, float height,
|
||||
MakeTreesLeaf(float range, int varieties, float width, float height,
|
||||
Effect* effect) :
|
||||
_range(range), _varieties(varieties),
|
||||
_width(width), _height(height), _effect(effect) {}
|
||||
|
||||
MakeTreesLeaf(const MakeTreesLeaf& rhs) :
|
||||
_range(rhs._range),
|
||||
_varieties(rhs._varieties), _width(rhs._width), _height(rhs._height),
|
||||
_varieties(rhs._varieties), _width(rhs._width), _height(rhs._height),
|
||||
_effect(rhs._effect)
|
||||
{}
|
||||
|
||||
@@ -255,7 +278,7 @@ struct AddTreesLeafObject
|
||||
void operator() (LOD* lod, const TreeBin::Tree& tree) const
|
||||
{
|
||||
Geode* geode = static_cast<Geode*>(lod->getChild(int(tree.position.x() * 10.0f) % lod->getNumChildren()));
|
||||
addTreeToLeafGeode(geode, tree.position);
|
||||
addTreeToLeafGeode(geode, tree.position, tree.tnormal);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -277,7 +300,8 @@ struct TreeTransformer
|
||||
TreeBin::Tree operator()(const TreeBin::Tree& tree) const
|
||||
{
|
||||
Vec3 pos = toOsg(tree.position);
|
||||
return TreeBin::Tree(toSG(pos * mat));
|
||||
Vec3 norm = toOsg(tree.tnormal);
|
||||
return TreeBin::Tree(toSG(pos * mat),toSG(norm * mat));
|
||||
}
|
||||
Matrix mat;
|
||||
};
|
||||
@@ -332,6 +356,20 @@ osg::Group* createForest(SGTreeBinList& forestList, const osg::Matrix& transform
|
||||
MatrixTransform* mt = new MatrixTransform(transform);
|
||||
|
||||
SGTreeBinList::iterator i;
|
||||
|
||||
use_tree_shadows = false;
|
||||
use_tree_normals = false;
|
||||
if (options) {
|
||||
SGPropertyNode* propertyNode = options->getPropertyNode().get();
|
||||
if (propertyNode) {
|
||||
use_tree_shadows
|
||||
= propertyNode->getBoolValue("/sim/rendering/random-vegetation-shadows",
|
||||
use_tree_shadows);
|
||||
use_tree_normals
|
||||
= propertyNode->getBoolValue("/sim/rendering/random-vegetation-normals",
|
||||
use_tree_normals);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = forestList.begin(); i != forestList.end(); ++i) {
|
||||
TreeBin* forest = *i;
|
||||
@@ -343,7 +381,7 @@ osg::Group* createForest(SGTreeBinList& forestList, const osg::Matrix& transform
|
||||
(!iter->second.lock(effect)))
|
||||
{
|
||||
SGPropertyNode_ptr effectProp = new SGPropertyNode;
|
||||
makeChild(effectProp, "inherits-from")->setStringValue("Effects/tree");
|
||||
makeChild(effectProp, "inherits-from")->setStringValue(forest->teffect);
|
||||
SGPropertyNode* params = makeChild(effectProp, "parameters");
|
||||
// emphasize n = 0
|
||||
params->getChild("texture", 0, true)->getChild("image", 0, true)
|
||||
|
||||
@@ -36,10 +36,10 @@ namespace simgear
|
||||
class TreeBin {
|
||||
public:
|
||||
struct Tree {
|
||||
Tree(const SGVec3f& p) :
|
||||
position(p)
|
||||
{ }
|
||||
SGVec3f position;
|
||||
SGVec3f tnormal;
|
||||
Tree(const SGVec3f& p, const SGVec3f& t) : position(p),tnormal(t)
|
||||
{ }
|
||||
};
|
||||
|
||||
typedef std::vector<Tree> TreeList;
|
||||
@@ -49,11 +49,13 @@ public:
|
||||
float height;
|
||||
float width;
|
||||
std::string texture;
|
||||
std::string teffect;
|
||||
|
||||
void insert(const Tree& t)
|
||||
{ _trees.push_back(t); }
|
||||
void insert(const SGVec3f& p, int t, float s)
|
||||
{ insert(Tree(p)); }
|
||||
|
||||
void insert(const SGVec3f& p, const SGVec3f& tnorm)
|
||||
{insert(Tree(p,tnorm));}
|
||||
|
||||
unsigned getNumTrees() const
|
||||
{ return _trees.size(); }
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user