Merge branch 'next' of https://git.code.sf.net/p/flightgear/simgear into next
This commit is contained in:
commit
e222c0888c
1
.gitignore
vendored
1
.gitignore
vendored
@ -15,3 +15,4 @@ install_manifest.txt
|
|||||||
build*
|
build*
|
||||||
Build
|
Build
|
||||||
CMakeLists.txt.user
|
CMakeLists.txt.user
|
||||||
|
3rdparty/expat_2.2.6/
|
||||||
|
2
3rdparty/expat/CMakeLists.txt
vendored
2
3rdparty/expat/CMakeLists.txt
vendored
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
configure_file (
|
configure_file (
|
||||||
"${PROJECT_SOURCE_DIR}/3rdparty/expat/expat_config_cmake.in"
|
"${PROJECT_SOURCE_DIR}/3rdparty/expat/expat_config_cmake.in"
|
||||||
"${PROJECT_BINARY_DIR}/3rdparty/expat/expat_config.h"
|
"${PROJECT_BINARY_DIR}/3rdparty/expat/simgear_expat_config.h"
|
||||||
)
|
)
|
||||||
|
|
||||||
set(expat_sources
|
set(expat_sources
|
||||||
|
2
3rdparty/expat/xmlparse.c
vendored
2
3rdparty/expat/xmlparse.c
vendored
@ -18,6 +18,8 @@
|
|||||||
#include "amigaconfig.h"
|
#include "amigaconfig.h"
|
||||||
#elif defined(__WATCOMC__)
|
#elif defined(__WATCOMC__)
|
||||||
#include "watcomconfig.h"
|
#include "watcomconfig.h"
|
||||||
|
#elif defined(HAVE_SIMGEAR_EXPAT_CONFIG_H)
|
||||||
|
#include "simgear_expat_config.h"
|
||||||
#elif defined(HAVE_EXPAT_CONFIG_H)
|
#elif defined(HAVE_EXPAT_CONFIG_H)
|
||||||
#include "expat_config.h"
|
#include "expat_config.h"
|
||||||
#endif /* ndef COMPILED_FROM_DSP */
|
#endif /* ndef COMPILED_FROM_DSP */
|
||||||
|
2
3rdparty/expat/xmlrole.c
vendored
2
3rdparty/expat/xmlrole.c
vendored
@ -12,6 +12,8 @@
|
|||||||
#include "amigaconfig.h"
|
#include "amigaconfig.h"
|
||||||
#elif defined(__WATCOMC__)
|
#elif defined(__WATCOMC__)
|
||||||
#include "watcomconfig.h"
|
#include "watcomconfig.h"
|
||||||
|
#elif defined(HAVE_SIMGEAR_EXPAT_CONFIG_H)
|
||||||
|
#include "simgear_expat_config.h"
|
||||||
#else
|
#else
|
||||||
#ifdef HAVE_EXPAT_CONFIG_H
|
#ifdef HAVE_EXPAT_CONFIG_H
|
||||||
#include "expat_config.h"
|
#include "expat_config.h"
|
||||||
|
2
3rdparty/expat/xmltok.c
vendored
2
3rdparty/expat/xmltok.c
vendored
@ -12,6 +12,8 @@
|
|||||||
#include "amigaconfig.h"
|
#include "amigaconfig.h"
|
||||||
#elif defined(__WATCOMC__)
|
#elif defined(__WATCOMC__)
|
||||||
#include "watcomconfig.h"
|
#include "watcomconfig.h"
|
||||||
|
#elif defined(HAVE_SIMGEAR_EXPAT_CONFIG_H)
|
||||||
|
#include "simgear_expat_config.h"
|
||||||
#else
|
#else
|
||||||
#ifdef HAVE_EXPAT_CONFIG_H
|
#ifdef HAVE_EXPAT_CONFIG_H
|
||||||
#include "expat_config.h"
|
#include "expat_config.h"
|
||||||
|
@ -10,6 +10,9 @@ if(COMMAND cmake_policy)
|
|||||||
if(POLICY CMP0067)
|
if(POLICY CMP0067)
|
||||||
cmake_policy(SET CMP0067 NEW)
|
cmake_policy(SET CMP0067 NEW)
|
||||||
endif()
|
endif()
|
||||||
|
if(POLICY CMP0093)
|
||||||
|
cmake_policy(SET CMP0093 NEW)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
@ -173,15 +176,17 @@ if (MSVC AND MSVC_3RDPARTY_ROOT)
|
|||||||
|
|
||||||
set( OSG_MSVC "msvc" )
|
set( OSG_MSVC "msvc" )
|
||||||
if (${MSVC_VERSION_MAJOR} EQUAL "19")
|
if (${MSVC_VERSION_MAJOR} EQUAL "19")
|
||||||
if (${MSVC_VERSION_MINOR} EQUAL "00")
|
if (${MSVC_VERSION_MINOR} GREATER_EQUAL "20")
|
||||||
set( OSG_MSVC ${OSG_MSVC}140 )
|
set( OSG_MSVC ${OSG_MSVC}142 )
|
||||||
else ()
|
elseif (${MSVC_VERSION_MINOR} GREATER_EQUAL "10")
|
||||||
set( OSG_MSVC ${OSG_MSVC}141 )
|
set( OSG_MSVC ${OSG_MSVC}141 )
|
||||||
|
else ()
|
||||||
|
set( OSG_MSVC ${OSG_MSVC}140 )
|
||||||
endif ()
|
endif ()
|
||||||
elseif (${MSVC_VERSION_MAJOR} EQUAL "18")
|
elseif (${MSVC_VERSION_MAJOR} EQUAL "18")
|
||||||
set( OSG_MSVC ${OSG_MSVC}120 )
|
set( OSG_MSVC ${OSG_MSVC}120 )
|
||||||
else ()
|
else ()
|
||||||
message(FATAL_ERROR "Visual Studio 2013/15/17 is required")
|
message(FATAL_ERROR "Visual Studio 2013 or higher is required")
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (CMAKE_CL_64)
|
if (CMAKE_CL_64)
|
||||||
@ -271,9 +276,6 @@ if (SYSTEM_EXPAT)
|
|||||||
|
|
||||||
else()
|
else()
|
||||||
message(STATUS "Using built-in expat code")
|
message(STATUS "Using built-in expat code")
|
||||||
# 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
|
set(EXPAT_INCLUDE_DIRS
|
||||||
${PROJECT_SOURCE_DIR}/3rdparty/expat
|
${PROJECT_SOURCE_DIR}/3rdparty/expat
|
||||||
${PROJECT_BINARY_DIR}/3rdparty/expat)
|
${PROJECT_BINARY_DIR}/3rdparty/expat)
|
||||||
@ -521,6 +523,10 @@ include(CheckCXXFeatures)
|
|||||||
# ahead of system-installed libs
|
# ahead of system-installed libs
|
||||||
include_directories(BEFORE ${PROJECT_BINARY_DIR}/simgear)
|
include_directories(BEFORE ${PROJECT_BINARY_DIR}/simgear)
|
||||||
|
|
||||||
|
if(${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD")
|
||||||
|
include_directories("/usr/X11R6/include")
|
||||||
|
endif()
|
||||||
|
|
||||||
add_definitions(-DHAVE_CONFIG_H)
|
add_definitions(-DHAVE_CONFIG_H)
|
||||||
|
|
||||||
# configure a header file to pass some of the CMake settings
|
# configure a header file to pass some of the CMake settings
|
||||||
|
@ -46,11 +46,17 @@ set(BOOST_TEST_TARGET_PREFIX "test")
|
|||||||
if(NOT Boost_FOUND)
|
if(NOT Boost_FOUND)
|
||||||
find_package(Boost 1.34.0 QUIET)
|
find_package(Boost 1.34.0 QUIET)
|
||||||
endif()
|
endif()
|
||||||
if("${Boost_VERSION}0" LESS "1034000")
|
|
||||||
|
if (NOT Boost_VERSION_MACRO)
|
||||||
|
# Compatibility with pre CMP0093 (CMake 3.15)
|
||||||
|
set(Boost_VERSION_MACRO ${Boost_VERSION})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if("${Boost_VERSION_MACRO}0" LESS "1034000")
|
||||||
set(_shared_msg
|
set(_shared_msg
|
||||||
"NOTE: boost::test-based targets and tests cannot "
|
"NOTE: boost::test-based targets and tests cannot "
|
||||||
"be added: boost >= 1.34.0 required but not found. "
|
"be added: boost >= 1.34.0 required but not found. "
|
||||||
"(found: '${Boost_VERSION}'; want >=103400) ")
|
"(found: '${Boost_VERSION_MACRO}'; want >=103400) ")
|
||||||
if(ENABLE_TESTS)
|
if(ENABLE_TESTS)
|
||||||
message(FATAL_ERROR
|
message(FATAL_ERROR
|
||||||
${_shared_msg}
|
${_shared_msg}
|
||||||
@ -66,7 +72,7 @@ endif()
|
|||||||
include(GetForceIncludeDefinitions)
|
include(GetForceIncludeDefinitions)
|
||||||
include(CopyResourcesToBuildTree)
|
include(CopyResourcesToBuildTree)
|
||||||
|
|
||||||
if(Boost_FOUND AND NOT "${Boost_VERSION}0" LESS "1034000")
|
if(Boost_FOUND AND NOT "${Boost_VERSION_MACRO}0" LESS "1034000")
|
||||||
set(_boosttesttargets_libs)
|
set(_boosttesttargets_libs)
|
||||||
set(_boostConfig "BoostTestTargetsIncluded.h")
|
set(_boostConfig "BoostTestTargetsIncluded.h")
|
||||||
if(NOT Boost_UNIT_TEST_FRAMEWORK_LIBRARY)
|
if(NOT Boost_UNIT_TEST_FRAMEWORK_LIBRARY)
|
||||||
@ -129,7 +135,7 @@ function(add_boost_test _name)
|
|||||||
"Syntax error in use of add_boost_test: at least one source file required!")
|
"Syntax error in use of add_boost_test: at least one source file required!")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(Boost_FOUND AND NOT "${Boost_VERSION}0" LESS "1034000")
|
if(Boost_FOUND AND NOT "${Boost_VERSION_MACRO}0" LESS "1034000")
|
||||||
|
|
||||||
include_directories(${Boost_INCLUDE_DIRS})
|
include_directories(${Boost_INCLUDE_DIRS})
|
||||||
|
|
||||||
@ -221,7 +227,7 @@ function(add_boost_test _name)
|
|||||||
set(_test_command ${_target_name})
|
set(_test_command ${_target_name})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(TESTS AND ( "${Boost_VERSION}" VERSION_GREATER "103799" ))
|
if(TESTS AND ( "${Boost_VERSION_MACRO}" VERSION_GREATER "103799" ))
|
||||||
foreach(_test ${TESTS})
|
foreach(_test ${TESTS})
|
||||||
add_test(
|
add_test(
|
||||||
${_name}-${_test}
|
${_name}-${_test}
|
||||||
|
@ -115,11 +115,22 @@ target_include_directories(SimGearCore BEFORE PUBLIC
|
|||||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>
|
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>
|
||||||
$<INSTALL_INTERFACE:include>)
|
$<INSTALL_INTERFACE:include>)
|
||||||
|
|
||||||
|
# so simgear/simgear_config.h is found
|
||||||
|
target_include_directories(SimGearCore BEFORE PUBLIC
|
||||||
|
$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}>
|
||||||
|
$<INSTALL_INTERFACE:include>)
|
||||||
|
|
||||||
target_include_directories(SimGearCore PUBLIC
|
target_include_directories(SimGearCore PUBLIC
|
||||||
${Boost_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIR})
|
${Boost_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIR})
|
||||||
target_include_directories(SimGearCore PRIVATE
|
target_include_directories(SimGearCore PRIVATE
|
||||||
${EXPAT_INCLUDE_DIRS} ${CURL_INCLUDE_DIRS})
|
${EXPAT_INCLUDE_DIRS} ${CURL_INCLUDE_DIRS})
|
||||||
|
|
||||||
|
if (NOT SYSTEM_EXPAT)
|
||||||
|
# XML_STATIC is important to avoid sg_expat_external.h
|
||||||
|
# declaring symbols as declspec(import)
|
||||||
|
target_compile_definitions(SimGearCore PRIVATE HAVE_SIMGEAR_EXPAT_CONFIG_H XML_STATIC)
|
||||||
|
endif()
|
||||||
|
|
||||||
install(TARGETS SimGearCore
|
install(TARGETS SimGearCore
|
||||||
EXPORT SimGearTargets
|
EXPORT SimGearTargets
|
||||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
@ -144,9 +155,10 @@ if (NOT SIMGEAR_HEADLESS)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# we expose ZLib in some of our headers
|
||||||
|
target_link_libraries(SimGearCore PUBLIC ${ZLIB_LIBRARY})
|
||||||
|
|
||||||
target_link_libraries(SimGearCore
|
target_link_libraries(SimGearCore PRIVATE
|
||||||
${ZLIB_LIBRARY}
|
|
||||||
${RT_LIBRARY}
|
${RT_LIBRARY}
|
||||||
${DL_LIBRARY}
|
${DL_LIBRARY}
|
||||||
${CMAKE_THREAD_LIBS_INIT}
|
${CMAKE_THREAD_LIBS_INIT}
|
||||||
@ -155,29 +167,29 @@ target_link_libraries(SimGearCore
|
|||||||
${WINSOCK_LIBRARY})
|
${WINSOCK_LIBRARY})
|
||||||
|
|
||||||
if(SYSTEM_EXPAT)
|
if(SYSTEM_EXPAT)
|
||||||
target_link_libraries(SimGearCore
|
target_link_libraries(SimGearCore PRIVATE ${EXPAT_LIBRARIES})
|
||||||
${EXPAT_LIBRARIES})
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(ENABLE_DNS AND SYSTEM_UDNS)
|
if(ENABLE_DNS AND SYSTEM_UDNS)
|
||||||
target_link_libraries(SimGearCore
|
target_link_libraries(SimGearCore PRIVATE ${UDNS_LIBRARIES})
|
||||||
${UDNS_LIBRARIES})
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT SIMGEAR_HEADLESS)
|
if(NOT SIMGEAR_HEADLESS)
|
||||||
target_include_directories(SimGearScene PRIVATE ${PROJECT_SOURCE_DIR}/simgear/canvas/ShivaVG/include)
|
target_include_directories(SimGearScene PRIVATE ${PROJECT_SOURCE_DIR}/simgear/canvas/ShivaVG/include)
|
||||||
|
|
||||||
target_link_libraries(SimGearScene
|
target_link_libraries(SimGearScene PUBLIC
|
||||||
SimGearCore
|
SimGearCore
|
||||||
${ZLIB_LIBRARY}
|
|
||||||
${OPENSCENEGRAPH_LIBRARIES}
|
${OPENSCENEGRAPH_LIBRARIES}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(SimGearScene PRIVATE
|
||||||
|
${ZLIB_LIBRARY}
|
||||||
${OPENAL_LIBRARY}
|
${OPENAL_LIBRARY}
|
||||||
${OPENGL_LIBRARY}
|
${OPENGL_LIBRARY}
|
||||||
${JPEG_LIBRARY})
|
${JPEG_LIBRARY})
|
||||||
|
|
||||||
if(ENABLE_GDAL)
|
if(ENABLE_GDAL)
|
||||||
target_link_libraries(SimGearScene
|
target_link_libraries(SimGearScene PRIVATE ${GDAL_LIBRARIES})
|
||||||
${GDAL_LIBRARIES})
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# only actually needed by canvas/KeyboardEvent.cxx
|
# only actually needed by canvas/KeyboardEvent.cxx
|
||||||
|
@ -68,8 +68,8 @@ namespace canvas
|
|||||||
Element* parent = 0 );
|
Element* parent = 0 );
|
||||||
virtual ~Window();
|
virtual ~Window();
|
||||||
|
|
||||||
virtual void update(double delta_time_sec);
|
void update(double delta_time_sec) override;
|
||||||
virtual void valueChanged(SGPropertyNode* node);
|
void valueChanged(SGPropertyNode* node) override;
|
||||||
|
|
||||||
const SGVec2<float> getPosition() const;
|
const SGVec2<float> getPosition() const;
|
||||||
const SGRect<float> getScreenRegion() const;
|
const SGRect<float> getScreenRegion() const;
|
||||||
@ -84,8 +84,8 @@ namespace canvas
|
|||||||
bool isResizable() const;
|
bool isResizable() const;
|
||||||
bool isCapturingEvents() const;
|
bool isCapturingEvents() const;
|
||||||
|
|
||||||
virtual void setVisible(bool visible);
|
void setVisible(bool visible) override;
|
||||||
virtual bool isVisible() const;
|
bool isVisible() const override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Moves window on top of all other windows with the same z-index.
|
* Moves window on top of all other windows with the same z-index.
|
||||||
|
@ -31,6 +31,11 @@
|
|||||||
// FreeBSD
|
// FreeBSD
|
||||||
#define VG_API_FREEBSD
|
#define VG_API_FREEBSD
|
||||||
|
|
||||||
|
#elif defined(__OpenBSD__)
|
||||||
|
|
||||||
|
// FreeBSD
|
||||||
|
#define VG_API_OPENBSD
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
// Unsupported system
|
// Unsupported system
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <float.h>
|
#include <float.h>
|
||||||
|
|
||||||
#if !defined(VG_API_MACOSX) && !defined(__FreeBSD__)
|
#if !defined(VG_API_MACOSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
|
||||||
# include <malloc.h>
|
# include <malloc.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -161,7 +161,7 @@ SHfloat getMaxFloat();
|
|||||||
|
|
||||||
/* OpenGL headers */
|
/* OpenGL headers */
|
||||||
|
|
||||||
#if defined(VG_API_LINUX) || defined(VG_API_FREEBSD)
|
#if defined(VG_API_LINUX) || defined(VG_API_FREEBSD) || defined(VG_API_OPENBSD)
|
||||||
#include <GL/gl.h>
|
#include <GL/gl.h>
|
||||||
#include <GL/glx.h>
|
#include <GL/glx.h>
|
||||||
#elif defined(VG_API_MACOSX)
|
#elif defined(VG_API_MACOSX)
|
||||||
|
@ -838,5 +838,98 @@ namespace canvas
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Image::fillRect(const SGRect<int>& rect, const std::string& c)
|
||||||
|
{
|
||||||
|
osg::Vec4 color(1,1,1,1);
|
||||||
|
if(!c.empty() && !parseColor(c, color))
|
||||||
|
return;
|
||||||
|
|
||||||
|
fillRect(rect, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fillRow(GLubyte* row, GLuint pixel, GLuint width, GLuint pixelBytes)
|
||||||
|
{
|
||||||
|
GLubyte* dst = row;
|
||||||
|
for (int x = 0; x < width; ++x) {
|
||||||
|
memcpy(dst, &pixel, pixelBytes);
|
||||||
|
dst += pixelBytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SGRect<int> intersectRect(const SGRect<int>& a, const SGRect<int>& b)
|
||||||
|
{
|
||||||
|
SGVec2<int> m1 = max(a.getMin(), b.getMin());
|
||||||
|
SGVec2<int> m2 = min(a.getMax(), b.getMax());
|
||||||
|
return SGRect<int>(m1, m2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Image::fillRect(const SGRect<int>& rect, const osg::Vec4& color)
|
||||||
|
{
|
||||||
|
osg::ref_ptr<osg::Image> image = _texture->getImage();
|
||||||
|
const auto format = image->getInternalTextureFormat();
|
||||||
|
|
||||||
|
auto clippedRect = intersectRect(rect, SGRect<int>(0, 0, image->s(), image->t()));
|
||||||
|
if ((clippedRect.width() == 0) || (clippedRect.height() == 0)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLubyte* rowData = nullptr;
|
||||||
|
size_t rowByteSize = 0;
|
||||||
|
GLuint pixelWidth = clippedRect.width();
|
||||||
|
GLuint pixel = 0;
|
||||||
|
GLuint pixelBytes = 0;
|
||||||
|
|
||||||
|
switch (format) {
|
||||||
|
case GL_RGBA8:
|
||||||
|
rowByteSize = pixelWidth * 4;
|
||||||
|
rowData = static_cast<GLubyte*>(alloca(rowByteSize));
|
||||||
|
|
||||||
|
// assume litte-endian, so read out backwards, hence when we memcpy
|
||||||
|
// the data, it ends up in RGBA order
|
||||||
|
pixel = color.asABGR();
|
||||||
|
pixelBytes = 4;
|
||||||
|
fillRow(rowData, pixel, pixelWidth, pixelBytes);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GL_RGB8:
|
||||||
|
rowByteSize = pixelWidth * 3;
|
||||||
|
rowData = static_cast<GLubyte*>(alloca(rowByteSize));
|
||||||
|
pixel = color.asABGR();
|
||||||
|
pixelBytes = 3;
|
||||||
|
fillRow(rowData, pixel, pixelWidth, pixelBytes);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
SG_LOG(SG_IO, SG_WARN, "Image::fillRect: unsupported internal image format:" << format);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int row=clippedRect.t(); row < clippedRect.b(); ++row) {
|
||||||
|
GLubyte* imageData = image->data(clippedRect.l(), row);
|
||||||
|
memcpy(imageData, rowData, rowByteSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
setImage(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Image::setPixel(int x, int y, const std::string& c)
|
||||||
|
{
|
||||||
|
osg::Vec4 color(1,1,1,1);
|
||||||
|
if(!c.empty() && !parseColor(c, color))
|
||||||
|
return;
|
||||||
|
|
||||||
|
setPixel(x, y, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Image::setPixel(int x, int y, const osg::Vec4& color)
|
||||||
|
{
|
||||||
|
osg::ref_ptr<osg::Image> image = _texture->getImage();
|
||||||
|
image->setColor(color, x, y);
|
||||||
|
|
||||||
|
// is this needed, or does OSG track modifications to the data
|
||||||
|
// automatically?
|
||||||
|
setImage(image);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace canvas
|
} // namespace canvas
|
||||||
} // namespace simgear
|
} // namespace simgear
|
||||||
|
@ -101,6 +101,22 @@ namespace canvas
|
|||||||
*/
|
*/
|
||||||
void setSourceRect(const SGRect<float>& sourceRect);
|
void setSourceRect(const SGRect<float>& sourceRect);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fill the specified rectangle of the image, with an RGB value
|
||||||
|
*/
|
||||||
|
void fillRect(const SGRect<int>& rect, const std::string& color);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fill the specified rectangle of the image, with an RGB value
|
||||||
|
*/
|
||||||
|
void fillRect(const SGRect<int>& rect, const osg::Vec4& color);
|
||||||
|
|
||||||
|
void setPixel(int x, int y, const std::string& color);
|
||||||
|
|
||||||
|
void setPixel(int x, int y, const osg::Vec4& color);
|
||||||
|
|
||||||
|
|
||||||
|
// void setRow(int row, int offset, )
|
||||||
protected:
|
protected:
|
||||||
enum ImageAttributes
|
enum ImageAttributes
|
||||||
{
|
{
|
||||||
|
@ -53,18 +53,19 @@ namespace canvas
|
|||||||
TextLine lineAt(size_t i) const;
|
TextLine lineAt(size_t i) const;
|
||||||
|
|
||||||
/// Get nearest line to given y-coordinate
|
/// Get nearest line to given y-coordinate
|
||||||
|
#if OSG_VERSION_LESS_THAN(3,6,5)
|
||||||
TextLine nearestLine(float pos_y) const;
|
TextLine nearestLine(float pos_y) const;
|
||||||
|
|
||||||
|
|
||||||
SGVec2i sizeForWidth(int w) const;
|
SGVec2i sizeForWidth(int w) const;
|
||||||
|
|
||||||
osg::BoundingBox
|
|
||||||
#if OSG_VERSION_LESS_THAN(3,3,2)
|
|
||||||
computeBound()
|
|
||||||
#else
|
#else
|
||||||
computeBoundingBox()
|
TextLine nearestLine(float pos_y);
|
||||||
|
SGVec2i sizeForWidth(int w);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if OSG_VERSION_LESS_THAN(3,3,2)
|
||||||
|
osg::BoundingBox computeBound() const override;
|
||||||
|
#else
|
||||||
|
osg::BoundingBox computeBoundingBox() const override;
|
||||||
#endif
|
#endif
|
||||||
const override;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend class TextLine;
|
friend class TextLine;
|
||||||
@ -126,7 +127,6 @@ namespace canvas
|
|||||||
|
|
||||||
_quads = &text->_textureGlyphQuadMap.begin()->second;
|
_quads = &text->_textureGlyphQuadMap.begin()->second;
|
||||||
|
|
||||||
|
|
||||||
#if OSG_VERSION_LESS_THAN(3,5,6)
|
#if OSG_VERSION_LESS_THAN(3,5,6)
|
||||||
GlyphQuads::LineNumbers const& line_numbers = _quads->_lineNumbers;
|
GlyphQuads::LineNumbers const& line_numbers = _quads->_lineNumbers;
|
||||||
GlyphQuads::LineNumbers::const_iterator begin_it =
|
GlyphQuads::LineNumbers::const_iterator begin_it =
|
||||||
@ -140,8 +140,7 @@ namespace canvas
|
|||||||
_end = std::upper_bound(begin_it, line_numbers.end(), _line)
|
_end = std::upper_bound(begin_it, line_numbers.end(), _line)
|
||||||
- line_numbers.begin();
|
- line_numbers.begin();
|
||||||
#else
|
#else
|
||||||
//OSG:TODO: Need 3.5.6 version of this
|
// TODO: Need 3.5.6 version of this
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,11 +170,17 @@ namespace canvas
|
|||||||
|
|
||||||
if( empty() )
|
if( empty() )
|
||||||
return pos;
|
return pos;
|
||||||
|
|
||||||
|
#if OSG_VERSION_GREATER_OR_EQUAL(3,5,6)
|
||||||
|
// TODO: need 3.5.6 version of this.
|
||||||
|
#else
|
||||||
#if OSG_VERSION_LESS_THAN(3,3,5)
|
#if OSG_VERSION_LESS_THAN(3,3,5)
|
||||||
GlyphQuads::Coords2 const& coords = _quads->_coords;
|
GlyphQuads::Coords2 const& coords = _quads->_coords;
|
||||||
#elif OSG_VERSION_LESS_THAN(3,5,6)
|
#else
|
||||||
GlyphQuads::Coords2 refCoords = _quads->_coords;
|
GlyphQuads::Coords2 refCoords = _quads->_coords;
|
||||||
GlyphQuads::Coords2::element_type &coords = *refCoords.get();
|
GlyphQuads::Coords2::element_type &coords = *refCoords.get();
|
||||||
|
#endif
|
||||||
|
|
||||||
size_t global_i = _begin + i;
|
size_t global_i = _begin + i;
|
||||||
|
|
||||||
if (global_i == _begin)
|
if (global_i == _begin)
|
||||||
@ -198,8 +203,6 @@ namespace canvas
|
|||||||
// position at center between characters
|
// position at center between characters
|
||||||
pos.x() = 0.5 * (prev_r + cur_l);
|
pos.x() = 0.5 * (prev_r + cur_l);
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
//OSG:TODO: need 3.5.7 version of this.
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return pos;
|
return pos;
|
||||||
@ -208,16 +211,21 @@ namespace canvas
|
|||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
osg::Vec2 TextLine::nearestCursor(float x) const
|
osg::Vec2 TextLine::nearestCursor(float x) const
|
||||||
{
|
{
|
||||||
if( empty() )
|
if (empty())
|
||||||
return cursorPos(0);
|
return cursorPos(0);
|
||||||
|
|
||||||
GlyphQuads::Glyphs const& glyphs = _quads->_glyphs;
|
#if OSG_VERSION_GREATER_OR_EQUAL(3,5,6)
|
||||||
|
// TODO: need 3.5.7 version of this.
|
||||||
|
return cursorPos(0);
|
||||||
|
#else
|
||||||
#if OSG_VERSION_LESS_THAN(3,3,5)
|
#if OSG_VERSION_LESS_THAN(3,3,5)
|
||||||
GlyphQuads::Coords2 const& coords = _quads->_coords;
|
GlyphQuads::Coords2 const& coords = _quads->_coords;
|
||||||
#elif OSG_VERSION_LESS_THAN(3,5,6)
|
#else
|
||||||
|
|
||||||
GlyphQuads::Coords2 refCoords = _quads->_coords;
|
GlyphQuads::Coords2 refCoords = _quads->_coords;
|
||||||
GlyphQuads::Coords2::element_type &coords = *refCoords.get();
|
GlyphQuads::Coords2::element_type &coords = *refCoords.get();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
GlyphQuads::Glyphs const& glyphs = _quads->_glyphs;
|
||||||
|
|
||||||
float const HIT_FRACTION = 0.6;
|
float const HIT_FRACTION = 0.6;
|
||||||
float const character_width = _text->getCharacterHeight()
|
float const character_width = _text->getCharacterHeight()
|
||||||
@ -237,9 +245,6 @@ namespace canvas
|
|||||||
}
|
}
|
||||||
|
|
||||||
return cursorPos(i - _begin);
|
return cursorPos(i - _begin);
|
||||||
#else
|
|
||||||
//OSG:TODO: need 3.5.7 version of this.
|
|
||||||
return cursorPos(0);
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,9 +324,16 @@ namespace canvas
|
|||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
|
#if OSG_VERSION_LESS_THAN(3,6,5)
|
||||||
TextLine Text::TextOSG::nearestLine(float pos_y) const
|
TextLine Text::TextOSG::nearestLine(float pos_y) const
|
||||||
{
|
{
|
||||||
osgText::Font const* font = getActiveFont();
|
osgText::Font const* font = getActiveFont();
|
||||||
|
#else
|
||||||
|
TextLine Text::TextOSG::nearestLine(float pos_y)
|
||||||
|
{
|
||||||
|
auto font = getActiveFont();
|
||||||
|
#endif
|
||||||
|
|
||||||
if( !font || lineCount() <= 0 )
|
if( !font || lineCount() <= 0 )
|
||||||
return TextLine(0, this);
|
return TextLine(0, this);
|
||||||
|
|
||||||
@ -343,12 +355,21 @@ namespace canvas
|
|||||||
// simplified version of osgText::Text::computeGlyphRepresentation() to
|
// simplified version of osgText::Text::computeGlyphRepresentation() to
|
||||||
// just calculate the size for a given weight. Glpyh calculations/creating
|
// just calculate the size for a given weight. Glpyh calculations/creating
|
||||||
// is not necessary for this...
|
// is not necessary for this...
|
||||||
|
#if OSG_VERSION_LESS_THAN(3,6,5)
|
||||||
SGVec2i Text::TextOSG::sizeForWidth(int w) const
|
SGVec2i Text::TextOSG::sizeForWidth(int w) const
|
||||||
|
#else
|
||||||
|
SGVec2i Text::TextOSG::sizeForWidth(int w)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
if( _text.empty() )
|
if( _text.empty() )
|
||||||
return SGVec2i(0, 0);
|
return SGVec2i(0, 0);
|
||||||
|
|
||||||
|
#if OSG_VERSION_LESS_THAN(3,6,5)
|
||||||
osgText::Font* activefont = const_cast<osgText::Font*>(getActiveFont());
|
osgText::Font* activefont = const_cast<osgText::Font*>(getActiveFont());
|
||||||
|
#else
|
||||||
|
auto activefont = getActiveFont();
|
||||||
|
#endif
|
||||||
|
|
||||||
if( !activefont )
|
if( !activefont )
|
||||||
return SGVec2i(-1, -1);
|
return SGVec2i(-1, -1);
|
||||||
|
|
||||||
@ -628,19 +649,16 @@ namespace canvas
|
|||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
osg::BoundingBox
|
|
||||||
#if OSG_VERSION_LESS_THAN(3,3,2)
|
#if OSG_VERSION_LESS_THAN(3,3,2)
|
||||||
Text::TextOSG::computeBound()
|
osg::BoundingBox Text::TextOSG::computeBound() const
|
||||||
#else
|
#else
|
||||||
Text::TextOSG::computeBoundingBox()
|
osg::BoundingBox Text::TextOSG::computeBoundingBox() const
|
||||||
#endif
|
#endif
|
||||||
const
|
|
||||||
{
|
{
|
||||||
osg::BoundingBox bb =
|
|
||||||
#if OSG_VERSION_LESS_THAN(3,3,2)
|
#if OSG_VERSION_LESS_THAN(3,3,2)
|
||||||
osgText::Text::computeBound();
|
osg::BoundingBox bb = osgText::Text::computeBound();
|
||||||
#else
|
#else
|
||||||
osgText::Text::computeBoundingBox();
|
osg::BoundingBox bb = osgText::Text::computeBoundingBox();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if OSG_VERSION_LESS_THAN(3,1,0)
|
#if OSG_VERSION_LESS_THAN(3,1,0)
|
||||||
|
@ -194,7 +194,13 @@ public:
|
|||||||
{
|
{
|
||||||
if (!shouldLog(c, p)) return;
|
if (!shouldLog(c, p)) return;
|
||||||
//fprintf(stderr, "%s\n", aMessage.c_str());
|
//fprintf(stderr, "%s\n", aMessage.c_str());
|
||||||
|
|
||||||
|
if (file && line != -1) {
|
||||||
|
fprintf(stderr, "%8.2f %s:%i: [%.8s]:%-10s %s\n", logTimer.elapsedMSec()/1000.0, file, line, debugPriorityToString(p), debugClassToString(c), aMessage.c_str());
|
||||||
|
}
|
||||||
|
else {
|
||||||
fprintf(stderr, "%8.2f [%.8s]:%-10s %s\n", logTimer.elapsedMSec()/1000.0, debugPriorityToString(p), debugClassToString(c), aMessage.c_str());
|
fprintf(stderr, "%8.2f [%.8s]:%-10s %s\n", logTimer.elapsedMSec()/1000.0, debugPriorityToString(p), debugClassToString(c), aMessage.c_str());
|
||||||
|
}
|
||||||
// file, line, aMessage.c_str());
|
// file, line, aMessage.c_str());
|
||||||
//fprintf(stderr, "%s:%d:%s:%d:%s\n", debugClassToString(c), p,
|
//fprintf(stderr, "%s:%d:%s:%d:%s\n", debugClassToString(c), p,
|
||||||
// file, line, aMessage.c_str());
|
// file, line, aMessage.c_str());
|
||||||
@ -407,6 +413,7 @@ public:
|
|||||||
bool m_stdout_isRedirectedAlready = false;
|
bool m_stdout_isRedirectedAlready = false;
|
||||||
#endif
|
#endif
|
||||||
bool m_developerMode = false;
|
bool m_developerMode = false;
|
||||||
|
bool m_fileLine = false;
|
||||||
|
|
||||||
// test suite mode.
|
// test suite mode.
|
||||||
bool m_testMode = false;
|
bool m_testMode = false;
|
||||||
@ -438,7 +445,7 @@ public:
|
|||||||
LogEntry entry(m_entries.pop());
|
LogEntry entry(m_entries.pop());
|
||||||
// special marker entry detected, terminate the thread since we are
|
// special marker entry detected, terminate the thread since we are
|
||||||
// making a configuration change or quitting the app
|
// making a configuration change or quitting the app
|
||||||
if ((entry.debugClass == SG_NONE) && !strcmp(entry.file, "done")) {
|
if ((entry.debugClass == SG_NONE) && entry.file && !strcmp(entry.file, "done")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
@ -535,6 +542,10 @@ public:
|
|||||||
const char* fileName, int line, const std::string& msg)
|
const char* fileName, int line, const std::string& msg)
|
||||||
{
|
{
|
||||||
p = translatePriority(p);
|
p = translatePriority(p);
|
||||||
|
if (!m_fileLine) {
|
||||||
|
/* This prevents output of file:line. */
|
||||||
|
line = -1;
|
||||||
|
}
|
||||||
LogEntry entry(c, p, fileName, line, msg);
|
LogEntry entry(c, p, fileName, line, msg);
|
||||||
m_entries.push(entry);
|
m_entries.push(entry);
|
||||||
}
|
}
|
||||||
@ -581,6 +592,10 @@ void logstream::setDeveloperMode(bool devMode)
|
|||||||
d->m_developerMode = devMode;
|
d->m_developerMode = devMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void logstream::setFileLine(bool fileLine)
|
||||||
|
{
|
||||||
|
d->m_fileLine = fileLine;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
logstream::addCallback(simgear::LogCallback* cb)
|
logstream::addCallback(simgear::LogCallback* cb)
|
||||||
|
@ -112,6 +112,12 @@ public:
|
|||||||
*/
|
*/
|
||||||
void setDeveloperMode(bool devMode);
|
void setDeveloperMode(bool devMode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set output of file:line mode on/off. If on, all log messages are
|
||||||
|
* prefixed by the file:line of the caller of SG_LOG().
|
||||||
|
*/
|
||||||
|
void setFileLine(bool fileLine);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the core logging method
|
* the core logging method
|
||||||
*/
|
*/
|
||||||
|
@ -32,20 +32,23 @@ class TestThreadRecipient : public simgear::Emesary::IReceiver
|
|||||||
public:
|
public:
|
||||||
TestThreadRecipient() : receiveCount(0)
|
TestThreadRecipient() : receiveCount(0)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::atomic<int> receiveCount;
|
std::atomic<int> receiveCount;
|
||||||
|
|
||||||
virtual simgear::Emesary::ReceiptStatus Receive(simgear::Emesary::INotification &n)
|
virtual simgear::Emesary::ReceiptStatus Receive(simgear::Emesary::INotification &n)
|
||||||
{
|
{
|
||||||
if (n.GetType() == (const char*)this)
|
if (n.GetType() == (const char*)this)
|
||||||
{
|
{
|
||||||
TestThreadNotification *tn = dynamic_cast<TestThreadNotification *>(&n);
|
// Unused: TestThreadNotification *tn = dynamic_cast<TestThreadNotification *>(&n);
|
||||||
receiveCount++;
|
receiveCount++;
|
||||||
|
|
||||||
TestThreadNotification onwardNotification("AL");
|
TestThreadNotification onwardNotification("AL");
|
||||||
simgear::Emesary::GlobalTransmitter::instance()->NotifyAll(onwardNotification);
|
simgear::Emesary::GlobalTransmitter::instance()->NotifyAll(onwardNotification);
|
||||||
|
|
||||||
return simgear::Emesary::ReceiptStatusOK;
|
return simgear::Emesary::ReceiptStatusOK;
|
||||||
}
|
}
|
||||||
|
|
||||||
return simgear::Emesary::ReceiptStatusOK;
|
return simgear::Emesary::ReceiptStatusOK;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -38,9 +38,12 @@
|
|||||||
# include <simgear_config.h>
|
# include <simgear_config.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <iomanip>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <ostream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
#include <simgear/debug/logstream.hxx>
|
#include <simgear/debug/logstream.hxx>
|
||||||
#include <simgear/structure/exception.hxx>
|
#include <simgear/structure/exception.hxx>
|
||||||
@ -155,6 +158,320 @@ SGMetar::~SGMetar()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const char *azimuthName(double d)
|
||||||
|
{
|
||||||
|
const char *dir[] = {
|
||||||
|
"N", "NNE", "NE", "ENE",
|
||||||
|
"E", "ESE", "SE", "SSE",
|
||||||
|
"S", "SSW", "SW", "WSW",
|
||||||
|
"W", "WNW", "NW", "NNW"
|
||||||
|
};
|
||||||
|
d += 11.25;
|
||||||
|
while (d < 0)
|
||||||
|
d += 360;
|
||||||
|
while (d >= 360)
|
||||||
|
d -= 360;
|
||||||
|
return dir[int(d / 22.5)];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// round double to 10^g
|
||||||
|
static double rnd(double r, int g = 0)
|
||||||
|
{
|
||||||
|
double f = pow(10.0, g);
|
||||||
|
return f * floor(r / f + 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* A manipulator that can use spaces to emulate tab characters. */
|
||||||
|
struct Tab
|
||||||
|
{
|
||||||
|
/* If <stops> is 0, we simply insert tab characters. Otherwise we insert
|
||||||
|
spaces to align with the next column at multiple of <stops>. */
|
||||||
|
explicit Tab(int stops)
|
||||||
|
:
|
||||||
|
_stops(stops)
|
||||||
|
{}
|
||||||
|
int _stops;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::ostream& operator << (std::ostream& out, const Tab& t)
|
||||||
|
{
|
||||||
|
if (t._stops == 0) {
|
||||||
|
return out << '\t';
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostringstream& out2 = *(std::ostringstream*) &out;
|
||||||
|
std::string s = out2.str();
|
||||||
|
|
||||||
|
if (t._stops < 0) {
|
||||||
|
if (!s.size() || s[s.size()-1] != ' ') {
|
||||||
|
out << ' ';
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto nl = s.rfind('\n');
|
||||||
|
if (nl < 0) nl = 0;
|
||||||
|
int column = 0;
|
||||||
|
for (auto i = nl+1; i != s.size(); ++i) {
|
||||||
|
if (s[i] == '\t')
|
||||||
|
column = (column + t._stops) / t._stops * t._stops;
|
||||||
|
else
|
||||||
|
column += 1;
|
||||||
|
}
|
||||||
|
int column2 = (column + t._stops) / t._stops * t._stops;
|
||||||
|
for (int i=column; i<column2; ++i) {
|
||||||
|
out << ' ';
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Manipulator for SGMetarVisibility using a Tab. */
|
||||||
|
struct SGMetarVisibilityManip
|
||||||
|
{
|
||||||
|
explicit SGMetarVisibilityManip(const SGMetarVisibility& v, const Tab& tab)
|
||||||
|
:
|
||||||
|
_v(v),
|
||||||
|
_tab(tab)
|
||||||
|
{}
|
||||||
|
const SGMetarVisibility& _v;
|
||||||
|
const Tab& _tab;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::ostream& operator << (std::ostream& out, const SGMetarVisibilityManip& v)
|
||||||
|
{
|
||||||
|
int m = v._v.getModifier();
|
||||||
|
const char *mod;
|
||||||
|
if (m == SGMetarVisibility::GREATER_THAN)
|
||||||
|
mod = ">=";
|
||||||
|
else if (m == SGMetarVisibility::LESS_THAN)
|
||||||
|
mod = "<";
|
||||||
|
else
|
||||||
|
mod = "";
|
||||||
|
out << mod;
|
||||||
|
|
||||||
|
double dist = rnd(v._v.getVisibility_m(), 1);
|
||||||
|
if (dist < 1000.0)
|
||||||
|
out << rnd(dist, 1) << " m";
|
||||||
|
else
|
||||||
|
out << rnd(dist / 1000.0, -1) << " km";
|
||||||
|
|
||||||
|
const char *dir = "";
|
||||||
|
int i;
|
||||||
|
if ((i = v._v.getDirection()) != -1) {
|
||||||
|
dir = azimuthName(i);
|
||||||
|
out << " " << dir;
|
||||||
|
}
|
||||||
|
out << v._tab << v._tab << v._tab << v._tab << v._tab;
|
||||||
|
out << mod << rnd(v._v.getVisibility_sm(), -1) << " US-miles " << dir;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string SGMetar::getDescription(int tabstops) const
|
||||||
|
{
|
||||||
|
std::ostringstream out;
|
||||||
|
const char *s;
|
||||||
|
char buf[256];
|
||||||
|
double d;
|
||||||
|
int i, lineno;
|
||||||
|
Tab tab(tabstops);
|
||||||
|
|
||||||
|
if ((i = getReportType()) == SGMetar::AUTO)
|
||||||
|
out << "(METAR automatically generated)\n";
|
||||||
|
else if (i == SGMetar::COR)
|
||||||
|
out << "(METAR manually corrected)\n";
|
||||||
|
else if (i == SGMetar::RTD)
|
||||||
|
out << "(METAR routine delayed)\n";
|
||||||
|
|
||||||
|
out << "Airport-Id:" << tab << tab << getId() << "\n";
|
||||||
|
|
||||||
|
// date/time
|
||||||
|
int year = getYear();
|
||||||
|
int month = getMonth();
|
||||||
|
out << "Report time:" << tab << tab << year << '/' << month << '/' << getDay();
|
||||||
|
out << ' ' << getHour() << ':';
|
||||||
|
out << std::setw(2) << std::setfill('0') << getMinute() << " UTC\n";
|
||||||
|
|
||||||
|
|
||||||
|
// visibility
|
||||||
|
SGMetarVisibility minvis = getMinVisibility();
|
||||||
|
SGMetarVisibility maxvis = getMaxVisibility();
|
||||||
|
double min = minvis.getVisibility_m();
|
||||||
|
double max = maxvis.getVisibility_m();
|
||||||
|
if (min != NaN) {
|
||||||
|
if (max != NaN) {
|
||||||
|
out << "min. Visibility:" << tab << SGMetarVisibilityManip(minvis, tab) << "\n";
|
||||||
|
out << "max. Visibility:" << tab << SGMetarVisibilityManip(maxvis, tab) << "\n";
|
||||||
|
} else {
|
||||||
|
out << "Visibility:" << tab << tab << SGMetarVisibilityManip(minvis, tab) << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// directed visibility
|
||||||
|
const SGMetarVisibility *dirvis = getDirVisibility();
|
||||||
|
for (i = 0; i < 8; i++, dirvis++)
|
||||||
|
if (dirvis->getVisibility_m() != NaN)
|
||||||
|
out << tab << tab << tab << SGMetarVisibilityManip(*dirvis, tab) << "\n";
|
||||||
|
|
||||||
|
|
||||||
|
// vertical visibility
|
||||||
|
SGMetarVisibility vertvis = getVertVisibility();
|
||||||
|
if ((d = vertvis.getVisibility_ft()) != NaN)
|
||||||
|
out << "Vert. visibility:" << tab << SGMetarVisibilityManip(vertvis, tab) << "\n";
|
||||||
|
else if (vertvis.getModifier() == SGMetarVisibility::NOGO)
|
||||||
|
out << "Vert. visibility:" << tab << "impossible to determine" << "\n";
|
||||||
|
|
||||||
|
|
||||||
|
// wind
|
||||||
|
d = getWindSpeed_kmh();
|
||||||
|
out << "Wind:" << tab << tab << tab;
|
||||||
|
if (d < .1)
|
||||||
|
out << "none" << "\n";
|
||||||
|
else {
|
||||||
|
if ((i = getWindDir()) == -1)
|
||||||
|
out << "from variable directions";
|
||||||
|
else
|
||||||
|
out << "from the " << azimuthName(i) << " (" << i << " deg)";
|
||||||
|
out << " at " << rnd(d, -1) << " km/h";
|
||||||
|
|
||||||
|
out << tab << tab << rnd(getWindSpeed_kt(), -1) << " kt";
|
||||||
|
out << " = " << rnd(getWindSpeed_mph(), -1) << " mph";
|
||||||
|
out << " = " << rnd(getWindSpeed_mps(), -1) << " m/s";
|
||||||
|
out << "\n";
|
||||||
|
|
||||||
|
d = getGustSpeed_kmh();
|
||||||
|
if (d != NaN && d != 0) {
|
||||||
|
out << tab << tab << tab << "with gusts at " << rnd(d, -1) << " km/h";
|
||||||
|
out << tab << tab << tab << rnd(getGustSpeed_kt(), -1) << " kt";
|
||||||
|
out << " = " << rnd(getGustSpeed_mph(), -1) << " mph";
|
||||||
|
out << " = " << rnd(getGustSpeed_mps(), -1) << " m/s";
|
||||||
|
out << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
int from = getWindRangeFrom();
|
||||||
|
int to = getWindRangeTo();
|
||||||
|
if (from != to) {
|
||||||
|
out << tab << tab << tab << "variable from " << azimuthName(from);
|
||||||
|
out << " to " << azimuthName(to);
|
||||||
|
out << " (" << from << "deg --" << to << " deg)" << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// temperature/humidity/air pressure
|
||||||
|
if ((d = getTemperature_C()) != NaN) {
|
||||||
|
out << "Temperature:" << tab << tab << d << " C" << tab << tab << tab << tab << tab;
|
||||||
|
out << rnd(getTemperature_F(), -1) << " F" << "\n";
|
||||||
|
|
||||||
|
if ((d = getDewpoint_C()) != NaN) {
|
||||||
|
out << "Dewpoint:" << tab << tab << d << " C" << tab << tab << tab << tab << tab;
|
||||||
|
out << rnd(getDewpoint_F(), -1) << " F" << "\n";
|
||||||
|
out << "Rel. Humidity: " << tab << tab << rnd(getRelHumidity()) << " %" << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((d = getPressure_hPa()) != NaN) {
|
||||||
|
out << "Pressure:" << tab << tab << rnd(d) << " hPa" << tab << tab << tab << tab;
|
||||||
|
out << rnd(getPressure_inHg(), -2) << " in. Hg" << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// weather phenomena
|
||||||
|
vector<string> wv = getWeather();
|
||||||
|
vector<string>::iterator weather;
|
||||||
|
for (i = 0, weather = wv.begin(); weather != wv.end(); weather++, i++) {
|
||||||
|
out << (i ? ", " : "Weather:") << tab << tab << weather->c_str();
|
||||||
|
}
|
||||||
|
if (i)
|
||||||
|
out << "\n";
|
||||||
|
|
||||||
|
|
||||||
|
// cloud layers
|
||||||
|
const char *coverage_string[5] = {
|
||||||
|
"clear skies", "few clouds", "scattered clouds", "broken clouds", "sky overcast"
|
||||||
|
};
|
||||||
|
vector<SGMetarCloud> cv = getClouds();
|
||||||
|
vector<SGMetarCloud>::iterator cloud;
|
||||||
|
for (lineno = 0, cloud = cv.begin(); cloud != cv.end(); cloud++, lineno++) {
|
||||||
|
if (lineno) out << tab << tab << tab;
|
||||||
|
else out << "Sky condition:" << tab << tab;
|
||||||
|
|
||||||
|
if ((i = cloud->getCoverage()) != -1)
|
||||||
|
out << coverage_string[i];
|
||||||
|
if ((d = cloud->getAltitude_ft()) != NaN)
|
||||||
|
out << " at " << rnd(d, 1) << " ft";
|
||||||
|
if ((s = cloud->getTypeLongString()))
|
||||||
|
out << " (" << s << ')';
|
||||||
|
if (d != NaN)
|
||||||
|
out << tab << tab << tab << rnd(cloud->getAltitude_m(), 1) << " m";
|
||||||
|
out << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// runways
|
||||||
|
map<string, SGMetarRunway> rm = getRunways();
|
||||||
|
map<string, SGMetarRunway>::iterator runway;
|
||||||
|
for (runway = rm.begin(); runway != rm.end(); runway++) {
|
||||||
|
lineno = 0;
|
||||||
|
if (!strcmp(runway->first.c_str(), "ALL"))
|
||||||
|
out << "All runways:" << tab << tab;
|
||||||
|
else
|
||||||
|
out << "Runway " << runway->first << ":" << tab << tab;
|
||||||
|
SGMetarRunway rwy = runway->second;
|
||||||
|
|
||||||
|
// assemble surface string
|
||||||
|
vector<string> surface;
|
||||||
|
if ((s = rwy.getDepositString()) && strlen(s))
|
||||||
|
surface.push_back(s);
|
||||||
|
if ((s = rwy.getExtentString()) && strlen(s))
|
||||||
|
surface.push_back(s);
|
||||||
|
if ((d = rwy.getDepth()) != NaN) {
|
||||||
|
sprintf(buf, "%.1lf mm", d * 1000.0);
|
||||||
|
surface.push_back(buf);
|
||||||
|
}
|
||||||
|
if ((s = rwy.getFrictionString()) && strlen(s))
|
||||||
|
surface.push_back(s);
|
||||||
|
if ((d = rwy.getFriction()) != NaN) {
|
||||||
|
sprintf(buf, "friction: %.2lf", d);
|
||||||
|
surface.push_back(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! surface.empty()) {
|
||||||
|
vector<string>::iterator rwysurf = surface.begin();
|
||||||
|
for (i = 0; rwysurf != surface.end(); rwysurf++, i++) {
|
||||||
|
if (i)
|
||||||
|
out << ", ";
|
||||||
|
out << *rwysurf;
|
||||||
|
}
|
||||||
|
lineno++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// assemble visibility string
|
||||||
|
SGMetarVisibility minvis = rwy.getMinVisibility();
|
||||||
|
SGMetarVisibility maxvis = rwy.getMaxVisibility();
|
||||||
|
if ((d = minvis.getVisibility_m()) != NaN) {
|
||||||
|
if (lineno++)
|
||||||
|
out << "\n" << tab << tab << tab;
|
||||||
|
out << SGMetarVisibilityManip(minvis, tab);
|
||||||
|
}
|
||||||
|
if (maxvis.getVisibility_m() != d) {
|
||||||
|
out << "\n" << tab << tab << tab << SGMetarVisibilityManip(maxvis, tab) << "\n";
|
||||||
|
lineno++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rwy.getWindShear()) {
|
||||||
|
if (lineno++)
|
||||||
|
out << "\n" << tab << tab << tab;
|
||||||
|
out << "critical wind shear" << "\n";
|
||||||
|
}
|
||||||
|
out << "\n";
|
||||||
|
}
|
||||||
|
out << "\n";
|
||||||
|
return out.str();
|
||||||
|
}
|
||||||
|
|
||||||
void SGMetar::useCurrentDate()
|
void SGMetar::useCurrentDate()
|
||||||
{
|
{
|
||||||
struct tm now;
|
struct tm now;
|
||||||
|
@ -235,6 +235,11 @@ public:
|
|||||||
inline const std::vector<std::string>& getWeather() const { return _weather; }
|
inline const std::vector<std::string>& getWeather() const { return _weather; }
|
||||||
inline const std::vector<struct Weather> getWeather2() const { return _weather2; }
|
inline const std::vector<struct Weather> getWeather2() const { return _weather2; }
|
||||||
|
|
||||||
|
/* Returns human-readable description. If tabtops is 0, we use tab
|
||||||
|
characters. If +ve we use spaces to pad to multiple of <tabstops>. If
|
||||||
|
-1 all sequences of tabs are represented by a single space. */
|
||||||
|
std::string getDescription(int tabstops) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::string _url;
|
std::string _url;
|
||||||
int _grpcount;
|
int _grpcount;
|
||||||
|
@ -431,7 +431,7 @@ void test_ZlibDecompressorIStream_readPutbackEtc()
|
|||||||
try {
|
try {
|
||||||
// 'Z' is not the last character read from the stream
|
// 'Z' is not the last character read from the stream
|
||||||
decompressor.putback('Z');
|
decompressor.putback('Z');
|
||||||
} catch (std::ios_base::failure) {
|
} catch (const std::ios_base::failure&) {
|
||||||
gotException = true;
|
gotException = true;
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
// gcc fails to catch std::ios_base::failure due to an inconsistent C++11
|
// gcc fails to catch std::ios_base::failure due to an inconsistent C++11
|
||||||
|
@ -48,6 +48,8 @@ namespace simgear
|
|||||||
assert(outer);
|
assert(outer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual ~ArchiveExtractorPrivate() = default;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
INVALID = 0,
|
INVALID = 0,
|
||||||
READING_HEADER,
|
READING_HEADER,
|
||||||
@ -614,10 +616,7 @@ ArchiveExtractor::ArchiveExtractor(const SGPath& rootPath) :
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
ArchiveExtractor::~ArchiveExtractor()
|
ArchiveExtractor::~ArchiveExtractor() = default;
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void ArchiveExtractor::extractBytes(const uint8_t* bytes, size_t count)
|
void ArchiveExtractor::extractBytes(const uint8_t* bytes, size_t count)
|
||||||
{
|
{
|
||||||
|
@ -34,7 +34,7 @@ class ArchiveExtractor
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ArchiveExtractor(const SGPath& rootPath);
|
ArchiveExtractor(const SGPath& rootPath);
|
||||||
~ArchiveExtractor();
|
virtual ~ArchiveExtractor();
|
||||||
|
|
||||||
enum DetermineResult
|
enum DetermineResult
|
||||||
{
|
{
|
||||||
|
@ -193,8 +193,8 @@ min(S s, SGVec2<T> v)
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
inline
|
inline
|
||||||
SGVec2<T>
|
SGVec2<T>
|
||||||
max(const SGVec2<T>& v1, const SGVec2<T>& v2)
|
max(SGVec2<T> v1, const SGVec2<T>& v2)
|
||||||
{ v1 = simd4::max(v1.simd2(), v2.simd2()); return v1; }
|
{ v1.simd2() = simd4::max(v1.simd2(), v2.simd2()); return v1; }
|
||||||
template<typename S, typename T>
|
template<typename S, typename T>
|
||||||
inline
|
inline
|
||||||
SGVec2<T>
|
SGVec2<T>
|
||||||
@ -375,6 +375,29 @@ interpolate(T tau, const SGVec2<T>& v1, const SGVec2<T>& v2)
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function for point_in_triangle
|
||||||
|
template <typename T>
|
||||||
|
inline
|
||||||
|
T
|
||||||
|
pt_determine(const SGVec2<T>& pt1, const SGVec2<T>& pt2, const SGVec2<T>& pt3)
|
||||||
|
{
|
||||||
|
return (pt1.x()-pt3.x()) * (pt2.y()-pt3.y()) - (pt2.x() - pt3.x()) * (pt1.y() - pt3.y());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is testpt inside the triangle formed by the other three points?
|
||||||
|
template <typename T>
|
||||||
|
inline
|
||||||
|
bool
|
||||||
|
point_in_triangle(const SGVec2<T>& testpt, const SGVec2<T>& pt1, const SGVec2<T>& pt2, const SGVec2<T>& pt3)
|
||||||
|
{
|
||||||
|
T d1 = pt_determine(testpt,pt1,pt2);
|
||||||
|
T d2 = pt_determine(testpt,pt2,pt3);
|
||||||
|
T d3 = pt_determine(testpt,pt3,pt1);
|
||||||
|
bool has_neg = (d1 < 0) || (d2 < 0) || (d3 < 0);
|
||||||
|
bool has_pos = (d1 > 0) || (d2 > 0) || (d3 > 0);
|
||||||
|
return !(has_neg && has_pos);
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline
|
inline
|
||||||
|
@ -1043,7 +1043,7 @@ std::string error_string(int errnum)
|
|||||||
retcode = strerror_s(buf, sizeof(buf), errnum);
|
retcode = strerror_s(buf, sizeof(buf), errnum);
|
||||||
#elif defined(_GNU_SOURCE)
|
#elif defined(_GNU_SOURCE)
|
||||||
return std::string(strerror_r(errnum, buf, sizeof(buf)));
|
return std::string(strerror_r(errnum, buf, sizeof(buf)));
|
||||||
#elif (_POSIX_C_SOURCE >= 200112L) || defined(SG_MAC) || defined(__FreeBSD__)
|
#elif (_POSIX_C_SOURCE >= 200112L) || defined(SG_MAC) || defined(__FreeBSD__) || defined(__OpenBSD__)
|
||||||
int retcode;
|
int retcode;
|
||||||
// POSIX.1-2001 and POSIX.1-2008
|
// POSIX.1-2001 and POSIX.1-2008
|
||||||
retcode = strerror_r(errnum, buf, sizeof(buf));
|
retcode = strerror_r(errnum, buf, sizeof(buf));
|
||||||
|
@ -235,6 +235,21 @@ void naFreeContext(naContext c)
|
|||||||
if(c->callChild) naFreeContext(c->callChild);
|
if(c->callChild) naFreeContext(c->callChild);
|
||||||
if(c->callParent) c->callParent->callChild = 0;
|
if(c->callParent) c->callParent->callChild = 0;
|
||||||
LOCK();
|
LOCK();
|
||||||
|
|
||||||
|
// 2019-09-21
|
||||||
|
// James adding this to ensure stray stuff in freed contexts gets GCed
|
||||||
|
// this shows up when doing a reset / shutdown of all Nasal - we drop
|
||||||
|
// all our contexts and saved refs, and run a GC pass. We expect *everything*
|
||||||
|
// to be freed but actually the freed contexts often have a ref in their
|
||||||
|
// opStack.
|
||||||
|
//
|
||||||
|
// The underlying cause is likely some operation which leaves a value on
|
||||||
|
// the opstack accidently, but tracing that down requires more Nasal-fu
|
||||||
|
// than I have right now. So instead I'm clearing the stack tops here, so
|
||||||
|
// a freed context looks the same as a new one returned by initContext.
|
||||||
|
|
||||||
|
c->fTop = c->opTop = c->markTop = 0;
|
||||||
|
|
||||||
c->nextFree = globals->freeContexts;
|
c->nextFree = globals->freeContexts;
|
||||||
globals->freeContexts = c;
|
globals->freeContexts = c;
|
||||||
UNLOCK();
|
UNLOCK();
|
||||||
|
@ -67,7 +67,6 @@ namespace nasal
|
|||||||
public:
|
public:
|
||||||
NasalMainLoopRecipient() : receiveCount(0) {
|
NasalMainLoopRecipient() : receiveCount(0) {
|
||||||
simgear::Emesary::GlobalTransmitter::instance()->Register(*this);
|
simgear::Emesary::GlobalTransmitter::instance()->Register(*this);
|
||||||
SG_LOG(SG_NASAL, SG_INFO, "NasalMainLoopRecipient created");
|
|
||||||
}
|
}
|
||||||
virtual ~NasalMainLoopRecipient() {
|
virtual ~NasalMainLoopRecipient() {
|
||||||
simgear::Emesary::GlobalTransmitter::instance()->DeRegister(*this);
|
simgear::Emesary::GlobalTransmitter::instance()->DeRegister(*this);
|
||||||
|
@ -207,6 +207,15 @@ namespace nasal
|
|||||||
return Vec2(vec[0], vec[1]);
|
return Vec2(vec[0], vec[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class Vec4>
|
||||||
|
std::enable_if_t<is_vec4<Vec4>::value, Vec4>
|
||||||
|
from_nasal_helper(naContext c, naRef ref, const Vec4*)
|
||||||
|
{
|
||||||
|
auto vec = from_nasal_helper(c, ref, static_cast<std::array<double, 4>*>(0));
|
||||||
|
return Vec4(vec[0], vec[1], vec[2], vec[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a Nasal vector with 4 elements ([x, y, w, h]) to an SGRect
|
* Convert a Nasal vector with 4 elements ([x, y, w, h]) to an SGRect
|
||||||
*/
|
*/
|
||||||
|
@ -28,6 +28,7 @@ class SGWeakReferenced;
|
|||||||
template<class T> class SGSharedPtr;
|
template<class T> class SGSharedPtr;
|
||||||
template<class T> class SGWeakPtr;
|
template<class T> class SGWeakPtr;
|
||||||
template<class T> class SGVec2;
|
template<class T> class SGVec2;
|
||||||
|
template<class T> class SGVec4;
|
||||||
|
|
||||||
namespace boost
|
namespace boost
|
||||||
{
|
{
|
||||||
@ -47,6 +48,9 @@ namespace osg
|
|||||||
class Vec2d;
|
class Vec2d;
|
||||||
class Vec2f;
|
class Vec2f;
|
||||||
class Vec2s;
|
class Vec2s;
|
||||||
|
|
||||||
|
class Vec4f;
|
||||||
|
class Vec4d;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The actual traits...
|
// The actual traits...
|
||||||
@ -55,6 +59,10 @@ namespace nasal
|
|||||||
template<class T>
|
template<class T>
|
||||||
struct is_vec2: public std::false_type {};
|
struct is_vec2: public std::false_type {};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct is_vec4: public std::false_type {};
|
||||||
|
|
||||||
|
|
||||||
#define SG_MAKE_TRAIT(templ,type,attr)\
|
#define SG_MAKE_TRAIT(templ,type,attr)\
|
||||||
template templ\
|
template templ\
|
||||||
struct attr< type >: public std::true_type {};
|
struct attr< type >: public std::true_type {};
|
||||||
@ -65,6 +73,10 @@ SG_MAKE_TRAIT(<>, osg::Vec2d, is_vec2)
|
|||||||
SG_MAKE_TRAIT(<>, osg::Vec2f, is_vec2)
|
SG_MAKE_TRAIT(<>, osg::Vec2f, is_vec2)
|
||||||
SG_MAKE_TRAIT(<>, osg::Vec2s, is_vec2)
|
SG_MAKE_TRAIT(<>, osg::Vec2s, is_vec2)
|
||||||
|
|
||||||
|
SG_MAKE_TRAIT(<class T>, SGVec4<T>, is_vec4)
|
||||||
|
SG_MAKE_TRAIT(<>, osg::Vec4d, is_vec4)
|
||||||
|
SG_MAKE_TRAIT(<>, osg::Vec4f, is_vec4)
|
||||||
|
|
||||||
#undef SG_MAKE_TRAIT
|
#undef SG_MAKE_TRAIT
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
|
@ -420,10 +420,14 @@ naRef naParseCode(struct Context* c, naRef srcFile, int firstLine,
|
|||||||
|
|
||||||
// Catch parser errors here.
|
// Catch parser errors here.
|
||||||
p.errLine = *errLine = 1;
|
p.errLine = *errLine = 1;
|
||||||
if(setjmp(p.jumpHandle)) {
|
if (setjmp(p.jumpHandle)) {
|
||||||
strncpy(c->error, p.err, sizeof(c->error));
|
size_t end_ = sizeof(c->error) - 1;
|
||||||
|
strncpy(c->error, p.err, end_);
|
||||||
|
c->error[end_] = '\0';
|
||||||
|
|
||||||
*errLine = p.errLine;
|
*errLine = p.errLine;
|
||||||
naParseDestroy(&p);
|
naParseDestroy(&p);
|
||||||
|
|
||||||
return naNil();
|
return naNil();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -849,7 +849,7 @@ SGPropertyNode::trace_read () const
|
|||||||
* Last used attribute
|
* Last used attribute
|
||||||
* Update as needed when enum Attribute is changed
|
* Update as needed when enum Attribute is changed
|
||||||
*/
|
*/
|
||||||
const int SGPropertyNode::LAST_USED_ATTRIBUTE = PROTECTED;
|
const int SGPropertyNode::LAST_USED_ATTRIBUTE = LISTENER_SAFE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default constructor: always creates a root node.
|
* Default constructor: always creates a root node.
|
||||||
|
@ -821,6 +821,7 @@ public:
|
|||||||
USERARCHIVE = 64,
|
USERARCHIVE = 64,
|
||||||
PRESERVE = 128,
|
PRESERVE = 128,
|
||||||
PROTECTED = 1 << 8,
|
PROTECTED = 1 << 8,
|
||||||
|
LISTENER_SAFE = 1 << 9, /// it's safe to listen to this property, even if it's tied
|
||||||
// beware: if you add another attribute here,
|
// beware: if you add another attribute here,
|
||||||
// also update value of "LAST_USED_ATTRIBUTE".
|
// also update value of "LAST_USED_ATTRIBUTE".
|
||||||
};
|
};
|
||||||
|
@ -918,8 +918,13 @@ void ShaderProgramBuilder::buildAttribute(Effect* effect, Pass* pass,
|
|||||||
resolvedKey.attributes = prgKey.attributes;
|
resolvedKey.attributes = prgKey.attributes;
|
||||||
BOOST_FOREACH(const ShaderKey& shaderKey, prgKey.shaders)
|
BOOST_FOREACH(const ShaderKey& shaderKey, prgKey.shaders)
|
||||||
{
|
{
|
||||||
const string& shaderName = shaderKey.first;
|
// FIXME orig: const string& shaderName = shaderKey.first;
|
||||||
|
string shaderName = shaderKey.first;
|
||||||
Shader::Type stype = (Shader::Type)shaderKey.second;
|
Shader::Type stype = (Shader::Type)shaderKey.second;
|
||||||
|
if (getPropertyRoot()->getBoolValue("/sim/version/compositor-support", false) &&
|
||||||
|
shaderName.substr(0, shaderName.find("/")) == "Shaders") {
|
||||||
|
shaderName = "Compositor/" + shaderName;
|
||||||
|
}
|
||||||
string fileName = SGModelLib::findDataFile(shaderName, options);
|
string fileName = SGModelLib::findDataFile(shaderName, options);
|
||||||
if (fileName.empty())
|
if (fileName.empty())
|
||||||
{
|
{
|
||||||
@ -1474,6 +1479,7 @@ static SGMutex realizeTechniques_lock;
|
|||||||
bool Effect::realizeTechniques(const SGReaderWriterOptions* options)
|
bool Effect::realizeTechniques(const SGReaderWriterOptions* options)
|
||||||
{
|
{
|
||||||
SGGuard<SGMutex> g(realizeTechniques_lock);
|
SGGuard<SGMutex> g(realizeTechniques_lock);
|
||||||
|
if (getPropertyRoot()->getBoolValue("/sim/version/compositor-support", false))
|
||||||
mergeSchemesFallbacks(this, options);
|
mergeSchemesFallbacks(this, options);
|
||||||
|
|
||||||
if (_isRealized)
|
if (_isRealized)
|
||||||
|
@ -130,8 +130,12 @@ Effect* makeEffect(const string& name,
|
|||||||
string effectFileName;
|
string effectFileName;
|
||||||
// Use getPropertyRoot() because the SGReaderWriterOptions might not have a
|
// Use getPropertyRoot() because the SGReaderWriterOptions might not have a
|
||||||
// valid property tree
|
// valid property tree
|
||||||
if (getPropertyRoot()->getBoolValue("/sim/version/compositor-support", false))
|
if (getPropertyRoot()->getBoolValue("/sim/version/compositor-support", false) &&
|
||||||
|
name.substr(0, name.find("/")) == "Effects") {
|
||||||
|
// Temporarily append the Compositor/ directory to every effect that should
|
||||||
|
// be inside Effects/.
|
||||||
effectFileName += "Compositor/";
|
effectFileName += "Compositor/";
|
||||||
|
}
|
||||||
effectFileName += name;
|
effectFileName += name;
|
||||||
effectFileName += ".eff";
|
effectFileName += ".eff";
|
||||||
string absFileName
|
string absFileName
|
||||||
|
@ -323,11 +323,25 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options,
|
|||||||
|
|
||||||
building_range = props->getDoubleValue("building-range-m", default_object_range);
|
building_range = props->getDoubleValue("building-range-m", default_object_range);
|
||||||
|
|
||||||
|
// There are some constraints on the maximum building size that we can sensibly render.
|
||||||
|
// Using values outside these ranges will result in the texture being stretched to fit,
|
||||||
|
// which may not be desireable. We will allow it, but display warnings.
|
||||||
|
// We do not display warnings for large buildings as we assume the textures are sufficiently
|
||||||
|
// generic to be stretched without problems.
|
||||||
|
if (building_small_max_floors > 3) SG_LOG(SG_GENERAL, SG_ALERT, "building-small-max-floors exceeds maximum (3). Texture will be stretched to fit.");
|
||||||
|
if (building_medium_max_floors > 8) SG_LOG(SG_GENERAL, SG_ALERT, "building-medium-max-floors exceeds maximum (8). Texture will be stretched to fit.");
|
||||||
|
if (building_large_max_floors > 22) SG_LOG(SG_GENERAL, SG_ALERT, "building-large-max-floors exceeds maximum (22). Texture will be stretched to fit.");
|
||||||
|
if (building_small_max_width > 192.0) SG_LOG(SG_GENERAL, SG_ALERT, "building-small-max-width-m exceeds maximum (192). Texture will be stretched to fit.");
|
||||||
|
if (building_small_max_depth > 192.0) SG_LOG(SG_GENERAL, SG_ALERT, "building-small-max-depth-m exceeds maximum (192). Texture will be stretched to fit.");
|
||||||
|
if (building_medium_max_width > 80.0) SG_LOG(SG_GENERAL, SG_ALERT, "building-medium-max-width-m exceeds maximum (80). Texture will be stretched to fit.");
|
||||||
|
if (building_medium_max_depth > 80.0) SG_LOG(SG_GENERAL, SG_ALERT, "building-medium-max-depth-m exceeds maximum (80). Texture will be stretched to fit.");
|
||||||
|
|
||||||
cos_object_max_density_slope_angle = cos(props->getFloatValue("object-max-density-angle-deg", 20.0) * osg::PI/180.0);
|
cos_object_max_density_slope_angle = cos(props->getFloatValue("object-max-density-angle-deg", 20.0) * osg::PI/180.0);
|
||||||
cos_object_zero_density_slope_angle = cos(props->getFloatValue("object-zero-density-angle-deg", 30.0) * osg::PI/180.0);
|
cos_object_zero_density_slope_angle = cos(props->getFloatValue("object-zero-density-angle-deg", 30.0) * osg::PI/180.0);
|
||||||
|
|
||||||
// Random vegetation properties
|
// Random vegetation properties
|
||||||
wood_coverage = props->getDoubleValue("wood-coverage", 0.0);
|
wood_coverage = props->getDoubleValue("wood-coverage", 0.0);
|
||||||
|
is_plantation = props->getBoolValue("plantation",false);
|
||||||
tree_effect = props->getStringValue("tree-effect", "Effects/tree");
|
tree_effect = props->getStringValue("tree-effect", "Effects/tree");
|
||||||
tree_height = props->getDoubleValue("tree-height-m", 0.0);
|
tree_height = props->getDoubleValue("tree-height-m", 0.0);
|
||||||
tree_width = props->getDoubleValue("tree-width-m", 0.0);
|
tree_width = props->getDoubleValue("tree-width-m", 0.0);
|
||||||
|
@ -244,6 +244,13 @@ public:
|
|||||||
*/
|
*/
|
||||||
inline double get_wood_coverage () const { return wood_coverage; }
|
inline double get_wood_coverage () const { return wood_coverage; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get whether or not vegetation is regularly spaced
|
||||||
|
*
|
||||||
|
* @return flag: if true, vegetation is regularly spaced.
|
||||||
|
*/
|
||||||
|
inline bool get_is_plantation () const { return is_plantation; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the tree height.
|
* Get the tree height.
|
||||||
*
|
*
|
||||||
@ -455,6 +462,9 @@ private:
|
|||||||
// coverage of woods
|
// coverage of woods
|
||||||
double wood_coverage;
|
double wood_coverage;
|
||||||
|
|
||||||
|
// are trees regularly planted?
|
||||||
|
bool is_plantation;
|
||||||
|
|
||||||
// Range at which trees become visible
|
// Range at which trees become visible
|
||||||
double tree_range;
|
double tree_range;
|
||||||
|
|
||||||
|
@ -18,6 +18,8 @@
|
|||||||
#ifndef BVHPageNodeOSG_hxx
|
#ifndef BVHPageNodeOSG_hxx
|
||||||
#define BVHPageNodeOSG_hxx
|
#define BVHPageNodeOSG_hxx
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "../../bvh/BVHPageNode.hxx"
|
#include "../../bvh/BVHPageNode.hxx"
|
||||||
|
|
||||||
#include <osg/ref_ptr>
|
#include <osg/ref_ptr>
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
// License along with this library; if not, write to the Free Software
|
// License along with this library; if not, write to the Free Software
|
||||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
#include "SGLight.hxx"
|
#include "SGLight.hxx"
|
||||||
|
|
||||||
#include <osg/Geode>
|
#include <osg/Geode>
|
||||||
@ -73,34 +75,41 @@ SGLight::appendLight(const SGPropertyNode *configNode,
|
|||||||
light->setSpotExponent(configNode->getFloatValue("spot-exponent"));
|
light->setSpotExponent(configNode->getFloatValue("spot-exponent"));
|
||||||
light->setSpotCutoff(configNode->getFloatValue("spot-cutoff"));
|
light->setSpotCutoff(configNode->getFloatValue("spot-cutoff"));
|
||||||
|
|
||||||
osg::Group *group = 0;
|
|
||||||
if ((p = configNode->getNode("offsets")) == NULL) {
|
|
||||||
group = new osg::Group;
|
|
||||||
} else {
|
|
||||||
// Set up the alignment node ("stolen" from animation.cxx)
|
|
||||||
// XXX Order of rotations is probably not correct.
|
|
||||||
osg::MatrixTransform *align = new osg::MatrixTransform;
|
osg::MatrixTransform *align = new osg::MatrixTransform;
|
||||||
osg::Matrix res_matrix;
|
align->addChild(light);
|
||||||
res_matrix.makeRotate(
|
|
||||||
p->getFloatValue("pitch-deg", 0.0)*SG_DEGREES_TO_RADIANS,
|
osg::Matrix t;
|
||||||
|
osg::Vec3 pos(configNode->getFloatValue("position/x-m"),
|
||||||
|
configNode->getFloatValue("position/y-m"),
|
||||||
|
configNode->getFloatValue("position/z-m"));
|
||||||
|
t.makeTranslate(pos);
|
||||||
|
|
||||||
|
osg::Matrix r;
|
||||||
|
if (const SGPropertyNode *dirNode = configNode->getNode("direction")) {
|
||||||
|
if (dirNode->hasValue("pitch-deg")) {
|
||||||
|
r.makeRotate(
|
||||||
|
dirNode->getFloatValue("pitch-deg")*SG_DEGREES_TO_RADIANS,
|
||||||
osg::Vec3(0, 1, 0),
|
osg::Vec3(0, 1, 0),
|
||||||
p->getFloatValue("roll-deg", 0.0)*SG_DEGREES_TO_RADIANS,
|
dirNode->getFloatValue("roll-deg")*SG_DEGREES_TO_RADIANS,
|
||||||
osg::Vec3(1, 0, 0),
|
osg::Vec3(1, 0, 0),
|
||||||
p->getFloatValue("heading-deg", 0.0)*SG_DEGREES_TO_RADIANS,
|
dirNode->getFloatValue("heading-deg")*SG_DEGREES_TO_RADIANS,
|
||||||
osg::Vec3(0, 0, 1));
|
osg::Vec3(0, 0, 1));
|
||||||
|
} else if (dirNode->hasValue("lookat-x-m")) {
|
||||||
osg::Matrix tmat;
|
osg::Vec3 lookAt(dirNode->getFloatValue("lookat-x-m"),
|
||||||
tmat.makeTranslate(configNode->getFloatValue("offsets/x-m", 0.0),
|
dirNode->getFloatValue("lookat-y-m"),
|
||||||
configNode->getFloatValue("offsets/y-m", 0.0),
|
dirNode->getFloatValue("lookat-z-m"));
|
||||||
configNode->getFloatValue("offsets/z-m", 0.0));
|
osg::Vec3 dir = lookAt - pos;
|
||||||
|
r.makeRotate(osg::Vec3(0, 0, -1), dir);
|
||||||
align->setMatrix(res_matrix * tmat);
|
} else {
|
||||||
group = align;
|
r.makeRotate(osg::Vec3(0, 0, -1),
|
||||||
|
osg::Vec3(dirNode->getFloatValue("x"),
|
||||||
|
dirNode->getFloatValue("y"),
|
||||||
|
dirNode->getFloatValue("z")));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
align->setMatrix(r * t);
|
||||||
|
|
||||||
group->addChild(light);
|
osg::Shape *debug_shape = nullptr;
|
||||||
|
|
||||||
osg::Shape *debug_shape;
|
|
||||||
if (light->getType() == SGLight::Type::POINT) {
|
if (light->getType() == SGLight::Type::POINT) {
|
||||||
debug_shape = new osg::Sphere(osg::Vec3(0, 0, 0), light->getRange());
|
debug_shape = new osg::Sphere(osg::Vec3(0, 0, 0), light->getRange());
|
||||||
} else if (light->getType() == SGLight::Type::SPOT) {
|
} else if (light->getType() == SGLight::Type::SPOT) {
|
||||||
@ -109,7 +118,10 @@ SGLight::appendLight(const SGPropertyNode *configNode,
|
|||||||
osg::Vec3(0, 0, -0.75 * light->getRange()),
|
osg::Vec3(0, 0, -0.75 * light->getRange()),
|
||||||
tan(light->getSpotCutoff() * SG_DEGREES_TO_RADIANS) * light->getRange(),
|
tan(light->getSpotCutoff() * SG_DEGREES_TO_RADIANS) * light->getRange(),
|
||||||
light->getRange());
|
light->getRange());
|
||||||
|
} else {
|
||||||
|
throw std::domain_error("Unsupported SGLight::Type");
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::ShapeDrawable *debug_drawable = new osg::ShapeDrawable(debug_shape);
|
osg::ShapeDrawable *debug_drawable = new osg::ShapeDrawable(debug_shape);
|
||||||
debug_drawable->setColor(osg::Vec4(1.0, 0.0, 0.0, 1.0));
|
debug_drawable->setColor(osg::Vec4(1.0, 0.0, 0.0, 1.0));
|
||||||
osg::Geode *debug_geode = new osg::Geode;
|
osg::Geode *debug_geode = new osg::Geode;
|
||||||
@ -125,14 +137,14 @@ SGLight::appendLight(const SGPropertyNode *configNode,
|
|||||||
debug_switch->addChild(debug_geode);
|
debug_switch->addChild(debug_geode);
|
||||||
simgear::getPropertyRoot()->getNode("/sim/debug/show-light-volumes", true)->
|
simgear::getPropertyRoot()->getNode("/sim/debug/show-light-volumes", true)->
|
||||||
addChangeListener(new SGLightDebugListener(debug_switch), true);
|
addChangeListener(new SGLightDebugListener(debug_switch), true);
|
||||||
group->addChild(debug_switch);
|
align->addChild(debug_switch);
|
||||||
|
|
||||||
if ((p = configNode->getNode("name")) != NULL)
|
if ((p = configNode->getNode("name")) != NULL)
|
||||||
group->setName(p->getStringValue());
|
align->setName(p->getStringValue());
|
||||||
else
|
else
|
||||||
group->setName("light");
|
align->setName("light");
|
||||||
|
|
||||||
return group;
|
return align;
|
||||||
}
|
}
|
||||||
|
|
||||||
SGLight::SGLight() :
|
SGLight::SGLight() :
|
||||||
|
@ -615,9 +615,8 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void buttonReleased( int keyModState,
|
void buttonReleased( int keyModState, const osgGA::GUIEventAdapter&,
|
||||||
const osgGA::GUIEventAdapter&,
|
const Info* ) override
|
||||||
const Info* )
|
|
||||||
{
|
{
|
||||||
if (!_condition || _condition->test()) {
|
if (!_condition || _condition->test()) {
|
||||||
|
|
||||||
@ -641,8 +640,7 @@ public:
|
|||||||
return _dragDirection;
|
return _dragDirection;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void mouseMoved( const osgGA::GUIEventAdapter& ea,
|
void mouseMoved( const osgGA::GUIEventAdapter& ea, const Info* ) override
|
||||||
const Info* )
|
|
||||||
{
|
{
|
||||||
if (!_condition || _condition->test()) {
|
if (!_condition || _condition->test()) {
|
||||||
_mousePos = eventToWindowCoords(ea);
|
_mousePos = eventToWindowCoords(ea);
|
||||||
@ -675,7 +673,7 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void update(double dt, int keyModState)
|
void update(double dt, int keyModState) override
|
||||||
{
|
{
|
||||||
if (_hasDragged) {
|
if (_hasDragged) {
|
||||||
return;
|
return;
|
||||||
@ -688,8 +686,7 @@ public:
|
|||||||
} // of repeat iteration
|
} // of repeat iteration
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool hover( const osg::Vec2d& windowPos,
|
bool hover( const osg::Vec2d& windowPos, const Info& ) override
|
||||||
const Info& )
|
|
||||||
{
|
{
|
||||||
if (!_condition || _condition->test()) {
|
if (!_condition || _condition->test()) {
|
||||||
|
|
||||||
@ -711,7 +708,7 @@ public:
|
|||||||
_cursorName = aName;
|
_cursorName = aName;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual std::string getCursor() const
|
std::string getCursor() const override
|
||||||
{ return _cursorName; }
|
{ return _cursorName; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -256,7 +256,7 @@ osg::ref_ptr<EffectGeode> SGNewCloud::genCloud() {
|
|||||||
geode->addDrawable(sg);
|
geode->addDrawable(sg);
|
||||||
geode->setName("3D cloud");
|
geode->setName("3D cloud");
|
||||||
geode->setEffect(effect.get());
|
geode->setEffect(effect.get());
|
||||||
geode->setNodeMask( ~simgear::MODELLIGHT_BIT );
|
geode->setNodeMask( ~(simgear::CASTSHADOW_BIT | simgear::MODELLIGHT_BIT) );
|
||||||
|
|
||||||
return geode;
|
return geode;
|
||||||
}
|
}
|
||||||
|
@ -95,6 +95,13 @@ static SGBucket bucketIndexFromFileName(const std::string& fileName)
|
|||||||
return SGBucket(index);
|
return SGBucket(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* callback per STG token, with access synced by a lock.
|
||||||
|
*/
|
||||||
|
using TokenCallbackMap = std::map<std::string,ReaderWriterSTG::STGObjectCallback>;
|
||||||
|
static TokenCallbackMap globalStgObjectCallbacks = {};
|
||||||
|
static OpenThreads::Mutex globalStgObjectCallbackLock;
|
||||||
|
|
||||||
struct ReaderWriterSTG::_ModelBin {
|
struct ReaderWriterSTG::_ModelBin {
|
||||||
struct _Object {
|
struct _Object {
|
||||||
SGPath _errorLocation;
|
SGPath _errorLocation;
|
||||||
@ -536,8 +543,26 @@ struct ReaderWriterSTG::_ModelBin {
|
|||||||
_buildingListList.push_back(buildinglist);
|
_buildingListList.push_back(buildinglist);
|
||||||
//SG_LOG(SG_TERRAIN, SG_ALERT, "Building list: " << buildinglist._filename << " " << buildinglist._material_name << " " << buildinglist._lon << " " << buildinglist._lat);
|
//SG_LOG(SG_TERRAIN, SG_ALERT, "Building list: " << buildinglist._filename << " " << buildinglist._material_name << " " << buildinglist._lon << " " << buildinglist._lat);
|
||||||
} else {
|
} else {
|
||||||
SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
|
// Check registered callback for token. Keep lock until callback completed to make sure it will not be
|
||||||
<< ": Unknown token '" << token << "'" );
|
// executed after a thread successfully executed removeSTGObjectHandler()
|
||||||
|
{
|
||||||
|
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(globalStgObjectCallbackLock);
|
||||||
|
STGObjectCallback callback = globalStgObjectCallbacks[token];
|
||||||
|
|
||||||
|
if (callback != nullptr) {
|
||||||
|
_ObjectStatic obj;
|
||||||
|
// pitch and roll are not common, so passed in "restofline" only
|
||||||
|
in >> obj._lon >> obj._lat >> obj._elev >> obj._hdg;
|
||||||
|
string_list restofline;
|
||||||
|
std::string buf;
|
||||||
|
while (in >> buf) {
|
||||||
|
restofline.push_back(buf);
|
||||||
|
}
|
||||||
|
callback(token,name, SGGeod::fromDegM(obj._lon, obj._lat, obj._elev), obj._hdg,restofline);
|
||||||
|
} else {
|
||||||
|
SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName << ": Unknown token '" << token << "'" );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -711,4 +736,16 @@ ReaderWriterSTG::readNode(const std::string& fileName, const osgDB::Options* opt
|
|||||||
return modelBin.load(bucket, options);
|
return modelBin.load(bucket, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ReaderWriterSTG::setSTGObjectHandler(const std::string &token, STGObjectCallback callback)
|
||||||
|
{
|
||||||
|
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(globalStgObjectCallbackLock);
|
||||||
|
globalStgObjectCallbacks[token] = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReaderWriterSTG::removeSTGObjectHandler(const std::string &token, STGObjectCallback callback)
|
||||||
|
{
|
||||||
|
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(globalStgObjectCallbackLock);
|
||||||
|
globalStgObjectCallbacks.erase(token);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,8 +22,12 @@
|
|||||||
#ifndef _READERWRITERSTG_HXX
|
#ifndef _READERWRITERSTG_HXX
|
||||||
#define _READERWRITERSTG_HXX
|
#define _READERWRITERSTG_HXX
|
||||||
|
|
||||||
#include <osgDB/ReaderWriter>
|
#include <functional>
|
||||||
|
|
||||||
|
#include <osgDB/ReaderWriter>
|
||||||
|
#include <simgear/math/sg_types.hxx>
|
||||||
|
|
||||||
|
class SGGeod;
|
||||||
class SGBucket;
|
class SGBucket;
|
||||||
|
|
||||||
namespace simgear {
|
namespace simgear {
|
||||||
@ -38,6 +42,11 @@ public:
|
|||||||
virtual ReadResult
|
virtual ReadResult
|
||||||
readNode(const std::string&, const osgDB::Options*) const;
|
readNode(const std::string&, const osgDB::Options*) const;
|
||||||
|
|
||||||
|
//pitch and roll are not common, so passed in "restofline" only
|
||||||
|
using STGObjectCallback = std::function<bool(const std::string& token, const std::string& modelpath, const SGGeod& position, const double hdg, const string_list& restofline)>;
|
||||||
|
//add/remove a callback that is invoked for unknown STG token
|
||||||
|
static void setSTGObjectHandler(const std::string &token, STGObjectCallback callback);
|
||||||
|
static void removeSTGObjectHandler(const std::string &token, STGObjectCallback callback);
|
||||||
private:
|
private:
|
||||||
struct _ModelBin;
|
struct _ModelBin;
|
||||||
};
|
};
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -35,6 +35,7 @@
|
|||||||
#include <osg/ShadeModel>
|
#include <osg/ShadeModel>
|
||||||
#include <osg/Material>
|
#include <osg/Material>
|
||||||
#include <osg/CullFace>
|
#include <osg/CullFace>
|
||||||
|
#include <osg/VertexAttribDivisor>
|
||||||
|
|
||||||
#include <simgear/scene/util/OsgMath.hxx>
|
#include <simgear/scene/util/OsgMath.hxx>
|
||||||
#include <simgear/scene/material/mat.hxx>
|
#include <simgear/scene/material/mat.hxx>
|
||||||
@ -47,141 +48,140 @@
|
|||||||
#include <simgear/scene/util/StateAttributeFactory.hxx>
|
#include <simgear/scene/util/StateAttributeFactory.hxx>
|
||||||
#include <simgear/structure/OSGUtils.hxx>
|
#include <simgear/structure/OSGUtils.hxx>
|
||||||
|
|
||||||
#define SG_BUILDING_QUAD_TREE_DEPTH 4
|
#define SG_BUILDING_QUAD_TREE_DEPTH 2
|
||||||
#define SG_BUILDING_FADE_OUT_LEVELS 4
|
#define SG_BUILDING_FADE_OUT_LEVELS 4
|
||||||
|
|
||||||
|
// these correspond to building.eff
|
||||||
|
const int BUILDING_POSITION_ATTR = 10; // (x,y,z)
|
||||||
|
const int BUILDING_SCALE_ATTR = 11; // (width, depth, height)
|
||||||
|
const int BUILDING_ROT_PITCH_TEX0X_ATTR = 12; // (rotation, pitch height, wall texture x offset)
|
||||||
|
const int BUILDING_TEX0Y_TEX1X_TEX1Y_ATTR = 13; // (wall texture y offset, texture x gain, texture y gain)
|
||||||
|
const int BUILDING_RTEX0X_RTEX0Y_ATTR = 14; // (roof texture x offset, roof texture y offset, unused)
|
||||||
|
const int BUILDING_ROFFTOPSCALE_ATTR = 15; // (roof texture x offset, roof texture y offset, unused)
|
||||||
|
|
||||||
using namespace osg;
|
using namespace osg;
|
||||||
|
|
||||||
namespace simgear
|
namespace simgear
|
||||||
{
|
{
|
||||||
|
|
||||||
|
struct BuildingBoundingBoxCallback : public Drawable::ComputeBoundingBoxCallback
|
||||||
|
{
|
||||||
|
BuildingBoundingBoxCallback() {}
|
||||||
|
BuildingBoundingBoxCallback(const BuildingBoundingBoxCallback&, const CopyOp&) {}
|
||||||
|
META_Object(simgear, BuildingBoundingBoxCallback);
|
||||||
|
virtual BoundingBox computeBound(const Drawable& drawable) const
|
||||||
|
{
|
||||||
|
BoundingBox bb;
|
||||||
|
const Geometry* geom = static_cast<const Geometry*>(&drawable);
|
||||||
|
const Vec3Array* pos = static_cast<const Vec3Array*>(geom->getVertexAttribArray(BUILDING_POSITION_ATTR));
|
||||||
|
|
||||||
|
for (unsigned int v=0; v<pos->size(); ++v) {
|
||||||
|
Vec3 pt = (*pos)[v];
|
||||||
|
bb.expandBy(pt);
|
||||||
|
}
|
||||||
|
return bb;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class SGBuildingBin {
|
class SGBuildingBin {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// Number of buildings to auto-generate. Individual
|
|
||||||
// building instances are taken from this set.
|
|
||||||
static const unsigned int BUILDING_SET_SIZE = 200;
|
|
||||||
|
|
||||||
static const unsigned int QUADS_PER_BUILDING = 12;
|
|
||||||
static const unsigned int VERTICES_PER_BUILDING = 4 * QUADS_PER_BUILDING;
|
|
||||||
static const unsigned int VERTICES_PER_BUILDING_SET = BUILDING_SET_SIZE * VERTICES_PER_BUILDING;
|
|
||||||
|
|
||||||
enum BuildingType {
|
enum BuildingType {
|
||||||
SMALL = 0,
|
SMALL = 0,
|
||||||
MEDIUM,
|
MEDIUM,
|
||||||
LARGE };
|
LARGE };
|
||||||
|
|
||||||
private:
|
struct BuildingInstance {
|
||||||
|
BuildingInstance(Vec3f p, float w, float d, float h, float ph, float r, Vec2f wt0, Vec2f rt0, Vec3f t1, Vec2f rs) :
|
||||||
struct Building {
|
position(p),
|
||||||
Building(BuildingType t, float w, float d, float h, int f, bool pitch) :
|
|
||||||
type(t),
|
|
||||||
width(w),
|
width(w),
|
||||||
depth(d),
|
depth(d),
|
||||||
height(h),
|
height(h),
|
||||||
floors(f),
|
pitch_height(ph),
|
||||||
pitched(pitch),
|
rotation(r),
|
||||||
radius(std::max(d, 0.5f*w))
|
walltex0(wt0),
|
||||||
|
rooftex0(rt0),
|
||||||
|
tex1(t1),
|
||||||
|
rooftop_scale(rs)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
BuildingType type;
|
BuildingInstance(Vec3f p, BuildingInstance b) :
|
||||||
|
position(p),
|
||||||
|
width(b.width),
|
||||||
|
depth(b.depth),
|
||||||
|
height(b.height),
|
||||||
|
pitch_height(b.pitch_height),
|
||||||
|
rotation(b.rotation),
|
||||||
|
walltex0(b.walltex0),
|
||||||
|
rooftex0(b.rooftex0),
|
||||||
|
tex1(b.tex1),
|
||||||
|
rooftop_scale(b.rooftop_scale)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
|
||||||
|
Vec3f position;
|
||||||
float width;
|
float width;
|
||||||
float depth;
|
float depth;
|
||||||
float height;
|
float height;
|
||||||
int floors;
|
float pitch_height;
|
||||||
bool pitched;
|
float rotation;
|
||||||
float radius;
|
|
||||||
|
|
||||||
float getFootprint() {
|
Vec2f walltex0;
|
||||||
return radius;
|
Vec2f rooftex0;
|
||||||
|
Vec3f tex1; // Texture gains for the front, roof and sides
|
||||||
|
|
||||||
|
Vec2f rooftop_scale;
|
||||||
|
|
||||||
|
// References to allow the QuadTreeBuilder to work
|
||||||
|
//const BuildingList* buildingList;
|
||||||
|
//ref_ptr<Geometry> sharedGeometry;
|
||||||
|
|
||||||
|
Vec3f getPosition() { return position; }
|
||||||
|
float getRotation() { return rotation; }
|
||||||
|
|
||||||
|
float getDistSqr(Vec3f p) {
|
||||||
|
return (p - position) * (p - position);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// The set of buildings that are instantiated
|
private:
|
||||||
typedef std::vector<Building> BuildingList;
|
|
||||||
BuildingList smallBuildings;
|
const SGMaterial *material;
|
||||||
BuildingList mediumBuildings;
|
|
||||||
BuildingList largeBuildings;
|
|
||||||
|
|
||||||
std::string* material_name;
|
std::string* material_name;
|
||||||
std::string* texture;
|
std::string* texture;
|
||||||
std::string* lightMap;
|
std::string* lightMap;
|
||||||
|
|
||||||
// Fraction of buildings of this type
|
|
||||||
float smallBuildingFraction;
|
|
||||||
float mediumBuildingFraction;
|
|
||||||
|
|
||||||
// The maximum radius of each building type
|
|
||||||
float smallBuildingMaxRadius;
|
|
||||||
float mediumBuildingMaxRadius;
|
|
||||||
float largeBuildingMaxRadius;
|
|
||||||
|
|
||||||
// The maximum depth of each building type
|
|
||||||
float smallBuildingMaxDepth;
|
|
||||||
float mediumBuildingMaxDepth;
|
|
||||||
float largeBuildingMaxDepth;
|
|
||||||
|
|
||||||
// Visibility range for buildings
|
// Visibility range for buildings
|
||||||
float buildingRange;
|
float buildingRange;
|
||||||
|
|
||||||
// Shared geometries of the building set
|
|
||||||
ref_ptr<Geometry> smallSharedGeometry;
|
|
||||||
ref_ptr<Geometry> mediumSharedGeometry;
|
|
||||||
ref_ptr<Geometry> largeSharedGeometry;
|
|
||||||
|
|
||||||
struct BuildingInstance {
|
|
||||||
BuildingInstance(SGVec3f p, float r, const BuildingList* bl, ref_ptr<Geometry> sg) :
|
|
||||||
position(p),
|
|
||||||
rotation(r),
|
|
||||||
buildingList(bl),
|
|
||||||
sharedGeometry(sg)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
BuildingInstance(SGVec3f p, BuildingInstance b) :
|
|
||||||
position(p),
|
|
||||||
rotation(b.rotation),
|
|
||||||
buildingList(b.buildingList),
|
|
||||||
sharedGeometry(b.sharedGeometry)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
SGVec3f position;
|
|
||||||
float rotation;
|
|
||||||
|
|
||||||
// References to allow the QuadTreeBuilder to work
|
|
||||||
const BuildingList* buildingList;
|
|
||||||
ref_ptr<Geometry> sharedGeometry;
|
|
||||||
|
|
||||||
SGVec3f getPosition() { return position; }
|
|
||||||
float getRotation() { return rotation; }
|
|
||||||
|
|
||||||
float getDistSqr(SGVec3f p) {
|
|
||||||
return distSqr(p, position);
|
|
||||||
}
|
|
||||||
|
|
||||||
const osg::Vec4f getColorValue() {
|
|
||||||
return osg::Vec4f(toOsg(position), rotation);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Information for an instance of a building - position and orientation
|
// Information for an instance of a building - position and orientation
|
||||||
typedef std::vector<BuildingInstance> BuildingInstanceList;
|
typedef std::vector<BuildingInstance> BuildingInstanceList;
|
||||||
BuildingInstanceList smallBuildingLocations;
|
BuildingInstanceList buildingLocations;
|
||||||
BuildingInstanceList mediumBuildingLocations;
|
|
||||||
BuildingInstanceList largeBuildingLocations;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
SGBuildingBin(const SGMaterial *mat, bool useVBOs);
|
SGBuildingBin(const SGMaterial *mat, bool useVBOs);
|
||||||
SGBuildingBin(const SGPath& absoluteFileName, const SGMaterial *mat, bool useVBOs);
|
SGBuildingBin(const SGPath& absoluteFileName, const SGMaterial *mat, bool useVBOs);
|
||||||
|
|
||||||
~SGBuildingBin() {
|
~SGBuildingBin();
|
||||||
smallBuildings.clear();
|
|
||||||
mediumBuildings.clear();
|
|
||||||
largeBuildings.clear();
|
|
||||||
smallBuildingLocations.clear();
|
|
||||||
mediumBuildingLocations.clear();
|
|
||||||
largeBuildingLocations.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Generate a building specifying the exact position, dimensions and texture index.
|
||||||
|
void insert(SGVec3f p,
|
||||||
|
float r,
|
||||||
|
BuildingType buildingtype,
|
||||||
|
float width,
|
||||||
|
float depth,
|
||||||
|
float height,
|
||||||
|
float pitch_height,
|
||||||
|
int floors,
|
||||||
|
int roof_shape,
|
||||||
|
int roof_orientation,
|
||||||
|
int wall_tex_index,
|
||||||
|
int roof_tex_index);
|
||||||
|
|
||||||
|
// Generate a building of a given type at a specified position, using the random building material definition to determine the dimensions and texture index.
|
||||||
void insert(SGVec3f p, float r, BuildingType type);
|
void insert(SGVec3f p, float r, BuildingType type);
|
||||||
int getNumBuildings();
|
int getNumBuildings();
|
||||||
|
|
||||||
@ -190,123 +190,9 @@ public:
|
|||||||
std::string* getMaterialName() { return material_name; }
|
std::string* getMaterialName() { return material_name; }
|
||||||
|
|
||||||
BuildingType getBuildingType(float roll);
|
BuildingType getBuildingType(float roll);
|
||||||
|
|
||||||
float getBuildingMaxRadius(BuildingType);
|
float getBuildingMaxRadius(BuildingType);
|
||||||
float getBuildingMaxDepth(BuildingType);
|
float getBuildingMaxDepth(BuildingType);
|
||||||
|
|
||||||
// Helper classes for creating the quad tree
|
|
||||||
struct MakeBuildingLeaf
|
|
||||||
{
|
|
||||||
MakeBuildingLeaf(float range, Effect* effect, bool fade) :
|
|
||||||
_range(range), _effect(effect), _fade_out(fade) {}
|
|
||||||
|
|
||||||
MakeBuildingLeaf(const MakeBuildingLeaf& rhs) :
|
|
||||||
_range(rhs._range), _effect(rhs._effect), _fade_out(rhs._fade_out)
|
|
||||||
{}
|
|
||||||
|
|
||||||
LOD* operator() () const
|
|
||||||
{
|
|
||||||
LOD* result = new LOD;
|
|
||||||
|
|
||||||
if (_fade_out) {
|
|
||||||
// Create a series of LOD nodes so building cover decreases
|
|
||||||
// gradually with distance from _range to 2*_range
|
|
||||||
for (float i = 0.0; i < SG_BUILDING_FADE_OUT_LEVELS; i++)
|
|
||||||
{
|
|
||||||
EffectGeode* geode = new EffectGeode;
|
|
||||||
geode->setEffect(_effect.get());
|
|
||||||
result->addChild(geode, 0, _range * (1.0 + i / (SG_BUILDING_FADE_OUT_LEVELS - 1.0)));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// No fade-out, so all are visible for 2X range
|
|
||||||
EffectGeode* geode = new EffectGeode;
|
|
||||||
geode->setEffect(_effect.get());
|
|
||||||
result->addChild(geode, 0, 2.0 * _range);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
float _range;
|
|
||||||
ref_ptr<Effect> _effect;
|
|
||||||
bool _fade_out;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct AddBuildingLeafObject
|
|
||||||
{
|
|
||||||
Geometry* createNewBuildingGeometryInstance(const BuildingInstance& building) const
|
|
||||||
{
|
|
||||||
Geometry* geom = simgear::clone(building.sharedGeometry.get(), CopyOp::SHALLOW_COPY);
|
|
||||||
geom->setColorArray(new Vec4Array, Array::BIND_PER_VERTEX);
|
|
||||||
geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS));
|
|
||||||
return geom;
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator() (LOD* lod, const BuildingInstance& building) const
|
|
||||||
{
|
|
||||||
Geode* geode = static_cast<Geode*>(lod->getChild(int(building.position.x() * 10.0f) % lod->getNumChildren()));
|
|
||||||
unsigned int numDrawables = geode->getNumDrawables();
|
|
||||||
|
|
||||||
// Get the last geometry of to be added and check if there is space for
|
|
||||||
// another building instance within it. This is done by checking
|
|
||||||
// if the number of Color values matches the number of vertices.
|
|
||||||
// The color array is used to store the position of a particular
|
|
||||||
// instance.
|
|
||||||
Geometry* geom;
|
|
||||||
|
|
||||||
if (numDrawables == 0) {
|
|
||||||
// Create a new copy of the shared geometry to instantiate
|
|
||||||
geom = createNewBuildingGeometryInstance(building);
|
|
||||||
geode->addDrawable(geom);
|
|
||||||
} else {
|
|
||||||
geom = static_cast<Geometry*>(geode->getDrawable(numDrawables - 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if this building is too close to any other others.
|
|
||||||
DrawArrays* primSet = static_cast<DrawArrays*>(geom->getPrimitiveSet(0));
|
|
||||||
Vec4Array* posArray = static_cast<Vec4Array*>(geom->getColorArray());
|
|
||||||
|
|
||||||
// Now check if this geometry is full.
|
|
||||||
if (posArray->size() >= static_cast<Vec3Array*>(geom->getVertexArray())->size()) {
|
|
||||||
// This particular geometry is full, so we generate another
|
|
||||||
// by taking a shallow copy of the shared Geomety.
|
|
||||||
geom = createNewBuildingGeometryInstance(building);
|
|
||||||
geode->addDrawable(geom);
|
|
||||||
posArray = static_cast<Vec4Array*>(geom->getColorArray());
|
|
||||||
SG_LOG(SG_TERRAIN, SG_DEBUG, "Added new geometry to building geod: " << geode->getNumDrawables());
|
|
||||||
}
|
|
||||||
|
|
||||||
// We now have a geometry with space for this new building.
|
|
||||||
// Set the position and rotation
|
|
||||||
osg::Vec4f c = osg::Vec4f(toOsg(building.position), building.rotation);
|
|
||||||
posArray->insert(posArray->end(), VERTICES_PER_BUILDING, c);
|
|
||||||
size_t numVerts = posArray->size();
|
|
||||||
primSet = static_cast<DrawArrays*>(geom->getPrimitiveSet(0));
|
|
||||||
primSet->setCount(numVerts);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct GetBuildingCoord
|
|
||||||
{
|
|
||||||
Vec3 operator() (const BuildingInstance& building) const
|
|
||||||
{
|
|
||||||
return toOsg(building.position);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef QuadTreeBuilder<LOD*, BuildingInstance, MakeBuildingLeaf, AddBuildingLeafObject,
|
|
||||||
GetBuildingCoord> BuildingGeometryQuadtree;
|
|
||||||
|
|
||||||
struct BuildingInstanceTransformer
|
|
||||||
{
|
|
||||||
BuildingInstanceTransformer(Matrix& mat_) : mat(mat_) {}
|
|
||||||
BuildingInstance operator()(const BuildingInstance& buildingInstance) const
|
|
||||||
{
|
|
||||||
Vec3 pos = toOsg(buildingInstance.position) * mat;
|
|
||||||
return BuildingInstance(toSG(pos), buildingInstance);
|
|
||||||
}
|
|
||||||
Matrix mat;
|
|
||||||
};
|
|
||||||
|
|
||||||
ref_ptr<Group> createBuildingsGroup(Matrix transInv, const SGReaderWriterOptions* options);
|
ref_ptr<Group> createBuildingsGroup(Matrix transInv, const SGReaderWriterOptions* options);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -291,6 +291,7 @@ public:
|
|||||||
float vegetation_density,
|
float vegetation_density,
|
||||||
float cos_max_density_angle,
|
float cos_max_density_angle,
|
||||||
float cos_zero_density_angle,
|
float cos_zero_density_angle,
|
||||||
|
bool is_plantation,
|
||||||
std::vector<SGVec3f>& points,
|
std::vector<SGVec3f>& points,
|
||||||
std::vector<SGVec3f>& normals)
|
std::vector<SGVec3f>& normals)
|
||||||
{
|
{
|
||||||
@ -334,6 +335,58 @@ public:
|
|||||||
if (area <= SGLimitsf::min())
|
if (area <= SGLimitsf::min())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (is_plantation) { // regularly-spaced vegetation
|
||||||
|
// separate vegetation in integral 1m units
|
||||||
|
int separation = (int) ceil(sqrt(wood_coverage));
|
||||||
|
float max_x = ceil(max(max(v1.x(),v2.x()),v0.x()));
|
||||||
|
float min_x = floor(min(min(v1.x(),v2.x()),v0.x()));
|
||||||
|
float max_y = ceil(max(max(v1.y(),v2.y()),v0.y()));
|
||||||
|
float min_y = floor(min(min(v1.y(),v2.y()),v0.y()));
|
||||||
|
|
||||||
|
/* equation of the plane ax+by+cz+d=0, need d */
|
||||||
|
|
||||||
|
float d = -1*(normal.x()*v0.x() + normal.y()*v0.y()+normal.z()*v0.z());
|
||||||
|
/* Now loop over a grid, skipping points not in the triangle */
|
||||||
|
int x_steps = (int) (max_x - min_x)/separation;
|
||||||
|
int y_steps = (int) (max_y - min_y)/separation;
|
||||||
|
SGVec2f v02d = SGVec2f(v0.x(),v0.y());
|
||||||
|
SGVec2f v12d = SGVec2f(v1.x(),v1.y());
|
||||||
|
SGVec2f v22d = SGVec2f(v2.x(),v2.y());
|
||||||
|
|
||||||
|
for (int jx = 0; jx < x_steps; jx++) {
|
||||||
|
float ptx = min_x + jx * separation;
|
||||||
|
|
||||||
|
for (int jy = 0; jy < y_steps; jy++) {
|
||||||
|
float pty = min_y + jy * separation;
|
||||||
|
SGVec2f newpt = SGVec2f(ptx,pty);
|
||||||
|
if (!point_in_triangle(newpt,v02d,v12d,v22d))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// z = (-ax-by-d)/c; c is not zero as
|
||||||
|
// that would be alpha of 1.0
|
||||||
|
|
||||||
|
float ptz = (-normal.x()*ptx - normal.y()*pty-d)/normal.z();
|
||||||
|
SGVec3f randomPoint = SGVec3f(ptx,pty,ptz);
|
||||||
|
|
||||||
|
if (object_mask != NULL) {
|
||||||
|
// Check this point against the object mask
|
||||||
|
// green (for trees) channel.
|
||||||
|
osg::Image* img = object_mask->getImage();
|
||||||
|
unsigned int x = (int) (img->s() * newpt.x()) % img->s();
|
||||||
|
unsigned int y = (int) (img->t() * newpt.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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
// Determine the number of trees, taking into account vegetation
|
// Determine the number of trees, taking into account vegetation
|
||||||
// density (which is linear) and the slope density factor.
|
// density (which is linear) and the slope density factor.
|
||||||
// Use a zombie door method to create the proper random chance
|
// Use a zombie door method to create the proper random chance
|
||||||
@ -341,8 +394,8 @@ public:
|
|||||||
int woodcount = (int) (vegetation_density * vegetation_density *
|
int woodcount = (int) (vegetation_density * vegetation_density *
|
||||||
slope_density *
|
slope_density *
|
||||||
area / wood_coverage + mt_rand(&seed));
|
area / wood_coverage + mt_rand(&seed));
|
||||||
|
|
||||||
for (int j = 0; j < woodcount; j++) {
|
for (int j = 0; j < woodcount; j++) {
|
||||||
|
// Use barycentric coordinates
|
||||||
float a = mt_rand(&seed);
|
float a = mt_rand(&seed);
|
||||||
float b = mt_rand(&seed);
|
float b = mt_rand(&seed);
|
||||||
|
|
||||||
@ -354,7 +407,6 @@ public:
|
|||||||
float c = 1.0f - a - b;
|
float c = 1.0f - a - b;
|
||||||
|
|
||||||
SGVec3f randomPoint = a*v0 + b*v1 + c*v2;
|
SGVec3f randomPoint = a*v0 + b*v1 + c*v2;
|
||||||
|
|
||||||
if (object_mask != NULL) {
|
if (object_mask != NULL) {
|
||||||
SGVec2f texCoord = a*t0 + b*t1 + c*t2;
|
SGVec2f texCoord = a*t0 + b*t1 + c*t2;
|
||||||
|
|
||||||
@ -372,6 +424,8 @@ public:
|
|||||||
} else {
|
} else {
|
||||||
points.push_back(randomPoint);
|
points.push_back(randomPoint);
|
||||||
normals.push_back(normalize(normal));
|
normals.push_back(normalize(normal));
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -338,7 +338,7 @@ osg::Node* SGOceanTile(const SGBucket& b, SGMaterialLib *matlib, int latPoints,
|
|||||||
transform->setMatrix(osg::Matrix::rotate(toOsg(hlOr))*
|
transform->setMatrix(osg::Matrix::rotate(toOsg(hlOr))*
|
||||||
osg::Matrix::translate(toOsg(cartCenter)));
|
osg::Matrix::translate(toOsg(cartCenter)));
|
||||||
transform->addChild(geode);
|
transform->addChild(geode);
|
||||||
transform->setNodeMask( ~simgear::MODELLIGHT_BIT );
|
transform->setNodeMask( ~(simgear::CASTSHADOW_BIT | simgear::MODELLIGHT_BIT) );
|
||||||
|
|
||||||
return transform;
|
return transform;
|
||||||
}
|
}
|
||||||
|
@ -216,6 +216,7 @@ public:
|
|||||||
float vegetation_density,
|
float vegetation_density,
|
||||||
float cos_max_density_angle,
|
float cos_max_density_angle,
|
||||||
float cos_zero_density_angle,
|
float cos_zero_density_angle,
|
||||||
|
bool is_plantation,
|
||||||
std::vector<SGVec3f>& points,
|
std::vector<SGVec3f>& points,
|
||||||
std::vector<SGVec3f>& normals)
|
std::vector<SGVec3f>& normals)
|
||||||
{
|
{
|
||||||
@ -249,7 +250,60 @@ public:
|
|||||||
float area = 0.5f*length(normal);
|
float area = 0.5f*length(normal);
|
||||||
if (area <= SGLimitsf::min())
|
if (area <= SGLimitsf::min())
|
||||||
continue;
|
continue;
|
||||||
|
if (is_plantation) { // regularly-spaced vegetation
|
||||||
|
|
||||||
|
int separation = (int) ceil(sqrt(wood_coverage));
|
||||||
|
float max_x = ceil(max(max(v1.x(),v2.x()),v0.x()));
|
||||||
|
float min_x = floor(min(min(v1.x(),v2.x()),v0.x()));
|
||||||
|
float max_y = ceil(max(max(v1.y(),v2.y()),v0.y()));
|
||||||
|
float min_y = floor(min(min(v1.y(),v2.y()),v0.y()));
|
||||||
|
|
||||||
|
// equation of the plane ax+by+cz+d=0, need d
|
||||||
|
|
||||||
|
float d = -1*(normal.x()*v0.x() + normal.y()*v0.y()+normal.z()*v0.z());
|
||||||
|
|
||||||
|
// Now loop over a grid, skipping points not in the triangle
|
||||||
|
|
||||||
|
int x_steps = (int) (max_x - min_x)/separation;
|
||||||
|
int y_steps = (int) (max_y - min_y)/separation;
|
||||||
|
SGVec2f v02d = SGVec2f(v0.x(),v0.y());
|
||||||
|
SGVec2f v12d = SGVec2f(v1.x(),v1.y());
|
||||||
|
SGVec2f v22d = SGVec2f(v2.x(),v2.y());
|
||||||
|
|
||||||
|
for (int jx = 0; jx < x_steps; jx++) {
|
||||||
|
float ptx = min_x + jx * separation;
|
||||||
|
|
||||||
|
for (int jy = 0; jy < y_steps; jy++) {
|
||||||
|
float pty = min_y + jy * separation;
|
||||||
|
SGVec2f newpt = SGVec2f(ptx,pty);
|
||||||
|
if (!point_in_triangle(newpt,v02d,v12d,v22d))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// z = (-ax-by-d)/c; c is not zero as
|
||||||
|
// that would be alpha of 1.0
|
||||||
|
|
||||||
|
float ptz = (-normal.x()*ptx - normal.y()*pty-d)/normal.z();
|
||||||
|
SGVec3f randomPoint = SGVec3f(ptx,pty,ptz);
|
||||||
|
|
||||||
|
if (object_mask != NULL) {
|
||||||
|
// Check this point against the object mask
|
||||||
|
// green (for trees) channel.
|
||||||
|
osg::Image* img = object_mask->getImage();
|
||||||
|
unsigned int x = (int) (img->s() * newpt.x()) % img->s();
|
||||||
|
unsigned int y = (int) (img->t() * newpt.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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
// Determine the number of trees, taking into account vegetation
|
// Determine the number of trees, taking into account vegetation
|
||||||
// density (which is linear) and the slope density factor.
|
// density (which is linear) and the slope density factor.
|
||||||
// Use a zombie door method to create the proper random chance
|
// Use a zombie door method to create the proper random chance
|
||||||
@ -292,6 +346,7 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void addRandomPoints(double coverage,
|
void addRandomPoints(double coverage,
|
||||||
double spacing,
|
double spacing,
|
||||||
|
@ -753,6 +753,7 @@ public:
|
|||||||
vegetation_density,
|
vegetation_density,
|
||||||
mat->get_cos_tree_max_density_slope_angle(),
|
mat->get_cos_tree_max_density_slope_angle(),
|
||||||
mat->get_cos_tree_zero_density_slope_angle(),
|
mat->get_cos_tree_zero_density_slope_angle(),
|
||||||
|
mat->get_is_plantation(),
|
||||||
randomPoints,
|
randomPoints,
|
||||||
randomPointNormals);
|
randomPointNormals);
|
||||||
|
|
||||||
@ -903,12 +904,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
Effect* runwayEffect = 0;
|
Effect* runwayEffect = 0;
|
||||||
if (runwayLights.getNumLights() > 0
|
if (runwayLights.getNumLights() > 0 || taxiLights.getNumLights() > 0) {
|
||||||
|| !rabitLights.empty()
|
|
||||||
|| !reilLights.empty()
|
|
||||||
|| !odalLights.empty()
|
|
||||||
|| taxiLights.getNumLights() > 0) {
|
|
||||||
|
|
||||||
runwayEffect = getLightEffect(16, osg::Vec3(1, 0.001, 0.0002), 1, 16, true, _options);
|
runwayEffect = getLightEffect(16, osg::Vec3(1, 0.001, 0.0002), 1, 16, true, _options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -918,49 +914,38 @@ public:
|
|||||||
|| !odalLights.empty()
|
|| !odalLights.empty()
|
||||||
|| !holdshortLights.empty()
|
|| !holdshortLights.empty()
|
||||||
|| !guardLights.empty()) {
|
|| !guardLights.empty()) {
|
||||||
osg::Group* rwyLights = new osg::Group;
|
|
||||||
|
|
||||||
osg::StateSet* ss = lightManager->getRunwayLightStateSet();
|
osg::Group* rwyLightsGroup = new osg::Group;
|
||||||
rwyLights->setStateSet(ss);
|
rwyLightsGroup->setStateSet(lightManager->getRunwayLightStateSet());
|
||||||
rwyLights->setNodeMask(RUNWAYLIGHTS_BIT);
|
rwyLightsGroup->setNodeMask(RUNWAYLIGHTS_BIT);
|
||||||
|
|
||||||
if (runwayLights.getNumLights() != 0) {
|
|
||||||
EffectGeode* geode = new EffectGeode;
|
|
||||||
geode->setEffect(runwayEffect);
|
|
||||||
|
|
||||||
osg::Drawable* rldraw = SGLightFactory::getLights(runwayLights);
|
|
||||||
geode->addDrawable( rldraw );
|
|
||||||
|
|
||||||
rwyLights->addChild(geode);
|
|
||||||
}
|
|
||||||
SGDirectionalLightListBin::const_iterator i;
|
SGDirectionalLightListBin::const_iterator i;
|
||||||
for (i = rabitLights.begin();
|
|
||||||
i != rabitLights.end(); ++i) {
|
for (i = rabitLights.begin() ; i != rabitLights.end() ; ++i) {
|
||||||
osg::Node* seqNode = SGLightFactory::getSequenced(*i, _options);
|
rwyLightsGroup->addChild(SGLightFactory::getSequenced(*i, _options));
|
||||||
rwyLights->addChild( seqNode );
|
|
||||||
}
|
}
|
||||||
for (i = reilLights.begin();
|
for (i = reilLights.begin() ; i != reilLights.end() ; ++i) {
|
||||||
i != reilLights.end(); ++i) {
|
rwyLightsGroup->addChild(SGLightFactory::getReil(*i, _options));
|
||||||
osg::Node* seqNode = SGLightFactory::getSequenced(*i, _options);
|
|
||||||
rwyLights->addChild(seqNode);
|
|
||||||
}
|
}
|
||||||
for (i = holdshortLights.begin();
|
for (i = holdshortLights.begin() ; i != holdshortLights.end() ; ++i) {
|
||||||
i != holdshortLights.end(); ++i) {
|
rwyLightsGroup->addChild(SGLightFactory::getHoldShort(*i, _options));
|
||||||
osg::Node* seqNode = SGLightFactory::getHoldShort(*i, _options);
|
|
||||||
rwyLights->addChild(seqNode);
|
|
||||||
}
|
}
|
||||||
for (i = guardLights.begin();
|
for (i = guardLights.begin() ; i != guardLights.end() ; ++i) {
|
||||||
i != guardLights.end(); ++i) {
|
rwyLightsGroup->addChild(SGLightFactory::getGuard(*i, _options));
|
||||||
osg::Node* seqNode = SGLightFactory::getGuard(*i, _options);
|
|
||||||
rwyLights->addChild(seqNode);
|
|
||||||
}
|
}
|
||||||
SGLightListBin::const_iterator j;
|
SGLightListBin::const_iterator j;
|
||||||
for (j = odalLights.begin();
|
for (j = odalLights.begin() ; j != odalLights.end() ; ++j) {
|
||||||
j != odalLights.end(); ++j) {
|
rwyLightsGroup->addChild(SGLightFactory::getOdal(*j, _options));
|
||||||
osg::Node* seqNode = SGLightFactory::getOdal(*j, _options);
|
|
||||||
rwyLights->addChild(seqNode);
|
|
||||||
}
|
}
|
||||||
lightGroup->addChild(rwyLights);
|
|
||||||
|
if (runwayLights.getNumLights() > 0) {
|
||||||
|
osg::ref_ptr<EffectGeode> geode = new EffectGeode;
|
||||||
|
geode->setEffect(runwayEffect);
|
||||||
|
geode->addDrawable(SGLightFactory::getLights(runwayLights));
|
||||||
|
rwyLightsGroup->addChild(geode);
|
||||||
|
}
|
||||||
|
|
||||||
|
lightGroup->addChild(rwyLightsGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (taxiLights.getNumLights() > 0) {
|
if (taxiLights.getNumLights() > 0) {
|
||||||
@ -1115,7 +1100,7 @@ public:
|
|||||||
if (forestNode.valid()) objectLOD->addChild(forestNode.get(), 0, 2.0 * object_range + SG_TILE_RADIUS);
|
if (forestNode.valid()) objectLOD->addChild(forestNode.get(), 0, 2.0 * object_range + SG_TILE_RADIUS);
|
||||||
if (buildingNode.valid()) objectLOD->addChild(buildingNode.get(), 0, 2.0 * object_range + SG_TILE_RADIUS);
|
if (buildingNode.valid()) objectLOD->addChild(buildingNode.get(), 0, 2.0 * object_range + SG_TILE_RADIUS);
|
||||||
|
|
||||||
unsigned nodeMask = SG_NODEMASK_CASTSHADOW_BIT | SG_NODEMASK_RECEIVESHADOW_BIT | SG_NODEMASK_TERRAIN_BIT;
|
unsigned nodeMask = SG_NODEMASK_RECEIVESHADOW_BIT | SG_NODEMASK_TERRAIN_BIT;
|
||||||
objectLOD->setNodeMask(nodeMask);
|
objectLOD->setNodeMask(nodeMask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,6 +159,6 @@ SGLoadBTG(const std::string& path, const simgear::SGReaderWriterOptions* options
|
|||||||
transform->addChild(pagedLOD);
|
transform->addChild(pagedLOD);
|
||||||
}
|
}
|
||||||
|
|
||||||
transform->setNodeMask( ~simgear::MODELLIGHT_BIT );
|
transform->setNodeMask( ~(simgear::CASTSHADOW_BIT | simgear::MODELLIGHT_BIT) );
|
||||||
return transform;
|
return transform;
|
||||||
}
|
}
|
||||||
|
@ -214,8 +214,7 @@ SGLightFactory::getLights(const SGLightBin& lights, unsigned inc, float alphaOff
|
|||||||
geometry->setDataVariance(osg::Object::STATIC);
|
geometry->setDataVariance(osg::Object::STATIC);
|
||||||
geometry->setVertexArray(vertices);
|
geometry->setVertexArray(vertices);
|
||||||
geometry->setNormalBinding(osg::Geometry::BIND_OFF);
|
geometry->setNormalBinding(osg::Geometry::BIND_OFF);
|
||||||
geometry->setColorArray(colors);
|
geometry->setColorArray(colors, osg::Array::BIND_PER_VERTEX);
|
||||||
geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
|
|
||||||
|
|
||||||
osg::DrawArrays* drawArrays;
|
osg::DrawArrays* drawArrays;
|
||||||
drawArrays = new osg::DrawArrays(osg::PrimitiveSet::POINTS,
|
drawArrays = new osg::DrawArrays(osg::PrimitiveSet::POINTS,
|
||||||
@ -275,7 +274,7 @@ SGLightFactory::getLights(const SGDirectionalLightBin& lights)
|
|||||||
//stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
|
//stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
|
||||||
|
|
||||||
osg::DrawArrays* drawArrays;
|
osg::DrawArrays* drawArrays;
|
||||||
drawArrays = new osg::DrawArrays(osg::PrimitiveSet::POINTS,
|
drawArrays = new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES,
|
||||||
0, vertices->size());
|
0, vertices->size());
|
||||||
geometry->addPrimitiveSet(drawArrays);
|
geometry->addPrimitiveSet(drawArrays);
|
||||||
return geometry;
|
return geometry;
|
||||||
@ -370,18 +369,46 @@ SGLightFactory::getSequenced(const SGDirectionalLightBin& lights, const SGReader
|
|||||||
|
|
||||||
// generate a repeatable random seed
|
// generate a repeatable random seed
|
||||||
sg_srandom(unsigned(lights.getLight(0).position[0]));
|
sg_srandom(unsigned(lights.getLight(0).position[0]));
|
||||||
float flashTime = 2e-2 + 5e-3*sg_random();
|
float flashTime = 0.065 + 0.003 * sg_random();
|
||||||
osg::Sequence* sequence = new osg::Sequence;
|
osg::Sequence* sequence = new osg::Sequence;
|
||||||
sequence->setDefaultTime(flashTime);
|
sequence->setDefaultTime(flashTime);
|
||||||
Effect* effect = getLightEffect(10.0f, osg::Vec3(1.0, 0.0001, 0.00000001),
|
Effect* effect = getLightEffect(24.0f, osg::Vec3(1.0, 0.0001, 0.000001),
|
||||||
6.0f, 10.0f, true, options);
|
1.0f, 24.0f, true, options);
|
||||||
for (int i = lights.getNumLights() - 1; 0 <= i; --i) {
|
for (int i = lights.getNumLights() - 1; 0 <= i; --i) {
|
||||||
EffectGeode* egeode = new EffectGeode;
|
EffectGeode* egeode = new EffectGeode;
|
||||||
egeode->setEffect(effect);
|
egeode->setEffect(effect);
|
||||||
egeode->addDrawable(getLightDrawable(lights.getLight(i)));
|
egeode->addDrawable(getLightDrawable(lights.getLight(i)));
|
||||||
sequence->addChild(egeode, flashTime);
|
sequence->addChild(egeode, flashTime);
|
||||||
}
|
}
|
||||||
sequence->addChild(new osg::Group, 1 + 1e-1*sg_random());
|
sequence->addChild(new osg::Group, 1.9 + (0.1 * sg_random()) - (lights.getNumLights() * flashTime));
|
||||||
|
sequence->setInterval(osg::Sequence::LOOP, 0, -1);
|
||||||
|
sequence->setDuration(1.0f, -1);
|
||||||
|
sequence->setMode(osg::Sequence::START);
|
||||||
|
sequence->setSync(true);
|
||||||
|
return sequence;
|
||||||
|
}
|
||||||
|
|
||||||
|
osg::Node*
|
||||||
|
SGLightFactory::getReil(const SGDirectionalLightBin& lights, const SGReaderWriterOptions* options)
|
||||||
|
{
|
||||||
|
if (lights.getNumLights() <= 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// generate a repeatable random seed
|
||||||
|
sg_srandom(unsigned(lights.getLight(0).position[0]));
|
||||||
|
float flashTime = 0.065 + 0.003 * sg_random();
|
||||||
|
osg::Sequence* sequence = new osg::Sequence;
|
||||||
|
sequence->setDefaultTime(flashTime);
|
||||||
|
Effect* effect = getLightEffect(24.0f, osg::Vec3(1.0, 0.0001, 0.000001),
|
||||||
|
1.0f, 24.0f, true, options);
|
||||||
|
EffectGeode* egeode = new EffectGeode;
|
||||||
|
egeode->setEffect(effect);
|
||||||
|
|
||||||
|
for (int i = lights.getNumLights() - 1; 0 <= i; --i) {
|
||||||
|
egeode->addDrawable(getLightDrawable(lights.getLight(i)));
|
||||||
|
}
|
||||||
|
sequence->addChild(egeode, flashTime);
|
||||||
|
sequence->addChild(new osg::Group, 1.9 + 0.1 * sg_random() - flashTime);
|
||||||
sequence->setInterval(osg::Sequence::LOOP, 0, -1);
|
sequence->setInterval(osg::Sequence::LOOP, 0, -1);
|
||||||
sequence->setDuration(1.0f, -1);
|
sequence->setDuration(1.0f, -1);
|
||||||
sequence->setMode(osg::Sequence::START);
|
sequence->setMode(osg::Sequence::START);
|
||||||
@ -397,11 +424,11 @@ SGLightFactory::getOdal(const SGLightBin& lights, const SGReaderWriterOptions* o
|
|||||||
|
|
||||||
// generate a repeatable random seed
|
// generate a repeatable random seed
|
||||||
sg_srandom(unsigned(lights.getLight(0).position[0]));
|
sg_srandom(unsigned(lights.getLight(0).position[0]));
|
||||||
float flashTime = 2e-2 + 5e-3*sg_random();
|
float flashTime = 0.065 + 0.003 * sg_random();
|
||||||
osg::Sequence* sequence = new osg::Sequence;
|
osg::Sequence* sequence = new osg::Sequence;
|
||||||
sequence->setDefaultTime(flashTime);
|
sequence->setDefaultTime(flashTime);
|
||||||
Effect* effect = getLightEffect(10.0f, osg::Vec3(1.0, 0.0001, 0.00000001),
|
Effect* effect = getLightEffect(20.0f, osg::Vec3(1.0, 0.0001, 0.000001),
|
||||||
6.0, 10.0, false, options);
|
1.0f, 20.0f, false, options);
|
||||||
// centerline lights
|
// centerline lights
|
||||||
for (int i = lights.getNumLights() - 1; i >= 2; i--) {
|
for (int i = lights.getNumLights() - 1; i >= 2; i--) {
|
||||||
EffectGeode* egeode = new EffectGeode;
|
EffectGeode* egeode = new EffectGeode;
|
||||||
@ -409,18 +436,18 @@ SGLightFactory::getOdal(const SGLightBin& lights, const SGReaderWriterOptions* o
|
|||||||
egeode->addDrawable(getLightDrawable(lights.getLight(i)));
|
egeode->addDrawable(getLightDrawable(lights.getLight(i)));
|
||||||
sequence->addChild(egeode, flashTime);
|
sequence->addChild(egeode, flashTime);
|
||||||
}
|
}
|
||||||
|
// add extra empty group for a break
|
||||||
|
sequence->addChild(new osg::Group, 4 * flashTime);
|
||||||
// runway end lights
|
// runway end lights
|
||||||
osg::Group* group = new osg::Group;
|
|
||||||
for (unsigned i = 0; i < 2; ++i) {
|
|
||||||
EffectGeode* egeode = new EffectGeode;
|
EffectGeode* egeode = new EffectGeode;
|
||||||
egeode->setEffect(effect);
|
egeode->setEffect(effect);
|
||||||
|
for (unsigned i = 0; i < 2; ++i) {
|
||||||
egeode->addDrawable(getLightDrawable(lights.getLight(i)));
|
egeode->addDrawable(getLightDrawable(lights.getLight(i)));
|
||||||
group->addChild(egeode);
|
|
||||||
}
|
}
|
||||||
sequence->addChild(group, flashTime);
|
sequence->addChild(egeode, flashTime);
|
||||||
|
|
||||||
// add an extra empty group for a break
|
// add an extra empty group for a break
|
||||||
sequence->addChild(new osg::Group, 2 + 1e-1*sg_random());
|
sequence->addChild(new osg::Group, 1.9 + (0.1 * sg_random()) - ((lights.getNumLights() + 2) * flashTime));
|
||||||
sequence->setInterval(osg::Sequence::LOOP, 0, -1);
|
sequence->setInterval(osg::Sequence::LOOP, 0, -1);
|
||||||
sequence->setDuration(1.0f, -1);
|
sequence->setDuration(1.0f, -1);
|
||||||
sequence->setMode(osg::Sequence::START);
|
sequence->setMode(osg::Sequence::START);
|
||||||
@ -437,23 +464,22 @@ SGLightFactory::getHoldShort(const SGDirectionalLightBin& lights, const SGReader
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
sg_srandom(unsigned(lights.getLight(0).position[0]));
|
sg_srandom(unsigned(lights.getLight(0).position[0]));
|
||||||
float flashTime = 1 + 0.1 * sg_random();
|
float flashTime = 0.9 + 0.2 * sg_random();
|
||||||
osg::Sequence* sequence = new osg::Sequence;
|
osg::Sequence* sequence = new osg::Sequence;
|
||||||
|
|
||||||
// start with lights off
|
// start with lights off
|
||||||
sequence->addChild(new osg::Group, flashTime);
|
sequence->addChild(new osg::Group, 0.2);
|
||||||
// ...and increase the lights in steps
|
// ...and increase the lights in steps
|
||||||
for (int i = 2; i < 7; i+=2) {
|
for (int i = 0; i < 5; i++) {
|
||||||
Effect* effect = getLightEffect(i, osg::Vec3(1, 0.001, 0.000002),
|
Effect* effect = getLightEffect(12.0f + i, osg::Vec3(1, 0.001, 0.0002),
|
||||||
0.0f, i, true, options);
|
1.0f, 12.0f + i, true, options);
|
||||||
EffectGeode* egeode = new EffectGeode;
|
EffectGeode* egeode = new EffectGeode;
|
||||||
|
egeode->setEffect(effect);
|
||||||
for (unsigned int j = 0; j < lights.getNumLights(); ++j) {
|
for (unsigned int j = 0; j < lights.getNumLights(); ++j) {
|
||||||
egeode->addDrawable(getLightDrawable(lights.getLight(j)));
|
egeode->addDrawable(getLightDrawable(lights.getLight(j)));
|
||||||
egeode->setEffect(effect);
|
|
||||||
}
|
}
|
||||||
sequence->addChild(egeode, (i==6) ? flashTime : 0.1);
|
sequence->addChild(egeode, (i==4) ? flashTime : 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
sequence->setInterval(osg::Sequence::SWING, 0, -1);
|
sequence->setInterval(osg::Sequence::SWING, 0, -1);
|
||||||
sequence->setDuration(1.0f, -1);
|
sequence->setDuration(1.0f, -1);
|
||||||
sequence->setMode(osg::Sequence::START);
|
sequence->setMode(osg::Sequence::START);
|
||||||
@ -470,11 +496,11 @@ SGLightFactory::getGuard(const SGDirectionalLightBin& lights, const SGReaderWrit
|
|||||||
|
|
||||||
// generate a repeatable random seed
|
// generate a repeatable random seed
|
||||||
sg_srandom(unsigned(lights.getLight(0).position[0]));
|
sg_srandom(unsigned(lights.getLight(0).position[0]));
|
||||||
float flashTime = 1.0f + 1*sg_random();
|
float flashTime = 0.9 + 0.2 * sg_random();
|
||||||
osg::Sequence* sequence = new osg::Sequence;
|
osg::Sequence* sequence = new osg::Sequence;
|
||||||
sequence->setDefaultTime(flashTime);
|
sequence->setDefaultTime(flashTime);
|
||||||
Effect* effect = getLightEffect(10.0f, osg::Vec3(1.0, 0.001, 0.000002),
|
Effect* effect = getLightEffect(16.0f, osg::Vec3(1.0, 0.001, 0.0002),
|
||||||
0.0f, 8.0f, true, options);
|
1.0f, 16.0f, true, options);
|
||||||
for (unsigned int i = 0; i < lights.getNumLights(); ++i) {
|
for (unsigned int i = 0; i < lights.getNumLights(); ++i) {
|
||||||
EffectGeode* egeode = new EffectGeode;
|
EffectGeode* egeode = new EffectGeode;
|
||||||
egeode->setEffect(effect);
|
egeode->setEffect(effect);
|
||||||
|
@ -87,6 +87,9 @@ public:
|
|||||||
static osg::Node*
|
static osg::Node*
|
||||||
getSequenced(const SGDirectionalLightBin& lights, const simgear::SGReaderWriterOptions* options);
|
getSequenced(const SGDirectionalLightBin& lights, const simgear::SGReaderWriterOptions* options);
|
||||||
|
|
||||||
|
static osg::Node*
|
||||||
|
getReil(const SGDirectionalLightBin& lights, const simgear::SGReaderWriterOptions* options);
|
||||||
|
|
||||||
static osg::Node*
|
static osg::Node*
|
||||||
getOdal(const SGLightBin& lights, const simgear::SGReaderWriterOptions* options);
|
getOdal(const SGLightBin& lights, const simgear::SGReaderWriterOptions* options);
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
|
|
||||||
#include <osg/io_utils>
|
#include <osg/io_utils>
|
||||||
|
|
||||||
|
#include <simgear/constants.h>
|
||||||
#include <simgear/structure/exception.hxx>
|
#include <simgear/structure/exception.hxx>
|
||||||
|
|
||||||
namespace simgear {
|
namespace simgear {
|
||||||
@ -40,7 +41,7 @@ const int MAX_SPOTLIGHTS = 256;
|
|||||||
// It must be a multiple of the size of a vec4 as per the std140 layout rules.
|
// It must be a multiple of the size of a vec4 as per the std140 layout rules.
|
||||||
// See https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_uniform_buffer_object.txt
|
// See https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_uniform_buffer_object.txt
|
||||||
const int POINTLIGHT_BLOCK_SIZE = 20;
|
const int POINTLIGHT_BLOCK_SIZE = 20;
|
||||||
const int SPOTLIGHT_BLOCK_SIZE = 8;
|
const int SPOTLIGHT_BLOCK_SIZE = 28;
|
||||||
|
|
||||||
ClusteredShading::ClusteredShading(osg::Camera *camera,
|
ClusteredShading::ClusteredShading(osg::Camera *camera,
|
||||||
const SGPropertyNode *config) :
|
const SGPropertyNode *config) :
|
||||||
@ -180,12 +181,38 @@ ClusteredShading::update(const SGLightList &light_list)
|
|||||||
PointlightBound point;
|
PointlightBound point;
|
||||||
point.light = light;
|
point.light = light;
|
||||||
point.position = osg::Vec4f(0.0f, 0.0f, 0.0f, 1.0f) *
|
point.position = osg::Vec4f(0.0f, 0.0f, 0.0f, 1.0f) *
|
||||||
osg::computeLocalToWorld(light->getParentalNodePaths()[0]) *
|
// The parenthesis are very important: if the vector is
|
||||||
_camera->getViewMatrix();
|
// multiplied by the local to world matrix first we'll have
|
||||||
|
// precision issues
|
||||||
|
(osg::computeLocalToWorld(light->getParentalNodePaths()[0]) *
|
||||||
|
_camera->getViewMatrix());
|
||||||
point.range = light->getRange();
|
point.range = light->getRange();
|
||||||
|
|
||||||
_point_bounds.push_back(point);
|
_point_bounds.push_back(point);
|
||||||
} else if (light->getType() == SGLight::Type::SPOT) {
|
} else if (light->getType() == SGLight::Type::SPOT) {
|
||||||
|
SpotlightBound spot;
|
||||||
|
spot.light = light;
|
||||||
|
spot.position = osg::Vec4f(0.0f, 0.0f, 0.0f, 1.0f) *
|
||||||
|
(osg::computeLocalToWorld(light->getParentalNodePaths()[0]) *
|
||||||
|
_camera->getViewMatrix());
|
||||||
|
spot.direction = osg::Vec4f(0.0f, 0.0f, -1.0f, 0.0f) *
|
||||||
|
(osg::computeLocalToWorld(light->getParentalNodePaths()[0]) *
|
||||||
|
_camera->getViewMatrix());
|
||||||
|
|
||||||
|
float range = light->getRange();
|
||||||
|
float angle = light->getSpotCutoff() * SG_DEGREES_TO_RADIANS;
|
||||||
|
spot.cos_cutoff = cos(angle);
|
||||||
|
if(angle > SGD_PI_4) {
|
||||||
|
spot.bounding_sphere.radius = range * tan(angle);
|
||||||
|
} else {
|
||||||
|
spot.bounding_sphere.radius =
|
||||||
|
range * 0.5f / pow(spot.cos_cutoff, 2.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
spot.bounding_sphere.center =
|
||||||
|
spot.position + spot.direction * spot.bounding_sphere.radius;
|
||||||
|
|
||||||
|
_spot_bounds.push_back(spot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (_point_bounds.size() > MAX_POINTLIGHTS ||
|
if (_point_bounds.size() > MAX_POINTLIGHTS ||
|
||||||
@ -265,8 +292,9 @@ ClusteredShading::update(const SGLightList &light_list)
|
|||||||
_light_grid->dirty();
|
_light_grid->dirty();
|
||||||
_light_indices->dirty();
|
_light_indices->dirty();
|
||||||
|
|
||||||
// Upload pointlight data
|
// Upload pointlight and spotlight data
|
||||||
writePointlightData();
|
writePointlightData();
|
||||||
|
writeSpotlightData();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -301,6 +329,7 @@ ClusteredShading::assignLightsToSlice(int slice)
|
|||||||
GLuint local_point_count = 0;
|
GLuint local_point_count = 0;
|
||||||
GLuint local_spot_count = 0;
|
GLuint local_spot_count = 0;
|
||||||
|
|
||||||
|
// Test point lights
|
||||||
for (GLushort point_iterator = 0;
|
for (GLushort point_iterator = 0;
|
||||||
point_iterator < _point_bounds.size();
|
point_iterator < _point_bounds.size();
|
||||||
++point_iterator) {
|
++point_iterator) {
|
||||||
@ -328,25 +357,40 @@ ClusteredShading::assignLightsToSlice(int slice)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test spot lights
|
||||||
|
for (GLushort spot_iterator = 0;
|
||||||
|
spot_iterator < _spot_bounds.size();
|
||||||
|
++spot_iterator) {
|
||||||
|
SpotlightBound spot = _spot_bounds[spot_iterator];
|
||||||
|
|
||||||
|
// Perform frustum-sphere collision tests
|
||||||
|
float distance = 0.0f;
|
||||||
|
for (int j = 0; j < 6; j++) {
|
||||||
|
distance = subfrustum.plane[j] * spot.bounding_sphere.center
|
||||||
|
+ spot.bounding_sphere.radius;
|
||||||
|
if (distance <= 0.0f)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (distance > 0.0f) {
|
||||||
|
// Update light index list
|
||||||
|
indices[_global_light_count] = spot_iterator;
|
||||||
|
++local_spot_count;
|
||||||
|
++_global_light_count; // Atomic increment
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_global_light_count >= MAX_LIGHT_INDICES) {
|
||||||
|
throw sg_range_exception(
|
||||||
|
"Clustered shading light index count is over the hardcoded limit ("
|
||||||
|
+ std::to_string(MAX_LIGHT_INDICES) + ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Update light grid
|
// Update light grid
|
||||||
grid[(z_offset + i) * 3 + 0] = start_offset;
|
grid[(z_offset + i) * 3 + 0] = start_offset;
|
||||||
grid[(z_offset + i) * 3 + 1] = local_point_count;
|
grid[(z_offset + i) * 3 + 1] = local_point_count;
|
||||||
grid[(z_offset + i) * 3 + 2] = local_spot_count;
|
grid[(z_offset + i) * 3 + 2] = local_spot_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
// for (int y = 0; y < _n_vtiles; ++y) {
|
|
||||||
// for (int x = 0; x < _n_htiles; ++x) {
|
|
||||||
// std::cout << grid[(y * _n_htiles + x) * 3 + 0] << ","
|
|
||||||
// << grid[(y * _n_htiles + x) * 3 + 1] << " ";
|
|
||||||
// }
|
|
||||||
// std::cout << std::endl;
|
|
||||||
// }
|
|
||||||
// std::cout << "\n\n";
|
|
||||||
|
|
||||||
// for (int i = 0; i < n_vtiles * n_htiles; ++i) {
|
|
||||||
// std::cout << indices[i] << " ";
|
|
||||||
// }
|
|
||||||
// std::cout << "\n";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -385,6 +429,51 @@ ClusteredShading::writePointlightData()
|
|||||||
_pointlight_data->dirty();
|
_pointlight_data->dirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ClusteredShading::writeSpotlightData()
|
||||||
|
{
|
||||||
|
GLfloat *data = reinterpret_cast<GLfloat *>(&(*_spotlight_data)[0]);
|
||||||
|
|
||||||
|
for (const auto &spot : _spot_bounds) {
|
||||||
|
// vec4 position
|
||||||
|
*data++ = spot.position.x();
|
||||||
|
*data++ = spot.position.y();
|
||||||
|
*data++ = spot.position.z();
|
||||||
|
*data++ = 1.0f;
|
||||||
|
// vec4 direction
|
||||||
|
*data++ = spot.direction.x();
|
||||||
|
*data++ = spot.direction.y();
|
||||||
|
*data++ = spot.direction.z();
|
||||||
|
*data++ = 0.0f;
|
||||||
|
// vec4 ambient
|
||||||
|
*data++ = spot.light->getAmbient().x();
|
||||||
|
*data++ = spot.light->getAmbient().y();
|
||||||
|
*data++ = spot.light->getAmbient().z();
|
||||||
|
*data++ = spot.light->getAmbient().w();
|
||||||
|
// vec4 diffuse
|
||||||
|
*data++ = spot.light->getDiffuse().x();
|
||||||
|
*data++ = spot.light->getDiffuse().y();
|
||||||
|
*data++ = spot.light->getDiffuse().z();
|
||||||
|
*data++ = spot.light->getDiffuse().w();
|
||||||
|
// vec4 specular
|
||||||
|
*data++ = spot.light->getSpecular().x();
|
||||||
|
*data++ = spot.light->getSpecular().y();
|
||||||
|
*data++ = spot.light->getSpecular().z();
|
||||||
|
*data++ = spot.light->getSpecular().w();
|
||||||
|
// vec4 attenuation (x = constant, y = linear, z = quadratic, w = range)
|
||||||
|
*data++ = spot.light->getConstantAttenuation();
|
||||||
|
*data++ = spot.light->getLinearAttenuation();
|
||||||
|
*data++ = spot.light->getQuadraticAttenuation();
|
||||||
|
*data++ = spot.light->getRange();
|
||||||
|
// float cos_cutoff
|
||||||
|
*data++ = spot.cos_cutoff;
|
||||||
|
// float exponent
|
||||||
|
*data++ = spot.light->getSpotExponent();
|
||||||
|
// Needs 2N padding (8 bytes)
|
||||||
|
}
|
||||||
|
_spotlight_data->dirty();
|
||||||
|
}
|
||||||
|
|
||||||
float
|
float
|
||||||
ClusteredShading::getDepthForSlice(int slice) const
|
ClusteredShading::getDepthForSlice(int slice) const
|
||||||
{
|
{
|
||||||
|
@ -42,19 +42,25 @@ protected:
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct PointlightBound {
|
struct PointlightBound {
|
||||||
SGLight *light;
|
SGLight *light = nullptr;
|
||||||
osg::Vec4f position;
|
osg::Vec4f position;
|
||||||
float range;
|
float range = 0.0f;
|
||||||
};
|
};
|
||||||
struct SpotlightBound {
|
struct SpotlightBound {
|
||||||
SGLight *light;
|
SGLight *light = nullptr;
|
||||||
osg::Vec4f position;
|
osg::Vec4f position;
|
||||||
float range;
|
osg::Vec4f direction;
|
||||||
|
float cos_cutoff = 0.0f;
|
||||||
|
struct {
|
||||||
|
osg::Vec4f center;
|
||||||
|
float radius = 0.0f;
|
||||||
|
} bounding_sphere;
|
||||||
};
|
};
|
||||||
|
|
||||||
void threadFunc(int thread_id);
|
void threadFunc(int thread_id);
|
||||||
void assignLightsToSlice(int slice);
|
void assignLightsToSlice(int slice);
|
||||||
void writePointlightData();
|
void writePointlightData();
|
||||||
|
void writeSpotlightData();
|
||||||
float getDepthForSlice(int slice) const;
|
float getDepthForSlice(int slice) const;
|
||||||
|
|
||||||
osg::observer_ptr<osg::Camera> _camera;
|
osg::observer_ptr<osg::Camera> _camera;
|
||||||
|
@ -36,6 +36,25 @@
|
|||||||
#include "Compositor.hxx"
|
#include "Compositor.hxx"
|
||||||
#include "CompositorUtil.hxx"
|
#include "CompositorUtil.hxx"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
osgUtil::RenderBin::RenderBinList
|
||||||
|
removeTransparentBins(simgear::EffectCullVisitor *cv)
|
||||||
|
{
|
||||||
|
osgUtil::RenderBin::RenderBinList transparent_bins;
|
||||||
|
osgUtil::RenderStage *stage = cv->getRenderStage();
|
||||||
|
osgUtil::RenderBin::RenderBinList &rbl = stage->getRenderBinList();
|
||||||
|
for (auto rbi = rbl.begin(); rbi != rbl.end(); ) {
|
||||||
|
if (rbi->second->getSortMode() == osgUtil::RenderBin::SORT_BACK_TO_FRONT) {
|
||||||
|
transparent_bins.insert(std::make_pair(rbi->first, rbi->second));
|
||||||
|
rbl.erase(rbi++);
|
||||||
|
} else {
|
||||||
|
++rbi;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return transparent_bins;
|
||||||
|
}
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
namespace simgear {
|
namespace simgear {
|
||||||
namespace compositor {
|
namespace compositor {
|
||||||
|
|
||||||
@ -112,15 +131,15 @@ PassBuilder::build(Compositor *compositor, const SGPropertyNode *root,
|
|||||||
camera->setClearStencil(root->getIntValue("clear-stencil", 0));
|
camera->setClearStencil(root->getIntValue("clear-stencil", 0));
|
||||||
|
|
||||||
GLbitfield clear_mask = 0;
|
GLbitfield clear_mask = 0;
|
||||||
if (root->getBoolValue("clear-color-bit", true))
|
std::stringstream ss;
|
||||||
clear_mask |= GL_COLOR_BUFFER_BIT;
|
std::string bit;
|
||||||
if (root->getBoolValue("clear-accum-bit", false))
|
// Default clear mask as in OSG
|
||||||
clear_mask |= GL_ACCUM_BUFFER_BIT;
|
ss << root->getStringValue("clear-mask", "color depth");
|
||||||
if (root->getBoolValue("clear-depth-bit", true))
|
while (ss >> bit) {
|
||||||
clear_mask |= GL_DEPTH_BUFFER_BIT;
|
if (bit == "color") clear_mask |= GL_COLOR_BUFFER_BIT;
|
||||||
if (root->getBoolValue("clear-stencil-bit", false))
|
else if (bit == "depth") clear_mask |= GL_DEPTH_BUFFER_BIT;
|
||||||
clear_mask |= GL_STENCIL_BUFFER_BIT;
|
else if (bit == "stencil") clear_mask |= GL_STENCIL_BUFFER_BIT;
|
||||||
// Default clear mask is GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, as in OSG
|
}
|
||||||
camera->setClearMask(clear_mask);
|
camera->setClearMask(clear_mask);
|
||||||
|
|
||||||
PropertyList p_bindings = root->getChildren("binding");
|
PropertyList p_bindings = root->getChildren("binding");
|
||||||
@ -384,9 +403,12 @@ public:
|
|||||||
|
|
||||||
virtual void operator()(osg::Node *node, osg::NodeVisitor *nv) {
|
virtual void operator()(osg::Node *node, osg::NodeVisitor *nv) {
|
||||||
osg::Camera *camera = static_cast<osg::Camera *>(node);
|
osg::Camera *camera = static_cast<osg::Camera *>(node);
|
||||||
|
EffectCullVisitor *cv = dynamic_cast<EffectCullVisitor *>(nv);
|
||||||
|
|
||||||
traverse(node, nv);
|
traverse(node, nv);
|
||||||
|
|
||||||
|
removeTransparentBins(cv);
|
||||||
|
|
||||||
// The light matrix uniform is updated after the traverse in case the
|
// The light matrix uniform is updated after the traverse in case the
|
||||||
// OSG near/far plane calculations were enabled
|
// OSG near/far plane calculations were enabled
|
||||||
osg::Matrixf light_matrix =
|
osg::Matrixf light_matrix =
|
||||||
@ -497,19 +519,18 @@ public:
|
|||||||
|
|
||||||
osg::Vec4 aim4 = osg::Vec4(bs.center(), 1.0) * view_inverse;
|
osg::Vec4 aim4 = osg::Vec4(bs.center(), 1.0) * view_inverse;
|
||||||
osg::Vec3 aim(aim4.x(), aim4.y(), aim4.z());
|
osg::Vec3 aim(aim4.x(), aim4.y(), aim4.z());
|
||||||
osg::Vec3 up(0.0f, 1.0f, 0.0f);
|
|
||||||
|
|
||||||
osg::Matrixd &light_view_matrix = camera->getViewMatrix();
|
osg::Matrixd &light_view_matrix = camera->getViewMatrix();
|
||||||
light_view_matrix.makeLookAt(
|
light_view_matrix.makeLookAt(
|
||||||
aim + (light_dir * bs.radius() * 2.0f),
|
aim + light_dir * (bs.radius() + 10.0f),
|
||||||
aim,
|
aim,
|
||||||
aim);
|
osg::Vec3(0.0f, 0.0f, 1.0f));
|
||||||
|
|
||||||
osg::Matrixd &light_proj_matrix = camera->getProjectionMatrix();
|
osg::Matrixd &light_proj_matrix = camera->getProjectionMatrix();
|
||||||
light_proj_matrix.makeOrtho(
|
light_proj_matrix.makeOrtho(
|
||||||
-bs.radius(), bs.radius(),
|
-bs.radius(), bs.radius(),
|
||||||
-bs.radius(), bs.radius(),
|
-bs.radius(), bs.radius(),
|
||||||
-bs.radius() * 6.0f, bs.radius() * 6.0f);
|
1.0, bs.radius() * 10.0);
|
||||||
|
|
||||||
// Do texel snapping to prevent flickering or shimmering.
|
// Do texel snapping to prevent flickering or shimmering.
|
||||||
// We are using double precision vectors and matrices because in FG
|
// We are using double precision vectors and matrices because in FG
|
||||||
@ -520,8 +541,8 @@ public:
|
|||||||
osg::Vec2d shadow_origin(shadow_origin4.x(), shadow_origin4.y());
|
osg::Vec2d shadow_origin(shadow_origin4.x(), shadow_origin4.y());
|
||||||
shadow_origin = osg::Vec2d(shadow_origin.x() * _half_sm_size.x(),
|
shadow_origin = osg::Vec2d(shadow_origin.x() * _half_sm_size.x(),
|
||||||
shadow_origin.y() * _half_sm_size.y());
|
shadow_origin.y() * _half_sm_size.y());
|
||||||
osg::Vec2d rounded_origin(std::round(shadow_origin.x()),
|
osg::Vec2d rounded_origin(std::floor(shadow_origin.x()),
|
||||||
std::round(shadow_origin.y()));
|
std::floor(shadow_origin.y()));
|
||||||
osg::Vec2d rounding = rounded_origin - shadow_origin;
|
osg::Vec2d rounding = rounded_origin - shadow_origin;
|
||||||
rounding = osg::Vec2d(rounding.x() / _half_sm_size.x(),
|
rounding = osg::Vec2d(rounding.x() / _half_sm_size.x(),
|
||||||
rounding.y() / _half_sm_size.y());
|
rounding.y() / _half_sm_size.y());
|
||||||
@ -546,8 +567,7 @@ struct ShadowMapPassBuilder : public PassBuilder {
|
|||||||
|
|
||||||
osg::Camera *camera = pass->camera;
|
osg::Camera *camera = pass->camera;
|
||||||
camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT);
|
camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT);
|
||||||
camera->setCullingMode(camera->getCullingMode() &
|
camera->setCullingMode(osg::CullSettings::ENABLE_ALL_CULLING);
|
||||||
~osg::CullSettings::SMALL_FEATURE_CULLING);
|
|
||||||
//camera->setComputeNearFarMode(
|
//camera->setComputeNearFarMode(
|
||||||
// osg::CullSettings::COMPUTE_NEAR_FAR_USING_BOUNDING_VOLUMES);
|
// osg::CullSettings::COMPUTE_NEAR_FAR_USING_BOUNDING_VOLUMES);
|
||||||
|
|
||||||
|
@ -38,28 +38,13 @@ if(ENABLE_TESTS AND ENABLE_SOUND)
|
|||||||
COMPILE_DEFINITIONS "SRC_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}\"" )
|
COMPILE_DEFINITIONS "SRC_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}\"" )
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
set( SOUND_TEST_LIBS
|
set( SOUND_TEST_LIBS ${TEST_LIBS} )
|
||||||
${TEST_LIBS}
|
|
||||||
)
|
|
||||||
|
|
||||||
if (USE_AEONWAVE)
|
if (USE_AEONWAVE)
|
||||||
if (SIMGEAR_SHARED)
|
set(SOUND_TEST_LIBS ${SOUND_TEST_LIBS} ${AAX_LIBRARY})
|
||||||
else()
|
|
||||||
set(SOUND_TEST_LIBS ${SOUND_TEST_LIBS}
|
|
||||||
${AAX_LIBRARY}
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
create_test(aeonwave_test1)
|
create_test(aeonwave_test1)
|
||||||
|
|
||||||
else ()
|
else ()
|
||||||
if (SIMGEAR_SHARED)
|
set(SOUND_TEST_LIBS ${SOUND_TEST_LIBS} ${OPENAL_LIBRARY})
|
||||||
else()
|
|
||||||
set(SOUND_TEST_LIBS ${SOUND_TEST_LIBS}
|
|
||||||
${OPENAL_LIBRARY}
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
create_test(openal_test1)
|
create_test(openal_test1)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
// Modified to match the new SoundSystem by Erik Hofman, October 2009
|
// Modified to match the new SoundSystem by Erik Hofman, October 2009
|
||||||
//
|
//
|
||||||
// Copyright (C) 2004 Curtis L. Olson - http://www.flightgear.org/~curt
|
// Copyright (C) 2004 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||||
// Copyright (C) 2009 Erik Hofman <erik@ehofman.com>
|
// Copyright (C) 2009-2019 Erik Hofman <erik@ehofman.com>
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License as
|
// modify it under the terms of the GNU General Public License as
|
||||||
@ -49,24 +49,6 @@ using std::string;
|
|||||||
// empty constructor
|
// empty constructor
|
||||||
SGSoundSampleInfo::SGSoundSampleInfo() :
|
SGSoundSampleInfo::SGSoundSampleInfo() :
|
||||||
_refname(random_string()),
|
_refname(random_string()),
|
||||||
_bits(16),
|
|
||||||
_tracks(1),
|
|
||||||
_samples(0),
|
|
||||||
_frequency(22500),
|
|
||||||
_compressed(false),
|
|
||||||
_loop(false),
|
|
||||||
_static_changed(true),
|
|
||||||
_playing(false),
|
|
||||||
_pitch(1.0f),
|
|
||||||
_volume(1.0f),
|
|
||||||
_master_volume(1.0f),
|
|
||||||
_use_pos_props(false),
|
|
||||||
_out_of_range(false),
|
|
||||||
_inner_angle(360.0f),
|
|
||||||
_outer_angle(360.0f),
|
|
||||||
_outer_gain(0.0f),
|
|
||||||
_reference_dist(500.0f),
|
|
||||||
_max_dist(3000.0f),
|
|
||||||
_absolute_pos(SGVec3d::zeros()),
|
_absolute_pos(SGVec3d::zeros()),
|
||||||
_relative_pos(SGVec3d::zeros()),
|
_relative_pos(SGVec3d::zeros()),
|
||||||
_direction(SGVec3d::zeros()),
|
_direction(SGVec3d::zeros()),
|
||||||
@ -97,27 +79,9 @@ std::string SGSoundSampleInfo::random_string()
|
|||||||
// SGSoundSample
|
// SGSoundSample
|
||||||
//
|
//
|
||||||
|
|
||||||
// empty constructor
|
|
||||||
SGSoundSample::SGSoundSample() :
|
|
||||||
_is_file(false),
|
|
||||||
_changed(true),
|
|
||||||
_valid_source(false),
|
|
||||||
_source(SGSoundMgr::NO_SOURCE),
|
|
||||||
_data(NULL),
|
|
||||||
_valid_buffer(false),
|
|
||||||
_buffer(SGSoundMgr::NO_BUFFER)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// constructor
|
// constructor
|
||||||
SGSoundSample::SGSoundSample(const char *file, const SGPath& currentDir) :
|
SGSoundSample::SGSoundSample(const char *file, const SGPath& currentDir) :
|
||||||
_is_file(true),
|
_is_file(true)
|
||||||
_changed(true),
|
|
||||||
_valid_source(false),
|
|
||||||
_source(SGSoundMgr::NO_SOURCE),
|
|
||||||
_data(NULL),
|
|
||||||
_valid_buffer(false),
|
|
||||||
_buffer(SGSoundMgr::NO_BUFFER)
|
|
||||||
{
|
{
|
||||||
SGPath p = simgear::ResourceManager::instance()->findPath(file, currentDir);
|
SGPath p = simgear::ResourceManager::instance()->findPath(file, currentDir);
|
||||||
_refname = p.utf8Str();
|
_refname = p.utf8Str();
|
||||||
@ -125,13 +89,7 @@ SGSoundSample::SGSoundSample(const char *file, const SGPath& currentDir) :
|
|||||||
|
|
||||||
// constructor
|
// constructor
|
||||||
SGSoundSample::SGSoundSample( const unsigned char** data,
|
SGSoundSample::SGSoundSample( const unsigned char** data,
|
||||||
int len, int freq, int format ) :
|
int len, int freq, int format )
|
||||||
_is_file(false),
|
|
||||||
_changed(true),
|
|
||||||
_valid_source(false),
|
|
||||||
_source(SGSoundMgr::NO_SOURCE),
|
|
||||||
_valid_buffer(false),
|
|
||||||
_buffer(SGSoundMgr::NO_BUFFER)
|
|
||||||
{
|
{
|
||||||
SG_LOG( SG_SOUND, SG_DEBUG, "In memory sounds sample" );
|
SG_LOG( SG_SOUND, SG_DEBUG, "In memory sounds sample" );
|
||||||
_data = (unsigned char*)*data; *data = NULL;
|
_data = (unsigned char*)*data; *data = NULL;
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
// Modified to match the new SoundSystem by Erik Hofman, October 2009
|
// Modified to match the new SoundSystem by Erik Hofman, October 2009
|
||||||
//
|
//
|
||||||
// Copyright (C) 2004 Curtis L. Olson - http://www.flightgear.org/~curt
|
// Copyright (C) 2004 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||||
// Copyright (C) 2009 Erik Hofman <erik@ehofman.com>
|
// Copyright (C) 2009-2019 Erik Hofman <erik@ehofman.com>
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License as
|
// modify it under the terms of the GNU General Public License as
|
||||||
@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
#include <simgear/math/SGMath.hxx>
|
#include <simgear/math/SGMath.hxx>
|
||||||
#include <simgear/props/props.hxx>
|
#include <simgear/props/props.hxx>
|
||||||
|
#include "soundmgr.hxx"
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
SG_SAMPLE_MONO = 1,
|
SG_SAMPLE_MONO = 1,
|
||||||
@ -164,10 +165,28 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Get maximum distance (in meters) of this sound.
|
* Get maximum distance (in meters) of this sound.
|
||||||
* This is the distance where this sound is no longer audible.
|
* This is the distance where this sound is no longer audible.
|
||||||
* @return dist Maximum distance
|
* @return Maximum distance
|
||||||
*/
|
*/
|
||||||
inline float get_max_dist() { return _max_dist; }
|
inline float get_max_dist() { return _max_dist; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the temperature (in degrees Celsius) at the current altitude.
|
||||||
|
* @return temperature in degrees Celsius
|
||||||
|
*/
|
||||||
|
inline float get_temperature() { return _degC; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the relative humidity at the current altitude.
|
||||||
|
* @return Percent relative humidity (0.0 to 1.0)
|
||||||
|
*/
|
||||||
|
inline float get_humidity() { return _humidity; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the pressure at the current altitude.
|
||||||
|
* @return Pressure in kPa
|
||||||
|
*/
|
||||||
|
inline float get_pressure() { return _pressure; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test if static data of audio sample configuration has changed.
|
* Test if static data of audio sample configuration has changed.
|
||||||
* Calling this function will reset the flag so calling it a second
|
* Calling this function will reset the flag so calling it a second
|
||||||
@ -181,32 +200,35 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
// static sound emitter info
|
// static sound emitter info
|
||||||
std::string _refname;
|
std::string _refname;
|
||||||
unsigned int _bits;
|
unsigned int _bits = 16;
|
||||||
unsigned int _tracks;
|
unsigned int _tracks = 1;
|
||||||
unsigned int _samples;
|
unsigned int _samples = 0;
|
||||||
unsigned int _frequency;
|
unsigned int _frequency = 22500;
|
||||||
unsigned int _block_align;
|
unsigned int _block_align = 2;
|
||||||
bool _compressed;
|
bool _compressed = false;
|
||||||
bool _loop;
|
bool _loop = false;
|
||||||
|
|
||||||
// dynamic sound emitter info (non 3d)
|
// dynamic sound emitter info (non 3d)
|
||||||
bool _static_changed;
|
bool _static_changed = true;
|
||||||
bool _playing;
|
bool _playing = false;
|
||||||
|
|
||||||
float _pitch;
|
float _pitch = 1.0f;
|
||||||
float _volume;
|
float _volume = 1.0f;
|
||||||
float _master_volume;
|
float _master_volume = 1.0f;
|
||||||
|
|
||||||
// dynamic sound emitter info (3d)
|
// dynamic sound emitter info (3d)
|
||||||
bool _use_pos_props;
|
bool _use_pos_props = false;
|
||||||
bool _out_of_range;
|
bool _out_of_range = false;
|
||||||
|
|
||||||
float _inner_angle;
|
float _inner_angle = 360.0f;
|
||||||
float _outer_angle;
|
float _outer_angle = 360.0f;
|
||||||
float _outer_gain;
|
float _outer_gain = 0.0f;
|
||||||
|
|
||||||
float _reference_dist;
|
float _reference_dist = 500.0f;
|
||||||
float _max_dist;
|
float _max_dist = 3000.0f;
|
||||||
|
float _pressure = 101.325f;
|
||||||
|
float _humidity = 0.5f;
|
||||||
|
float _degC = 20.0f;
|
||||||
|
|
||||||
SGPropertyNode_ptr _pos_prop[3];
|
SGPropertyNode_ptr _pos_prop[3];
|
||||||
SGVec3d _absolute_pos; // absolute position
|
SGVec3d _absolute_pos; // absolute position
|
||||||
@ -233,7 +255,7 @@ public:
|
|||||||
* Empty constructor, can be used to read data to the systems
|
* Empty constructor, can be used to read data to the systems
|
||||||
* memory and not to the driver.
|
* memory and not to the driver.
|
||||||
*/
|
*/
|
||||||
SGSoundSample();
|
SGSoundSample() {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
@ -540,6 +562,20 @@ public:
|
|||||||
_velocity = vel; _changed = true;
|
_velocity = vel; _changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set both the temperature and relative humidity at the current altitude.
|
||||||
|
* @param t Temperature in degrees Celsius
|
||||||
|
* @param h Percent relative humidity (0.0 to 1.0)
|
||||||
|
* @param p Pressure in kPa;
|
||||||
|
*/
|
||||||
|
inline void set_atmosphere(float t, float h, float p) {
|
||||||
|
if (fabsf(_degC - t) > 1.0f || fabsf(_humidity - h) > 0.1f ||
|
||||||
|
fabsf(_pressure - p) > 1.0f)
|
||||||
|
{
|
||||||
|
_degC = t, _humidity = h; _pressure = p; _static_changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set reference distance (in meters) of this sound.
|
* Set reference distance (in meters) of this sound.
|
||||||
* This is the distance where the gain will be half.
|
* This is the distance where the gain will be half.
|
||||||
@ -563,19 +599,19 @@ public:
|
|||||||
void update_pos_and_orientation();
|
void update_pos_and_orientation();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool _is_file;
|
bool _is_file = false;
|
||||||
bool _changed;
|
bool _changed = true;
|
||||||
|
|
||||||
// Sources are points emitting sound.
|
// Sources are points emitting sound.
|
||||||
bool _valid_source;
|
bool _valid_source = false;
|
||||||
unsigned int _source;
|
unsigned int _source = SGSoundMgr::NO_SOURCE;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
unsigned char* _data;
|
unsigned char* _data = NULL;
|
||||||
|
|
||||||
// Buffers hold sound data.
|
// Buffers hold sound data.
|
||||||
bool _valid_buffer;
|
bool _valid_buffer = false;
|
||||||
unsigned int _buffer;
|
unsigned int _buffer = SGSoundMgr::NO_BUFFER;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // _SG_SAMPLE_HXX
|
#endif // _SG_SAMPLE_HXX
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
//
|
//
|
||||||
// Written for the new SoundSystem by Erik Hofman, October 2009
|
// Written for the new SoundSystem by Erik Hofman, October 2009
|
||||||
//
|
//
|
||||||
// Copyright (C) 2009 Erik Hofman <erik@ehofman.com>
|
// Copyright (C) 2009-2019 Erik Hofman <erik@ehofman.com>
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or
|
// This program is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU General Public License as
|
// modify it under the terms of the GNU General Public License as
|
||||||
@ -31,16 +31,7 @@
|
|||||||
#include "soundmgr.hxx"
|
#include "soundmgr.hxx"
|
||||||
#include "sample_group.hxx"
|
#include "sample_group.hxx"
|
||||||
|
|
||||||
SGSampleGroup::SGSampleGroup () :
|
SGSampleGroup::SGSampleGroup ()
|
||||||
_smgr(NULL),
|
|
||||||
_refname(""),
|
|
||||||
_active(false),
|
|
||||||
_changed(false),
|
|
||||||
_pause(false),
|
|
||||||
_volume(1.0),
|
|
||||||
_tied_to_listener(false),
|
|
||||||
_velocity(SGVec3d::zeros()),
|
|
||||||
_orientation(SGQuatd::zeros())
|
|
||||||
{
|
{
|
||||||
_samples.clear();
|
_samples.clear();
|
||||||
}
|
}
|
||||||
@ -48,14 +39,7 @@ SGSampleGroup::SGSampleGroup () :
|
|||||||
SGSampleGroup::SGSampleGroup ( SGSoundMgr *smgr,
|
SGSampleGroup::SGSampleGroup ( SGSoundMgr *smgr,
|
||||||
const std::string &refname ):
|
const std::string &refname ):
|
||||||
_smgr(smgr),
|
_smgr(smgr),
|
||||||
_refname(refname),
|
_refname(refname)
|
||||||
_active(false),
|
|
||||||
_changed(false),
|
|
||||||
_pause(false),
|
|
||||||
_volume(1.0),
|
|
||||||
_tied_to_listener(false),
|
|
||||||
_velocity(SGVec3d::zeros()),
|
|
||||||
_orientation(SGQuatd::zeros())
|
|
||||||
{
|
{
|
||||||
_smgr->add(this, refname);
|
_smgr->add(this, refname);
|
||||||
_samples.clear();
|
_samples.clear();
|
||||||
@ -318,6 +302,7 @@ void SGSampleGroup::update_pos_and_orientation() {
|
|||||||
sample->set_rotation( ec2body );
|
sample->set_rotation( ec2body );
|
||||||
sample->set_position(base_position);
|
sample->set_position(base_position);
|
||||||
sample->set_velocity( velocity );
|
sample->set_velocity( velocity );
|
||||||
|
sample->set_atmosphere( _degC, _humidity, _pressure );
|
||||||
|
|
||||||
// Test if a sample is farther away than max distance, if so
|
// Test if a sample is farther away than max distance, if so
|
||||||
// stop the sound playback and free it's source.
|
// stop the sound playback and free it's source.
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
//
|
//
|
||||||
// Written for the new SoundSystem by Erik Hofman, October 2009
|
// Written for the new SoundSystem by Erik Hofman, October 2009
|
||||||
//
|
//
|
||||||
// Copyright (C) 2009 Erik Hofman <erik@ehofman.com>
|
// Copyright (C) 2009-2019 Erik Hofman <erik@ehofman.com>
|
||||||
//
|
//
|
||||||
// This library is free software; you can redistribute it and/or
|
// This library is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU Library General Public
|
// modify it under the terms of the GNU Library General Public
|
||||||
@ -196,29 +196,43 @@ public:
|
|||||||
_orientation = ori; _changed = true;
|
_orientation = ori; _changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set both the temperature and relative humidity at the current altitude.
|
||||||
|
* @param t Temperature in degrees Celsius
|
||||||
|
* @param h Percent relative humidity (0.0 to 1.0)
|
||||||
|
* @param p Pressure in kPa
|
||||||
|
*/
|
||||||
|
void set_atmosphere(float t, float h, float p ) {
|
||||||
|
_degC = t, _humidity = h; _pressure = p; _changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tie this sample group to the listener position, orientation and velocity
|
* Tie this sample group to the listener position, orientation and velocity
|
||||||
*/
|
*/
|
||||||
void tie_to_listener() { _tied_to_listener = true; }
|
void tie_to_listener() { _tied_to_listener = true; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
SGSoundMgr *_smgr;
|
SGSoundMgr *_smgr = NULL;
|
||||||
std::string _refname;
|
std::string _refname = "";
|
||||||
bool _active;
|
bool _active = false;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void cleanup_removed_samples();
|
void cleanup_removed_samples();
|
||||||
void start_playing_sample(SGSoundSample *sample);
|
void start_playing_sample(SGSoundSample *sample);
|
||||||
void check_playing_sample(SGSoundSample *sample);
|
void check_playing_sample(SGSoundSample *sample);
|
||||||
|
|
||||||
bool _changed;
|
bool _changed = false;
|
||||||
bool _pause;
|
bool _pause = false;
|
||||||
float _volume;
|
float _volume = 1.0f;
|
||||||
bool _tied_to_listener;
|
float _pressure = 101.325f;
|
||||||
|
float _humidity = 0.5f;
|
||||||
|
float _degC = 20.0f;
|
||||||
|
|
||||||
SGVec3d _velocity;
|
bool _tied_to_listener = false;
|
||||||
|
|
||||||
|
SGVec3d _velocity = SGVec3d::zeros();
|
||||||
|
SGQuatd _orientation = SGQuatd::zeros();
|
||||||
SGGeod _base_pos;
|
SGGeod _base_pos;
|
||||||
SGQuatd _orientation;
|
|
||||||
|
|
||||||
sample_map _samples;
|
sample_map _samples;
|
||||||
std::vector< SGSharedPtr<SGSoundSample> > _removed_samples;
|
std::vector< SGSharedPtr<SGSoundSample> > _removed_samples;
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
// Modified for the new SoundSystem by Erik Hofman, October 2009
|
// Modified for the new SoundSystem by Erik Hofman, October 2009
|
||||||
//
|
//
|
||||||
// Copyright (C) 2001 Curtis L. Olson - http://www.flightgear.org/~curt
|
// Copyright (C) 2001 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||||
// Copyright (C) 2009 Erik Hofman <erik@ehofman.com>
|
// Copyright (C) 2009-2019 Erik Hofman <erik@ehofman.com>
|
||||||
//
|
//
|
||||||
// This library is free software; you can redistribute it and/or
|
// This library is free software; you can redistribute it and/or
|
||||||
// modify it under the terms of the GNU Library General Public
|
// modify it under the terms of the GNU Library General Public
|
||||||
@ -68,7 +68,7 @@ public:
|
|||||||
* Select a specific sound device.
|
* Select a specific sound device.
|
||||||
* Requires a init/reinit call before sound is actually switched.
|
* Requires a init/reinit call before sound is actually switched.
|
||||||
*/
|
*/
|
||||||
inline void select_device(const char* devname) {_device_name = devname;}
|
inline void select_device(const char* devname) { _device_name = devname; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test is the sound manager is in a working condition.
|
* Test is the sound manager is in a working condition.
|
||||||
|
@ -191,7 +191,7 @@ void SGSoundMgr::init()
|
|||||||
TRY( d->_aax.set(dsp) );
|
TRY( d->_aax.set(dsp) );
|
||||||
|
|
||||||
dsp = aax::dsp(d->_aax, AAX_DISTANCE_FILTER);
|
dsp = aax::dsp(d->_aax, AAX_DISTANCE_FILTER);
|
||||||
TRY( dsp.set(AAX_AL_INVERSE_DISTANCE_CLAMPED) );
|
TRY( dsp.set(AAX_ISO9613_DISTANCE) );
|
||||||
TRY( d->_aax.set(dsp) );
|
TRY( d->_aax.set(dsp) );
|
||||||
|
|
||||||
dsp = aax::dsp(d->_aax, AAX_VELOCITY_EFFECT);
|
dsp = aax::dsp(d->_aax, AAX_VELOCITY_EFFECT);
|
||||||
@ -257,7 +257,7 @@ void SGSoundMgr::stop()
|
|||||||
|
|
||||||
if (is_working()) {
|
if (is_working()) {
|
||||||
_active = false;
|
_active = false;
|
||||||
TRY( d->_aax.set(AAX_STOPPED) );
|
TRY( d->_aax.set(AAX_PROCESSED) );
|
||||||
|
|
||||||
_renderer = "unknown";
|
_renderer = "unknown";
|
||||||
_vendor = "unknown";
|
_vendor = "unknown";
|
||||||
@ -313,12 +313,6 @@ void SGSoundMgr::update( double dt )
|
|||||||
TRY( dsp.set(AAX_GAIN, _volume) );
|
TRY( dsp.set(AAX_GAIN, _volume) );
|
||||||
TRY( d->_aax.set(dsp) );
|
TRY( d->_aax.set(dsp) );
|
||||||
|
|
||||||
#if 0
|
|
||||||
// TODO: altitude dependent
|
|
||||||
dsp = d->_aax.get(AAX_VELOCITY_EFFECT);
|
|
||||||
TRY( dsp.set(AAX_SOUND_VELOCITY, 340.3f) );
|
|
||||||
TRY( d->_aax.set(dsp) );
|
|
||||||
#endif
|
|
||||||
aax::Matrix64 mtx = d->_mtx;
|
aax::Matrix64 mtx = d->_mtx;
|
||||||
mtx.inverse();
|
mtx.inverse();
|
||||||
TRY( d->_aax.sensor_matrix(mtx) );
|
TRY( d->_aax.sensor_matrix(mtx) );
|
||||||
@ -417,11 +411,8 @@ void SGSoundMgr::release_source( unsigned int source )
|
|||||||
if ( source_it != d->_sources.end() )
|
if ( source_it != d->_sources.end() )
|
||||||
{
|
{
|
||||||
aax::Emitter& emitter = source_it->second;
|
aax::Emitter& emitter = source_it->second;
|
||||||
enum aaxState state = emitter.state();
|
|
||||||
if (state != AAX_PROCESSED) {
|
|
||||||
TRY( emitter.set(AAX_PROCESSED) );
|
TRY( emitter.set(AAX_PROCESSED) );
|
||||||
TRY( d->_aax.remove(emitter) );
|
TRY( d->_aax.remove(emitter) );
|
||||||
}
|
|
||||||
TRY( emitter.remove_buffer() );
|
TRY( emitter.remove_buffer() );
|
||||||
d->_sources.erase(source_it);
|
d->_sources.erase(source_it);
|
||||||
}
|
}
|
||||||
@ -568,7 +559,7 @@ void SGSoundMgr::sample_play( SGSoundSample *sample )
|
|||||||
|
|
||||||
aax::dsp dsp = emitter.get(AAX_DISTANCE_FILTER);
|
aax::dsp dsp = emitter.get(AAX_DISTANCE_FILTER);
|
||||||
TRY( dsp.set(AAX_ROLLOFF_FACTOR, 0.3f) );
|
TRY( dsp.set(AAX_ROLLOFF_FACTOR, 0.3f) );
|
||||||
TRY( dsp.set(AAX_AL_INVERSE_DISTANCE_CLAMPED) );
|
TRY( dsp.set(AAX_ISO9613_DISTANCE) );
|
||||||
TRY( emitter.set(dsp) );
|
TRY( emitter.set(dsp) );
|
||||||
|
|
||||||
TRY( emitter.set(AAX_LOOPING, sample->is_looping()) );
|
TRY( emitter.set(AAX_LOOPING, sample->is_looping()) );
|
||||||
@ -627,7 +618,7 @@ bool SGSoundMgr::is_sample_stopped(SGSoundSample *sample)
|
|||||||
assert(sample->is_valid_source());
|
assert(sample->is_valid_source());
|
||||||
aax::Emitter& emitter = d->get_emitter(sample->get_source());
|
aax::Emitter& emitter = d->get_emitter(sample->get_source());
|
||||||
int result = emitter.state();
|
int result = emitter.state();
|
||||||
return (result == AAX_STOPPED);
|
return (result == AAX_PROCESSED);
|
||||||
#else
|
#else
|
||||||
return true;
|
return true;
|
||||||
#endif
|
#endif
|
||||||
@ -657,15 +648,18 @@ void SGSoundMgr::update_sample_config( SGSoundSample *sample, SGVec3d& position,
|
|||||||
TRY( emitter.set(dsp) );
|
TRY( emitter.set(dsp) );
|
||||||
|
|
||||||
if ( sample->has_static_data_changed() ) {
|
if ( sample->has_static_data_changed() ) {
|
||||||
dsp = emitter.get(AAX_ANGULAR_FILTER);
|
dsp = emitter.get(AAX_DIRECTIONAL_FILTER);
|
||||||
TRY( dsp.set(AAX_INNER_ANGLE, sample->get_innerangle()) );
|
TRY( dsp.set(AAX_INNER_ANGLE, sample->get_innerangle(), AAX_DEGREES) );
|
||||||
TRY( dsp.set(AAX_OUTER_ANGLE, sample->get_outerangle()) );
|
TRY( dsp.set(AAX_OUTER_ANGLE, sample->get_outerangle(), AAX_DEGREES) );
|
||||||
TRY( dsp.set(AAX_OUTER_GAIN, sample->get_outergain()) );
|
TRY( dsp.set(AAX_OUTER_GAIN, sample->get_outergain()) );
|
||||||
TRY( emitter.set(dsp) );
|
TRY( emitter.set(dsp) );
|
||||||
|
|
||||||
dsp = emitter.get(AAX_DISTANCE_FILTER);
|
dsp = emitter.get(AAX_DISTANCE_FILTER);
|
||||||
TRY( dsp.set(AAX_REF_DISTANCE, sample->get_reference_dist()) );
|
TRY( dsp.set(AAX_REF_DISTANCE, sample->get_reference_dist()) );
|
||||||
TRY( dsp.set(AAX_MAX_DISTANCE, sample->get_max_dist()) );
|
TRY( dsp.set(AAX_MAX_DISTANCE, sample->get_max_dist()) );
|
||||||
|
TRY( dsp.set(AAX_RELATIVE_HUMIDITY, sample->get_humidity()) );
|
||||||
|
TRY( dsp.set(AAX_TEMPERATURE, sample->get_temperature(),
|
||||||
|
AAX_DEGREES_CELSIUS) );
|
||||||
TRY( emitter.set(dsp) );
|
TRY( emitter.set(dsp) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -693,6 +687,7 @@ vector<std::string> SGSoundMgr::get_available_devices()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
testForError("get_available_devices");
|
||||||
#endif
|
#endif
|
||||||
return devices;
|
return devices;
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,9 @@ int main( int argc, char *argv[] ) {
|
|||||||
smgr->set_volume(0.9);
|
smgr->set_volume(0.9);
|
||||||
smgr->activate();
|
smgr->activate();
|
||||||
|
|
||||||
|
// prevent NaNs
|
||||||
|
smgr->set_position( SGVec3d(0, 0, 0), SGGeod::fromDegFt(0, 0, 0) );
|
||||||
|
|
||||||
SGPath srcDir(SRC_DIR);
|
SGPath srcDir(SRC_DIR);
|
||||||
|
|
||||||
SGSoundSample *sample1 = new SGSoundSample("jet.wav", srcDir);
|
SGSoundSample *sample1 = new SGSoundSample("jet.wav", srcDir);
|
||||||
|
@ -30,6 +30,9 @@ int main( int argc, char *argv[] ) {
|
|||||||
|
|
||||||
SGPath srcDir(SRC_DIR);
|
SGPath srcDir(SRC_DIR);
|
||||||
|
|
||||||
|
// prevent NaNs
|
||||||
|
smgr->set_position( SGVec3d(0, 0, 0), SGGeod::fromDegFt(0, 0, 0) );
|
||||||
|
|
||||||
printf("default position and orientation\n");
|
printf("default position and orientation\n");
|
||||||
SGSoundSample *sample1 = new SGSoundSample("jet.wav", srcDir);
|
SGSoundSample *sample1 = new SGSoundSample("jet.wav", srcDir);
|
||||||
sample1->set_volume(1.0);
|
sample1->set_volume(1.0);
|
||||||
|
@ -95,9 +95,11 @@ SGReadValueFromString(const char* str, bool& value)
|
|||||||
SG_LOG(SG_IO, SG_ALERT, "Cannot read string content.");
|
SG_LOG(SG_IO, SG_ALERT, "Cannot read string content.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::stringstream s;
|
std::stringstream s;
|
||||||
s.str(std::string(str));
|
s.str(std::string(str));
|
||||||
s >> value;
|
s >> value;
|
||||||
|
|
||||||
if (!s.fail())
|
if (!s.fail())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
@ -109,10 +111,12 @@ SGReadValueFromString(const char* str, bool& value)
|
|||||||
value = true;
|
value = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stdstr == "false" || stdstr == "False" || stdstr == "FALSE") {
|
if (stdstr == "false" || stdstr == "False" || stdstr == "FALSE") {
|
||||||
value = true;
|
value = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user