This commit is contained in:
gallaert 2019-12-19 11:48:09 +00:00
commit e222c0888c
64 changed files with 2419 additions and 1566 deletions

1
.gitignore vendored
View File

@ -15,3 +15,4 @@ install_manifest.txt
build* build*
Build Build
CMakeLists.txt.user CMakeLists.txt.user
3rdparty/expat_2.2.6/

View File

@ -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

View File

@ -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 */

View File

@ -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"

View File

@ -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"

View File

@ -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

View File

@ -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}

568
Doxyfile

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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
{ {

View File

@ -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)
@ -727,9 +745,9 @@ namespace canvas
} }
#else #else
void Text::TextOSG::computePositionsImplementation() void Text::TextOSG::computePositionsImplementation()
{ {
TextBase::computePositionsImplementation(); TextBase::computePositionsImplementation();
} }
#endif #endif
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------

View File

@ -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());
fprintf(stderr, "%8.2f [%.8s]:%-10s %s\n", logTimer.elapsedMSec()/1000.0, debugPriorityToString(p), debugClassToString(c), 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());
}
// 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)

View File

@ -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
*/ */

View File

@ -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;
} }
}; };

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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)
{ {

View File

@ -34,7 +34,7 @@ class ArchiveExtractor
{ {
public: public:
ArchiveExtractor(const SGPath& rootPath); ArchiveExtractor(const SGPath& rootPath);
~ArchiveExtractor(); virtual ~ArchiveExtractor();
enum DetermineResult enum DetermineResult
{ {

View File

@ -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

View File

@ -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));

View File

@ -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();

View File

@ -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);

View File

@ -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
*/ */

View File

@ -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>

View File

@ -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();
} }

View File

@ -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.

View File

@ -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".
}; };

View File

@ -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,7 +1479,8 @@ 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);
mergeSchemesFallbacks(this, options); if (getPropertyRoot()->getBoolValue("/sim/version/compositor-support", false))
mergeSchemesFallbacks(this, options);
if (_isRealized) if (_isRealized)
return true; return true;

View File

@ -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

View File

@ -108,7 +108,7 @@ SGMaterial::SGMaterial( const SGReaderWriterOptions* options,
} }
SGMaterial::SGMaterial( const osgDB::Options* options, SGMaterial::SGMaterial( const osgDB::Options* options,
const SGPropertyNode *props, const SGPropertyNode *props,
SGPropertyNode *prop_root, SGPropertyNode *prop_root,
AreaList *a, AreaList *a,
SGSharedPtr<const SGCondition> c) SGSharedPtr<const SGCondition> c)
@ -210,7 +210,7 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options,
_internal_state st( NULL, tpath.local8BitStr(), true, options ); _internal_state st( NULL, tpath.local8BitStr(), true, options );
_status.push_back( st ); _status.push_back( st );
} }
std::vector<SGPropertyNode_ptr> masks = props->getChildren("object-mask"); std::vector<SGPropertyNode_ptr> masks = props->getChildren("object-mask");
for (unsigned int i = 0; i < masks.size(); i++) for (unsigned int i = 0; i < masks.size(); i++)
{ {
@ -243,8 +243,8 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options,
// the object mask, as DDS textures have an origin at the bottom // the object mask, as DDS textures have an origin at the bottom
// left rather than top left. Therefore we flip a copy of the image // left rather than top left. Therefore we flip a copy of the image
// (otherwise a second reference to the object mask would flip it // (otherwise a second reference to the object mask would flip it
// back!). // back!).
SG_LOG(SG_GENERAL, SG_DEBUG, "Flipping object mask" << omname); SG_LOG(SG_GENERAL, SG_DEBUG, "Flipping object mask" << omname);
image = (osg::Image* ) image->clone(osg::CopyOp::SHALLOW_COPY); image = (osg::Image* ) image->clone(osg::CopyOp::SHALLOW_COPY);
image->flipVertical(); image->flipVertical();
} }
@ -271,30 +271,30 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options,
wrapv = props->getBoolValue("wrapv", true); wrapv = props->getBoolValue("wrapv", true);
mipmap = props->getBoolValue("mipmap", true); mipmap = props->getBoolValue("mipmap", true);
light_coverage = props->getDoubleValue("light-coverage", 0.0); light_coverage = props->getDoubleValue("light-coverage", 0.0);
// Building properties // Building properties
building_coverage = props->getDoubleValue("building-coverage", 0.0); building_coverage = props->getDoubleValue("building-coverage", 0.0);
building_spacing = props->getDoubleValue("building-spacing-m", 5.0); building_spacing = props->getDoubleValue("building-spacing-m", 5.0);
std::string bt = props->getStringValue( "building-texture", std::string bt = props->getStringValue( "building-texture",
"Textures/buildings.png" ); "Textures/buildings.png" );
building_texture = SGModelLib::findDataFile(bt, options); building_texture = SGModelLib::findDataFile(bt, options);
if (building_texture.empty()) { if (building_texture.empty()) {
SG_LOG(SG_GENERAL, SG_ALERT, "Cannot find texture \"" << bt); SG_LOG(SG_GENERAL, SG_ALERT, "Cannot find texture \"" << bt);
} }
bt = props->getStringValue("building-lightmap", "Textures/buildings-lightmap.png"); bt = props->getStringValue("building-lightmap", "Textures/buildings-lightmap.png");
building_lightmap = SGModelLib::findDataFile(bt, options); building_lightmap = SGModelLib::findDataFile(bt, options);
if (building_lightmap.empty()) { if (building_lightmap.empty()) {
SG_LOG(SG_GENERAL, SG_ALERT, "Cannot find texture \"" << bt); SG_LOG(SG_GENERAL, SG_ALERT, "Cannot find texture \"" << bt);
} }
building_small_ratio = props->getDoubleValue("building-small-ratio", 0.8); building_small_ratio = props->getDoubleValue("building-small-ratio", 0.8);
building_medium_ratio = props->getDoubleValue("building-medium-ratio", 0.15); building_medium_ratio = props->getDoubleValue("building-medium-ratio", 0.15);
building_large_ratio = props->getDoubleValue("building-large-ratio", 0.05); building_large_ratio = props->getDoubleValue("building-large-ratio", 0.05);
building_small_pitch = props->getDoubleValue("building-small-pitch", 0.8); building_small_pitch = props->getDoubleValue("building-small-pitch", 0.8);
building_medium_pitch = props->getDoubleValue("building-medium-pitch", 0.2); building_medium_pitch = props->getDoubleValue("building-medium-pitch", 0.2);
building_large_pitch = props->getDoubleValue("building-large-pitch", 0.1); building_large_pitch = props->getDoubleValue("building-large-pitch", 0.1);
@ -305,29 +305,43 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options,
building_medium_max_floors = props->getIntValue("building-medium-max-floors", 8); building_medium_max_floors = props->getIntValue("building-medium-max-floors", 8);
building_large_min_floors = props->getIntValue("building-large-min-floors", 5); building_large_min_floors = props->getIntValue("building-large-min-floors", 5);
building_large_max_floors = props->getIntValue("building-large-max-floors", 20); building_large_max_floors = props->getIntValue("building-large-max-floors", 20);
building_small_min_width = props->getFloatValue("building-small-min-width-m", 15.0); building_small_min_width = props->getFloatValue("building-small-min-width-m", 15.0);
building_small_max_width = props->getFloatValue("building-small-max-width-m", 60.0); building_small_max_width = props->getFloatValue("building-small-max-width-m", 60.0);
building_small_min_depth = props->getFloatValue("building-small-min-depth-m", 10.0); building_small_min_depth = props->getFloatValue("building-small-min-depth-m", 10.0);
building_small_max_depth = props->getFloatValue("building-small-max-depth-m", 20.0); building_small_max_depth = props->getFloatValue("building-small-max-depth-m", 20.0);
building_medium_min_width = props->getFloatValue("building-medium-min-width-m", 25.0); building_medium_min_width = props->getFloatValue("building-medium-min-width-m", 25.0);
building_medium_max_width = props->getFloatValue("building-medium-max-width-m", 50.0); building_medium_max_width = props->getFloatValue("building-medium-max-width-m", 50.0);
building_medium_min_depth = props->getFloatValue("building-medium-min-depth-m", 20.0); building_medium_min_depth = props->getFloatValue("building-medium-min-depth-m", 20.0);
building_medium_max_depth = props->getFloatValue("building-medium-max-depth-m", 50.0); building_medium_max_depth = props->getFloatValue("building-medium-max-depth-m", 50.0);
building_large_min_width = props->getFloatValue("building-large-min-width-m", 50.0); building_large_min_width = props->getFloatValue("building-large-min-width-m", 50.0);
building_large_max_width = props->getFloatValue("building-large-max-width-m", 75.0); building_large_max_width = props->getFloatValue("building-large-max-width-m", 75.0);
building_large_min_depth = props->getFloatValue("building-large-min-depth-m", 50.0); building_large_min_depth = props->getFloatValue("building-large-min-depth-m", 50.0);
building_large_max_depth = props->getFloatValue("building-large-max-depth-m", 75.0); building_large_max_depth = props->getFloatValue("building-large-max-depth-m", 75.0);
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);
@ -337,7 +351,7 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options,
cos_tree_zero_density_slope_angle = cos(props->getFloatValue("tree-zero-density-angle-deg", 45.0) * osg::PI/180.0); cos_tree_zero_density_slope_angle = cos(props->getFloatValue("tree-zero-density-angle-deg", 45.0) * osg::PI/180.0);
const SGPropertyNode* treeTexNode = props->getChild("tree-texture"); const SGPropertyNode* treeTexNode = props->getChild("tree-texture");
if (treeTexNode) { if (treeTexNode) {
std::string treeTexPath = props->getStringValue("tree-texture"); std::string treeTexPath = props->getStringValue("tree-texture");
@ -398,7 +412,7 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options,
if (name) if (name)
glyphs[name] = new SGMaterialGlyph(glyph_nodes[i]); glyphs[name] = new SGMaterialGlyph(glyph_nodes[i]);
} }
// Read parameters entry, which is passed into the effect // Read parameters entry, which is passed into the effect
if (props->hasChild("parameters")) { if (props->hasChild("parameters")) {
parameters = props->getChild("parameters"); parameters = props->getChild("parameters");
@ -413,7 +427,7 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options,
// Private methods. // Private methods.
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
void void
SGMaterial::init () SGMaterial::init ()
{ {
_status.clear(); _status.clear();
@ -437,7 +451,7 @@ SGMaterial::init ()
} }
Effect* SGMaterial::get_effect(int i) Effect* SGMaterial::get_effect(int i)
{ {
if(!_status[i].effect_realized) { if(!_status[i].effect_realized) {
if (!_status[i].effect.valid()) if (!_status[i].effect.valid())
return 0; return 0;
@ -454,7 +468,7 @@ Effect* SGMaterial::get_one_effect(int texIndex)
SG_LOG( SG_GENERAL, SG_WARN, "No effect available."); SG_LOG( SG_GENERAL, SG_WARN, "No effect available.");
return 0; return 0;
} }
int i = texIndex % _status.size(); int i = texIndex % _status.size();
return get_effect(i); return get_effect(i);
} }
@ -472,9 +486,9 @@ osg::Texture2D* SGMaterial::get_one_object_mask(int texIndex)
SG_LOG( SG_GENERAL, SG_WARN, "No mask available."); SG_LOG( SG_GENERAL, SG_WARN, "No mask available.");
return 0; return 0;
} }
// Note that the object mask is closely linked to the texture/effect // Note that the object mask is closely linked to the texture/effect
// so we index based on the texture index, // so we index based on the texture index,
unsigned int i = texIndex % _status.size(); unsigned int i = texIndex % _status.size();
if (i < _masks.size()) { if (i < _masks.size()) {
return _masks[i].get(); return _masks[i].get();
@ -489,10 +503,10 @@ void SGMaterial::buildEffectProperties(const SGReaderWriterOptions* options)
ref_ptr<SGMaterialUserData> user = new SGMaterialUserData(this); ref_ptr<SGMaterialUserData> user = new SGMaterialUserData(this);
SGPropertyNode_ptr propRoot = new SGPropertyNode(); SGPropertyNode_ptr propRoot = new SGPropertyNode();
makeChild(propRoot, "inherits-from")->setStringValue(effect); makeChild(propRoot, "inherits-from")->setStringValue(effect);
SGPropertyNode* paramProp = makeChild(propRoot, "parameters"); SGPropertyNode* paramProp = makeChild(propRoot, "parameters");
copyProperties(parameters, paramProp); copyProperties(parameters, paramProp);
SGPropertyNode* materialProp = makeChild(paramProp, "material"); SGPropertyNode* materialProp = makeChild(paramProp, "material");
makeChild(materialProp, "ambient")->setValue(SGVec4d(ambient)); makeChild(materialProp, "ambient")->setValue(SGVec4d(ambient));
makeChild(materialProp, "diffuse")->setValue(SGVec4d(diffuse)); makeChild(materialProp, "diffuse")->setValue(SGVec4d(diffuse));

View File

@ -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;

View File

@ -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>

View File

@ -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; osg::MatrixTransform *align = new osg::MatrixTransform;
if ((p = configNode->getNode("offsets")) == NULL) { align->addChild(light);
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::Matrix res_matrix;
res_matrix.makeRotate(
p->getFloatValue("pitch-deg", 0.0)*SG_DEGREES_TO_RADIANS,
osg::Vec3(0, 1, 0),
p->getFloatValue("roll-deg", 0.0)*SG_DEGREES_TO_RADIANS,
osg::Vec3(1, 0, 0),
p->getFloatValue("heading-deg", 0.0)*SG_DEGREES_TO_RADIANS,
osg::Vec3(0, 0, 1));
osg::Matrix tmat; osg::Matrix t;
tmat.makeTranslate(configNode->getFloatValue("offsets/x-m", 0.0), osg::Vec3 pos(configNode->getFloatValue("position/x-m"),
configNode->getFloatValue("offsets/y-m", 0.0), configNode->getFloatValue("position/y-m"),
configNode->getFloatValue("offsets/z-m", 0.0)); configNode->getFloatValue("position/z-m"));
t.makeTranslate(pos);
align->setMatrix(res_matrix * tmat); osg::Matrix r;
group = align; 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),
dirNode->getFloatValue("roll-deg")*SG_DEGREES_TO_RADIANS,
osg::Vec3(1, 0, 0),
dirNode->getFloatValue("heading-deg")*SG_DEGREES_TO_RADIANS,
osg::Vec3(0, 0, 1));
} else if (dirNode->hasValue("lookat-x-m")) {
osg::Vec3 lookAt(dirNode->getFloatValue("lookat-x-m"),
dirNode->getFloatValue("lookat-y-m"),
dirNode->getFloatValue("lookat-z-m"));
osg::Vec3 dir = lookAt - pos;
r.makeRotate(osg::Vec3(0, 0, -1), dir);
} else {
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() :

View File

@ -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:

View File

@ -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;
} }

View File

@ -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);
}
} }

View File

@ -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

View File

@ -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 };
struct BuildingInstance {
BuildingInstance(Vec3f p, float w, float d, float h, float ph, float r, Vec2f wt0, Vec2f rt0, Vec3f t1, Vec2f rs) :
position(p),
width(w),
depth(d),
height(h),
pitch_height(ph),
rotation(r),
walltex0(wt0),
rooftex0(rt0),
tex1(t1),
rooftop_scale(rs)
{ }
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 depth;
float height;
float pitch_height;
float rotation;
Vec2f walltex0;
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);
}
};
private: private:
struct Building { const SGMaterial *material;
Building(BuildingType t, float w, float d, float h, int f, bool pitch) :
type(t),
width(w),
depth(d),
height(h),
floors(f),
pitched(pitch),
radius(std::max(d, 0.5f*w))
{ }
BuildingType type;
float width;
float depth;
float height;
int floors;
bool pitched;
float radius;
float getFootprint() {
return radius;
}
};
// The set of buildings that are instantiated
typedef std::vector<Building> BuildingList;
BuildingList smallBuildings;
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);
}; };

View File

@ -285,100 +285,154 @@ public:
} }
} }
} }
void addRandomTreePoints(float wood_coverage, void addRandomTreePoints(float wood_coverage,
osg::Texture2D* object_mask, osg::Texture2D* object_mask,
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)
{ {
if ( !geometries.empty() ) { if ( !geometries.empty() ) {
const osg::Vec3Array* vertices = dynamic_cast<osg::Vec3Array*>(geometries[0]->getVertexArray()); const osg::Vec3Array* vertices = dynamic_cast<osg::Vec3Array*>(geometries[0]->getVertexArray());
const osg::Vec2Array* texcoords = dynamic_cast<osg::Vec2Array*>(geometries[0]->getTexCoordArray(0)); const osg::Vec2Array* texcoords = dynamic_cast<osg::Vec2Array*>(geometries[0]->getTexCoordArray(0));
int numPrimitiveSets = geometries[0]->getNumPrimitiveSets(); int numPrimitiveSets = geometries[0]->getNumPrimitiveSets();
if ( numPrimitiveSets > 0 ) { if ( numPrimitiveSets > 0 ) {
const osg::PrimitiveSet* ps = geometries[0]->getPrimitiveSet(0); const osg::PrimitiveSet* ps = geometries[0]->getPrimitiveSet(0);
unsigned int numIndices = ps->getNumIndices(); unsigned int numIndices = ps->getNumIndices();
for ( unsigned int i=2; i<numIndices; i+= 3 ) { for ( unsigned int i=2; i<numIndices; i+= 3 ) {
SGVec3f v0 = toSG(vertices->operator[](ps->index(i-2))); SGVec3f v0 = toSG(vertices->operator[](ps->index(i-2)));
SGVec3f v1 = toSG(vertices->operator[](ps->index(i-1))); SGVec3f v1 = toSG(vertices->operator[](ps->index(i-1)));
SGVec3f v2 = toSG(vertices->operator[](ps->index(i-0))); SGVec3f v2 = toSG(vertices->operator[](ps->index(i-0)));
SGVec2f t0 = toSG(texcoords->operator[](ps->index(i-2))); SGVec2f t0 = toSG(texcoords->operator[](ps->index(i-2)));
SGVec2f t1 = toSG(texcoords->operator[](ps->index(i-1))); SGVec2f t1 = toSG(texcoords->operator[](ps->index(i-1)));
SGVec2f t2 = toSG(texcoords->operator[](ps->index(i-0))); SGVec2f t2 = toSG(texcoords->operator[](ps->index(i-0)));
SGVec3f normal = cross(v1 - v0, v2 - v0); SGVec3f normal = cross(v1 - v0, v2 - v0);
// Ensure the slope isn't too steep by checking the // Ensure the slope isn't too steep by checking the
// cos of the angle between the slope normal and the // cos of the angle between the slope normal and the
// vertical (conveniently the z-component of the normalized // vertical (conveniently the z-component of the normalized
// normal) and values passed in. // normal) and values passed in.
float alpha = normalize(normal).z(); float alpha = normalize(normal).z();
float slope_density = 1.0; float slope_density = 1.0;
if (alpha < cos_zero_density_angle) if (alpha < cos_zero_density_angle)
continue; // Too steep for any vegetation continue; // Too steep for any vegetation
if (alpha < cos_max_density_angle) { if (alpha < cos_max_density_angle) {
slope_density = slope_density =
(alpha - cos_zero_density_angle) / (cos_max_density_angle - cos_zero_density_angle); (alpha - cos_zero_density_angle) / (cos_max_density_angle - cos_zero_density_angle);
} }
// Compute the area // Compute the area
float area = 0.5f*length(normal); float area = 0.5f*length(normal);
if (area <= SGLimitsf::min()) if (area <= SGLimitsf::min())
continue; continue;
// Determine the number of trees, taking into account vegetation if (is_plantation) { // regularly-spaced vegetation
// density (which is linear) and the slope density factor. // separate vegetation in integral 1m units
// Use a zombie door method to create the proper random chance int separation = (int) ceil(sqrt(wood_coverage));
// of a tree being created for partial values. float max_x = ceil(max(max(v1.x(),v2.x()),v0.x()));
int woodcount = (int) (vegetation_density * vegetation_density * float min_x = floor(min(min(v1.x(),v2.x()),v0.x()));
slope_density * float max_y = ceil(max(max(v1.y(),v2.y()),v0.y()));
area / wood_coverage + mt_rand(&seed)); float min_y = floor(min(min(v1.y(),v2.y()),v0.y()));
for (int j = 0; j < woodcount; j++) { /* equation of the plane ax+by+cz+d=0, need d */
float a = mt_rand(&seed);
float b = mt_rand(&seed); 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 */
if ( a + b > 1.0f ) { int x_steps = (int) (max_x - min_x)/separation;
a = 1.0f - a; int y_steps = (int) (max_y - min_y)/separation;
b = 1.0f - b; SGVec2f v02d = SGVec2f(v0.x(),v0.y());
} SGVec2f v12d = SGVec2f(v1.x(),v1.y());
SGVec2f v22d = SGVec2f(v2.x(),v2.y());
float c = 1.0f - a - b;
for (int jx = 0; jx < x_steps; jx++) {
SGVec3f randomPoint = a*v0 + b*v1 + c*v2; float ptx = min_x + jx * separation;
if (object_mask != NULL) { for (int jy = 0; jy < y_steps; jy++) {
SGVec2f texCoord = a*t0 + b*t1 + c*t2; float pty = min_y + jy * separation;
SGVec2f newpt = SGVec2f(ptx,pty);
// Check this random point against the object mask if (!point_in_triangle(newpt,v02d,v12d,v22d))
// green (for trees) channel. continue;
osg::Image* img = object_mask->getImage();
unsigned int x = (int) (img->s() * texCoord.x()) % img->s(); // z = (-ax-by-d)/c; c is not zero as
unsigned int y = (int) (img->t() * texCoord.y()) % img->t(); // that would be alpha of 1.0
if (mt_rand(&seed) < img->getColor(x, y).g()) { float ptz = (-normal.x()*ptx - normal.y()*pty-d)/normal.z();
// The red channel contains the rotation for this object SGVec3f randomPoint = SGVec3f(ptx,pty,ptz);
points.push_back(randomPoint);
normals.push_back(normalize(normal)); 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 { }
points.push_back(randomPoint); } else {
normals.push_back(normalize(normal)); // Determine the number of trees, taking into account vegetation
} // density (which is linear) and the slope density factor.
// Use a zombie door method to create the proper random chance
// of a tree being created for partial values.
int woodcount = (int) (vegetation_density * vegetation_density *
slope_density *
area / wood_coverage + mt_rand(&seed));
for (int j = 0; j < woodcount; j++) {
// Use barycentric coordinates
float a = mt_rand(&seed);
float b = mt_rand(&seed);
if ( a + b > 1.0f ) {
a = 1.0f - a;
b = 1.0f - b;
}
float c = 1.0f - a - b;
SGVec3f randomPoint = a*v0 + b*v1 + c*v2;
if (object_mask != NULL) {
SGVec2f texCoord = a*t0 + b*t1 + c*t2;
// Check this random point against the object mask
// green (for trees) channel.
osg::Image* img = object_mask->getImage();
unsigned int x = (int) (img->s() * texCoord.x()) % img->s();
unsigned int y = (int) (img->t() * texCoord.y()) % img->t();
if (mt_rand(&seed) < img->getColor(x, y).g()) {
// The red channel contains the rotation for this object
points.push_back(randomPoint);
normals.push_back(normalize(normal));
}
} else {
points.push_back(randomPoint);
normals.push_back(normalize(normal));
}
}
} }
} }
} }
} }
} }
#if 0 #if 0
// debug : this will save the tile as a shapefile that can be viewed in QGIS. // debug : this will save the tile as a shapefile that can be viewed in QGIS.
// NOTE: this is really slow.... // NOTE: this is really slow....

View File

@ -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;
} }

View File

@ -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,50 +250,104 @@ 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
// Determine the number of trees, taking into account vegetation
// density (which is linear) and the slope density factor.
// Use a zombie door method to create the proper random chance
// of a tree being created for partial values.
int woodcount = (int) (vegetation_density * vegetation_density *
slope_density *
area / wood_coverage + mt_rand(&seed));
for (int j = 0; j < woodcount; j++) {
float a = mt_rand(&seed);
float b = mt_rand(&seed);
if ( a + b > 1.0f ) { int separation = (int) ceil(sqrt(wood_coverage));
a = 1.0f - a; float max_x = ceil(max(max(v1.x(),v2.x()),v0.x()));
b = 1.0f - b; 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()));
float c = 1.0f - a - b; // equation of the plane ax+by+cz+d=0, need d
SGVec3f randomPoint = a*v0 + b*v1 + c*v2; float d = -1*(normal.x()*v0.x() + normal.y()*v0.y()+normal.z()*v0.z());
if (object_mask != NULL) { // Now loop over a grid, skipping points not in the triangle
SGVec2f texCoord = a*t0 + b*t1 + c*t2;
int x_steps = (int) (max_x - min_x)/separation;
// Check this random point against the object mask int y_steps = (int) (max_y - min_y)/separation;
// green (for trees) channel. SGVec2f v02d = SGVec2f(v0.x(),v0.y());
osg::Image* img = object_mask->getImage(); SGVec2f v12d = SGVec2f(v1.x(),v1.y());
unsigned int x = (int) (img->s() * texCoord.x()) % img->s(); SGVec2f v22d = SGVec2f(v2.x(),v2.y());
unsigned int y = (int) (img->t() * texCoord.y()) % img->t();
for (int jx = 0; jx < x_steps; jx++) {
if (mt_rand(&seed) < img->getColor(x, y).g()) { float ptx = min_x + jx * separation;
// The red channel contains the rotation for this object
points.push_back(randomPoint); for (int jy = 0; jy < y_steps; jy++) {
normals.push_back(normalize(normal)); 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 { }
points.push_back(randomPoint); } else {
normals.push_back(normalize(normal)); // Determine the number of trees, taking into account vegetation
} // density (which is linear) and the slope density factor.
// Use a zombie door method to create the proper random chance
// of a tree being created for partial values.
int woodcount = (int) (vegetation_density * vegetation_density *
slope_density *
area / wood_coverage + mt_rand(&seed));
for (int j = 0; j < woodcount; j++) {
float a = mt_rand(&seed);
float b = mt_rand(&seed);
if ( a + b > 1.0f ) {
a = 1.0f - a;
b = 1.0f - b;
}
float c = 1.0f - a - b;
SGVec3f randomPoint = a*v0 + b*v1 + c*v2;
if (object_mask != NULL) {
SGVec2f texCoord = a*t0 + b*t1 + c*t2;
// Check this random point against the object mask
// green (for trees) channel.
osg::Image* img = object_mask->getImage();
unsigned int x = (int) (img->s() * texCoord.x()) % img->s();
unsigned int y = (int) (img->t() * texCoord.y()) % img->t();
if (mt_rand(&seed) < img->getColor(x, y).g()) {
// The red channel contains the rotation for this object
points.push_back(randomPoint);
normals.push_back(normalize(normal));
}
} else {
points.push_back(randomPoint);
normals.push_back(normalize(normal));
}
}
} }
} }
} }
void addRandomPoints(double coverage, void addRandomPoints(double coverage,
double spacing, double spacing,
osg::Texture2D* object_mask, osg::Texture2D* object_mask,

View File

@ -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,64 +904,48 @@ 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);
} }
if (runwayLights.getNumLights() > 0 if (runwayLights.getNumLights() > 0
|| !rabitLights.empty() || !rabitLights.empty()
|| !reilLights.empty() || !reilLights.empty()
|| !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);
} }

View File

@ -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;
} }

View File

@ -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; EffectGeode* egeode = new EffectGeode;
egeode->setEffect(effect);
for (unsigned i = 0; i < 2; ++i) { for (unsigned i = 0; i < 2; ++i) {
EffectGeode* egeode = new EffectGeode;
egeode->setEffect(effect);
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);

View File

@ -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);

View File

@ -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
{ {

View File

@ -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;

View File

@ -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);

View File

@ -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()

View File

@ -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;

View File

@ -1,10 +1,10 @@
// sample.hxx -- Audio sample encapsulation class // sample.hxx -- Audio sample encapsulation class
// //
// Written by Curtis Olson, started April 2004. // Written by Curtis Olson, started April 2004.
// 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,7 +32,8 @@
#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,
SG_SAMPLE_STEREO = 2, SG_SAMPLE_STEREO = 2,
@ -76,7 +77,7 @@ public:
* Returns the block alignment of this audio sample. * Returns the block alignment of this audio sample.
* @return block alignment in bytes * @return block alignment in bytes
*/ */
inline unsigned int get_block_align() { inline unsigned int get_block_align() {
return _block_align; return _block_align;
} }
@ -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
@ -307,7 +329,7 @@ public:
*/ */
inline void play_once() { play(false); } inline void play_once() { play(false); }
/** /**
* Schedule this audio sample to play looped. * Schedule this audio sample to play looped.
* @see #play * @see #play
*/ */
@ -389,7 +411,7 @@ public:
*/ */
inline void set_buffer(unsigned int bid) { inline void set_buffer(unsigned int bid) {
_buffer = bid; _valid_buffer = true; _changed = true; _buffer = bid; _valid_buffer = true; _changed = true;
} }
/** /**
* Get the buffer-id of this source * Get the buffer-id of this source
@ -409,7 +431,7 @@ public:
inline void no_valid_buffer() { _valid_buffer = false; } inline void no_valid_buffer() { _valid_buffer = false; }
/** /**
* Set the playback pitch of this audio sample. * Set the playback pitch of this audio sample.
* Should be between 0.0 and 2.0 for maximum compatibility. * Should be between 0.0 and 2.0 for maximum compatibility.
* @param p Pitch * @param p Pitch
*/ */
@ -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

View File

@ -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.

View File

@ -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
@ -131,7 +131,7 @@ public:
* @return true if the audio sample exsists and is scheduled for playing * @return true if the audio sample exsists and is scheduled for playing
*/ */
bool play( const std::string& refname, bool looping = false ); bool play( const std::string& refname, bool looping = false );
/** /**
* Request to start playing the referred audio sample looping. * Request to start playing the referred audio sample looping.
* @param refname Reference name of the audio sample to start playing * @param refname Reference name of the audio sample to start playing
@ -173,7 +173,7 @@ public:
/** /**
* Set the velocity vector of this sample group. * Set the velocity vector of this sample group.
* This is in the local frame coordinate system; x=north, y=east, z=down * This is in the local frame coordinate system; x=north, y=east, z=down
* @param vel Velocity vector * @param vel Velocity vector
*/ */
void set_velocity( const SGVec3d& vel ) { void set_velocity( const SGVec3d& vel ) {
_velocity = vel; _changed = true; _velocity = vel; _changed = true;
@ -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 _pause;
float _volume;
bool _tied_to_listener;
SGVec3d _velocity; bool _changed = false;
bool _pause = false;
float _volume = 1.0f;
float _pressure = 101.325f;
float _humidity = 0.5f;
float _degC = 20.0f;
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;

View File

@ -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.

View File

@ -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(); TRY( emitter.set(AAX_PROCESSED) );
if (state != AAX_PROCESSED) { TRY( d->_aax.remove(emitter) );
TRY( emitter.set(AAX_PROCESSED) );
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;
} }

View File

@ -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);

View File

@ -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);

View File

@ -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;
} }