Merge branch 'next' of https://git.code.sf.net/p/flightgear/simgear into next
This commit is contained in:
commit
e222c0888c
1
.gitignore
vendored
1
.gitignore
vendored
@ -15,3 +15,4 @@ install_manifest.txt
|
||||
build*
|
||||
Build
|
||||
CMakeLists.txt.user
|
||||
3rdparty/expat_2.2.6/
|
||||
|
2
3rdparty/expat/CMakeLists.txt
vendored
2
3rdparty/expat/CMakeLists.txt
vendored
@ -1,7 +1,7 @@
|
||||
|
||||
configure_file (
|
||||
"${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
|
||||
|
2
3rdparty/expat/xmlparse.c
vendored
2
3rdparty/expat/xmlparse.c
vendored
@ -18,6 +18,8 @@
|
||||
#include "amigaconfig.h"
|
||||
#elif defined(__WATCOMC__)
|
||||
#include "watcomconfig.h"
|
||||
#elif defined(HAVE_SIMGEAR_EXPAT_CONFIG_H)
|
||||
#include "simgear_expat_config.h"
|
||||
#elif defined(HAVE_EXPAT_CONFIG_H)
|
||||
#include "expat_config.h"
|
||||
#endif /* ndef COMPILED_FROM_DSP */
|
||||
|
2
3rdparty/expat/xmlrole.c
vendored
2
3rdparty/expat/xmlrole.c
vendored
@ -12,6 +12,8 @@
|
||||
#include "amigaconfig.h"
|
||||
#elif defined(__WATCOMC__)
|
||||
#include "watcomconfig.h"
|
||||
#elif defined(HAVE_SIMGEAR_EXPAT_CONFIG_H)
|
||||
#include "simgear_expat_config.h"
|
||||
#else
|
||||
#ifdef HAVE_EXPAT_CONFIG_H
|
||||
#include "expat_config.h"
|
||||
|
2
3rdparty/expat/xmltok.c
vendored
2
3rdparty/expat/xmltok.c
vendored
@ -12,6 +12,8 @@
|
||||
#include "amigaconfig.h"
|
||||
#elif defined(__WATCOMC__)
|
||||
#include "watcomconfig.h"
|
||||
#elif defined(HAVE_SIMGEAR_EXPAT_CONFIG_H)
|
||||
#include "simgear_expat_config.h"
|
||||
#else
|
||||
#ifdef HAVE_EXPAT_CONFIG_H
|
||||
#include "expat_config.h"
|
||||
|
@ -10,6 +10,9 @@ if(COMMAND cmake_policy)
|
||||
if(POLICY CMP0067)
|
||||
cmake_policy(SET CMP0067 NEW)
|
||||
endif()
|
||||
if(POLICY CMP0093)
|
||||
cmake_policy(SET CMP0093 NEW)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
@ -173,15 +176,17 @@ if (MSVC AND MSVC_3RDPARTY_ROOT)
|
||||
|
||||
set( OSG_MSVC "msvc" )
|
||||
if (${MSVC_VERSION_MAJOR} EQUAL "19")
|
||||
if (${MSVC_VERSION_MINOR} EQUAL "00")
|
||||
set( OSG_MSVC ${OSG_MSVC}140 )
|
||||
else ()
|
||||
if (${MSVC_VERSION_MINOR} GREATER_EQUAL "20")
|
||||
set( OSG_MSVC ${OSG_MSVC}142 )
|
||||
elseif (${MSVC_VERSION_MINOR} GREATER_EQUAL "10")
|
||||
set( OSG_MSVC ${OSG_MSVC}141 )
|
||||
else ()
|
||||
set( OSG_MSVC ${OSG_MSVC}140 )
|
||||
endif ()
|
||||
elseif (${MSVC_VERSION_MAJOR} EQUAL "18")
|
||||
set( OSG_MSVC ${OSG_MSVC}120 )
|
||||
else ()
|
||||
message(FATAL_ERROR "Visual Studio 2013/15/17 is required")
|
||||
message(FATAL_ERROR "Visual Studio 2013 or higher is required")
|
||||
endif ()
|
||||
|
||||
if (CMAKE_CL_64)
|
||||
@ -271,9 +276,6 @@ if (SYSTEM_EXPAT)
|
||||
|
||||
else()
|
||||
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
|
||||
${PROJECT_SOURCE_DIR}/3rdparty/expat
|
||||
${PROJECT_BINARY_DIR}/3rdparty/expat)
|
||||
@ -521,6 +523,10 @@ include(CheckCXXFeatures)
|
||||
# ahead of system-installed libs
|
||||
include_directories(BEFORE ${PROJECT_BINARY_DIR}/simgear)
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD")
|
||||
include_directories("/usr/X11R6/include")
|
||||
endif()
|
||||
|
||||
add_definitions(-DHAVE_CONFIG_H)
|
||||
|
||||
# configure a header file to pass some of the CMake settings
|
||||
|
@ -46,11 +46,17 @@ set(BOOST_TEST_TARGET_PREFIX "test")
|
||||
if(NOT Boost_FOUND)
|
||||
find_package(Boost 1.34.0 QUIET)
|
||||
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
|
||||
"NOTE: boost::test-based targets and tests cannot "
|
||||
"be added: boost >= 1.34.0 required but not found. "
|
||||
"(found: '${Boost_VERSION}'; want >=103400) ")
|
||||
"(found: '${Boost_VERSION_MACRO}'; want >=103400) ")
|
||||
if(ENABLE_TESTS)
|
||||
message(FATAL_ERROR
|
||||
${_shared_msg}
|
||||
@ -66,7 +72,7 @@ endif()
|
||||
include(GetForceIncludeDefinitions)
|
||||
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(_boostConfig "BoostTestTargetsIncluded.h")
|
||||
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!")
|
||||
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})
|
||||
|
||||
@ -221,7 +227,7 @@ function(add_boost_test _name)
|
||||
set(_test_command ${_target_name})
|
||||
endif()
|
||||
|
||||
if(TESTS AND ( "${Boost_VERSION}" VERSION_GREATER "103799" ))
|
||||
if(TESTS AND ( "${Boost_VERSION_MACRO}" VERSION_GREATER "103799" ))
|
||||
foreach(_test ${TESTS})
|
||||
add_test(
|
||||
${_name}-${_test}
|
||||
|
@ -115,11 +115,22 @@ target_include_directories(SimGearCore BEFORE PUBLIC
|
||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>
|
||||
$<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
|
||||
${Boost_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIR})
|
||||
target_include_directories(SimGearCore PRIVATE
|
||||
${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
|
||||
EXPORT SimGearTargets
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
@ -144,9 +155,10 @@ if (NOT SIMGEAR_HEADLESS)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# we expose ZLib in some of our headers
|
||||
target_link_libraries(SimGearCore PUBLIC ${ZLIB_LIBRARY})
|
||||
|
||||
target_link_libraries(SimGearCore
|
||||
${ZLIB_LIBRARY}
|
||||
target_link_libraries(SimGearCore PRIVATE
|
||||
${RT_LIBRARY}
|
||||
${DL_LIBRARY}
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
@ -155,29 +167,29 @@ target_link_libraries(SimGearCore
|
||||
${WINSOCK_LIBRARY})
|
||||
|
||||
if(SYSTEM_EXPAT)
|
||||
target_link_libraries(SimGearCore
|
||||
${EXPAT_LIBRARIES})
|
||||
target_link_libraries(SimGearCore PRIVATE ${EXPAT_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(ENABLE_DNS AND SYSTEM_UDNS)
|
||||
target_link_libraries(SimGearCore
|
||||
${UDNS_LIBRARIES})
|
||||
target_link_libraries(SimGearCore PRIVATE ${UDNS_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(NOT SIMGEAR_HEADLESS)
|
||||
target_include_directories(SimGearScene PRIVATE ${PROJECT_SOURCE_DIR}/simgear/canvas/ShivaVG/include)
|
||||
|
||||
target_link_libraries(SimGearScene
|
||||
target_link_libraries(SimGearScene PUBLIC
|
||||
SimGearCore
|
||||
${ZLIB_LIBRARY}
|
||||
${OPENSCENEGRAPH_LIBRARIES}
|
||||
)
|
||||
|
||||
target_link_libraries(SimGearScene PRIVATE
|
||||
${ZLIB_LIBRARY}
|
||||
${OPENAL_LIBRARY}
|
||||
${OPENGL_LIBRARY}
|
||||
${JPEG_LIBRARY})
|
||||
|
||||
if(ENABLE_GDAL)
|
||||
target_link_libraries(SimGearScene
|
||||
${GDAL_LIBRARIES})
|
||||
target_link_libraries(SimGearScene PRIVATE ${GDAL_LIBRARIES})
|
||||
endif()
|
||||
|
||||
# only actually needed by canvas/KeyboardEvent.cxx
|
||||
|
@ -68,8 +68,8 @@ namespace canvas
|
||||
Element* parent = 0 );
|
||||
virtual ~Window();
|
||||
|
||||
virtual void update(double delta_time_sec);
|
||||
virtual void valueChanged(SGPropertyNode* node);
|
||||
void update(double delta_time_sec) override;
|
||||
void valueChanged(SGPropertyNode* node) override;
|
||||
|
||||
const SGVec2<float> getPosition() const;
|
||||
const SGRect<float> getScreenRegion() const;
|
||||
@ -84,8 +84,8 @@ namespace canvas
|
||||
bool isResizable() const;
|
||||
bool isCapturingEvents() const;
|
||||
|
||||
virtual void setVisible(bool visible);
|
||||
virtual bool isVisible() const;
|
||||
void setVisible(bool visible) override;
|
||||
bool isVisible() const override;
|
||||
|
||||
/**
|
||||
* Moves window on top of all other windows with the same z-index.
|
||||
|
@ -31,6 +31,11 @@
|
||||
// FreeBSD
|
||||
#define VG_API_FREEBSD
|
||||
|
||||
#elif defined(__OpenBSD__)
|
||||
|
||||
// FreeBSD
|
||||
#define VG_API_OPENBSD
|
||||
|
||||
#else
|
||||
|
||||
// Unsupported system
|
||||
|
@ -34,7 +34,7 @@
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
|
||||
#if !defined(VG_API_MACOSX) && !defined(__FreeBSD__)
|
||||
#if !defined(VG_API_MACOSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
|
||||
# include <malloc.h>
|
||||
#endif
|
||||
|
||||
@ -161,7 +161,7 @@ SHfloat getMaxFloat();
|
||||
|
||||
/* 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/glx.h>
|
||||
#elif defined(VG_API_MACOSX)
|
||||
|
@ -838,5 +838,98 @@ namespace canvas
|
||||
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 simgear
|
||||
|
@ -101,6 +101,22 @@ namespace canvas
|
||||
*/
|
||||
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:
|
||||
enum ImageAttributes
|
||||
{
|
||||
|
@ -53,18 +53,19 @@ namespace canvas
|
||||
TextLine lineAt(size_t i) const;
|
||||
|
||||
/// Get nearest line to given y-coordinate
|
||||
#if OSG_VERSION_LESS_THAN(3,6,5)
|
||||
TextLine nearestLine(float pos_y) const;
|
||||
|
||||
|
||||
SGVec2i sizeForWidth(int w) const;
|
||||
|
||||
osg::BoundingBox
|
||||
#if OSG_VERSION_LESS_THAN(3,3,2)
|
||||
computeBound()
|
||||
#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
|
||||
const override;
|
||||
|
||||
protected:
|
||||
friend class TextLine;
|
||||
@ -126,7 +127,6 @@ namespace canvas
|
||||
|
||||
_quads = &text->_textureGlyphQuadMap.begin()->second;
|
||||
|
||||
|
||||
#if OSG_VERSION_LESS_THAN(3,5,6)
|
||||
GlyphQuads::LineNumbers const& line_numbers = _quads->_lineNumbers;
|
||||
GlyphQuads::LineNumbers::const_iterator begin_it =
|
||||
@ -140,8 +140,7 @@ namespace canvas
|
||||
_end = std::upper_bound(begin_it, line_numbers.end(), _line)
|
||||
- line_numbers.begin();
|
||||
#else
|
||||
//OSG:TODO: Need 3.5.6 version of this
|
||||
|
||||
// TODO: Need 3.5.6 version of this
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -171,11 +170,17 @@ namespace canvas
|
||||
|
||||
if( empty() )
|
||||
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)
|
||||
GlyphQuads::Coords2 const& coords = _quads->_coords;
|
||||
#elif OSG_VERSION_LESS_THAN(3,5,6)
|
||||
#else
|
||||
GlyphQuads::Coords2 refCoords = _quads->_coords;
|
||||
GlyphQuads::Coords2::element_type &coords = *refCoords.get();
|
||||
#endif
|
||||
|
||||
size_t global_i = _begin + i;
|
||||
|
||||
if (global_i == _begin)
|
||||
@ -198,8 +203,6 @@ namespace canvas
|
||||
// position at center between characters
|
||||
pos.x() = 0.5 * (prev_r + cur_l);
|
||||
}
|
||||
#else
|
||||
//OSG:TODO: need 3.5.7 version of this.
|
||||
#endif
|
||||
|
||||
return pos;
|
||||
@ -208,16 +211,21 @@ namespace canvas
|
||||
//----------------------------------------------------------------------------
|
||||
osg::Vec2 TextLine::nearestCursor(float x) const
|
||||
{
|
||||
if( empty() )
|
||||
if (empty())
|
||||
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)
|
||||
GlyphQuads::Coords2 const& coords = _quads->_coords;
|
||||
#elif OSG_VERSION_LESS_THAN(3,5,6)
|
||||
|
||||
#else
|
||||
GlyphQuads::Coords2 refCoords = _quads->_coords;
|
||||
GlyphQuads::Coords2::element_type &coords = *refCoords.get();
|
||||
#endif
|
||||
|
||||
GlyphQuads::Glyphs const& glyphs = _quads->_glyphs;
|
||||
|
||||
float const HIT_FRACTION = 0.6;
|
||||
float const character_width = _text->getCharacterHeight()
|
||||
@ -237,9 +245,6 @@ namespace canvas
|
||||
}
|
||||
|
||||
return cursorPos(i - _begin);
|
||||
#else
|
||||
//OSG:TODO: need 3.5.7 version of this.
|
||||
return cursorPos(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -319,9 +324,16 @@ namespace canvas
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
#if OSG_VERSION_LESS_THAN(3,6,5)
|
||||
TextLine Text::TextOSG::nearestLine(float pos_y) const
|
||||
{
|
||||
osgText::Font const* font = getActiveFont();
|
||||
#else
|
||||
TextLine Text::TextOSG::nearestLine(float pos_y)
|
||||
{
|
||||
auto font = getActiveFont();
|
||||
#endif
|
||||
|
||||
if( !font || lineCount() <= 0 )
|
||||
return TextLine(0, this);
|
||||
|
||||
@ -343,12 +355,21 @@ namespace canvas
|
||||
// simplified version of osgText::Text::computeGlyphRepresentation() to
|
||||
// just calculate the size for a given weight. Glpyh calculations/creating
|
||||
// is not necessary for this...
|
||||
#if OSG_VERSION_LESS_THAN(3,6,5)
|
||||
SGVec2i Text::TextOSG::sizeForWidth(int w) const
|
||||
#else
|
||||
SGVec2i Text::TextOSG::sizeForWidth(int w)
|
||||
#endif
|
||||
{
|
||||
if( _text.empty() )
|
||||
return SGVec2i(0, 0);
|
||||
|
||||
#if OSG_VERSION_LESS_THAN(3,6,5)
|
||||
osgText::Font* activefont = const_cast<osgText::Font*>(getActiveFont());
|
||||
#else
|
||||
auto activefont = getActiveFont();
|
||||
#endif
|
||||
|
||||
if( !activefont )
|
||||
return SGVec2i(-1, -1);
|
||||
|
||||
@ -628,19 +649,16 @@ namespace canvas
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
osg::BoundingBox
|
||||
#if OSG_VERSION_LESS_THAN(3,3,2)
|
||||
Text::TextOSG::computeBound()
|
||||
osg::BoundingBox Text::TextOSG::computeBound() const
|
||||
#else
|
||||
Text::TextOSG::computeBoundingBox()
|
||||
osg::BoundingBox Text::TextOSG::computeBoundingBox() const
|
||||
#endif
|
||||
const
|
||||
{
|
||||
osg::BoundingBox bb =
|
||||
#if OSG_VERSION_LESS_THAN(3,3,2)
|
||||
osgText::Text::computeBound();
|
||||
osg::BoundingBox bb = osgText::Text::computeBound();
|
||||
#else
|
||||
osgText::Text::computeBoundingBox();
|
||||
osg::BoundingBox bb = osgText::Text::computeBoundingBox();
|
||||
#endif
|
||||
|
||||
#if OSG_VERSION_LESS_THAN(3,1,0)
|
||||
@ -727,9 +745,9 @@ namespace canvas
|
||||
}
|
||||
|
||||
#else
|
||||
void Text::TextOSG::computePositionsImplementation()
|
||||
void Text::TextOSG::computePositionsImplementation()
|
||||
{
|
||||
TextBase::computePositionsImplementation();
|
||||
TextBase::computePositionsImplementation();
|
||||
}
|
||||
#endif
|
||||
//----------------------------------------------------------------------------
|
||||
|
@ -194,7 +194,13 @@ public:
|
||||
{
|
||||
if (!shouldLog(c, p)) return;
|
||||
//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());
|
||||
//fprintf(stderr, "%s:%d:%s:%d:%s\n", debugClassToString(c), p,
|
||||
// file, line, aMessage.c_str());
|
||||
@ -407,6 +413,7 @@ public:
|
||||
bool m_stdout_isRedirectedAlready = false;
|
||||
#endif
|
||||
bool m_developerMode = false;
|
||||
bool m_fileLine = false;
|
||||
|
||||
// test suite mode.
|
||||
bool m_testMode = false;
|
||||
@ -438,7 +445,7 @@ public:
|
||||
LogEntry entry(m_entries.pop());
|
||||
// special marker entry detected, terminate the thread since we are
|
||||
// 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;
|
||||
}
|
||||
{
|
||||
@ -535,6 +542,10 @@ public:
|
||||
const char* fileName, int line, const std::string& msg)
|
||||
{
|
||||
p = translatePriority(p);
|
||||
if (!m_fileLine) {
|
||||
/* This prevents output of file:line. */
|
||||
line = -1;
|
||||
}
|
||||
LogEntry entry(c, p, fileName, line, msg);
|
||||
m_entries.push(entry);
|
||||
}
|
||||
@ -581,6 +592,10 @@ void logstream::setDeveloperMode(bool devMode)
|
||||
d->m_developerMode = devMode;
|
||||
}
|
||||
|
||||
void logstream::setFileLine(bool fileLine)
|
||||
{
|
||||
d->m_fileLine = fileLine;
|
||||
}
|
||||
|
||||
void
|
||||
logstream::addCallback(simgear::LogCallback* cb)
|
||||
|
@ -112,6 +112,12 @@ public:
|
||||
*/
|
||||
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
|
||||
*/
|
||||
|
@ -32,20 +32,23 @@ class TestThreadRecipient : public simgear::Emesary::IReceiver
|
||||
public:
|
||||
TestThreadRecipient() : receiveCount(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::atomic<int> receiveCount;
|
||||
|
||||
virtual simgear::Emesary::ReceiptStatus Receive(simgear::Emesary::INotification &n)
|
||||
{
|
||||
if (n.GetType() == (const char*)this)
|
||||
{
|
||||
TestThreadNotification *tn = dynamic_cast<TestThreadNotification *>(&n);
|
||||
// Unused: TestThreadNotification *tn = dynamic_cast<TestThreadNotification *>(&n);
|
||||
receiveCount++;
|
||||
|
||||
TestThreadNotification onwardNotification("AL");
|
||||
simgear::Emesary::GlobalTransmitter::instance()->NotifyAll(onwardNotification);
|
||||
|
||||
return simgear::Emesary::ReceiptStatusOK;
|
||||
}
|
||||
|
||||
return simgear::Emesary::ReceiptStatusOK;
|
||||
}
|
||||
};
|
||||
|
@ -38,9 +38,12 @@
|
||||
# include <simgear_config.h>
|
||||
#endif
|
||||
|
||||
#include <iomanip>
|
||||
#include <string>
|
||||
#include <time.h>
|
||||
#include <cstring>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
|
||||
#include <simgear/debug/logstream.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()
|
||||
{
|
||||
struct tm now;
|
||||
|
@ -235,6 +235,11 @@ public:
|
||||
inline const std::vector<std::string>& getWeather() const { return _weather; }
|
||||
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:
|
||||
std::string _url;
|
||||
int _grpcount;
|
||||
|
@ -431,7 +431,7 @@ void test_ZlibDecompressorIStream_readPutbackEtc()
|
||||
try {
|
||||
// 'Z' is not the last character read from the stream
|
||||
decompressor.putback('Z');
|
||||
} catch (std::ios_base::failure) {
|
||||
} catch (const std::ios_base::failure&) {
|
||||
gotException = true;
|
||||
} catch (const std::exception& e) {
|
||||
// gcc fails to catch std::ios_base::failure due to an inconsistent C++11
|
||||
|
@ -48,6 +48,8 @@ namespace simgear
|
||||
assert(outer);
|
||||
}
|
||||
|
||||
virtual ~ArchiveExtractorPrivate() = default;
|
||||
|
||||
typedef enum {
|
||||
INVALID = 0,
|
||||
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)
|
||||
{
|
||||
|
@ -34,7 +34,7 @@ class ArchiveExtractor
|
||||
{
|
||||
public:
|
||||
ArchiveExtractor(const SGPath& rootPath);
|
||||
~ArchiveExtractor();
|
||||
virtual ~ArchiveExtractor();
|
||||
|
||||
enum DetermineResult
|
||||
{
|
||||
|
@ -193,8 +193,8 @@ min(S s, SGVec2<T> v)
|
||||
template<typename T>
|
||||
inline
|
||||
SGVec2<T>
|
||||
max(const SGVec2<T>& v1, const SGVec2<T>& v2)
|
||||
{ v1 = simd4::max(v1.simd2(), v2.simd2()); return v1; }
|
||||
max(SGVec2<T> v1, const SGVec2<T>& v2)
|
||||
{ v1.simd2() = simd4::max(v1.simd2(), v2.simd2()); return v1; }
|
||||
template<typename S, typename T>
|
||||
inline
|
||||
SGVec2<T>
|
||||
@ -375,6 +375,29 @@ interpolate(T tau, const SGVec2<T>& v1, const SGVec2<T>& v2)
|
||||
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
|
||||
template<typename T>
|
||||
inline
|
||||
|
@ -1043,7 +1043,7 @@ std::string error_string(int errnum)
|
||||
retcode = strerror_s(buf, sizeof(buf), errnum);
|
||||
#elif defined(_GNU_SOURCE)
|
||||
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;
|
||||
// POSIX.1-2001 and POSIX.1-2008
|
||||
retcode = strerror_r(errnum, buf, sizeof(buf));
|
||||
|
@ -235,6 +235,21 @@ void naFreeContext(naContext c)
|
||||
if(c->callChild) naFreeContext(c->callChild);
|
||||
if(c->callParent) c->callParent->callChild = 0;
|
||||
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;
|
||||
globals->freeContexts = c;
|
||||
UNLOCK();
|
||||
|
@ -67,7 +67,6 @@ namespace nasal
|
||||
public:
|
||||
NasalMainLoopRecipient() : receiveCount(0) {
|
||||
simgear::Emesary::GlobalTransmitter::instance()->Register(*this);
|
||||
SG_LOG(SG_NASAL, SG_INFO, "NasalMainLoopRecipient created");
|
||||
}
|
||||
virtual ~NasalMainLoopRecipient() {
|
||||
simgear::Emesary::GlobalTransmitter::instance()->DeRegister(*this);
|
||||
|
@ -207,6 +207,15 @@ namespace nasal
|
||||
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
|
||||
*/
|
||||
|
@ -28,6 +28,7 @@ class SGWeakReferenced;
|
||||
template<class T> class SGSharedPtr;
|
||||
template<class T> class SGWeakPtr;
|
||||
template<class T> class SGVec2;
|
||||
template<class T> class SGVec4;
|
||||
|
||||
namespace boost
|
||||
{
|
||||
@ -47,6 +48,9 @@ namespace osg
|
||||
class Vec2d;
|
||||
class Vec2f;
|
||||
class Vec2s;
|
||||
|
||||
class Vec4f;
|
||||
class Vec4d;
|
||||
}
|
||||
|
||||
// The actual traits...
|
||||
@ -55,6 +59,10 @@ namespace nasal
|
||||
template<class T>
|
||||
struct is_vec2: public std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct is_vec4: public std::false_type {};
|
||||
|
||||
|
||||
#define SG_MAKE_TRAIT(templ,type,attr)\
|
||||
template templ\
|
||||
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::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
|
||||
|
||||
template<class T>
|
||||
|
@ -420,10 +420,14 @@ naRef naParseCode(struct Context* c, naRef srcFile, int firstLine,
|
||||
|
||||
// Catch parser errors here.
|
||||
p.errLine = *errLine = 1;
|
||||
if(setjmp(p.jumpHandle)) {
|
||||
strncpy(c->error, p.err, sizeof(c->error));
|
||||
if (setjmp(p.jumpHandle)) {
|
||||
size_t end_ = sizeof(c->error) - 1;
|
||||
strncpy(c->error, p.err, end_);
|
||||
c->error[end_] = '\0';
|
||||
|
||||
*errLine = p.errLine;
|
||||
naParseDestroy(&p);
|
||||
|
||||
return naNil();
|
||||
}
|
||||
|
||||
|
@ -849,7 +849,7 @@ SGPropertyNode::trace_read () const
|
||||
* Last used attribute
|
||||
* 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.
|
||||
|
@ -821,6 +821,7 @@ public:
|
||||
USERARCHIVE = 64,
|
||||
PRESERVE = 128,
|
||||
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,
|
||||
// also update value of "LAST_USED_ATTRIBUTE".
|
||||
};
|
||||
|
@ -918,8 +918,13 @@ void ShaderProgramBuilder::buildAttribute(Effect* effect, Pass* pass,
|
||||
resolvedKey.attributes = prgKey.attributes;
|
||||
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;
|
||||
if (getPropertyRoot()->getBoolValue("/sim/version/compositor-support", false) &&
|
||||
shaderName.substr(0, shaderName.find("/")) == "Shaders") {
|
||||
shaderName = "Compositor/" + shaderName;
|
||||
}
|
||||
string fileName = SGModelLib::findDataFile(shaderName, options);
|
||||
if (fileName.empty())
|
||||
{
|
||||
@ -1474,7 +1479,8 @@ static SGMutex realizeTechniques_lock;
|
||||
bool Effect::realizeTechniques(const SGReaderWriterOptions* options)
|
||||
{
|
||||
SGGuard<SGMutex> g(realizeTechniques_lock);
|
||||
mergeSchemesFallbacks(this, options);
|
||||
if (getPropertyRoot()->getBoolValue("/sim/version/compositor-support", false))
|
||||
mergeSchemesFallbacks(this, options);
|
||||
|
||||
if (_isRealized)
|
||||
return true;
|
||||
|
@ -130,8 +130,12 @@ Effect* makeEffect(const string& name,
|
||||
string effectFileName;
|
||||
// Use getPropertyRoot() because the SGReaderWriterOptions might not have a
|
||||
// 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 += name;
|
||||
effectFileName += ".eff";
|
||||
string absFileName
|
||||
|
@ -108,7 +108,7 @@ SGMaterial::SGMaterial( const SGReaderWriterOptions* options,
|
||||
}
|
||||
|
||||
SGMaterial::SGMaterial( const osgDB::Options* options,
|
||||
const SGPropertyNode *props,
|
||||
const SGPropertyNode *props,
|
||||
SGPropertyNode *prop_root,
|
||||
AreaList *a,
|
||||
SGSharedPtr<const SGCondition> c)
|
||||
@ -210,7 +210,7 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options,
|
||||
_internal_state st( NULL, tpath.local8BitStr(), true, options );
|
||||
_status.push_back( st );
|
||||
}
|
||||
|
||||
|
||||
std::vector<SGPropertyNode_ptr> masks = props->getChildren("object-mask");
|
||||
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
|
||||
// left rather than top left. Therefore we flip a copy of the image
|
||||
// (otherwise a second reference to the object mask would flip it
|
||||
// back!).
|
||||
SG_LOG(SG_GENERAL, SG_DEBUG, "Flipping object mask" << omname);
|
||||
// back!).
|
||||
SG_LOG(SG_GENERAL, SG_DEBUG, "Flipping object mask" << omname);
|
||||
image = (osg::Image* ) image->clone(osg::CopyOp::SHALLOW_COPY);
|
||||
image->flipVertical();
|
||||
}
|
||||
@ -271,30 +271,30 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options,
|
||||
wrapv = props->getBoolValue("wrapv", true);
|
||||
mipmap = props->getBoolValue("mipmap", true);
|
||||
light_coverage = props->getDoubleValue("light-coverage", 0.0);
|
||||
|
||||
|
||||
// Building properties
|
||||
building_coverage = props->getDoubleValue("building-coverage", 0.0);
|
||||
building_spacing = props->getDoubleValue("building-spacing-m", 5.0);
|
||||
|
||||
|
||||
std::string bt = props->getStringValue( "building-texture",
|
||||
"Textures/buildings.png" );
|
||||
building_texture = SGModelLib::findDataFile(bt, options);
|
||||
|
||||
building_texture = SGModelLib::findDataFile(bt, options);
|
||||
|
||||
if (building_texture.empty()) {
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "Cannot find texture \"" << bt);
|
||||
}
|
||||
|
||||
|
||||
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()) {
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "Cannot find texture \"" << bt);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
building_small_ratio = props->getDoubleValue("building-small-ratio", 0.8);
|
||||
building_medium_ratio = props->getDoubleValue("building-medium-ratio", 0.15);
|
||||
building_large_ratio = props->getDoubleValue("building-large-ratio", 0.05);
|
||||
|
||||
|
||||
building_small_pitch = props->getDoubleValue("building-small-pitch", 0.8);
|
||||
building_medium_pitch = props->getDoubleValue("building-medium-pitch", 0.2);
|
||||
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_large_min_floors = props->getIntValue("building-large-min-floors", 5);
|
||||
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_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_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_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_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_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_max_depth = props->getFloatValue("building-large-max-depth-m", 75.0);
|
||||
|
||||
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_zero_density_slope_angle = cos(props->getFloatValue("object-zero-density-angle-deg", 30.0) * osg::PI/180.0);
|
||||
|
||||
|
||||
// Random vegetation properties
|
||||
wood_coverage = props->getDoubleValue("wood-coverage", 0.0);
|
||||
is_plantation = props->getBoolValue("plantation",false);
|
||||
tree_effect = props->getStringValue("tree-effect", "Effects/tree");
|
||||
tree_height = props->getDoubleValue("tree-height-m", 0.0);
|
||||
tree_width = props->getDoubleValue("tree-width-m", 0.0);
|
||||
@ -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);
|
||||
|
||||
const SGPropertyNode* treeTexNode = props->getChild("tree-texture");
|
||||
|
||||
|
||||
if (treeTexNode) {
|
||||
std::string treeTexPath = props->getStringValue("tree-texture");
|
||||
|
||||
@ -398,7 +412,7 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options,
|
||||
if (name)
|
||||
glyphs[name] = new SGMaterialGlyph(glyph_nodes[i]);
|
||||
}
|
||||
|
||||
|
||||
// Read parameters entry, which is passed into the effect
|
||||
if (props->hasChild("parameters")) {
|
||||
parameters = props->getChild("parameters");
|
||||
@ -413,7 +427,7 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options,
|
||||
// Private methods.
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void
|
||||
void
|
||||
SGMaterial::init ()
|
||||
{
|
||||
_status.clear();
|
||||
@ -437,7 +451,7 @@ SGMaterial::init ()
|
||||
}
|
||||
|
||||
Effect* SGMaterial::get_effect(int i)
|
||||
{
|
||||
{
|
||||
if(!_status[i].effect_realized) {
|
||||
if (!_status[i].effect.valid())
|
||||
return 0;
|
||||
@ -454,7 +468,7 @@ Effect* SGMaterial::get_one_effect(int texIndex)
|
||||
SG_LOG( SG_GENERAL, SG_WARN, "No effect available.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int i = texIndex % _status.size();
|
||||
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.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// 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();
|
||||
if (i < _masks.size()) {
|
||||
return _masks[i].get();
|
||||
@ -489,10 +503,10 @@ void SGMaterial::buildEffectProperties(const SGReaderWriterOptions* options)
|
||||
ref_ptr<SGMaterialUserData> user = new SGMaterialUserData(this);
|
||||
SGPropertyNode_ptr propRoot = new SGPropertyNode();
|
||||
makeChild(propRoot, "inherits-from")->setStringValue(effect);
|
||||
|
||||
|
||||
SGPropertyNode* paramProp = makeChild(propRoot, "parameters");
|
||||
copyProperties(parameters, paramProp);
|
||||
|
||||
|
||||
SGPropertyNode* materialProp = makeChild(paramProp, "material");
|
||||
makeChild(materialProp, "ambient")->setValue(SGVec4d(ambient));
|
||||
makeChild(materialProp, "diffuse")->setValue(SGVec4d(diffuse));
|
||||
|
@ -244,6 +244,13 @@ public:
|
||||
*/
|
||||
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.
|
||||
*
|
||||
@ -455,6 +462,9 @@ private:
|
||||
// coverage of woods
|
||||
double wood_coverage;
|
||||
|
||||
// are trees regularly planted?
|
||||
bool is_plantation;
|
||||
|
||||
// Range at which trees become visible
|
||||
double tree_range;
|
||||
|
||||
|
@ -18,6 +18,8 @@
|
||||
#ifndef BVHPageNodeOSG_hxx
|
||||
#define BVHPageNodeOSG_hxx
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "../../bvh/BVHPageNode.hxx"
|
||||
|
||||
#include <osg/ref_ptr>
|
||||
|
@ -14,6 +14,8 @@
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "SGLight.hxx"
|
||||
|
||||
#include <osg/Geode>
|
||||
@ -73,34 +75,41 @@ SGLight::appendLight(const SGPropertyNode *configNode,
|
||||
light->setSpotExponent(configNode->getFloatValue("spot-exponent"));
|
||||
light->setSpotCutoff(configNode->getFloatValue("spot-cutoff"));
|
||||
|
||||
osg::Group *group = 0;
|
||||
if ((p = configNode->getNode("offsets")) == NULL) {
|
||||
group = new osg::Group;
|
||||
} else {
|
||||
// Set up the alignment node ("stolen" from animation.cxx)
|
||||
// XXX Order of rotations is probably not correct.
|
||||
osg::MatrixTransform *align = new osg::MatrixTransform;
|
||||
osg::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::MatrixTransform *align = new osg::MatrixTransform;
|
||||
align->addChild(light);
|
||||
|
||||
osg::Matrix tmat;
|
||||
tmat.makeTranslate(configNode->getFloatValue("offsets/x-m", 0.0),
|
||||
configNode->getFloatValue("offsets/y-m", 0.0),
|
||||
configNode->getFloatValue("offsets/z-m", 0.0));
|
||||
osg::Matrix t;
|
||||
osg::Vec3 pos(configNode->getFloatValue("position/x-m"),
|
||||
configNode->getFloatValue("position/y-m"),
|
||||
configNode->getFloatValue("position/z-m"));
|
||||
t.makeTranslate(pos);
|
||||
|
||||
align->setMatrix(res_matrix * tmat);
|
||||
group = align;
|
||||
osg::Matrix r;
|
||||
if (const SGPropertyNode *dirNode = configNode->getNode("direction")) {
|
||||
if (dirNode->hasValue("pitch-deg")) {
|
||||
r.makeRotate(
|
||||
dirNode->getFloatValue("pitch-deg")*SG_DEGREES_TO_RADIANS,
|
||||
osg::Vec3(0, 1, 0),
|
||||
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;
|
||||
osg::Shape *debug_shape = nullptr;
|
||||
if (light->getType() == SGLight::Type::POINT) {
|
||||
debug_shape = new osg::Sphere(osg::Vec3(0, 0, 0), light->getRange());
|
||||
} else if (light->getType() == SGLight::Type::SPOT) {
|
||||
@ -109,7 +118,10 @@ SGLight::appendLight(const SGPropertyNode *configNode,
|
||||
osg::Vec3(0, 0, -0.75 * light->getRange()),
|
||||
tan(light->getSpotCutoff() * SG_DEGREES_TO_RADIANS) * light->getRange(),
|
||||
light->getRange());
|
||||
} else {
|
||||
throw std::domain_error("Unsupported SGLight::Type");
|
||||
}
|
||||
|
||||
osg::ShapeDrawable *debug_drawable = new osg::ShapeDrawable(debug_shape);
|
||||
debug_drawable->setColor(osg::Vec4(1.0, 0.0, 0.0, 1.0));
|
||||
osg::Geode *debug_geode = new osg::Geode;
|
||||
@ -125,14 +137,14 @@ SGLight::appendLight(const SGPropertyNode *configNode,
|
||||
debug_switch->addChild(debug_geode);
|
||||
simgear::getPropertyRoot()->getNode("/sim/debug/show-light-volumes", true)->
|
||||
addChangeListener(new SGLightDebugListener(debug_switch), true);
|
||||
group->addChild(debug_switch);
|
||||
align->addChild(debug_switch);
|
||||
|
||||
if ((p = configNode->getNode("name")) != NULL)
|
||||
group->setName(p->getStringValue());
|
||||
align->setName(p->getStringValue());
|
||||
else
|
||||
group->setName("light");
|
||||
align->setName("light");
|
||||
|
||||
return group;
|
||||
return align;
|
||||
}
|
||||
|
||||
SGLight::SGLight() :
|
||||
|
@ -615,9 +615,8 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void buttonReleased( int keyModState,
|
||||
const osgGA::GUIEventAdapter&,
|
||||
const Info* )
|
||||
void buttonReleased( int keyModState, const osgGA::GUIEventAdapter&,
|
||||
const Info* ) override
|
||||
{
|
||||
if (!_condition || _condition->test()) {
|
||||
|
||||
@ -641,8 +640,7 @@ public:
|
||||
return _dragDirection;
|
||||
}
|
||||
|
||||
virtual void mouseMoved( const osgGA::GUIEventAdapter& ea,
|
||||
const Info* )
|
||||
void mouseMoved( const osgGA::GUIEventAdapter& ea, const Info* ) override
|
||||
{
|
||||
if (!_condition || _condition->test()) {
|
||||
_mousePos = eventToWindowCoords(ea);
|
||||
@ -675,7 +673,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
virtual void update(double dt, int keyModState)
|
||||
void update(double dt, int keyModState) override
|
||||
{
|
||||
if (_hasDragged) {
|
||||
return;
|
||||
@ -688,8 +686,7 @@ public:
|
||||
} // of repeat iteration
|
||||
}
|
||||
|
||||
virtual bool hover( const osg::Vec2d& windowPos,
|
||||
const Info& )
|
||||
bool hover( const osg::Vec2d& windowPos, const Info& ) override
|
||||
{
|
||||
if (!_condition || _condition->test()) {
|
||||
|
||||
@ -711,7 +708,7 @@ public:
|
||||
_cursorName = aName;
|
||||
}
|
||||
|
||||
virtual std::string getCursor() const
|
||||
std::string getCursor() const override
|
||||
{ return _cursorName; }
|
||||
|
||||
private:
|
||||
|
@ -256,7 +256,7 @@ osg::ref_ptr<EffectGeode> SGNewCloud::genCloud() {
|
||||
geode->addDrawable(sg);
|
||||
geode->setName("3D cloud");
|
||||
geode->setEffect(effect.get());
|
||||
geode->setNodeMask( ~simgear::MODELLIGHT_BIT );
|
||||
geode->setNodeMask( ~(simgear::CASTSHADOW_BIT | simgear::MODELLIGHT_BIT) );
|
||||
|
||||
return geode;
|
||||
}
|
||||
|
@ -95,6 +95,13 @@ static SGBucket bucketIndexFromFileName(const std::string& fileName)
|
||||
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 _Object {
|
||||
SGPath _errorLocation;
|
||||
@ -536,8 +543,26 @@ struct ReaderWriterSTG::_ModelBin {
|
||||
_buildingListList.push_back(buildinglist);
|
||||
//SG_LOG(SG_TERRAIN, SG_ALERT, "Building list: " << buildinglist._filename << " " << buildinglist._material_name << " " << buildinglist._lon << " " << buildinglist._lat);
|
||||
} else {
|
||||
SG_LOG( SG_TERRAIN, SG_ALERT, absoluteFileName
|
||||
<< ": Unknown token '" << token << "'" );
|
||||
// Check registered callback for token. Keep lock until callback completed to make sure it will not be
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
||||
void ReaderWriterSTG::setSTGObjectHandler(const std::string &token, STGObjectCallback callback)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(globalStgObjectCallbackLock);
|
||||
globalStgObjectCallbacks[token] = callback;
|
||||
}
|
||||
|
||||
void ReaderWriterSTG::removeSTGObjectHandler(const std::string &token, STGObjectCallback callback)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(globalStgObjectCallbackLock);
|
||||
globalStgObjectCallbacks.erase(token);
|
||||
}
|
||||
}
|
||||
|
@ -22,8 +22,12 @@
|
||||
#ifndef _READERWRITERSTG_HXX
|
||||
#define _READERWRITERSTG_HXX
|
||||
|
||||
#include <osgDB/ReaderWriter>
|
||||
#include <functional>
|
||||
|
||||
#include <osgDB/ReaderWriter>
|
||||
#include <simgear/math/sg_types.hxx>
|
||||
|
||||
class SGGeod;
|
||||
class SGBucket;
|
||||
|
||||
namespace simgear {
|
||||
@ -38,6 +42,11 @@ public:
|
||||
virtual ReadResult
|
||||
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:
|
||||
struct _ModelBin;
|
||||
};
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -35,6 +35,7 @@
|
||||
#include <osg/ShadeModel>
|
||||
#include <osg/Material>
|
||||
#include <osg/CullFace>
|
||||
#include <osg/VertexAttribDivisor>
|
||||
|
||||
#include <simgear/scene/util/OsgMath.hxx>
|
||||
#include <simgear/scene/material/mat.hxx>
|
||||
@ -47,141 +48,140 @@
|
||||
#include <simgear/scene/util/StateAttributeFactory.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
|
||||
|
||||
// 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;
|
||||
|
||||
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 {
|
||||
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 {
|
||||
SMALL = 0,
|
||||
MEDIUM,
|
||||
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:
|
||||
|
||||
struct Building {
|
||||
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;
|
||||
const SGMaterial *material;
|
||||
|
||||
std::string* material_name;
|
||||
std::string* texture;
|
||||
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
|
||||
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
|
||||
typedef std::vector<BuildingInstance> BuildingInstanceList;
|
||||
BuildingInstanceList smallBuildingLocations;
|
||||
BuildingInstanceList mediumBuildingLocations;
|
||||
BuildingInstanceList largeBuildingLocations;
|
||||
BuildingInstanceList buildingLocations;
|
||||
|
||||
public:
|
||||
|
||||
SGBuildingBin(const SGMaterial *mat, bool useVBOs);
|
||||
SGBuildingBin(const SGPath& absoluteFileName, const SGMaterial *mat, bool useVBOs);
|
||||
|
||||
~SGBuildingBin() {
|
||||
smallBuildings.clear();
|
||||
mediumBuildings.clear();
|
||||
largeBuildings.clear();
|
||||
smallBuildingLocations.clear();
|
||||
mediumBuildingLocations.clear();
|
||||
largeBuildingLocations.clear();
|
||||
}
|
||||
~SGBuildingBin();
|
||||
|
||||
// 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);
|
||||
int getNumBuildings();
|
||||
|
||||
@ -190,123 +190,9 @@ public:
|
||||
std::string* getMaterialName() { return material_name; }
|
||||
|
||||
BuildingType getBuildingType(float roll);
|
||||
|
||||
float getBuildingMaxRadius(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);
|
||||
|
||||
};
|
||||
|
@ -285,100 +285,154 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void addRandomTreePoints(float wood_coverage,
|
||||
osg::Texture2D* object_mask,
|
||||
float vegetation_density,
|
||||
float cos_max_density_angle,
|
||||
float cos_zero_density_angle,
|
||||
bool is_plantation,
|
||||
std::vector<SGVec3f>& points,
|
||||
std::vector<SGVec3f>& normals)
|
||||
{
|
||||
if ( !geometries.empty() ) {
|
||||
const osg::Vec3Array* vertices = dynamic_cast<osg::Vec3Array*>(geometries[0]->getVertexArray());
|
||||
const osg::Vec2Array* texcoords = dynamic_cast<osg::Vec2Array*>(geometries[0]->getTexCoordArray(0));
|
||||
|
||||
|
||||
int numPrimitiveSets = geometries[0]->getNumPrimitiveSets();
|
||||
if ( numPrimitiveSets > 0 ) {
|
||||
const osg::PrimitiveSet* ps = geometries[0]->getPrimitiveSet(0);
|
||||
unsigned int numIndices = ps->getNumIndices();
|
||||
|
||||
|
||||
for ( unsigned int i=2; i<numIndices; i+= 3 ) {
|
||||
SGVec3f v0 = toSG(vertices->operator[](ps->index(i-2)));
|
||||
SGVec3f v1 = toSG(vertices->operator[](ps->index(i-1)));
|
||||
SGVec3f v2 = toSG(vertices->operator[](ps->index(i-0)));
|
||||
|
||||
|
||||
SGVec2f t0 = toSG(texcoords->operator[](ps->index(i-2)));
|
||||
SGVec2f t1 = toSG(texcoords->operator[](ps->index(i-1)));
|
||||
SGVec2f t2 = toSG(texcoords->operator[](ps->index(i-0)));
|
||||
|
||||
|
||||
SGVec3f normal = cross(v1 - v0, v2 - v0);
|
||||
|
||||
|
||||
// Ensure the slope isn't too steep by checking the
|
||||
// cos of the angle between the slope normal and the
|
||||
// vertical (conveniently the z-component of the normalized
|
||||
// normal) and values passed in.
|
||||
float alpha = normalize(normal).z();
|
||||
float slope_density = 1.0;
|
||||
|
||||
|
||||
if (alpha < cos_zero_density_angle)
|
||||
continue; // Too steep for any vegetation
|
||||
|
||||
|
||||
if (alpha < cos_max_density_angle) {
|
||||
slope_density =
|
||||
(alpha - cos_zero_density_angle) / (cos_max_density_angle - cos_zero_density_angle);
|
||||
}
|
||||
|
||||
|
||||
// Compute the area
|
||||
float area = 0.5f*length(normal);
|
||||
if (area <= SGLimitsf::min())
|
||||
continue;
|
||||
|
||||
// Determine the number of trees, taking into account vegetation
|
||||
// density (which is linear) and the slope density factor.
|
||||
// Use a zombie door method to create the proper random chance
|
||||
// of a tree being created for partial values.
|
||||
int woodcount = (int) (vegetation_density * vegetation_density *
|
||||
slope_density *
|
||||
area / wood_coverage + mt_rand(&seed));
|
||||
|
||||
for (int j = 0; j < woodcount; j++) {
|
||||
float a = mt_rand(&seed);
|
||||
float b = mt_rand(&seed);
|
||||
|
||||
if ( a + b > 1.0f ) {
|
||||
a = 1.0f - a;
|
||||
b = 1.0f - b;
|
||||
}
|
||||
|
||||
float c = 1.0f - a - b;
|
||||
|
||||
SGVec3f randomPoint = a*v0 + b*v1 + c*v2;
|
||||
|
||||
if (object_mask != NULL) {
|
||||
SGVec2f texCoord = a*t0 + b*t1 + c*t2;
|
||||
|
||||
// Check this random point against the object mask
|
||||
// green (for trees) channel.
|
||||
osg::Image* img = object_mask->getImage();
|
||||
unsigned int x = (int) (img->s() * texCoord.x()) % img->s();
|
||||
unsigned int y = (int) (img->t() * texCoord.y()) % img->t();
|
||||
|
||||
if (mt_rand(&seed) < img->getColor(x, y).g()) {
|
||||
// The red channel contains the rotation for this object
|
||||
points.push_back(randomPoint);
|
||||
normals.push_back(normalize(normal));
|
||||
|
||||
if (is_plantation) { // regularly-spaced vegetation
|
||||
// separate vegetation in integral 1m units
|
||||
int separation = (int) ceil(sqrt(wood_coverage));
|
||||
float max_x = ceil(max(max(v1.x(),v2.x()),v0.x()));
|
||||
float min_x = floor(min(min(v1.x(),v2.x()),v0.x()));
|
||||
float max_y = ceil(max(max(v1.y(),v2.y()),v0.y()));
|
||||
float min_y = floor(min(min(v1.y(),v2.y()),v0.y()));
|
||||
|
||||
/* equation of the plane ax+by+cz+d=0, need d */
|
||||
|
||||
float d = -1*(normal.x()*v0.x() + normal.y()*v0.y()+normal.z()*v0.z());
|
||||
/* Now loop over a grid, skipping points not in the triangle */
|
||||
int x_steps = (int) (max_x - min_x)/separation;
|
||||
int y_steps = (int) (max_y - min_y)/separation;
|
||||
SGVec2f v02d = SGVec2f(v0.x(),v0.y());
|
||||
SGVec2f v12d = SGVec2f(v1.x(),v1.y());
|
||||
SGVec2f v22d = SGVec2f(v2.x(),v2.y());
|
||||
|
||||
for (int jx = 0; jx < x_steps; jx++) {
|
||||
float ptx = min_x + jx * separation;
|
||||
|
||||
for (int jy = 0; jy < y_steps; jy++) {
|
||||
float pty = min_y + jy * separation;
|
||||
SGVec2f newpt = SGVec2f(ptx,pty);
|
||||
if (!point_in_triangle(newpt,v02d,v12d,v22d))
|
||||
continue;
|
||||
|
||||
// z = (-ax-by-d)/c; c is not zero as
|
||||
// that would be alpha of 1.0
|
||||
|
||||
float ptz = (-normal.x()*ptx - normal.y()*pty-d)/normal.z();
|
||||
SGVec3f randomPoint = SGVec3f(ptx,pty,ptz);
|
||||
|
||||
if (object_mask != NULL) {
|
||||
// Check this point against the object mask
|
||||
// green (for trees) channel.
|
||||
osg::Image* img = object_mask->getImage();
|
||||
unsigned int x = (int) (img->s() * newpt.x()) % img->s();
|
||||
unsigned int y = (int) (img->t() * newpt.y()) % img->t();
|
||||
|
||||
if (mt_rand(&seed) < img->getColor(x, y).g()) {
|
||||
// The red channel contains the rotation for this object
|
||||
points.push_back(randomPoint);
|
||||
normals.push_back(normalize(normal));
|
||||
}
|
||||
} else {
|
||||
points.push_back(randomPoint);
|
||||
normals.push_back(normalize(normal));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
points.push_back(randomPoint);
|
||||
normals.push_back(normalize(normal));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 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
|
||||
// debug : this will save the tile as a shapefile that can be viewed in QGIS.
|
||||
// NOTE: this is really slow....
|
||||
|
@ -338,7 +338,7 @@ osg::Node* SGOceanTile(const SGBucket& b, SGMaterialLib *matlib, int latPoints,
|
||||
transform->setMatrix(osg::Matrix::rotate(toOsg(hlOr))*
|
||||
osg::Matrix::translate(toOsg(cartCenter)));
|
||||
transform->addChild(geode);
|
||||
transform->setNodeMask( ~simgear::MODELLIGHT_BIT );
|
||||
transform->setNodeMask( ~(simgear::CASTSHADOW_BIT | simgear::MODELLIGHT_BIT) );
|
||||
|
||||
return transform;
|
||||
}
|
||||
|
@ -216,6 +216,7 @@ public:
|
||||
float vegetation_density,
|
||||
float cos_max_density_angle,
|
||||
float cos_zero_density_angle,
|
||||
bool is_plantation,
|
||||
std::vector<SGVec3f>& points,
|
||||
std::vector<SGVec3f>& normals)
|
||||
{
|
||||
@ -249,50 +250,104 @@ public:
|
||||
float area = 0.5f*length(normal);
|
||||
if (area <= SGLimitsf::min())
|
||||
continue;
|
||||
|
||||
// Determine the number of trees, taking into account vegetation
|
||||
// density (which is linear) and the slope density factor.
|
||||
// Use a zombie door method to create the proper random chance
|
||||
// of a tree being created for partial values.
|
||||
int woodcount = (int) (vegetation_density * vegetation_density *
|
||||
slope_density *
|
||||
area / wood_coverage + mt_rand(&seed));
|
||||
|
||||
for (int j = 0; j < woodcount; j++) {
|
||||
float a = mt_rand(&seed);
|
||||
float b = mt_rand(&seed);
|
||||
if (is_plantation) { // regularly-spaced vegetation
|
||||
|
||||
if ( a + b > 1.0f ) {
|
||||
a = 1.0f - a;
|
||||
b = 1.0f - b;
|
||||
}
|
||||
int separation = (int) ceil(sqrt(wood_coverage));
|
||||
float max_x = ceil(max(max(v1.x(),v2.x()),v0.x()));
|
||||
float min_x = floor(min(min(v1.x(),v2.x()),v0.x()));
|
||||
float max_y = ceil(max(max(v1.y(),v2.y()),v0.y()));
|
||||
float min_y = floor(min(min(v1.y(),v2.y()),v0.y()));
|
||||
|
||||
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;
|
||||
|
||||
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));
|
||||
float d = -1*(normal.x()*v0.x() + normal.y()*v0.y()+normal.z()*v0.z());
|
||||
|
||||
// Now loop over a grid, skipping points not in the triangle
|
||||
|
||||
int x_steps = (int) (max_x - min_x)/separation;
|
||||
int y_steps = (int) (max_y - min_y)/separation;
|
||||
SGVec2f v02d = SGVec2f(v0.x(),v0.y());
|
||||
SGVec2f v12d = SGVec2f(v1.x(),v1.y());
|
||||
SGVec2f v22d = SGVec2f(v2.x(),v2.y());
|
||||
|
||||
for (int jx = 0; jx < x_steps; jx++) {
|
||||
float ptx = min_x + jx * separation;
|
||||
|
||||
for (int jy = 0; jy < y_steps; jy++) {
|
||||
float pty = min_y + jy * separation;
|
||||
SGVec2f newpt = SGVec2f(ptx,pty);
|
||||
if (!point_in_triangle(newpt,v02d,v12d,v22d))
|
||||
continue;
|
||||
|
||||
// z = (-ax-by-d)/c; c is not zero as
|
||||
// that would be alpha of 1.0
|
||||
|
||||
float ptz = (-normal.x()*ptx - normal.y()*pty-d)/normal.z();
|
||||
SGVec3f randomPoint = SGVec3f(ptx,pty,ptz);
|
||||
|
||||
if (object_mask != NULL) {
|
||||
// Check this point against the object mask
|
||||
// green (for trees) channel.
|
||||
osg::Image* img = object_mask->getImage();
|
||||
unsigned int x = (int) (img->s() * newpt.x()) % img->s();
|
||||
unsigned int y = (int) (img->t() * newpt.y()) % img->t();
|
||||
|
||||
if (mt_rand(&seed) < img->getColor(x, y).g()) {
|
||||
// The red channel contains the rotation for this object
|
||||
points.push_back(randomPoint);
|
||||
normals.push_back(normalize(normal));
|
||||
}
|
||||
} else {
|
||||
points.push_back(randomPoint);
|
||||
normals.push_back(normalize(normal));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
points.push_back(randomPoint);
|
||||
normals.push_back(normalize(normal));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 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,
|
||||
double spacing,
|
||||
osg::Texture2D* object_mask,
|
||||
|
@ -753,6 +753,7 @@ public:
|
||||
vegetation_density,
|
||||
mat->get_cos_tree_max_density_slope_angle(),
|
||||
mat->get_cos_tree_zero_density_slope_angle(),
|
||||
mat->get_is_plantation(),
|
||||
randomPoints,
|
||||
randomPointNormals);
|
||||
|
||||
@ -903,64 +904,48 @@ public:
|
||||
}
|
||||
|
||||
Effect* runwayEffect = 0;
|
||||
if (runwayLights.getNumLights() > 0
|
||||
|| !rabitLights.empty()
|
||||
|| !reilLights.empty()
|
||||
|| !odalLights.empty()
|
||||
|| taxiLights.getNumLights() > 0) {
|
||||
|
||||
if (runwayLights.getNumLights() > 0 || taxiLights.getNumLights() > 0) {
|
||||
runwayEffect = getLightEffect(16, osg::Vec3(1, 0.001, 0.0002), 1, 16, true, _options);
|
||||
}
|
||||
|
||||
|
||||
if (runwayLights.getNumLights() > 0
|
||||
|| !rabitLights.empty()
|
||||
|| !reilLights.empty()
|
||||
|| !odalLights.empty()
|
||||
|| !holdshortLights.empty()
|
||||
|| !guardLights.empty()) {
|
||||
osg::Group* rwyLights = new osg::Group;
|
||||
|
||||
osg::StateSet* ss = lightManager->getRunwayLightStateSet();
|
||||
rwyLights->setStateSet(ss);
|
||||
rwyLights->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);
|
||||
}
|
||||
osg::Group* rwyLightsGroup = new osg::Group;
|
||||
rwyLightsGroup->setStateSet(lightManager->getRunwayLightStateSet());
|
||||
rwyLightsGroup->setNodeMask(RUNWAYLIGHTS_BIT);
|
||||
|
||||
SGDirectionalLightListBin::const_iterator i;
|
||||
for (i = rabitLights.begin();
|
||||
i != rabitLights.end(); ++i) {
|
||||
osg::Node* seqNode = SGLightFactory::getSequenced(*i, _options);
|
||||
rwyLights->addChild( seqNode );
|
||||
|
||||
for (i = rabitLights.begin() ; i != rabitLights.end() ; ++i) {
|
||||
rwyLightsGroup->addChild(SGLightFactory::getSequenced(*i, _options));
|
||||
}
|
||||
for (i = reilLights.begin();
|
||||
i != reilLights.end(); ++i) {
|
||||
osg::Node* seqNode = SGLightFactory::getSequenced(*i, _options);
|
||||
rwyLights->addChild(seqNode);
|
||||
for (i = reilLights.begin() ; i != reilLights.end() ; ++i) {
|
||||
rwyLightsGroup->addChild(SGLightFactory::getReil(*i, _options));
|
||||
}
|
||||
for (i = holdshortLights.begin();
|
||||
i != holdshortLights.end(); ++i) {
|
||||
osg::Node* seqNode = SGLightFactory::getHoldShort(*i, _options);
|
||||
rwyLights->addChild(seqNode);
|
||||
for (i = holdshortLights.begin() ; i != holdshortLights.end() ; ++i) {
|
||||
rwyLightsGroup->addChild(SGLightFactory::getHoldShort(*i, _options));
|
||||
}
|
||||
for (i = guardLights.begin();
|
||||
i != guardLights.end(); ++i) {
|
||||
osg::Node* seqNode = SGLightFactory::getGuard(*i, _options);
|
||||
rwyLights->addChild(seqNode);
|
||||
for (i = guardLights.begin() ; i != guardLights.end() ; ++i) {
|
||||
rwyLightsGroup->addChild(SGLightFactory::getGuard(*i, _options));
|
||||
}
|
||||
SGLightListBin::const_iterator j;
|
||||
for (j = odalLights.begin();
|
||||
j != odalLights.end(); ++j) {
|
||||
osg::Node* seqNode = SGLightFactory::getOdal(*j, _options);
|
||||
rwyLights->addChild(seqNode);
|
||||
for (j = odalLights.begin() ; j != odalLights.end() ; ++j) {
|
||||
rwyLightsGroup->addChild(SGLightFactory::getOdal(*j, _options));
|
||||
}
|
||||
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) {
|
||||
@ -1115,7 +1100,7 @@ public:
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -159,6 +159,6 @@ SGLoadBTG(const std::string& path, const simgear::SGReaderWriterOptions* options
|
||||
transform->addChild(pagedLOD);
|
||||
}
|
||||
|
||||
transform->setNodeMask( ~simgear::MODELLIGHT_BIT );
|
||||
transform->setNodeMask( ~(simgear::CASTSHADOW_BIT | simgear::MODELLIGHT_BIT) );
|
||||
return transform;
|
||||
}
|
||||
|
@ -214,8 +214,7 @@ SGLightFactory::getLights(const SGLightBin& lights, unsigned inc, float alphaOff
|
||||
geometry->setDataVariance(osg::Object::STATIC);
|
||||
geometry->setVertexArray(vertices);
|
||||
geometry->setNormalBinding(osg::Geometry::BIND_OFF);
|
||||
geometry->setColorArray(colors);
|
||||
geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
|
||||
geometry->setColorArray(colors, osg::Array::BIND_PER_VERTEX);
|
||||
|
||||
osg::DrawArrays* drawArrays;
|
||||
drawArrays = new osg::DrawArrays(osg::PrimitiveSet::POINTS,
|
||||
@ -275,7 +274,7 @@ SGLightFactory::getLights(const SGDirectionalLightBin& lights)
|
||||
//stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
|
||||
|
||||
osg::DrawArrays* drawArrays;
|
||||
drawArrays = new osg::DrawArrays(osg::PrimitiveSet::POINTS,
|
||||
drawArrays = new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES,
|
||||
0, vertices->size());
|
||||
geometry->addPrimitiveSet(drawArrays);
|
||||
return geometry;
|
||||
@ -370,18 +369,46 @@ SGLightFactory::getSequenced(const SGDirectionalLightBin& lights, const SGReader
|
||||
|
||||
// generate a repeatable random seed
|
||||
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;
|
||||
sequence->setDefaultTime(flashTime);
|
||||
Effect* effect = getLightEffect(10.0f, osg::Vec3(1.0, 0.0001, 0.00000001),
|
||||
6.0f, 10.0f, true, options);
|
||||
Effect* effect = getLightEffect(24.0f, osg::Vec3(1.0, 0.0001, 0.000001),
|
||||
1.0f, 24.0f, true, options);
|
||||
for (int i = lights.getNumLights() - 1; 0 <= i; --i) {
|
||||
EffectGeode* egeode = new EffectGeode;
|
||||
egeode->setEffect(effect);
|
||||
egeode->addDrawable(getLightDrawable(lights.getLight(i)));
|
||||
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->setDuration(1.0f, -1);
|
||||
sequence->setMode(osg::Sequence::START);
|
||||
@ -397,11 +424,11 @@ SGLightFactory::getOdal(const SGLightBin& lights, const SGReaderWriterOptions* o
|
||||
|
||||
// generate a repeatable random seed
|
||||
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;
|
||||
sequence->setDefaultTime(flashTime);
|
||||
Effect* effect = getLightEffect(10.0f, osg::Vec3(1.0, 0.0001, 0.00000001),
|
||||
6.0, 10.0, false, options);
|
||||
Effect* effect = getLightEffect(20.0f, osg::Vec3(1.0, 0.0001, 0.000001),
|
||||
1.0f, 20.0f, false, options);
|
||||
// centerline lights
|
||||
for (int i = lights.getNumLights() - 1; i >= 2; i--) {
|
||||
EffectGeode* egeode = new EffectGeode;
|
||||
@ -409,18 +436,18 @@ SGLightFactory::getOdal(const SGLightBin& lights, const SGReaderWriterOptions* o
|
||||
egeode->addDrawable(getLightDrawable(lights.getLight(i)));
|
||||
sequence->addChild(egeode, flashTime);
|
||||
}
|
||||
// add extra empty group for a break
|
||||
sequence->addChild(new osg::Group, 4 * flashTime);
|
||||
// runway end lights
|
||||
osg::Group* group = new osg::Group;
|
||||
EffectGeode* egeode = new EffectGeode;
|
||||
egeode->setEffect(effect);
|
||||
for (unsigned i = 0; i < 2; ++i) {
|
||||
EffectGeode* egeode = new EffectGeode;
|
||||
egeode->setEffect(effect);
|
||||
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
|
||||
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->setDuration(1.0f, -1);
|
||||
sequence->setMode(osg::Sequence::START);
|
||||
@ -437,23 +464,22 @@ SGLightFactory::getHoldShort(const SGDirectionalLightBin& lights, const SGReader
|
||||
return 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;
|
||||
|
||||
// start with lights off
|
||||
sequence->addChild(new osg::Group, flashTime);
|
||||
sequence->addChild(new osg::Group, 0.2);
|
||||
// ...and increase the lights in steps
|
||||
for (int i = 2; i < 7; i+=2) {
|
||||
Effect* effect = getLightEffect(i, osg::Vec3(1, 0.001, 0.000002),
|
||||
0.0f, i, true, options);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
Effect* effect = getLightEffect(12.0f + i, osg::Vec3(1, 0.001, 0.0002),
|
||||
1.0f, 12.0f + i, true, options);
|
||||
EffectGeode* egeode = new EffectGeode;
|
||||
egeode->setEffect(effect);
|
||||
for (unsigned int j = 0; j < lights.getNumLights(); ++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->setDuration(1.0f, -1);
|
||||
sequence->setMode(osg::Sequence::START);
|
||||
@ -470,11 +496,11 @@ SGLightFactory::getGuard(const SGDirectionalLightBin& lights, const SGReaderWrit
|
||||
|
||||
// generate a repeatable random seed
|
||||
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;
|
||||
sequence->setDefaultTime(flashTime);
|
||||
Effect* effect = getLightEffect(10.0f, osg::Vec3(1.0, 0.001, 0.000002),
|
||||
0.0f, 8.0f, true, options);
|
||||
Effect* effect = getLightEffect(16.0f, osg::Vec3(1.0, 0.001, 0.0002),
|
||||
1.0f, 16.0f, true, options);
|
||||
for (unsigned int i = 0; i < lights.getNumLights(); ++i) {
|
||||
EffectGeode* egeode = new EffectGeode;
|
||||
egeode->setEffect(effect);
|
||||
|
@ -87,6 +87,9 @@ public:
|
||||
static osg::Node*
|
||||
getSequenced(const SGDirectionalLightBin& lights, const simgear::SGReaderWriterOptions* options);
|
||||
|
||||
static osg::Node*
|
||||
getReil(const SGDirectionalLightBin& lights, const simgear::SGReaderWriterOptions* options);
|
||||
|
||||
static osg::Node*
|
||||
getOdal(const SGLightBin& lights, const simgear::SGReaderWriterOptions* options);
|
||||
|
||||
|
@ -27,6 +27,7 @@
|
||||
|
||||
#include <osg/io_utils>
|
||||
|
||||
#include <simgear/constants.h>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
|
||||
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.
|
||||
// See https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_uniform_buffer_object.txt
|
||||
const int POINTLIGHT_BLOCK_SIZE = 20;
|
||||
const int SPOTLIGHT_BLOCK_SIZE = 8;
|
||||
const int SPOTLIGHT_BLOCK_SIZE = 28;
|
||||
|
||||
ClusteredShading::ClusteredShading(osg::Camera *camera,
|
||||
const SGPropertyNode *config) :
|
||||
@ -180,12 +181,38 @@ ClusteredShading::update(const SGLightList &light_list)
|
||||
PointlightBound point;
|
||||
point.light = light;
|
||||
point.position = osg::Vec4f(0.0f, 0.0f, 0.0f, 1.0f) *
|
||||
osg::computeLocalToWorld(light->getParentalNodePaths()[0]) *
|
||||
_camera->getViewMatrix();
|
||||
// The parenthesis are very important: if the vector is
|
||||
// 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_bounds.push_back(point);
|
||||
} 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 ||
|
||||
@ -265,8 +292,9 @@ ClusteredShading::update(const SGLightList &light_list)
|
||||
_light_grid->dirty();
|
||||
_light_indices->dirty();
|
||||
|
||||
// Upload pointlight data
|
||||
// Upload pointlight and spotlight data
|
||||
writePointlightData();
|
||||
writeSpotlightData();
|
||||
}
|
||||
|
||||
void
|
||||
@ -301,6 +329,7 @@ ClusteredShading::assignLightsToSlice(int slice)
|
||||
GLuint local_point_count = 0;
|
||||
GLuint local_spot_count = 0;
|
||||
|
||||
// Test point lights
|
||||
for (GLushort point_iterator = 0;
|
||||
point_iterator < _point_bounds.size();
|
||||
++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
|
||||
grid[(z_offset + i) * 3 + 0] = start_offset;
|
||||
grid[(z_offset + i) * 3 + 1] = local_point_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
|
||||
@ -385,6 +429,51 @@ ClusteredShading::writePointlightData()
|
||||
_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
|
||||
ClusteredShading::getDepthForSlice(int slice) const
|
||||
{
|
||||
|
@ -42,19 +42,25 @@ protected:
|
||||
};
|
||||
|
||||
struct PointlightBound {
|
||||
SGLight *light;
|
||||
SGLight *light = nullptr;
|
||||
osg::Vec4f position;
|
||||
float range;
|
||||
float range = 0.0f;
|
||||
};
|
||||
struct SpotlightBound {
|
||||
SGLight *light;
|
||||
SGLight *light = nullptr;
|
||||
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 assignLightsToSlice(int slice);
|
||||
void writePointlightData();
|
||||
void writeSpotlightData();
|
||||
float getDepthForSlice(int slice) const;
|
||||
|
||||
osg::observer_ptr<osg::Camera> _camera;
|
||||
|
@ -36,6 +36,25 @@
|
||||
#include "Compositor.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 compositor {
|
||||
|
||||
@ -112,15 +131,15 @@ PassBuilder::build(Compositor *compositor, const SGPropertyNode *root,
|
||||
camera->setClearStencil(root->getIntValue("clear-stencil", 0));
|
||||
|
||||
GLbitfield clear_mask = 0;
|
||||
if (root->getBoolValue("clear-color-bit", true))
|
||||
clear_mask |= GL_COLOR_BUFFER_BIT;
|
||||
if (root->getBoolValue("clear-accum-bit", false))
|
||||
clear_mask |= GL_ACCUM_BUFFER_BIT;
|
||||
if (root->getBoolValue("clear-depth-bit", true))
|
||||
clear_mask |= GL_DEPTH_BUFFER_BIT;
|
||||
if (root->getBoolValue("clear-stencil-bit", false))
|
||||
clear_mask |= GL_STENCIL_BUFFER_BIT;
|
||||
// Default clear mask is GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, as in OSG
|
||||
std::stringstream ss;
|
||||
std::string bit;
|
||||
// Default clear mask as in OSG
|
||||
ss << root->getStringValue("clear-mask", "color depth");
|
||||
while (ss >> bit) {
|
||||
if (bit == "color") clear_mask |= GL_COLOR_BUFFER_BIT;
|
||||
else if (bit == "depth") clear_mask |= GL_DEPTH_BUFFER_BIT;
|
||||
else if (bit == "stencil") clear_mask |= GL_STENCIL_BUFFER_BIT;
|
||||
}
|
||||
camera->setClearMask(clear_mask);
|
||||
|
||||
PropertyList p_bindings = root->getChildren("binding");
|
||||
@ -384,9 +403,12 @@ public:
|
||||
|
||||
virtual void operator()(osg::Node *node, osg::NodeVisitor *nv) {
|
||||
osg::Camera *camera = static_cast<osg::Camera *>(node);
|
||||
EffectCullVisitor *cv = dynamic_cast<EffectCullVisitor *>(nv);
|
||||
|
||||
traverse(node, nv);
|
||||
|
||||
removeTransparentBins(cv);
|
||||
|
||||
// The light matrix uniform is updated after the traverse in case the
|
||||
// OSG near/far plane calculations were enabled
|
||||
osg::Matrixf light_matrix =
|
||||
@ -497,19 +519,18 @@ public:
|
||||
|
||||
osg::Vec4 aim4 = osg::Vec4(bs.center(), 1.0) * view_inverse;
|
||||
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();
|
||||
light_view_matrix.makeLookAt(
|
||||
aim + (light_dir * bs.radius() * 2.0f),
|
||||
aim + light_dir * (bs.radius() + 10.0f),
|
||||
aim,
|
||||
aim);
|
||||
osg::Vec3(0.0f, 0.0f, 1.0f));
|
||||
|
||||
osg::Matrixd &light_proj_matrix = camera->getProjectionMatrix();
|
||||
light_proj_matrix.makeOrtho(
|
||||
-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.
|
||||
// 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());
|
||||
shadow_origin = osg::Vec2d(shadow_origin.x() * _half_sm_size.x(),
|
||||
shadow_origin.y() * _half_sm_size.y());
|
||||
osg::Vec2d rounded_origin(std::round(shadow_origin.x()),
|
||||
std::round(shadow_origin.y()));
|
||||
osg::Vec2d rounded_origin(std::floor(shadow_origin.x()),
|
||||
std::floor(shadow_origin.y()));
|
||||
osg::Vec2d rounding = rounded_origin - shadow_origin;
|
||||
rounding = osg::Vec2d(rounding.x() / _half_sm_size.x(),
|
||||
rounding.y() / _half_sm_size.y());
|
||||
@ -546,8 +567,7 @@ struct ShadowMapPassBuilder : public PassBuilder {
|
||||
|
||||
osg::Camera *camera = pass->camera;
|
||||
camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT);
|
||||
camera->setCullingMode(camera->getCullingMode() &
|
||||
~osg::CullSettings::SMALL_FEATURE_CULLING);
|
||||
camera->setCullingMode(osg::CullSettings::ENABLE_ALL_CULLING);
|
||||
//camera->setComputeNearFarMode(
|
||||
// osg::CullSettings::COMPUTE_NEAR_FAR_USING_BOUNDING_VOLUMES);
|
||||
|
||||
|
@ -38,28 +38,13 @@ if(ENABLE_TESTS AND ENABLE_SOUND)
|
||||
COMPILE_DEFINITIONS "SRC_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}\"" )
|
||||
endfunction()
|
||||
|
||||
set( SOUND_TEST_LIBS
|
||||
${TEST_LIBS}
|
||||
)
|
||||
set( SOUND_TEST_LIBS ${TEST_LIBS} )
|
||||
|
||||
if (USE_AEONWAVE)
|
||||
if (SIMGEAR_SHARED)
|
||||
else()
|
||||
set(SOUND_TEST_LIBS ${SOUND_TEST_LIBS}
|
||||
${AAX_LIBRARY}
|
||||
)
|
||||
endif()
|
||||
|
||||
set(SOUND_TEST_LIBS ${SOUND_TEST_LIBS} ${AAX_LIBRARY})
|
||||
create_test(aeonwave_test1)
|
||||
|
||||
else ()
|
||||
if (SIMGEAR_SHARED)
|
||||
else()
|
||||
set(SOUND_TEST_LIBS ${SOUND_TEST_LIBS}
|
||||
${OPENAL_LIBRARY}
|
||||
)
|
||||
endif()
|
||||
|
||||
set(SOUND_TEST_LIBS ${SOUND_TEST_LIBS} ${OPENAL_LIBRARY})
|
||||
create_test(openal_test1)
|
||||
endif()
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
// Modified to match the new SoundSystem by Erik Hofman, October 2009
|
||||
//
|
||||
// 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
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
@ -49,24 +49,6 @@ using std::string;
|
||||
// empty constructor
|
||||
SGSoundSampleInfo::SGSoundSampleInfo() :
|
||||
_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()),
|
||||
_relative_pos(SGVec3d::zeros()),
|
||||
_direction(SGVec3d::zeros()),
|
||||
@ -97,27 +79,9 @@ std::string SGSoundSampleInfo::random_string()
|
||||
// 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
|
||||
SGSoundSample::SGSoundSample(const char *file, const SGPath& currentDir) :
|
||||
_is_file(true),
|
||||
_changed(true),
|
||||
_valid_source(false),
|
||||
_source(SGSoundMgr::NO_SOURCE),
|
||||
_data(NULL),
|
||||
_valid_buffer(false),
|
||||
_buffer(SGSoundMgr::NO_BUFFER)
|
||||
_is_file(true)
|
||||
{
|
||||
SGPath p = simgear::ResourceManager::instance()->findPath(file, currentDir);
|
||||
_refname = p.utf8Str();
|
||||
@ -125,13 +89,7 @@ SGSoundSample::SGSoundSample(const char *file, const SGPath& currentDir) :
|
||||
|
||||
// constructor
|
||||
SGSoundSample::SGSoundSample( const unsigned char** data,
|
||||
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)
|
||||
int len, int freq, int format )
|
||||
{
|
||||
SG_LOG( SG_SOUND, SG_DEBUG, "In memory sounds sample" );
|
||||
_data = (unsigned char*)*data; *data = NULL;
|
||||
|
@ -1,10 +1,10 @@
|
||||
// sample.hxx -- Audio sample encapsulation class
|
||||
//
|
||||
//
|
||||
// Written by Curtis Olson, started April 2004.
|
||||
// Modified to match the new SoundSystem by Erik Hofman, October 2009
|
||||
//
|
||||
// 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
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
@ -32,7 +32,8 @@
|
||||
|
||||
#include <simgear/math/SGMath.hxx>
|
||||
#include <simgear/props/props.hxx>
|
||||
|
||||
#include "soundmgr.hxx"
|
||||
|
||||
enum {
|
||||
SG_SAMPLE_MONO = 1,
|
||||
SG_SAMPLE_STEREO = 2,
|
||||
@ -76,7 +77,7 @@ public:
|
||||
* Returns the block alignment of this audio sample.
|
||||
* @return block alignment in bytes
|
||||
*/
|
||||
inline unsigned int get_block_align() {
|
||||
inline unsigned int get_block_align() {
|
||||
return _block_align;
|
||||
}
|
||||
|
||||
@ -164,10 +165,28 @@ public:
|
||||
/**
|
||||
* Get maximum distance (in meters) of this sound.
|
||||
* 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; }
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Calling this function will reset the flag so calling it a second
|
||||
@ -181,32 +200,35 @@ public:
|
||||
protected:
|
||||
// static sound emitter info
|
||||
std::string _refname;
|
||||
unsigned int _bits;
|
||||
unsigned int _tracks;
|
||||
unsigned int _samples;
|
||||
unsigned int _frequency;
|
||||
unsigned int _block_align;
|
||||
bool _compressed;
|
||||
bool _loop;
|
||||
unsigned int _bits = 16;
|
||||
unsigned int _tracks = 1;
|
||||
unsigned int _samples = 0;
|
||||
unsigned int _frequency = 22500;
|
||||
unsigned int _block_align = 2;
|
||||
bool _compressed = false;
|
||||
bool _loop = false;
|
||||
|
||||
// dynamic sound emitter info (non 3d)
|
||||
bool _static_changed;
|
||||
bool _playing;
|
||||
bool _static_changed = true;
|
||||
bool _playing = false;
|
||||
|
||||
float _pitch;
|
||||
float _volume;
|
||||
float _master_volume;
|
||||
float _pitch = 1.0f;
|
||||
float _volume = 1.0f;
|
||||
float _master_volume = 1.0f;
|
||||
|
||||
// dynamic sound emitter info (3d)
|
||||
bool _use_pos_props;
|
||||
bool _out_of_range;
|
||||
bool _use_pos_props = false;
|
||||
bool _out_of_range = false;
|
||||
|
||||
float _inner_angle;
|
||||
float _outer_angle;
|
||||
float _outer_gain;
|
||||
float _inner_angle = 360.0f;
|
||||
float _outer_angle = 360.0f;
|
||||
float _outer_gain = 0.0f;
|
||||
|
||||
float _reference_dist;
|
||||
float _max_dist;
|
||||
float _reference_dist = 500.0f;
|
||||
float _max_dist = 3000.0f;
|
||||
float _pressure = 101.325f;
|
||||
float _humidity = 0.5f;
|
||||
float _degC = 20.0f;
|
||||
|
||||
SGPropertyNode_ptr _pos_prop[3];
|
||||
SGVec3d _absolute_pos; // absolute position
|
||||
@ -233,7 +255,7 @@ public:
|
||||
* Empty constructor, can be used to read data to the systems
|
||||
* memory and not to the driver.
|
||||
*/
|
||||
SGSoundSample();
|
||||
SGSoundSample() {};
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -307,7 +329,7 @@ public:
|
||||
*/
|
||||
inline void play_once() { play(false); }
|
||||
|
||||
/**
|
||||
/**
|
||||
* Schedule this audio sample to play looped.
|
||||
* @see #play
|
||||
*/
|
||||
@ -389,7 +411,7 @@ public:
|
||||
*/
|
||||
inline void set_buffer(unsigned int bid) {
|
||||
_buffer = bid; _valid_buffer = true; _changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the buffer-id of this source
|
||||
@ -409,7 +431,7 @@ public:
|
||||
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.
|
||||
* @param p Pitch
|
||||
*/
|
||||
@ -540,6 +562,20 @@ public:
|
||||
_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.
|
||||
* This is the distance where the gain will be half.
|
||||
@ -563,19 +599,19 @@ public:
|
||||
void update_pos_and_orientation();
|
||||
|
||||
protected:
|
||||
bool _is_file;
|
||||
bool _changed;
|
||||
bool _is_file = false;
|
||||
bool _changed = true;
|
||||
|
||||
// Sources are points emitting sound.
|
||||
bool _valid_source;
|
||||
unsigned int _source;
|
||||
bool _valid_source = false;
|
||||
unsigned int _source = SGSoundMgr::NO_SOURCE;
|
||||
|
||||
private:
|
||||
unsigned char* _data;
|
||||
unsigned char* _data = NULL;
|
||||
|
||||
// Buffers hold sound data.
|
||||
bool _valid_buffer;
|
||||
unsigned int _buffer;
|
||||
bool _valid_buffer = false;
|
||||
unsigned int _buffer = SGSoundMgr::NO_BUFFER;
|
||||
};
|
||||
|
||||
#endif // _SG_SAMPLE_HXX
|
||||
|
@ -2,7 +2,7 @@
|
||||
//
|
||||
// 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
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
@ -31,16 +31,7 @@
|
||||
#include "soundmgr.hxx"
|
||||
#include "sample_group.hxx"
|
||||
|
||||
SGSampleGroup::SGSampleGroup () :
|
||||
_smgr(NULL),
|
||||
_refname(""),
|
||||
_active(false),
|
||||
_changed(false),
|
||||
_pause(false),
|
||||
_volume(1.0),
|
||||
_tied_to_listener(false),
|
||||
_velocity(SGVec3d::zeros()),
|
||||
_orientation(SGQuatd::zeros())
|
||||
SGSampleGroup::SGSampleGroup ()
|
||||
{
|
||||
_samples.clear();
|
||||
}
|
||||
@ -48,14 +39,7 @@ SGSampleGroup::SGSampleGroup () :
|
||||
SGSampleGroup::SGSampleGroup ( SGSoundMgr *smgr,
|
||||
const std::string &refname ):
|
||||
_smgr(smgr),
|
||||
_refname(refname),
|
||||
_active(false),
|
||||
_changed(false),
|
||||
_pause(false),
|
||||
_volume(1.0),
|
||||
_tied_to_listener(false),
|
||||
_velocity(SGVec3d::zeros()),
|
||||
_orientation(SGQuatd::zeros())
|
||||
_refname(refname)
|
||||
{
|
||||
_smgr->add(this, refname);
|
||||
_samples.clear();
|
||||
@ -318,6 +302,7 @@ void SGSampleGroup::update_pos_and_orientation() {
|
||||
sample->set_rotation( ec2body );
|
||||
sample->set_position(base_position);
|
||||
sample->set_velocity( velocity );
|
||||
sample->set_atmosphere( _degC, _humidity, _pressure );
|
||||
|
||||
// Test if a sample is farther away than max distance, if so
|
||||
// stop the sound playback and free it's source.
|
||||
|
@ -6,7 +6,7 @@
|
||||
//
|
||||
// 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
|
||||
// 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
|
||||
*/
|
||||
bool play( const std::string& refname, bool looping = false );
|
||||
|
||||
|
||||
/**
|
||||
* Request to start playing the referred audio sample looping.
|
||||
* @param refname Reference name of the audio sample to start playing
|
||||
@ -173,7 +173,7 @@ public:
|
||||
/**
|
||||
* Set the velocity vector of this sample group.
|
||||
* 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 ) {
|
||||
_velocity = vel; _changed = true;
|
||||
@ -196,29 +196,43 @@ public:
|
||||
_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
|
||||
*/
|
||||
void tie_to_listener() { _tied_to_listener = true; }
|
||||
|
||||
protected:
|
||||
SGSoundMgr *_smgr;
|
||||
std::string _refname;
|
||||
bool _active;
|
||||
SGSoundMgr *_smgr = NULL;
|
||||
std::string _refname = "";
|
||||
bool _active = false;
|
||||
|
||||
private:
|
||||
void cleanup_removed_samples();
|
||||
void start_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;
|
||||
SGQuatd _orientation;
|
||||
|
||||
sample_map _samples;
|
||||
std::vector< SGSharedPtr<SGSoundSample> > _removed_samples;
|
||||
|
@ -11,7 +11,7 @@
|
||||
// Modified for the new SoundSystem by Erik Hofman, October 2009
|
||||
//
|
||||
// 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
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
@ -68,7 +68,7 @@ public:
|
||||
* Select a specific sound device.
|
||||
* 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.
|
||||
|
@ -191,7 +191,7 @@ void SGSoundMgr::init()
|
||||
TRY( d->_aax.set(dsp) );
|
||||
|
||||
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) );
|
||||
|
||||
dsp = aax::dsp(d->_aax, AAX_VELOCITY_EFFECT);
|
||||
@ -257,7 +257,7 @@ void SGSoundMgr::stop()
|
||||
|
||||
if (is_working()) {
|
||||
_active = false;
|
||||
TRY( d->_aax.set(AAX_STOPPED) );
|
||||
TRY( d->_aax.set(AAX_PROCESSED) );
|
||||
|
||||
_renderer = "unknown";
|
||||
_vendor = "unknown";
|
||||
@ -313,12 +313,6 @@ void SGSoundMgr::update( double dt )
|
||||
TRY( dsp.set(AAX_GAIN, _volume) );
|
||||
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;
|
||||
mtx.inverse();
|
||||
TRY( d->_aax.sensor_matrix(mtx) );
|
||||
@ -417,11 +411,8 @@ void SGSoundMgr::release_source( unsigned int source )
|
||||
if ( source_it != d->_sources.end() )
|
||||
{
|
||||
aax::Emitter& emitter = source_it->second;
|
||||
enum aaxState state = emitter.state();
|
||||
if (state != AAX_PROCESSED) {
|
||||
TRY( emitter.set(AAX_PROCESSED) );
|
||||
TRY( d->_aax.remove(emitter) );
|
||||
}
|
||||
TRY( emitter.set(AAX_PROCESSED) );
|
||||
TRY( d->_aax.remove(emitter) );
|
||||
TRY( emitter.remove_buffer() );
|
||||
d->_sources.erase(source_it);
|
||||
}
|
||||
@ -568,7 +559,7 @@ void SGSoundMgr::sample_play( SGSoundSample *sample )
|
||||
|
||||
aax::dsp dsp = emitter.get(AAX_DISTANCE_FILTER);
|
||||
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(AAX_LOOPING, sample->is_looping()) );
|
||||
@ -627,7 +618,7 @@ bool SGSoundMgr::is_sample_stopped(SGSoundSample *sample)
|
||||
assert(sample->is_valid_source());
|
||||
aax::Emitter& emitter = d->get_emitter(sample->get_source());
|
||||
int result = emitter.state();
|
||||
return (result == AAX_STOPPED);
|
||||
return (result == AAX_PROCESSED);
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
@ -657,15 +648,18 @@ void SGSoundMgr::update_sample_config( SGSoundSample *sample, SGVec3d& position,
|
||||
TRY( emitter.set(dsp) );
|
||||
|
||||
if ( sample->has_static_data_changed() ) {
|
||||
dsp = emitter.get(AAX_ANGULAR_FILTER);
|
||||
TRY( dsp.set(AAX_INNER_ANGLE, sample->get_innerangle()) );
|
||||
TRY( dsp.set(AAX_OUTER_ANGLE, sample->get_outerangle()) );
|
||||
dsp = emitter.get(AAX_DIRECTIONAL_FILTER);
|
||||
TRY( dsp.set(AAX_INNER_ANGLE, sample->get_innerangle(), AAX_DEGREES) );
|
||||
TRY( dsp.set(AAX_OUTER_ANGLE, sample->get_outerangle(), AAX_DEGREES) );
|
||||
TRY( dsp.set(AAX_OUTER_GAIN, sample->get_outergain()) );
|
||||
TRY( emitter.set(dsp) );
|
||||
|
||||
dsp = emitter.get(AAX_DISTANCE_FILTER);
|
||||
TRY( dsp.set(AAX_REF_DISTANCE, sample->get_reference_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) );
|
||||
}
|
||||
}
|
||||
@ -693,6 +687,7 @@ vector<std::string> SGSoundMgr::get_available_devices()
|
||||
}
|
||||
}
|
||||
}
|
||||
testForError("get_available_devices");
|
||||
#endif
|
||||
return devices;
|
||||
}
|
||||
|
@ -29,6 +29,9 @@ int main( int argc, char *argv[] ) {
|
||||
smgr->set_volume(0.9);
|
||||
smgr->activate();
|
||||
|
||||
// prevent NaNs
|
||||
smgr->set_position( SGVec3d(0, 0, 0), SGGeod::fromDegFt(0, 0, 0) );
|
||||
|
||||
SGPath srcDir(SRC_DIR);
|
||||
|
||||
SGSoundSample *sample1 = new SGSoundSample("jet.wav", srcDir);
|
||||
|
@ -30,6 +30,9 @@ int main( int argc, char *argv[] ) {
|
||||
|
||||
SGPath srcDir(SRC_DIR);
|
||||
|
||||
// prevent NaNs
|
||||
smgr->set_position( SGVec3d(0, 0, 0), SGGeod::fromDegFt(0, 0, 0) );
|
||||
|
||||
printf("default position and orientation\n");
|
||||
SGSoundSample *sample1 = new SGSoundSample("jet.wav", srcDir);
|
||||
sample1->set_volume(1.0);
|
||||
|
@ -95,9 +95,11 @@ SGReadValueFromString(const char* str, bool& value)
|
||||
SG_LOG(SG_IO, SG_ALERT, "Cannot read string content.");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::stringstream s;
|
||||
s.str(std::string(str));
|
||||
s >> value;
|
||||
|
||||
if (!s.fail())
|
||||
return true;
|
||||
|
||||
@ -109,10 +111,12 @@ SGReadValueFromString(const char* str, bool& value)
|
||||
value = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (stdstr == "false" || stdstr == "False" || stdstr == "FALSE") {
|
||||
value = true;
|
||||
value = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user