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

1
.gitignore vendored
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

446
Doxyfile

File diff suppressed because it is too large Load Diff

View File

@ -115,11 +115,22 @@ target_include_directories(SimGearCore BEFORE PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>
$<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

View File

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

View File

@ -31,6 +31,11 @@
// FreeBSD
#define VG_API_FREEBSD
#elif defined(__OpenBSD__)
// FreeBSD
#define VG_API_OPENBSD
#else
// Unsupported system

View File

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

View File

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

View File

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

View File

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

View File

@ -194,7 +194,13 @@ public:
{
if (!shouldLog(c, p)) return;
//fprintf(stderr, "%s\n", aMessage.c_str());
if (file && line != -1) {
fprintf(stderr, "%8.2f %s:%i: [%.8s]:%-10s %s\n", logTimer.elapsedMSec()/1000.0, file, line, debugPriorityToString(p), debugClassToString(c), aMessage.c_str());
}
else {
fprintf(stderr, "%8.2f [%.8s]:%-10s %s\n", logTimer.elapsedMSec()/1000.0, debugPriorityToString(p), debugClassToString(c), aMessage.c_str());
}
// 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)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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,6 +1479,7 @@ static SGMutex realizeTechniques_lock;
bool Effect::realizeTechniques(const SGReaderWriterOptions* options)
{
SGGuard<SGMutex> g(realizeTechniques_lock);
if (getPropertyRoot()->getBoolValue("/sim/version/compositor-support", false))
mergeSchemesFallbacks(this, options);
if (_isRealized)

View File

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

View File

@ -323,11 +323,25 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options,
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);

View File

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

View File

@ -18,6 +18,8 @@
#ifndef BVHPageNodeOSG_hxx
#define BVHPageNodeOSG_hxx
#include <string>
#include "../../bvh/BVHPageNode.hxx"
#include <osg/ref_ptr>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -291,6 +291,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)
{
@ -334,6 +335,58 @@ public:
if (area <= SGLimitsf::min())
continue;
if (is_plantation) { // regularly-spaced vegetation
// separate vegetation in integral 1m units
int separation = (int) ceil(sqrt(wood_coverage));
float max_x = ceil(max(max(v1.x(),v2.x()),v0.x()));
float min_x = floor(min(min(v1.x(),v2.x()),v0.x()));
float max_y = ceil(max(max(v1.y(),v2.y()),v0.y()));
float min_y = floor(min(min(v1.y(),v2.y()),v0.y()));
/* equation of the plane ax+by+cz+d=0, need d */
float d = -1*(normal.x()*v0.x() + normal.y()*v0.y()+normal.z()*v0.z());
/* Now loop over a grid, skipping points not in the triangle */
int x_steps = (int) (max_x - min_x)/separation;
int y_steps = (int) (max_y - min_y)/separation;
SGVec2f v02d = SGVec2f(v0.x(),v0.y());
SGVec2f v12d = SGVec2f(v1.x(),v1.y());
SGVec2f v22d = SGVec2f(v2.x(),v2.y());
for (int jx = 0; jx < x_steps; jx++) {
float ptx = min_x + jx * separation;
for (int jy = 0; jy < y_steps; jy++) {
float pty = min_y + jy * separation;
SGVec2f newpt = SGVec2f(ptx,pty);
if (!point_in_triangle(newpt,v02d,v12d,v22d))
continue;
// z = (-ax-by-d)/c; c is not zero as
// that would be alpha of 1.0
float ptz = (-normal.x()*ptx - normal.y()*pty-d)/normal.z();
SGVec3f randomPoint = SGVec3f(ptx,pty,ptz);
if (object_mask != NULL) {
// Check this point against the object mask
// green (for trees) channel.
osg::Image* img = object_mask->getImage();
unsigned int x = (int) (img->s() * newpt.x()) % img->s();
unsigned int y = (int) (img->t() * newpt.y()) % img->t();
if (mt_rand(&seed) < img->getColor(x, y).g()) {
// The red channel contains the rotation for this object
points.push_back(randomPoint);
normals.push_back(normalize(normal));
}
} else {
points.push_back(randomPoint);
normals.push_back(normalize(normal));
}
}
}
} else {
// Determine the number of trees, taking into account vegetation
// density (which is linear) and the slope density factor.
// Use a zombie door method to create the proper random chance
@ -341,8 +394,8 @@ public:
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);
@ -354,7 +407,6 @@ public:
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;
@ -372,6 +424,8 @@ public:
} else {
points.push_back(randomPoint);
normals.push_back(normalize(normal));
}
}
}
}

View File

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

View File

@ -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,7 +250,60 @@ public:
float area = 0.5f*length(normal);
if (area <= SGLimitsf::min())
continue;
if (is_plantation) { // regularly-spaced vegetation
int separation = (int) ceil(sqrt(wood_coverage));
float max_x = ceil(max(max(v1.x(),v2.x()),v0.x()));
float min_x = floor(min(min(v1.x(),v2.x()),v0.x()));
float max_y = ceil(max(max(v1.y(),v2.y()),v0.y()));
float min_y = floor(min(min(v1.y(),v2.y()),v0.y()));
// equation of the plane ax+by+cz+d=0, need d
float d = -1*(normal.x()*v0.x() + normal.y()*v0.y()+normal.z()*v0.z());
// Now loop over a grid, skipping points not in the triangle
int x_steps = (int) (max_x - min_x)/separation;
int y_steps = (int) (max_y - min_y)/separation;
SGVec2f v02d = SGVec2f(v0.x(),v0.y());
SGVec2f v12d = SGVec2f(v1.x(),v1.y());
SGVec2f v22d = SGVec2f(v2.x(),v2.y());
for (int jx = 0; jx < x_steps; jx++) {
float ptx = min_x + jx * separation;
for (int jy = 0; jy < y_steps; jy++) {
float pty = min_y + jy * separation;
SGVec2f newpt = SGVec2f(ptx,pty);
if (!point_in_triangle(newpt,v02d,v12d,v22d))
continue;
// z = (-ax-by-d)/c; c is not zero as
// that would be alpha of 1.0
float ptz = (-normal.x()*ptx - normal.y()*pty-d)/normal.z();
SGVec3f randomPoint = SGVec3f(ptx,pty,ptz);
if (object_mask != NULL) {
// Check this point against the object mask
// green (for trees) channel.
osg::Image* img = object_mask->getImage();
unsigned int x = (int) (img->s() * newpt.x()) % img->s();
unsigned int y = (int) (img->t() * newpt.y()) % img->t();
if (mt_rand(&seed) < img->getColor(x, y).g()) {
// The red channel contains the rotation for this object
points.push_back(randomPoint);
normals.push_back(normalize(normal));
}
} else {
points.push_back(randomPoint);
normals.push_back(normalize(normal));
}
}
}
} else {
// Determine the number of trees, taking into account vegetation
// density (which is linear) and the slope density factor.
// Use a zombie door method to create the proper random chance
@ -292,6 +346,7 @@ public:
}
}
}
}
void addRandomPoints(double coverage,
double spacing,

View File

@ -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,12 +904,7 @@ 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);
}
@ -918,49 +914,38 @@ public:
|| !odalLights.empty()
|| !holdshortLights.empty()
|| !guardLights.empty()) {
osg::Group* rwyLights = new osg::Group;
osg::StateSet* ss = lightManager->getRunwayLightStateSet();
rwyLights->setStateSet(ss);
rwyLights->setNodeMask(RUNWAYLIGHTS_BIT);
osg::Group* rwyLightsGroup = new osg::Group;
rwyLightsGroup->setStateSet(lightManager->getRunwayLightStateSet());
rwyLightsGroup->setNodeMask(RUNWAYLIGHTS_BIT);
if (runwayLights.getNumLights() != 0) {
EffectGeode* geode = new EffectGeode;
geode->setEffect(runwayEffect);
osg::Drawable* rldraw = SGLightFactory::getLights(runwayLights);
geode->addDrawable( rldraw );
rwyLights->addChild(geode);
}
SGDirectionalLightListBin::const_iterator i;
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);
}

View File

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

View File

@ -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;
for (unsigned i = 0; i < 2; ++i) {
EffectGeode* egeode = new EffectGeode;
egeode->setEffect(effect);
for (unsigned i = 0; i < 2; ++i) {
egeode->addDrawable(getLightDrawable(lights.getLight(i)));
group->addChild(egeode);
}
sequence->addChild(group, flashTime);
sequence->addChild(egeode, flashTime);
// add an extra empty group for a break
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);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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
@ -32,6 +32,7 @@
#include <simgear/math/SGMath.hxx>
#include <simgear/props/props.hxx>
#include "soundmgr.hxx"
enum {
SG_SAMPLE_MONO = 1,
@ -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
@ -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

View File

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

View File

@ -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
@ -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;
bool _changed = false;
bool _pause = false;
float _volume = 1.0f;
float _pressure = 101.325f;
float _humidity = 0.5f;
float _degC = 20.0f;
SGVec3d _velocity;
bool _tied_to_listener = false;
SGVec3d _velocity = SGVec3d::zeros();
SGQuatd _orientation = SGQuatd::zeros();
SGGeod _base_pos;
SGQuatd _orientation;
sample_map _samples;
std::vector< SGSharedPtr<SGSoundSample> > _removed_samples;

View File

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

View File

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

View File

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

View File

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

View File

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