Compare commits

..

20 Commits

Author SHA1 Message Date
James Turner
1b3c048363 New version : 2018.3.4 2019-08-07 09:58:45 +01:00
Dan Wickstrom
01f689a0e4 Aircraft model reinit deletes sound effect samples, but leaves them defined in the sample group, so a reload doesn't re-add them. 2019-07-30 16:36:49 +01:00
Scott Giese
b155f2e40f [soundmgr_openal] Pause/Resume Sound.
The following changes fixes a case for me where I hear the sound change levels up and down for each pause un-pause cycle.
Patch provided by daniel.c.wickstrom@gmail.com.
2019-07-30 16:36:49 +01:00
Tim Moore
10ab8b830a nasal/lib.c: Make copy of va_list for each traversal
It's not portable to traverse a va_list more than once.
2019-07-30 16:36:49 +01:00
James Turner
8a4871db83 Improve HTTP redirect handling, and add test.
Ensure we get the final status code for the request after redirecting.
2019-07-30 16:36:49 +01:00
Torsten Dreyer
9a8c10cb0b New version: 2018.3.3 2019-07-28 22:43:16 +02:00
Erik Hofman
fd32023437 Do not attempt to deregister the same emitter more than once 2019-01-26 10:17:11 +01:00
Erik Hofman
c1ee4a9172 Use AAX_PROCESSED since AAX_STOPPED is only a request to stop but the library decides when it is actually stopped. And AeonWave has become more picky about destroying emitters which aren't completely processed yet since MIDI support was added. 2019-01-26 10:16:38 +01:00
Richard Harrison
a5b32f8eb2 Fix for deleting referenced object from model registry
This should have been in the previous commit - However I managed to mess up the merging of this module due to other changes related to the DDS texture cache.
2019-01-25 21:39:03 +00:00
Richard Harrison
4a86368c8f Fix null ref during load.
This happened a few times
2019-01-25 21:39:03 +00:00
Richard Harrison
3730cc48a5 Fix particles active even when disabled during load.
Possibly this could be fixed better by using the plugin string data - but there is nothing that currently set this; and it seems easier to use the particle callback enabled flag.
2019-01-25 21:39:03 +00:00
Richard Harrison
8a55c2f44f Fix for deleting still referenced object
ref https://sourceforge.net/p/flightgear/codetickets/2105/

Use the thread safe versions (getRef) of the objectcache methods
2019-01-25 21:39:03 +00:00
Erik Hofman
ef1cbae22b Split up SIMD support in ENABLE_SIMD which enables sse2 support for the compiler and ENABLE_SIMD_CODE which enables the hand crafted SIMD math functions which defaults to OFF now since compilers have catched up on generating optimized vectorized SIMD code. 2019-01-15 11:01:24 +01:00
James Turner
61f322f201 Bump patch version to 2018.3.2 2019-01-06 16:14:10 +00:00
Stuart Buchanan
becbef96f5 Fix effects for MP models - ticket 2076
https://sourceforge.net/p/flightgear/codetickets/2076/

Effects were being instantiated by the loader for
all models, rather than just simple .ac/.obj models.
2018-11-06 17:44:06 +00:00
James Turner
89b3fadf0f Fix for assert with empty systems
Empty subsystem groups didn’t set their init state correctly, leading
to an assert on post-init. Fix this and add a test for it.

https://sourceforge.net/p/flightgear/codetickets/2043/
2018-10-23 15:30:32 +01:00
James Turner
4a1a9ea9c1 Catalogs: allow migration to alternate IDs 2018-10-17 16:24:26 +01:00
James Turner
efc609577f Packages: check for existing update when scheduling
This is fixing an issue identified in the launcher in a secondary way,
to ensure if another user of the API tries to schedule an already
scheduled package, we ignore the second request.
2018-10-17 16:24:20 +01:00
James Turner
6ffc501566 Mac: Set CMake OS-X deployment target correctly
Also raises the OS-X min version to 10.9 for libc++ compat
2018-10-17 16:24:15 +01:00
James Turner
9785cadbd0 Fix a debug message left in the terrasync code 2018-10-05 10:41:39 +01:00
77 changed files with 531 additions and 6702 deletions

View File

@@ -1193,7 +1193,7 @@ VG_API_CALL VGboolean vgInterpolatePath(VGPath dstPath, VGPath startPath,
SHfloat *procData1, *procData2;
SHint procSegCount1=0, procSegCount2=0;
SHint procDataCount1=0, procDataCount2=0;
SHuint8 *newSegs, *newData=0;
SHuint8 *newSegs, *newData;
void *userData[4];
SHint segment1, segment2;
SHint segindex, s,d,i;

View File

@@ -71,12 +71,8 @@ namespace canvas
canvas::Text *_text_element;
#if OSG_VERSION_LESS_THAN(3,5,6)
void computePositions(unsigned int contextID) const override;
#else
void computePositionsImplementation() override;
#endif
};
void computePositions(unsigned int contextID) const override;
};
class TextLine
{
@@ -126,8 +122,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 =
std::lower_bound(line_numbers.begin(), line_numbers.end(), _line);
@@ -139,10 +133,6 @@ namespace canvas
_begin = begin_it - line_numbers.begin();
_end = std::upper_bound(begin_it, line_numbers.end(), _line)
- line_numbers.begin();
#else
//OSG:TODO: Need 3.5.6 version of this
#endif
}
//----------------------------------------------------------------------------
@@ -172,35 +162,33 @@ namespace canvas
if( empty() )
return pos;
#if OSG_VERSION_LESS_THAN(3,3,5)
GlyphQuads::Coords2 const& coords = _quads->_coords;
#elif OSG_VERSION_LESS_THAN(3,5,6)
GlyphQuads::Coords2 refCoords = _quads->_coords;
GlyphQuads::Coords2::element_type &coords = *refCoords.get();
size_t global_i = _begin + i;
if (global_i == _begin)
// before first character of line
pos.x() = coords[_begin * 4].x();
else if (global_i == _end)
// After Last character of line
pos.x() = coords[(_end - 1) * 4 + 2].x();
else
{
float prev_l = coords[(global_i - 1) * 4].x(),
prev_r = coords[(global_i - 1) * 4 + 2].x(),
cur_l = coords[global_i * 4].x();
if (prev_l == prev_r)
// If previous character width is zero set to begin of next character
// (Happens eg. with spaces)
pos.x() = cur_l;
else
// position at center between characters
pos.x() = 0.5 * (prev_r + cur_l);
}
GlyphQuads::Coords2 const& coords = _quads->_coords;
#else
//OSG:TODO: need 3.5.7 version of this.
GlyphQuads::Coords2 refCoords = _quads->_coords;
GlyphQuads::Coords2::element_type &coords = *refCoords.get();
#endif
size_t global_i = _begin + i;
if( global_i == _begin )
// before first character of line
pos.x() = coords[_begin * 4].x();
else if( global_i == _end )
// After Last character of line
pos.x() = coords[(_end - 1) * 4 + 2].x();
else
{
float prev_l = coords[(global_i - 1) * 4].x(),
prev_r = coords[(global_i - 1) * 4 + 2].x(),
cur_l = coords[global_i * 4].x();
if( prev_l == prev_r )
// If previous character width is zero set to begin of next character
// (Happens eg. with spaces)
pos.x() = cur_l;
else
// position at center between characters
pos.x() = 0.5 * (prev_r + cur_l);
}
return pos;
}
@@ -212,12 +200,12 @@ namespace canvas
return cursorPos(0);
GlyphQuads::Glyphs const& glyphs = _quads->_glyphs;
#if OSG_VERSION_LESS_THAN(3,3,5)
#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
float const HIT_FRACTION = 0.6;
float const character_width = _text->getCharacterHeight()
@@ -237,10 +225,6 @@ namespace canvas
}
return cursorPos(i - _begin);
#else
//OSG:TODO: need 3.5.7 version of this.
return cursorPos(0);
#endif
}
//----------------------------------------------------------------------------
@@ -656,7 +640,7 @@ namespace canvas
return bb;
}
#if OSG_VERSION_LESS_THAN(3,5,6)
//----------------------------------------------------------------------------
void Text::TextOSG::computePositions(unsigned int contextID) const
{
if( _textureGlyphQuadMap.empty() || _layout == VERTICAL )
@@ -726,14 +710,6 @@ namespace canvas
return osgText::Text::computePositions(contextID);
}
#else
void Text::TextOSG::computePositionsImplementation()
{
TextBase::computePositionsImplementation();
}
#endif
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
const std::string Text::TYPE_NAME = "text";

View File

@@ -6,10 +6,10 @@ using namespace osg;
/**
* merge OSG output into our logging system, so it gets recorded to file,
* and so we can display a GUI console with renderer issues, especially
* shader compilation warnings and errors.
*/
* merge OSG output into our logging system, so it gets recorded to file,
* and so we can display a GUI console with renderer issues, especially
* shader compilation warnings and errors.
*/
class NotifyLogger : public osg::NotifyHandler
{
public:
@@ -22,33 +22,24 @@ public:
if (strstr(message, "the final reference count was")) {
// as this is going to segfault ignore the translation of severity and always output the message.
SG_LOG(SG_GL, SG_ALERT, message);
#ifndef DEBUG
throw new std::string(message);
//int* trigger_segfault = 0;
//*trigger_segfault = 0;
#endif
int* trigger_segfault = 0;
*trigger_segfault = 0;
return;
}
char*tmessage = strdup(message);
char*lf = strrchr(tmessage, '\n');
if (lf)
*lf = 0;
SG_LOG(SG_OSG, translateSeverity(severity), tmessage);
free(tmessage);
SG_LOG(SG_GL, translateSeverity(severity), message);
}
private:
sgDebugPriority translateSeverity(osg::NotifySeverity severity) {
switch (severity) {
case osg::ALWAYS:
case osg::FATAL: return SG_ALERT;
case osg::WARN: return SG_WARN;
case osg::NOTICE:
case osg::INFO: return SG_INFO;
case osg::DEBUG_FP:
case osg::DEBUG_INFO: return SG_DEBUG;
default: return SG_ALERT;
case osg::ALWAYS:
case osg::FATAL: return SG_ALERT;
case osg::WARN: return SG_WARN;
case osg::NOTICE:
case osg::INFO: return SG_INFO;
case osg::DEBUG_FP:
case osg::DEBUG_INFO: return SG_DEBUG;
default: return SG_ALERT;
}
}
};

View File

@@ -35,10 +35,7 @@ typedef enum {
SG_TERRASYNC = 0x01000000,
SG_PARTICLES = 0x02000000,
SG_HEADLESS = 0x04000000,
// SG_OSG (OSG notify) - will always be displayed regardless of FG log settings as OSG log level is configured
// separately and thus it makes more sense to allow these message through.
SG_OSG = 0x08000000,
SG_UNDEFD = 0x10000000, // For range checking
SG_UNDEFD = 0x08000000, // For range checking
SG_ALL = 0xFFFFFFFF
} sgDebugClass;

View File

@@ -38,7 +38,6 @@
#include <simgear/io/iostreams/sgstream.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/timing/timestamp.hxx>
#if defined (SG_WINDOWS)
// for AllocConsole, OutputDebugString
@@ -62,12 +61,7 @@ LogCallback::LogCallback(sgDebugClass c, sgDebugPriority p) :
bool LogCallback::shouldLog(sgDebugClass c, sgDebugPriority p) const
{
if ((c & m_class) != 0 && p >= m_priority)
return true;
if (c == SG_OSG) // always have OSG logging as it OSG logging is configured separately.
return true;
return false;
return ((c & m_class) != 0 && p >= m_priority);
}
void LogCallback::setLogLevels( sgDebugClass c, sgDebugPriority p )
@@ -75,18 +69,6 @@ void LogCallback::setLogLevels( sgDebugClass c, sgDebugPriority p )
m_priority = p;
m_class = c;
}
const char* LogCallback::debugPriorityToString(sgDebugPriority p)
{
switch (p) {
case SG_ALERT: return "ALRT";
case SG_BULK: return "BULK";
case SG_DEBUG: return "DBUG";
case SG_INFO: return "INFO";
case SG_POPUP: return "POPU";
case SG_WARN: return "WARN";
default: return "UNKN";
}
}
const char* LogCallback::debugClassToString(sgDebugClass c)
{
@@ -119,7 +101,6 @@ const char* LogCallback::debugClassToString(sgDebugClass c)
case SG_TERRASYNC: return "terrasync";
case SG_PARTICLES: return "particles";
case SG_HEADLESS: return "headless";
case SG_OSG: return "OSG";
default: return "unknown";
}
}
@@ -131,41 +112,18 @@ const char* LogCallback::debugClassToString(sgDebugClass c)
class FileLogCallback : public simgear::LogCallback
{
public:
SGTimeStamp logTimer;
FileLogCallback(const SGPath& aPath, sgDebugClass c, sgDebugPriority p) :
simgear::LogCallback(c, p)
{
m_file.open(aPath, std::ios_base::out | std::ios_base::trunc);
logTimer.stamp();
}
virtual void operator()(sgDebugClass c, sgDebugPriority p,
const char* file, int line, const std::string& message)
{
if (!shouldLog(c, p)) return;
// fprintf(stderr, "%7.2f [%.8s]:%-10s %s\n", logTimer.elapsedMSec() / 1000.0, debugPriorityToString(p), debugClassToString(c), aMessage.c_str());
m_file
<< std::fixed
<< std::setprecision(2)
<< std::setw(8)
<< std::right
<< (logTimer.elapsedMSec() / 1000.0)
<< std::setw(8)
<< std::left
<< " ["+std::string(debugPriorityToString(p))+"]:"
<< std::setw(10)
<< std::left
<< debugClassToString(c)
<< " "
<< file
<< ":"
<< line
<< ":"
<< message << std::endl;
//m_file << debugClassToString(c) << ":" << (int)p
// << ":" << file << ":" << line << ":" << message << std::endl;
m_file << debugClassToString(c) << ":" << (int) p
<< ":" << file << ":" << line << ":" << message << std::endl;
}
private:
sg_ofstream m_file;
@@ -174,12 +132,9 @@ private:
class StderrLogCallback : public simgear::LogCallback
{
public:
SGTimeStamp logTimer;
StderrLogCallback(sgDebugClass c, sgDebugPriority p) :
simgear::LogCallback(c, p)
{
logTimer.stamp();
}
#if defined (SG_WINDOWS)
@@ -193,9 +148,8 @@ public:
const char* file, int line, const std::string& aMessage)
{
if (!shouldLog(c, p)) return;
//fprintf(stderr, "%s\n", aMessage.c_str());
fprintf(stderr, "%8.2f [%.8s]:%-10s %s\n", logTimer.elapsedMSec()/1000.0, debugPriorityToString(p), debugClassToString(c), aMessage.c_str());
// file, line, aMessage.c_str());
fprintf(stderr, "%s\n", aMessage.c_str());
//fprintf(stderr, "%s:%d:%s:%d:%s\n", debugClassToString(c), p,
// file, line, aMessage.c_str());
fflush(stderr);
@@ -516,10 +470,6 @@ public:
// Testing mode, so always log.
if (m_testMode) return true;
// SG_OSG (OSG notify) - will always be displayed regardless of FG log settings as OSG log level is configured
// separately and thus it makes more sense to allow these message through.
if (static_cast<unsigned>(p) == static_cast<unsigned>(SG_OSG)) return true;
p = translatePriority(p);
if (p >= SG_INFO) return true;
return ((c & m_logClass) != 0 && p >= m_logPriority);

View File

@@ -52,7 +52,6 @@ protected:
bool shouldLog(sgDebugClass c, sgDebugPriority p) const;
static const char* debugClassToString(sgDebugClass c);
static const char* debugPriorityToString(sgDebugPriority p);
private:
sgDebugClass m_class;
sgDebugPriority m_priority;

View File

@@ -44,10 +44,6 @@ SGPrecipitation::SGPrecipitation() :
void SGPrecipitation::setEnabled( bool value )
{
_enabled = value;
if (!_enabled) {
_precipitationEffect->snow(0);
_precipitationEffect->rain(0);
}
}
void SGPrecipitation::setDropletExternal( bool value )
@@ -68,9 +64,6 @@ bool SGPrecipitation::getEnabled() const
*/
osg::Group* SGPrecipitation::build(void)
{
if (!_enabled)
return nullptr;
osg::ref_ptr<osg::Group> group = new osg::Group;
_precipitationEffect->snow(0);
@@ -234,9 +227,6 @@ void SGPrecipitation::setWindProperty(double heading, double speed)
*/
bool SGPrecipitation::update(void)
{
if (!_enabled)
return false;
if (this->_freeze) {
if (this->_rain_intensity > 0) {
this->_snow_intensity = this->_rain_intensity;

View File

@@ -48,8 +48,6 @@
#include <simgear/debug/logstream.hxx>
#include <simgear/timing/timestamp.hxx>
#include <simgear/structure/exception.hxx>
#include <simgear/threads/SGThread.hxx>
#include <simgear/threads/SGGuard.hxx>
#if defined( HAVE_VERSION_H ) && HAVE_VERSION_H
#include "version.h"
@@ -125,9 +123,6 @@ Client::Client() :
setUserAgent("SimGear-" SG_STRINGIZE(SIMGEAR_VERSION));
static bool didInitCurlGlobal = false;
static SGMutex initMutex;
SGGuard<SGMutex> g(initMutex);
if (!didInitCurlGlobal) {
curl_global_init(CURL_GLOBAL_ALL);
didInitCurlGlobal = true;
@@ -278,10 +273,6 @@ void Client::makeRequest(const Request_ptr& r)
curl_easy_setopt(curlRequest, CURLOPT_USERAGENT, d->userAgent.c_str());
curl_easy_setopt(curlRequest, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
if (sglog().would_log(SG_TERRASYNC, SG_DEBUG)) {
curl_easy_setopt(curlRequest, CURLOPT_VERBOSE, 1);
}
curl_easy_setopt(curlRequest, CURLOPT_FOLLOWLOCATION, 1);
if (!d->proxy.empty()) {

View File

@@ -160,7 +160,7 @@ public:
}
}
int32_t readInt()
float readInt()
{
unsigned int* p = reinterpret_cast<unsigned int*>(ptr + offset);
if ( sgIsBigEndian() ) {

View File

@@ -64,31 +64,6 @@ SGFile::SGFile( int existingFd ) :
SGFile::~SGFile() {
}
#include <simgear/misc/sg_hash.hxx>
#include <simgear/structure/exception.hxx>
#include "simgear/misc/strutils.hxx"
std::string SGFile::computeHash()
{
if (!file_name.exists())
return std::string();
simgear::sha1nfo info;
sha1_init(&info);
char* buf = static_cast<char*>(malloc(1024 * 1024));
size_t readLen;
SGBinaryFile f(file_name);
if (!f.open(SG_IO_IN)) {
throw sg_io_exception("Couldn't open file for compute hash", file_name);
}
while ((readLen = f.read(buf, 1024 * 1024)) > 0) {
sha1_write(&info, buf, readLen);
}
f.close();
free(buf);
std::string hashBytes((char*)sha1_result(&info), HASH_LENGTH);
return simgear::strutils::encodeHex(hashBytes);
}
// open the file based on specified direction
bool SGFile::open( const SGProtocolDir d ) {

View File

@@ -87,9 +87,6 @@ public:
/** @return true of eof conditions exists */
virtual bool eof() const { return eof_flag; };
std::string computeHash();
};
class SGBinaryFile : public SGFile {

View File

@@ -623,22 +623,6 @@ namespace simgear {
*p = tolower(*p);
}
}
bool iequals(const std::string& a, const std::string& b)
{
const auto lenA = a.length();
const auto lenB = b.length();
if (lenA != lenB) return false;
const char* aPtr = a.data();
const char* bPtr = b.data();
for (size_t i = 0; i < lenA; ++i) {
if (tolower(*aPtr++) != tolower(*bPtr++)) return false;
}
return true;
}
#if defined(SG_WINDOWS)
static std::wstring convertMultiByteToWString(DWORD encoding, const std::string& a)

View File

@@ -264,11 +264,6 @@ namespace simgear {
*/
void lowercase(std::string &s);
/**
* case-insensitive string comparisom
*/
bool iequals(const std::string& a, const std::string& b);
/**
* convert a string in the local Windows 8-bit encoding to UTF-8
* (no-op on other platforms)

View File

@@ -99,16 +99,6 @@ void test_to_int()
SG_CHECK_EQUAL(strutils::to_int("-10000"), -10000);
}
void test_iequals()
{
SG_VERIFY(strutils::iequals("abcdef", "AbCDeF"));
SG_VERIFY(strutils::iequals("", ""));
SG_VERIFY(!strutils::iequals("abcdE", "ABCD"));
SG_VERIFY(strutils::iequals("%$abcdef12", "%$AbCDeF12"));
SG_VERIFY(strutils::iequals("VOR-DME", "vor-dme"));
SG_VERIFY(!strutils::iequals("VOR-DME", "vor_dme"));
}
// Auxiliary function for test_readNonNegativeInt()
void aux_readNonNegativeInt_setUpOStringStream(std::ostringstream& oss, int base)
{
@@ -747,7 +737,6 @@ int main(int argc, char* argv[])
test_utf8Convert();
test_parseGeod();
test_formatGeod();
test_iequals();
return EXIT_SUCCESS;
}

View File

@@ -24,11 +24,6 @@
#include <simgear/structure/map.hxx>
#include <boost/iterator/iterator_facade.hpp>
#if BOOST_VERSION >= 105600
#include <boost/core/enable_if.hpp>
#else
#include <boost/utility/enable_if.hpp>
#endif
namespace nasal
{

View File

@@ -302,15 +302,18 @@ static char* dosprintf(char* f, ...)
char* buf;
va_list va;
int olen, len = 16;
va_start(va, f);
while(1) {
buf = naAlloc(len);
va_start(va, f);
olen = vsnprintf(buf, len, f, va);
va_list vaCopy;
va_copy(vaCopy, va);
olen = vsnprintf(buf, len, f, vaCopy);
if(olen >= 0 && olen < len) {
va_end(va);
va_end(vaCopy);
return buf;
}
va_end(va);
va_end(vaCopy);
naFree(buf);
len *= 2;
}

View File

@@ -148,7 +148,6 @@ int parseTest()
SGPath rootPath = simgear::Dir::current().path();
rootPath.append("testRoot");
pkg::Root* root = new pkg::Root(rootPath, "8.1.12");
root->setLocale("de");
pkg::CatalogRef cat = pkg::Catalog::createFromPath(root, SGPath(SRC_DIR "/catalogTest1"));
SG_VERIFY(cat.valid());
@@ -173,7 +172,7 @@ int parseTest()
pkg::PackageRef p2 = cat->getPackageById("c172p");
SG_VERIFY(p2.valid());
SG_CHECK_EQUAL(p2->qualifiedId(), "org.flightgear.test.catalog1.c172p");
SG_CHECK_EQUAL(p2->description(), "German description of C172");
SG_CHECK_EQUAL(p2->description(), "A plane made by Cessna on Jupiter");
pkg::Package::PreviewVec thumbs = p2->previewsForVariant(0);
SG_CHECK_EQUAL(thumbs.size(), 3);
@@ -218,7 +217,7 @@ int parseTest()
SG_CHECK_EQUAL(skisVariant, skisVariantFull);
SG_CHECK_EQUAL(p2->getLocalisedProp("description", skisVariant),
"German description of C172 with skis");
"A plane with skis");
SG_CHECK_EQUAL(p2->getLocalisedProp("author", skisVariant),
"Standard author");
@@ -226,7 +225,6 @@ int parseTest()
SG_VERIFY(floatsVariant > 0);
SG_CHECK_EQUAL(p2->parentIdForVariant(floatsVariant), "c172p");
// no DE localisation description provided for the floats variant
SG_CHECK_EQUAL(p2->getLocalisedProp("description", floatsVariant),
"A plane with floats");
SG_CHECK_EQUAL(p2->getLocalisedProp("author", floatsVariant),
@@ -259,12 +257,6 @@ int parseTest()
string_list primaries = {"c172p", "c172r"};
SG_VERIFY(p2->primaryVariants() == primaries);
///////////
pkg::PackageRef p3 = cat->getPackageById("b737-NG");
SG_VERIFY(p3.valid());
SG_CHECK_EQUAL(p3->description(), "German description of B737NG XYZ");
// test filtering / searching too
string_set tags(p2->tags());
SG_CHECK_EQUAL(tags.size(), 4);
@@ -313,16 +305,6 @@ int parseTest()
queryText->setStringValue("any-of/description", "float");
SG_VERIFY(p2->matches(queryText.ptr()));
}
// match localized variant descriptions
{
SGPropertyNode_ptr queryText(new SGPropertyNode);
queryText->setStringValue("any-of/description", "XYZ");
SG_VERIFY(p3->matches(queryText.ptr()));
}
delete root;
return EXIT_SUCCESS;
}

View File

@@ -44,7 +44,7 @@ void Package::initWithProps(const SGPropertyNode* aProps)
{
m_props = const_cast<SGPropertyNode*>(aProps);
// cache tag values
for (auto c : aProps->getChildren("tag")) {
BOOST_FOREACH(const SGPropertyNode* c, aProps->getChildren("tag")) {
std::string t(c->getStringValue());
m_tags.insert(boost::to_lower_copy(t));
}
@@ -133,8 +133,22 @@ bool Package::matches(const SGPropertyNode* aFilter) const
if ((filter_name == "text") || (filter_name == "description")) {
handled = true;
if (matchesDescription(aFilter->getStringValue())) {
return true;
std::string n(aFilter->getStringValue());
boost::to_lower(n);
size_t pos = boost::to_lower_copy(description()).find(n);
if (pos != std::string::npos) {
return true;
}
for (auto var : m_props->getChildren("variant")) {
if (var->hasChild("description")) {
std::string variantDesc(var->getStringValue("description"));
boost::to_lower(variantDesc);
size_t pos = variantDesc.find(n);
if (pos != std::string::npos) {
return true;
}
}
}
}
@@ -144,50 +158,6 @@ bool Package::matches(const SGPropertyNode* aFilter) const
return false;
}
bool Package::matchesDescription(const std::string &search) const
{
std::string n(search);
boost::to_lower(n);
bool localized;
auto d = getLocalisedString(m_props, "description", &localized);
boost::to_lower(d);
if (d.find(n) != std::string::npos) {
return true;
}
// try non-localized description too, if the abovce was a localized one
if (localized) {
const std::string baseDesc = m_props->getStringValue("description");
auto pos = boost::to_lower_copy(baseDesc).find(n);
if (pos != std::string::npos) {
return true;
}
}
// try each variant's description
for (auto var : m_props->getChildren("variant")) {
auto vd = getLocalisedString(var, "description", &localized);
if (!vd.empty()) {
boost::to_lower(vd);
if (vd.find(n) != std::string::npos) {
return true;
}
}
if (localized) {
// try non-localized variant description
std::string vd = var->getStringValue("description");
boost::to_lower(vd);
if (vd.find(n) != std::string::npos) {
return true;
}
}
} // of variant iteration
return false;
}
bool Package::isInstalled() const
{
@@ -337,29 +307,14 @@ std::string Package::getLocalisedProp(const std::string& aName, const unsigned i
return getLocalisedString(propsForVariant(vIndex, aName.c_str()), aName.c_str());
}
std::string Package::getLocalisedString(const SGPropertyNode* aRoot, const char* aName, bool* isLocalized) const
std::string Package::getLocalisedString(const SGPropertyNode* aRoot, const char* aName) const
{
// we used to place localised strings under /sim/<locale>/name - but this
// potentially pollutes the /sim namespace
// we now check first in /sim/localized/<locale>/name first
const auto& locale = m_catalog->root()->getLocale();
if (isLocalized) *isLocalized = false;
if (locale.empty()) {
return aRoot->getStringValue(aName);
}
const SGPropertyNode* localeRoot;
if (aRoot->hasChild("localized")) {
localeRoot = aRoot->getChild("localized")->getChild(locale);
} else {
// old behaviour where locale nodes are directly beneath /sim
localeRoot = aRoot->getChild(locale);
}
if (localeRoot && localeRoot->hasChild(aName)) {
if (isLocalized) *isLocalized = true;
return localeRoot->getStringValue(aName);
std::string locale = m_catalog->root()->getLocale();
if (aRoot->hasChild(locale)) {
const SGPropertyNode* localeRoot = aRoot->getChild(locale.c_str());
if (localeRoot->hasChild(aName)) {
return localeRoot->getStringValue(aName);
}
}
return aRoot->getStringValue(aName);

View File

@@ -225,14 +225,12 @@ private:
*/
bool validate() const;
std::string getLocalisedString(const SGPropertyNode* aRoot, const char* aName, bool* isLocalized = nullptr) const;
std::string getLocalisedString(const SGPropertyNode* aRoot, const char* aName) const;
PreviewVec previewsFromProps(const SGPropertyNode_ptr& ptr) const;
SGPropertyNode_ptr propsForVariant(const unsigned int vIndex, const char* propName = nullptr) const;
bool matchesDescription(const std::string &search) const;
SGPropertyNode_ptr m_props;
std::string m_id;
string_set m_tags;

View File

@@ -32,15 +32,6 @@
<file-size-bytes type="int">860</file-size-bytes>
<author>Standard author</author>
<localized>
<de>
<description>German description of C172</description>
</de>
<fr>
<description>French description of C172</description>
</fr>
</localized>
<tag>cessna</tag>
<tag>ga</tag>
<tag>piston</tag>
@@ -108,15 +99,6 @@
<name>C172 with skis</name>
<description>A plane with skis</description>
<localized>
<de>
<description>German description of C172 with skis</description>
</de>
<fr>
<description>French description of C172 with skis</description>
</fr>
</localized>
<variant-of>c172p</variant-of>
<preview>
@@ -175,14 +157,6 @@
<tag>jet</tag>
<tag>ifr</tag>
<!-- not within a localized element -->
<de>
<description>German description of B737NG XYZ</description>
</de>
<fr>
<description>French description of B737NG</description>
</fr>
<rating>
<FDM type="int">5</FDM>
<systems type="int">5</systems>

View File

@@ -92,12 +92,6 @@ public:
return p;
}
static PropertyObject<T> create(SGPropertyNode_ptr aNode)
{
PropertyObject<T> p(aNode);
return p;
}
static PropertyObject<T> create(SGPropertyNode* aNode, T aValue)
{
PropertyObject<T> p(aNode);
@@ -163,8 +157,6 @@ template <>
class PropertyObject<std::string> : PropertyObjectBase
{
public:
PropertyObject() = default;
explicit PropertyObject(const char* aChild) :
PropertyObjectBase(aChild)
{ }
@@ -191,12 +183,6 @@ public:
return p;
}
static PropertyObject<std::string> create(SGPropertyNode_ptr aNode)
{
PropertyObject<std::string> p(aNode);
return p;
}
static PropertyObject<std::string> create(SGPropertyNode* aNode, const std::string& aValue)
{
PropertyObject<std::string> p(aNode);

View File

@@ -205,34 +205,6 @@ void testCreate()
}
void testDeclare()
{
PropertyObject<bool> a;
PropertyObject<int> b;
PropertyObject<double> c;
PropertyObject<std::string> d;
}
void testDeclareThenDefine()
{
// Declare.
PropertyObject<bool> a;
PropertyObject<std::string> b;
// Property nodes.
SGPropertyNode_ptr propsA = new SGPropertyNode;
SGPropertyNode_ptr propsB = new SGPropertyNode;
propsA->setValue(true);
propsB->setValue("test");
// Now define.
a = PropertyObject<bool>::create(propsA);
b = PropertyObject<std::string>::create(propsB);
assert(a == true);
assert(b == "test");
}
int main(int argc, char* argv[])
{
testRoot = new SGPropertyNode();
@@ -255,8 +227,6 @@ int main(int argc, char* argv[])
testAssignment();
testSTLContainer();
testCreate();
testDeclare();
testDeclareThenDefine();
return EXIT_SUCCESS;
}

View File

@@ -118,7 +118,7 @@ parse_name (const SGPropertyNode *node, const Range &path)
} else {
std::string err = "'";
err.push_back(*i);
err.append("' found in propertyname after '"+node->getPath()+"'");
err.append("' found in propertyname after '"+node->getNameString()+"'");
err.append("\nname may contain only ._- and alphanumeric characters");
throw err;
}
@@ -130,7 +130,7 @@ parse_name (const SGPropertyNode *node, const Range &path)
if (path.begin() == i) {
std::string err = "'";
err.push_back(*i);
err.append("' found in propertyname after '"+node->getPath()+"'");
err.append("' found in propertyname after '"+node->getNameString()+"'");
err.append("\nname must begin with alpha or '_'");
throw err;
}
@@ -536,7 +536,7 @@ find_node (SGPropertyNode * current,
using namespace boost;
typedef split_iterator<typename range_result_iterator<Range>::type>
PathSplitIterator;
PathSplitIterator itr
= make_split_iterator(path, first_finder("/", is_equal()));
if (*path.begin() == '/')
@@ -874,7 +874,7 @@ SGPropertyNode::SGPropertyNode (const SGPropertyNode &node)
}
switch (_type) {
case props::BOOL:
set_bool(node.get_bool());
set_bool(node.get_bool());
break;
case props::INT:
set_int(node.get_int());
@@ -963,11 +963,6 @@ SGPropertyNode::alias (SGPropertyNode * target)
{
if (target && (_type != props::ALIAS) && (!_tied))
{
/* loop protection: check alias chain; must not contain self */
for (auto p = target; p; p = ((p->_type == props::ALIAS) ? p->_value.alias : nullptr)) {
if (p == this) return false;
}
clearValue();
get(target);
_value.alias = target;
@@ -978,7 +973,7 @@ SGPropertyNode::alias (SGPropertyNode * target)
if (!target)
{
SG_LOG(SG_GENERAL, SG_ALERT,
"Failed to set alias " << getPath() << ". "
"Failed to create alias for " << getPath() << ". "
"The target property does not exist.");
}
else
@@ -986,15 +981,15 @@ SGPropertyNode::alias (SGPropertyNode * target)
{
if (_value.alias == target)
return true; // ok, identical alias requested
SG_LOG(SG_GENERAL, SG_ALERT, "alias(): "<< getPath() <<
" is already pointing to " << _value.alias->getPath() <<
" so it cannot alias '" << target->getPath() << ". Use unalias() first.");
SG_LOG(SG_GENERAL, SG_ALERT,
"Failed to create alias at " << target->getPath() << ". "
"Source "<< getPath() << " is already aliasing another property.");
}
else
if (_tied)
{
SG_LOG(SG_GENERAL, SG_ALERT, "alias(): " << getPath() <<
" is a tied property. It cannot alias " << target->getPath() << ".");
SG_LOG(SG_GENERAL, SG_ALERT, "Failed to create alias at " << target->getPath() << ". "
"Source " << getPath() << " is a tied property.");
}
return false;
@@ -1316,7 +1311,7 @@ SGPropertyNode::getType () const
}
bool
bool
SGPropertyNode::getBoolValue () const
{
// Shortcut for common case
@@ -1349,7 +1344,7 @@ SGPropertyNode::getBoolValue () const
}
}
int
int
SGPropertyNode::getIntValue () const
{
// Shortcut for common case
@@ -1382,7 +1377,7 @@ SGPropertyNode::getIntValue () const
}
}
long
long
SGPropertyNode::getLongValue () const
{
// Shortcut for common case
@@ -1415,7 +1410,7 @@ SGPropertyNode::getLongValue () const
}
}
float
float
SGPropertyNode::getFloatValue () const
{
// Shortcut for common case
@@ -1448,7 +1443,7 @@ SGPropertyNode::getFloatValue () const
}
}
double
double
SGPropertyNode::getDoubleValue () const
{
// Shortcut for common case
@@ -2574,7 +2569,7 @@ template<>
std::ostream& SGRawBase<SGVec4d>::printOn(std::ostream& stream) const
{
const SGVec4d vec
= static_cast<const SGRawValue<SGVec4d>*>(this)->getValue();
= static_cast<const SGRawValue<SGVec4d>*>(this)->getValue();
for (int i = 0; i < 4; ++i) {
stream << vec[i];
if (i < 3)

View File

@@ -55,18 +55,13 @@ namespace boost {
struct disable_if : public disable_if_c<Cond::value, T> {};
}
#else
# include <boost/utility.hpp>
# include <boost/type_traits/is_enum.hpp>
#if BOOST_VERSION >= 105600
#include <boost/core/enable_if.hpp>
#else
#include <boost/utility/enable_if.hpp>
#endif
# include <simgear/debug/logstream.hxx>
# include <simgear/math/SGMathFwd.hxx>
# include <simgear/math/sg_types.hxx>
#endif
#include <simgear/structure/SGReferenced.hxx>
#include <simgear/structure/SGSharedPtr.hxx>

View File

@@ -9,7 +9,6 @@ foreach( mylibfolder
tgdb
util
tsync
viewer
)
add_subdirectory(${mylibfolder})

View File

@@ -830,9 +830,6 @@ void reload_shaders()
if (!fileName.empty()) {
shader->loadShaderSourceFromFile(fileName);
}
else
SG_LOG(SG_INPUT, SG_ALERT, "Could not locate shader: " << fileName);
}
}
@@ -920,13 +917,8 @@ void ShaderProgramBuilder::buildAttribute(Effect* effect, Pass* pass,
Shader::Type stype = (Shader::Type)shaderKey.second;
string fileName = SGModelLib::findDataFile(shaderName, options);
if (fileName.empty())
{
SG_LOG(SG_INPUT, SG_ALERT, "Could not locate shader" << shaderName);
throw BuilderException(string("couldn't find shader ") +
shaderName);
}
shaderName);
resolvedKey.shaders.push_back(ShaderKey(fileName, stype));
}
ProgramMap::iterator resitr = resolvedProgramMap.find(resolvedKey);
@@ -1413,11 +1405,8 @@ bool makeParametersFromStateSet(SGPropertyNode* effectRoot, const StateSet* ss)
// Walk the techniques property tree, building techniques and
// passes.
static SGMutex realizeTechniques_lock;
bool Effect::realizeTechniques(const SGReaderWriterOptions* options)
{
SGGuard<SGMutex> g(realizeTechniques_lock);
if (_isRealized)
return true;
PropertyList tniqList = root->getChildren("technique");

View File

@@ -34,9 +34,8 @@ namespace simgear
using osgUtil::CullVisitor;
EffectCullVisitor::EffectCullVisitor(bool collectLights, Effect *effectOverride) :
_collectLights(collectLights),
_effectOverride(effectOverride)
EffectCullVisitor::EffectCullVisitor(bool collectLights) :
_collectLights(collectLights)
{
}
@@ -62,18 +61,12 @@ void EffectCullVisitor::apply(osg::Geode& node)
if (_collectLights && ( eg->getNodeMask() & MODELLIGHT_BIT ) ) {
_lightList.push_back( eg );
}
Effect *effect;
if (_effectOverride) {
effect = _effectOverride;
} else {
effect = eg->getEffect();
if (!effect) {
CullVisitor::apply(node);
return;
}
}
Effect* effect = eg->getEffect();
Technique* technique = 0;
if (!(technique = effect->chooseTechnique(&getRenderInfo()))) {
if (!effect) {
CullVisitor::apply(node);
return;
} else if (!(technique = effect->chooseTechnique(&getRenderInfo()))) {
return;
}
// push the node's state.

View File

@@ -29,12 +29,11 @@ class Texture2D;
namespace simgear
{
class Effect;
class EffectGeode;
class EffectCullVisitor : public osgUtil::CullVisitor
{
public:
EffectCullVisitor(bool collectLights = false, Effect *effectOverride = 0);
EffectCullVisitor(bool collectLights = false);
EffectCullVisitor(const EffectCullVisitor&);
virtual osgUtil::CullVisitor* clone() const;
using osgUtil::CullVisitor::apply;
@@ -49,7 +48,6 @@ private:
std::map<std::string,osg::ref_ptr<osg::Texture2D> > _bufferList;
std::vector<osg::ref_ptr<EffectGeode> > _lightList;
bool _collectLights;
osg::ref_ptr<Effect> _effectOverride;
};
}
#endif

View File

@@ -23,7 +23,6 @@
#include "Pass.hxx"
#include <osg/Version>
#include <osg/PointSprite>
#include <osg/TexEnv>
#include <osg/TexEnvCombine>
@@ -77,7 +76,7 @@ osg::Texture* TextureBuilder::buildFromType(Effect* effect, Pass* pass, const st
typedef boost::tuple<string, Texture::FilterMode, Texture::FilterMode,
Texture::WrapMode, Texture::WrapMode, Texture::WrapMode,
string, MipMapTuple, ImageInternalFormat> TexTuple;
string, MipMapTuple> TexTuple;
EffectNameValue<TexEnv::Mode> texEnvModesInit[] =
{
@@ -240,17 +239,6 @@ TexTuple makeTexTuple(Effect* effect, const SGPropertyNode* props,
}
}
const SGPropertyNode* pInternalFormat = getEffectPropertyChild(effect, props, "internal-format");
pInternalFormat = props->getChild("internal-format");
ImageInternalFormat iformat = ImageInternalFormat::Unspecified;
if (pInternalFormat) {
std::string internalFormat = pInternalFormat->getStringValue();
if (internalFormat == "normalized") {
iformat = ImageInternalFormat::Normalized;
SG_LOG(SG_INPUT, SG_ALERT, "internal-format normalized '" << imageName << "'");
}
}
const SGPropertyNode* pMipmapControl
= getEffectPropertyChild(effect, props, "mipmap-control");
MipMapTuple mipmapFunctions( AUTOMATIC, AUTOMATIC, AUTOMATIC, AUTOMATIC );
@@ -258,7 +246,7 @@ TexTuple makeTexTuple(Effect* effect, const SGPropertyNode* props,
mipmapFunctions = makeMipMapTuple(effect, pMipmapControl, options);
return TexTuple(absFileName, minFilter, magFilter, sWrap, tWrap, rWrap,
texType, mipmapFunctions, iformat);
texType, mipmapFunctions);
}
bool setAttrs(const TexTuple& attrs, Texture* tex,
@@ -270,18 +258,7 @@ bool setAttrs(const TexTuple& attrs, Texture* tex,
osgDB::ReaderWriter::ReadResult result;
// load texture for effect
SGReaderWriterOptions::LoadOriginHint origLOH = options->getLoadOriginHint();
if(attrs.get<8>() == ImageInternalFormat::Normalized)
options->setLoadOriginHint(SGReaderWriterOptions::LoadOriginHint::ORIGIN_EFFECTS_NORMALIZED);
else
options->setLoadOriginHint(SGReaderWriterOptions::LoadOriginHint::ORIGIN_EFFECTS);
#if OSG_VERSION_LESS_THAN(3,4,2)
result = osgDB::readImageFile(imageName, options);
#else
result = osgDB::readRefImageFile(imageName, options);
#endif
options->setLoadOriginHint(origLOH);
osg::ref_ptr<osg::Image> image;
if (result.success())
image = result.getImage();
@@ -296,9 +273,11 @@ bool setAttrs(const TexTuple& attrs, Texture* tex,
} else if (t < s && 32 <= t) {
SGSceneFeatures::instance()->setTextureCompression(tex);
}
tex->setMaxAnisotropy(SGSceneFeatures::instance()->getTextureFilter());
tex->setMaxAnisotropy(SGSceneFeatures::instance()
->getTextureFilter());
} else {
SG_LOG(SG_INPUT, SG_ALERT, "failed to load effect texture file " << imageName);
SG_LOG(SG_INPUT, SG_ALERT, "failed to load effect texture file "
<< imageName);
return false;
}
@@ -607,64 +586,36 @@ Texture* CubeMapBuilder::build(Effect* effect, Pass* pass, const SGPropertyNode*
cubeTexture->setWrap(osg::Texture3D::WRAP_R, osg::Texture::CLAMP_TO_EDGE);
osgDB::ReaderWriter::ReadResult result;
SGReaderWriterOptions* wOpts = (SGReaderWriterOptions*)options;
SGReaderWriterOptions::LoadOriginHint origLOH = wOpts->getLoadOriginHint();
wOpts->setLoadOriginHint(SGReaderWriterOptions::LoadOriginHint::ORIGIN_EFFECTS);
#if OSG_VERSION_LESS_THAN(3,4,1)
result = osgDB::readImageFile(_tuple.get<0>(), options);
#else
result = osgDB::readRefImageFile(_tuple.get<0>(), options);
#endif
if(result.success()) {
osg::Image* image = result.getImage();
cubeTexture->setImage(TextureCubeMap::POSITIVE_X, image);
}
#if OSG_VERSION_LESS_THAN(3,4,1)
result = osgDB::readImageFile(_tuple.get<1>(), options);
#else
result = osgDB::readRefImageFile(_tuple.get<1>(), options);
#endif
if(result.success()) {
osg::Image* image = result.getImage();
cubeTexture->setImage(TextureCubeMap::NEGATIVE_X, image);
}
#if OSG_VERSION_LESS_THAN(3,4,1)
result = osgDB::readImageFile(_tuple.get<2>(), options);
#else
result = osgDB::readRefImageFile(_tuple.get<2>(), options);
#endif
if(result.success()) {
osg::Image* image = result.getImage();
cubeTexture->setImage(TextureCubeMap::POSITIVE_Y, image);
}
#if OSG_VERSION_LESS_THAN(3,4,1)
result = osgDB::readImageFile(_tuple.get<3>(), options);
#else
result = osgDB::readRefImageFile(_tuple.get<3>(), options);
#endif
if(result.success()) {
osg::Image* image = result.getImage();
cubeTexture->setImage(TextureCubeMap::NEGATIVE_Y, image);
}
#if OSG_VERSION_LESS_THAN(3,4,1)
result = osgDB::readImageFile(_tuple.get<4>(), options);
#else
result = osgDB::readRefImageFile(_tuple.get<4>(), options);
#endif
if(result.success()) {
osg::Image* image = result.getImage();
cubeTexture->setImage(TextureCubeMap::POSITIVE_Z, image);
}
#if OSG_VERSION_LESS_THAN(3,4,1)
result = osgDB::readImageFile(_tuple.get<5>(), options);
#else
result = osgDB::readRefImageFile(_tuple.get<5>(), options);
#endif
if(result.success()) {
osg::Image* image = result.getImage();
cubeTexture->setImage(TextureCubeMap::NEGATIVE_Z, image);
}
wOpts->setLoadOriginHint(origLOH);
if (itr == _cubemaps.end())
_cubemaps[_tuple] = cubeTexture;
@@ -684,11 +635,7 @@ Texture* CubeMapBuilder::build(Effect* effect, Pass* pass, const SGPropertyNode*
return cubeTexture.release();
osgDB::ReaderWriter::ReadResult result;
#if OSG_VERSION_LESS_THAN(3,4,1)
result = osgDB::readImageFile(texname, options);
#else
result = osgDB::readRefImageFile(texname, options);
#endif
if(result.success()) {
osg::Image* image = result.getImage();
image->flipVertical(); // Seems like the image coordinates are somewhat funny, flip to get better ones

View File

@@ -227,11 +227,7 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options,
}
else
{
#if OSG_VERSION_LESS_THAN(3,4,0)
osg::Image* image = osgDB::readImageFile(fullMaskPath, options);
#else
osg::Image* image = osgDB::readRefImageFile(fullMaskPath, options);
#endif
if (image && image->valid())
{
Texture2DRef object_mask = new osg::Texture2D;

View File

@@ -392,8 +392,6 @@ osg::Image* computeMipmap( osg::Image* image, MipMapTuple attrs )
image->getInternalTextureFormat(), image->getPixelFormat(),
image->getDataType(), data, osg::Image::USE_NEW_DELETE, image->getPacking() );
mipmaps->setMipmapLevels( mipmapOffsets );
mipmaps->setName(image->getName());
mipmaps->setFileName(image->getFileName());
return mipmaps.release();
}

View File

@@ -31,18 +31,14 @@ class Effect;
class SGReaderWriterOptions;
namespace effect {
enum MipMapFunction {
AUTOMATIC,
AVERAGE,
SUM,
PRODUCT,
MIN,
MAX
};
enum ImageInternalFormat {
Unspecified,
Normalized,
};
enum MipMapFunction {
AUTOMATIC,
AVERAGE,
SUM,
PRODUCT,
MIN,
MAX
};
typedef boost::tuple<MipMapFunction, MipMapFunction, MipMapFunction, MipMapFunction> MipMapTuple;

View File

@@ -111,11 +111,6 @@ public:
if (transform->getNumChildren())
_group->addChild(transform.get());
}
virtual void apply(BVHPageNode& leaf)
{
leaf.traverse(*this);
}
virtual void apply(BVHLineGeometry&)
{
}

View File

@@ -24,7 +24,6 @@
#include "../../bvh/BVHPageRequest.hxx"
#include "../../bvh/BVHPager.hxx"
#include <osg/Version>
#include <osg/io_utils>
#include <osg/Camera>
#include <osg/Drawable>
@@ -216,11 +215,7 @@ public:
if (pagedLOD.getMinRange(i) <= 0) {
osg::ref_ptr<const osgDB::Options> options;
options = getOptions(pagedLOD.getDatabaseOptions(), pagedLOD.getDatabasePath());
#if OSG_VERSION_LESS_THAN(3,4,0)
node = osgDB::readNodeFile(pagedLOD.getFileName(i), options.get());
#else
node = osgDB::readRefNodeFile(pagedLOD.getFileName(i), options.get());
#endif
}
if (!node.valid())
node = new osg::Group;
@@ -261,11 +256,7 @@ public:
osg::ref_ptr<const osgDB::Options> options;
options = getOptions(proxyNode.getDatabaseOptions(), proxyNode.getDatabasePath());
osg::ref_ptr<osg::Node> node;
#if OSG_VERSION_LESS_THAN(3,4,0)
node = osgDB::readNodeFile(proxyNode.getFileName(i), options.get());
#else
node = osgDB::readRefNodeFile(proxyNode.getFileName(i), options.get());
#endif
if (!node.valid())
node = new osg::Group;
if (i < proxyNode.getNumChildren())
@@ -362,11 +353,7 @@ SGSharedPtr<BVHNode>
BVHPageNodeOSG::load(const std::string& name, const osg::ref_ptr<const osg::Referenced>& options)
{
osg::ref_ptr<osg::Node> node;
#if OSG_VERSION_LESS_THAN(3,4,0)
node = osgDB::readNodeFile(name, dynamic_cast<const osgDB::Options*>(options.get()));
#else
node = osgDB::readRefNodeFile(name, dynamic_cast<const osgDB::Options*>(options.get()));
#endif
if (!node.valid())
return SGSharedPtr<BVHNode>();

View File

@@ -22,8 +22,6 @@
#endif
#include "ModelRegistry.hxx"
#include <simgear/scene/util/SGImageUtils.hxx>
#include "../material/mipmap.hxx"
#include <algorithm>
#include <utility>
@@ -46,7 +44,6 @@
#include <osgDB/Registry>
#include <osgDB/SharedStateManager>
#include <osgUtil/Optimizer>
#include <osg/Texture>
#include <simgear/sg_inlines.h>
@@ -60,16 +57,10 @@
#include <simgear/props/props.hxx>
#include <simgear/props/props_io.hxx>
#include <simgear/props/condition.hxx>
#include <simgear/io/sg_file.hxx>
#include <simgear/threads/SGGuard.hxx>
#include "BoundingVolumeBuildVisitor.hxx"
#include "model.hxx"
#include <boost/lexical_cast.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_comparison.hpp>
using namespace std;
using namespace osg;
using namespace osgUtil;
@@ -182,583 +173,110 @@ public:
} // namespace
static int nearestPowerOfTwo(unsigned int _v)
{
// uint v; // compute the next highest power of 2 of 32-bit v
unsigned int v = (unsigned int)_v;
bool neg = _v < 0;
if (neg)
v = (unsigned int)(-_v);
v &= (2 << 16) - 1; // make +ve
// bit twiddle to round up to nearest pot.
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
if (neg)
_v = -(int)v;
else
_v = (int)v;
return v;
}
static bool isPowerOfTwo(int v)
{
return ((v & (v - 1)) == 0);
}
osg::Node* DefaultProcessPolicy::process(osg::Node* node, const std::string& filename,
const Options* opt)
Node* DefaultProcessPolicy::process(Node* node, const string& filename,
const Options* opt)
{
TextureNameVisitor nameVisitor;
node->accept(nameVisitor);
return node;
}
//#define LOCAL_IMAGE_CACHE
#ifdef LOCAL_IMAGE_CACHE
typedef std::map<std::string, osg::ref_ptr<Image>> ImageMap;
ImageMap _imageMap;
//typedef std::map<std::string, osg::ref_ptr<osg::Image> > ImageMap;
//ImageMap _imageMap;
osg::Image* getImageByName(const std::string& filename)
{
ImageMap::iterator itr = _imageMap.find(filename);
if (itr != _imageMap.end()) return itr->second.get();
return nullptr;
}
#endif
// a cache which evicts the least recently used item when it is full
#include <map>
#include <list>
#include <utility>
#include <boost/optional.hpp>
template<class Key, class Value>
class lru_cache
{
public:
SGMutex _mutex;
typedef Key key_type;
typedef Value value_type;
typedef std::list<key_type> list_type;
typedef std::map<
key_type,
std::pair<value_type, typename list_type::iterator>
> map_type;
lru_cache(size_t capacity)
: m_capacity(capacity)
{
}
~lru_cache()
{
}
size_t size() const
{
return m_map.size();
}
size_t capacity() const
{
return m_capacity;
}
bool empty() const
{
return m_map.empty();
}
bool contains(const key_type &key)
{
SGGuard<SGMutex> scopeLock(_mutex);
return m_map.find(key) != m_map.end();
}
void insert(const key_type &key, const value_type &value)
{
SGGuard<SGMutex> scopeLock(_mutex);
typename map_type::iterator i = m_map.find(key);
if (i == m_map.end()) {
// insert item into the cache, but first check if it is full
if (size() >= m_capacity) {
// cache is full, evict the least recently used item
evict();
}
// insert the new item
m_list.push_front(key);
m_map[key] = std::make_pair(value, m_list.begin());
}
}
boost::optional<key_type> findValue(const std::string &requiredValue)
{
SGGuard<SGMutex> scopeLock(_mutex);
for (typename map_type::iterator it = m_map.begin(); it != m_map.end(); ++it)
if (it->second.first == requiredValue)
return it->first;
return boost::none;
}
boost::optional<value_type> get(const key_type &key)
{
SGGuard<SGMutex> scopeLock(_mutex);
// lookup value in the cache
typename map_type::iterator i = m_map.find(key);
if (i == m_map.end()) {
// value not in cache
return boost::none;
}
// return the value, but first update its place in the most
// recently used list
typename list_type::iterator j = i->second.second;
if (j != m_list.begin()) {
// move item to the front of the most recently used list
m_list.erase(j);
m_list.push_front(key);
// update iterator in map
j = m_list.begin();
const value_type &value = i->second.first;
m_map[key] = std::make_pair(value, j);
// return the value
return value;
}
else {
// the item is already at the front of the most recently
// used list so just return it
return i->second.first;
}
}
void clear()
{
SGGuard<SGMutex> scopeLock(_mutex);
m_map.clear();
m_list.clear();
}
private:
void evict()
{
SGGuard<SGMutex> scopeLock(_mutex);
// evict item from the end of most recently used list
typename list_type::iterator i = --m_list.end();
m_map.erase(*i);
m_list.erase(i);
}
private:
map_type m_map;
list_type m_list;
size_t m_capacity;
};
lru_cache < std::string, std::string> filename_hash_cache(100000);
lru_cache < std::string, bool> filesCleaned(100000);
static bool refreshCache = false;
ReaderWriter::ReadResult
ModelRegistry::readImage(const string& fileName,
const Options* opt)
const Options* opt)
{
/*
* processor is the interface to the osg_nvtt plugin
*/
static bool init = false;
static osgDB::ImageProcessor *processor = 0;
int max_texture_size = SGSceneFeatures::instance()->getMaxTextureSize();
if (!init) {
processor = osgDB::Registry::instance()->getImageProcessor();
init = true;
}
bool cache_active = SGSceneFeatures::instance()->getTextureCacheActive();
bool compress_solid = SGSceneFeatures::instance()->getTextureCacheCompressionActive();
bool compress_transparent = SGSceneFeatures::instance()->getTextureCacheCompressionActiveTransparent();
//
// heuristically less than 2048 is more likely to be a badly reported size rather than
// something that is valid so we'll have a minimum size of 2048.
if (max_texture_size < 2048)
max_texture_size = 2048;
std::string fileExtension = getFileExtension(fileName);
CallbackMap::iterator iter = imageCallbackMap.find(fileExtension);
if (iter != imageCallbackMap.end() && iter->second.valid())
return iter->second->readImage(fileName, opt);
string absFileName = SGModelLib::findDataFile(fileName, opt);
string originalFileName = absFileName;
if (!fileExists(absFileName)) {
SG_LOG(SG_IO, SG_ALERT, "Cannot find image file \""
<< fileName << "\"");
return ReaderWriter::ReadResult::FILE_NOT_FOUND;
}
Registry* registry = Registry::instance();
ReaderWriter::ReadResult res;
if (cache_active) {
if (fileExtension != "dds" && fileExtension != "gz") {
const SGReaderWriterOptions* sgoptC = dynamic_cast<const SGReaderWriterOptions*>(opt);
std::string root = getPathRoot(absFileName);
std::string prr = getPathRelative(root, absFileName);
std::string cache_root = SGSceneFeatures::instance()->getTextureCompressionPath().c_str();
std::string newName = cache_root + "/" + prr;
SGPath file(absFileName);
std::stringstream tstream;
// calucate and use hash for storing cached image. This also
// helps with sharing of identical images between models.
if (fileExists(absFileName)) {
SGFile f(absFileName);
std::string hash;
boost::optional<std::string> cachehash = filename_hash_cache.get(absFileName);
if (cachehash) {
hash = *cachehash;
// SG_LOG(SG_IO, SG_ALERT, "Hash for " + absFileName + " in cache " + hash);
}
else {
// SG_LOG(SG_IO, SG_ALERT, "Creating hash for " + absFileName);
try {
hash = f.computeHash();
}
catch (sg_io_exception &e) {
SG_LOG(SG_INPUT, SG_ALERT, "Modelregistry::failed to compute filehash '" << absFileName << "' " << e.getFormattedMessage());
hash = std::string();
}
}
if (hash != std::string()) {
filename_hash_cache.insert(absFileName, hash);
boost::optional<std::string> cacheFilename = filename_hash_cache.findValue(hash);
// possibly a shared texture - but warn the user to allow investigation.
if (cacheFilename && *cacheFilename != absFileName) {
SG_LOG(SG_IO, SG_ALERT, " Already have " + hash + " : " + *cacheFilename + " not " + absFileName);
}
// SG_LOG(SG_IO, SG_ALERT, " >>>> " + hash + " :: " + newName);
}
newName = cache_root + "/" + hash.substr(0, 2) + "/" + hash + ".cache.dds";
}
else
{
tstream << std::hex << file.modTime();
newName += "." + tstream.str();
newName += ".cache.dds";
}
bool doRefresh = refreshCache;
//if (fileExists(newName) && sgoptC && sgoptC->getLoadOriginHint() == SGReaderWriterOptions::LoadOriginHint::ORIGIN_EFFECTS) {
// doRefresh = true;
//
//}
if (newName != std::string() && fileExists(newName) && doRefresh) {
if (!filesCleaned.contains(newName)) {
SG_LOG(SG_IO, SG_ALERT, "Removing previously cached effects image " + newName);
SGPath(newName).remove();
filesCleaned.insert(newName, true);
}
}
if (newName != std::string() && !fileExists(newName)) {
res = registry->readImageImplementation(absFileName, opt);
if (res.validImage()) {
osg::ref_ptr<osg::Image> srcImage = res.getImage();
int width = srcImage->s();
bool transparent = srcImage->isImageTranslucent();
bool isNormalMap = false;
bool isEffect = false;
/*
* decide if we need to compress this.
*/
bool can_compress = (transparent && compress_transparent) || (!transparent && compress_solid);
int height = srcImage->t();
// use the new file origin to determine any special processing
// we handle the following
// - normal maps
// - images loaded from effects
if (sgoptC && transparent && sgoptC->getLoadOriginHint() == SGReaderWriterOptions::LoadOriginHint::ORIGIN_EFFECTS_NORMALIZED) {
isNormalMap = true;
}
else if (sgoptC && transparent && sgoptC->getLoadOriginHint() == SGReaderWriterOptions::LoadOriginHint::ORIGIN_EFFECTS) {
SG_LOG(SG_IO, SG_ALERT, "From effects transparent " + absFileName);
isEffect = true;
// can_compress = false;
}
else if (sgoptC && transparent && sgoptC->getLoadOriginHint() == SGReaderWriterOptions::LoadOriginHint::ORIGIN_EFFECTS) {
SG_LOG(SG_IO, SG_ALERT, "From effects " + absFileName);
isEffect = true;
}
if (can_compress)
{
std::string pot_message;
bool resize = false;
if (!isPowerOfTwo(width)) {
width = nearestPowerOfTwo(width);
resize = true;
pot_message += std::string(" not POT: resized width to ") + std::to_string(width);
}
if (!isPowerOfTwo(height)) {
height = nearestPowerOfTwo(height);
resize = true;
pot_message += std::string(" not POT: resized height to ") + std::to_string(height);
}
if (pot_message.size())
SG_LOG(SG_IO, SG_WARN, pot_message << " " << absFileName);
// unlikely that after resizing in height the width will still be outside of the max texture size.
if (height > max_texture_size)
{
SG_LOG(SG_IO, SG_WARN, "Image texture too high (max " << max_texture_size << ") " << width << "," << height << " " << absFileName);
int factor = height / max_texture_size;
height /= factor;
width /= factor;
resize = true;
}
if (width > max_texture_size)
{
SG_LOG(SG_IO, SG_WARN, "Image texture too wide (max " << max_texture_size << ") " << width << "," << height << " " << absFileName);
int factor = width / max_texture_size;
height /= factor;
width /= factor;
resize = true;
}
if (resize) {
osg::ref_ptr<osg::Image> resizedImage;
if (ImageUtils::resizeImage(srcImage, width, height, resizedImage))
srcImage = resizedImage;
}
//
// only cache power of two textures that are of a reasonable size
if (width >= 4 && height >= 4) {
simgear::effect::MipMapTuple mipmapFunctions(simgear::effect::AVERAGE, simgear::effect::AVERAGE, simgear::effect::AVERAGE, simgear::effect::AVERAGE);
SGPath filePath(newName);
filePath.create_dir();
// setup the options string for saving the texture as we don't want OSG to auto flip the texture
// as this complicates loading as it requires a flag to flip it back which will preclude the
// image from being cached because we will have to clone the options to set the flag and thus lose
// the link to the cache in the options from the caller.
osg::ref_ptr<Options> nopt;
nopt = opt->cloneOptions();
std::string optionstring = nopt->getOptionString();
if (!optionstring.empty())
optionstring += " ";
nopt->setOptionString(optionstring + "ddsNoAutoFlipWrite");
//GLenum srcImageType = srcImage->getDataType();
// printf("--- %-80s --> f=%8x t=%8x\n", newName.c_str(), srcImage->getPixelFormat(), srcImageType);
try
{
if (can_compress) {
osg::Texture::InternalFormatMode targetFormat = osg::Texture::USE_S3TC_DXT1_COMPRESSION;
if (isNormalMap) {
if (transparent) {
targetFormat = osg::Texture::USE_S3TC_DXT5_COMPRESSION;
}
else
targetFormat = osg::Texture::USE_S3TC_DXT5_COMPRESSION;
}
else if (isEffect)
{
if (transparent) {
targetFormat = osg::Texture::USE_S3TC_DXT5_COMPRESSION;
}
else
targetFormat = osg::Texture::USE_S3TC_DXT1_COMPRESSION;
}
else{
if (transparent) {
targetFormat = osg::Texture::USE_S3TC_DXT3_COMPRESSION;
}
else
targetFormat = osg::Texture::USE_S3TC_DXT1_COMPRESSION;
}
if (processor)
{
SG_LOG(SG_IO, SG_ALERT, "Creating " << targetFormat << " for " + absFileName);
// normal maps:
// nvdxt.exe - quality_highest - rescaleKaiser - Kaiser - dxt5nm - norm
processor->compress(*srcImage, targetFormat, true, true, osgDB::ImageProcessor::USE_CPU, osgDB::ImageProcessor::PRODUCTION);
SG_LOG(SG_IO, SG_ALERT, "-- finished creating DDS: " + newName);
//processor->generateMipMap(*srcImage, true, osgDB::ImageProcessor::USE_CPU);
}
else {
simgear::effect::MipMapTuple mipmapFunctions(simgear::effect::AVERAGE, simgear::effect::AVERAGE, simgear::effect::AVERAGE, simgear::effect::AVERAGE);
SG_LOG(SG_IO, SG_WARN, "Texture compression plugin (osg_nvtt) not available; storing uncompressed image: " << absFileName);
srcImage = simgear::effect::computeMipmap(srcImage, mipmapFunctions);
}
}
else {
SG_LOG(SG_IO, SG_ALERT, "Creating uncompressed DDS for " + absFileName);
if (processor) {
processor->generateMipMap(*srcImage, true, osgDB::ImageProcessor::USE_CPU);
}
else {
simgear::effect::MipMapTuple mipmapFunctions(simgear::effect::AVERAGE, simgear::effect::AVERAGE, simgear::effect::AVERAGE, simgear::effect::AVERAGE);
srcImage = simgear::effect::computeMipmap(srcImage, mipmapFunctions);
}
}
//}
//else
// printf("--- no compress or mipmap of format %s\n", newName.c_str());
registry->writeImage(*srcImage, newName, nopt);
{
std::string mdlDirectory = cache_root + "/cache-index.txt";
FILE *f = ::fopen(mdlDirectory.c_str(), "a");
if (f)
{
::fprintf(f, "%s, %s\n", absFileName.c_str(), newName.c_str());
::fclose(f);
}
}
absFileName = newName;
}
catch (...) {
SG_LOG(SG_IO, SG_ALERT, "Exception processing " << absFileName << " may be corrupted");
}
}
else
SG_LOG(SG_IO, SG_WARN, absFileName + " too small " << width << "," << height);
}
}
}
else {
if (newName != std::string())
absFileName = newName;
}
}
}
res = registry->readImageImplementation(absFileName, opt);
if (!res.success()) {
SG_LOG(SG_IO, SG_WARN, "Image loading failed:" << res.message());
return res;
}
osg::ref_ptr<osg::Image> srcImage1 = res.getImage();
//printf(" --> finished loading %s [%s] (%s) %d\n", absFileName.c_str(), srcImage1->getFileName().c_str(), res.loadedFromCache() ? "from cache" : "from disk", res.getImage()->getOrigin());
/*
* Fixup the filename - as when loading from eg. dds.gz the originating filename is lost in the conversion due to the way the OSG loader works
*/
if (srcImage1->getFileName().empty()) {
srcImage1->setFileName(absFileName);
}
srcImage1->setFileName(originalFileName);
if(cache_active && getFileExtension(absFileName) != "dds")
CallbackMap::iterator iter
= imageCallbackMap.find(getFileExtension(fileName));
{
if (processor) {
processor->generateMipMap(*srcImage1, true, osgDB::ImageProcessor::USE_CPU);
SG_LOG(SG_IO, SG_ALERT, "Created nvtt mipmaps DDS for " + absFileName);
if (iter != imageCallbackMap.end() && iter->second.valid())
return iter->second->readImage(fileName, opt);
string absFileName = SGModelLib::findDataFile(fileName, opt);
if (!fileExists(absFileName)) {
SG_LOG(SG_IO, SG_ALERT, "Cannot find image file \""
<< fileName << "\"");
return ReaderWriter::ReadResult::FILE_NOT_FOUND;
}
else {
simgear::effect::MipMapTuple mipmapFunctions(simgear::effect::AVERAGE, simgear::effect::AVERAGE, simgear::effect::AVERAGE, simgear::effect::AVERAGE);
srcImage1 = simgear::effect::computeMipmap(srcImage1, mipmapFunctions);
SG_LOG(SG_IO, SG_ALERT, "Created sg mipmaps DDS for " + absFileName);
Registry* registry = Registry::instance();
ReaderWriter::ReadResult res;
res = registry->readImageImplementation(absFileName, opt);
if (!res.success()) {
SG_LOG(SG_IO, SG_WARN, "Image loading failed:" << res.message());
return res;
}
}
if (res.loadedFromCache())
SG_LOG(SG_IO, SG_BULK, "Returning cached image \""
<< res.getImage()->getFileName() << "\"");
else
SG_LOG(SG_IO, SG_BULK, "Reading image \""
<< res.getImage()->getFileName() << "\"");
if (res.loadedFromCache())
SG_LOG(SG_IO, SG_BULK, "Returning cached image \""
<< res.getImage()->getFileName() << "\"");
else
SG_LOG(SG_IO, SG_BULK, "Reading image \""
<< res.getImage()->getFileName() << "\"");
// as of March 2018 all patents have expired, https://en.wikipedia.org/wiki/S3_Texture_Compression#Patent
// there is support for S3TC DXT1..5 in MESA https://www.phoronix.com/scan.php?page=news_item&px=S3TC-Lands-In-Mesa
// so it seems that there isn't a valid reason to warn any longer; and beside this is one of those cases where it should
// really only be a developer message
#ifdef WARN_DDS_TEXTURES
// Check for precompressed textures that depend on an extension
switch (res.getImage()->getPixelFormat()) {
switch (res.getImage()->getPixelFormat()) {
// GL_EXT_texture_compression_s3tc
// patented, no way to decompress these
// GL_EXT_texture_compression_s3tc
// patented, no way to decompress these
#ifndef GL_EXT_texture_compression_s3tc
#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0
#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1
#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2
#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
#endif
case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
// GL_EXT_texture_sRGB
// patented, no way to decompress these
// GL_EXT_texture_sRGB
// patented, no way to decompress these
#ifndef GL_EXT_texture_sRGB
#define GL_COMPRESSED_SRGB_S3TC_DXT1_EXT 0x8C4C
#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT 0x8C4D
#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT 0x8C4E
#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT 0x8C4F
#endif
case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
// GL_TDFX_texture_compression_FXT1
// can decompress these in software but
// no code present in simgear.
// GL_TDFX_texture_compression_FXT1
// can decompress these in software but
// no code present in simgear.
#ifndef GL_3DFX_texture_compression_FXT1
#define GL_COMPRESSED_RGB_FXT1_3DFX 0x86B0
#define GL_COMPRESSED_RGBA_FXT1_3DFX 0x86B1
#endif
case GL_COMPRESSED_RGB_FXT1_3DFX:
case GL_COMPRESSED_RGBA_FXT1_3DFX:
case GL_COMPRESSED_RGB_FXT1_3DFX:
case GL_COMPRESSED_RGBA_FXT1_3DFX:
// GL_EXT_texture_compression_rgtc
// can decompress these in software but
// no code present in simgear.
// GL_EXT_texture_compression_rgtc
// can decompress these in software but
// no code present in simgear.
#ifndef GL_EXT_texture_compression_rgtc
#define GL_COMPRESSED_RED_RGTC1_EXT 0x8DBB
#define GL_COMPRESSED_SIGNED_RED_RGTC1_EXT 0x8DBC
#define GL_COMPRESSED_RED_GREEN_RGTC2_EXT 0x8DBD
#define GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT 0x8DBE
#endif
case GL_COMPRESSED_RED_RGTC1_EXT:
case GL_COMPRESSED_SIGNED_RED_RGTC1_EXT:
case GL_COMPRESSED_RED_GREEN_RGTC2_EXT:
case GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT:
case GL_COMPRESSED_RED_RGTC1_EXT:
case GL_COMPRESSED_SIGNED_RED_RGTC1_EXT:
case GL_COMPRESSED_RED_GREEN_RGTC2_EXT:
case GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT:
SG_LOG(SG_IO, SG_WARN, "Image \"" << fileName << "\"\n"
"uses compressed textures which cannot be supported on "
"some systems.\n"
"Please decompress this texture for improved portability.");
break;
SG_LOG(SG_IO, SG_WARN, "Image \"" << fileName << "\"\n"
"uses compressed textures which cannot be supported on "
"some systems.\n"
"Please decompress this texture for improved portability.");
break;
default:
break;
default:
break;
}
return res;
}
#endif
return res;
}
@@ -914,7 +432,7 @@ public:
setObjectCacheHint((Options::CacheHintOptions)cacheOptions);
registry->setOptions(options);
registry->getOrCreateSharedStateManager()->
setShareMode(SharedStateManager::SHARE_ALL);
setShareMode(SharedStateManager::SHARE_STATESETS);
registry->setReadFileCallback(ModelRegistry::instance());
}
};
@@ -1004,79 +522,9 @@ typedef ModelRegistryCallback<OBJProcessPolicy,
OSGSubstitutePolicy, BuildLeafBVHPolicy>
OBJCallback;
// we get optimal geometry from the loader (Hah!).
struct IVEOptimizePolicy : public OptimizeModelPolicy {
IVEOptimizePolicy(const string& extension) :
OptimizeModelPolicy(extension)
{
_osgOptions &= ~Optimizer::TRISTRIP_GEOMETRY;
}
Node* optimize(Node* node, const string& fileName,
const Options* opt)
{
ref_ptr<Node> optimized
= OptimizeModelPolicy::optimize(node, fileName, opt);
Group* group = dynamic_cast<Group*>(optimized.get());
MatrixTransform* transform
= dynamic_cast<MatrixTransform*>(optimized.get());
if (((transform && transform->getMatrix().isIdentity()) || group)
&& group->getName().empty()
&& group->getNumChildren() == 1) {
optimized = static_cast<Node*>(group->getChild(0));
group = dynamic_cast<Group*>(optimized.get());
if (group && group->getName().empty()
&& group->getNumChildren() == 1)
optimized = static_cast<Node*>(group->getChild(0));
}
const SGReaderWriterOptions* sgopt
= dynamic_cast<const SGReaderWriterOptions*>(opt);
if (sgopt && sgopt->getInstantiateMaterialEffects()) {
optimized = instantiateMaterialEffects(optimized.get(), sgopt);
}
else if (sgopt && sgopt->getInstantiateEffects()) {
optimized = instantiateEffects(optimized.get(), sgopt);
}
return optimized.release();
}
};
struct IVEProcessPolicy {
IVEProcessPolicy(const string& extension) {}
Node* process(Node* node, const string& filename,
const Options* opt)
{
Matrix m(1, 0, 0, 0,
0, 0, 1, 0,
0, -1, 0, 0,
0, 0, 0, 1);
// XXX Does there need to be a Group node here to trick the
// optimizer into optimizing the static transform?
osg::Group* root = new Group;
MatrixTransform* transform = new MatrixTransform;
root->addChild(transform);
transform->setDataVariance(Object::STATIC);
transform->setMatrix(m);
transform->addChild(node);
return root;
}
};
typedef ModelRegistryCallback<IVEProcessPolicy, DefaultCachePolicy,
IVEOptimizePolicy,
OSGSubstitutePolicy, BuildLeafBVHPolicy>
IVECallback;
namespace
{
ModelRegistryCallbackProxy<ACCallback> g_acRegister("ac");
ModelRegistryCallbackProxy<OBJCallback> g_objRegister("obj");
ModelRegistryCallbackProxy<IVECallback> g_iveRegister("ive");
ModelRegistryCallbackProxy<IVECallback> g_osgtRegister("osgt");
ModelRegistryCallbackProxy<IVECallback> g_osgbRegister("osgb");
}

View File

@@ -27,7 +27,6 @@
#include <boost/bind.hpp>
#include <osg/Version>
#include <osg/Geode>
#include <osg/MatrixTransform>
#include <osgDB/ReadFile>
@@ -57,7 +56,7 @@ using namespace std;
using namespace simgear;
using namespace osg;
static std::tuple<int, osg::Node *>
static osg::Node *
sgLoad3DModel_internal(const SGPath& path,
const osgDB::Options* options,
SGPropertyNode *overlay = 0);
@@ -89,10 +88,8 @@ SGReaderWriterXML::readNode(const std::string& name,
if (!p.exists()) {
return ReadResult::FILE_NOT_FOUND;
}
static std::tuple<int, osg::Node *> retval;
int num_anims;
std::tie(num_anims, result) = sgLoad3DModel_internal(p, options);
result=sgLoad3DModel_internal(p, options);
} catch (const sg_exception &t) {
SG_LOG(SG_INPUT, SG_ALERT, "Failed to load model: " << t.getFormattedMessage()
<< "\n\tfrom:" << fileName);
@@ -163,13 +160,13 @@ void makeEffectAnimations(PropertyList& animation_nodes,
SGPropertyNode* typeProp = animProp->getChild("type");
if (!typeProp)
continue;
const char* typeString = typeProp->getStringValue();
if (!strcmp(typeString, "material")) {
effectProp
= SGMaterialAnimation::makeEffectProperties(animProp);
} else if (!strcmp(typeString, "shader")) {
SGPropertyNode* shaderProp = animProp->getChild("shader");
if (!shaderProp || strcmp(shaderProp->getStringValue(), "chrome"))
continue;
@@ -218,7 +215,7 @@ private:
osg::Node::NodeMask nodeMaskSet;
osg::Node::NodeMask nodeMaskClear;
};
}
namespace {
@@ -232,7 +229,7 @@ namespace {
return (typeString == "pick") || (typeString == "knob") || (typeString == "slider") || (typeString == "touch");
}
};
bool removeNamedNode(osg::Group* aGroup, const std::string& aName)
{
int nKids = aGroup->getNumChildren();
@@ -243,28 +240,28 @@ namespace {
return true;
}
}
for (int i = 0; i < nKids; i++) {
osg::Group* childGroup = aGroup->getChild(i)->asGroup();
if (!childGroup)
continue;
if (removeNamedNode(childGroup, aName))
return true;
} // of child groups traversal
return false;
}
}
static std::tuple<int, osg::Node *>
static osg::Node *
sgLoad3DModel_internal(const SGPath& path,
const osgDB::Options* dbOptions,
SGPropertyNode *overlay)
{
if (!path.exists()) {
SG_LOG(SG_INPUT, SG_ALERT, "Failed to load file: \"" << path << "\"");
return std::make_tuple(0, (osg::Node *) NULL);
return NULL;
}
osg::ref_ptr<SGReaderWriterOptions> options;
@@ -273,20 +270,19 @@ sgLoad3DModel_internal(const SGPath& path,
SGPath modelpath(path);
SGPath texturepath(path);
SGPath modelDir(modelpath.dir());
int animationcount = 0;
SGSharedPtr<SGPropertyNode> prop_root = options->getPropertyNode();
if (!prop_root.valid())
prop_root = new SGPropertyNode;
// The model data appear to be only used in the topmost model
osg::ref_ptr<SGModelData> data = options->getModelData();
options->setModelData(0);
osg::ref_ptr<osg::Node> model;
osg::ref_ptr<osg::Group> group;
SGPropertyNode_ptr props = new SGPropertyNode;
bool previewMode = (dbOptions->getPluginStringData("SimGear::PREVIEW") == "ON");
// Check for an XML wrapper
if (modelpath.extension() == "xml") {
try {
@@ -300,9 +296,9 @@ sgLoad3DModel_internal(const SGPath& path,
copyProperties(overlay, props);
if (previewMode && props->hasChild("nopreview")) {
return std::make_tuple(0, (osg::Node *) NULL);
return NULL;
}
if (props->hasValue("/path")) {
string modelPathStr = props->getStringValue("/path");
modelpath = SGModelLib::findDataFile(modelPathStr, NULL, modelDir);
@@ -339,11 +335,7 @@ sgLoad3DModel_internal(const SGPath& path,
options->setDatabasePath(texturepath.local8BitStr());
osgDB::ReaderWriter::ReadResult modelResult;
#if OSG_VERSION_LESS_THAN(3,4,1)
modelResult = osgDB::readNodeFile(modelpath.local8BitStr(), options.get());
#else
modelResult = osgDB::readRefNodeFile(modelpath.local8BitStr(), options.get());
#endif
if (!modelResult.validNode())
throw sg_io_exception("Failed to load 3D model:" + modelResult.message(),
modelpath);
@@ -407,9 +399,9 @@ sgLoad3DModel_internal(const SGPath& path,
SGPath submodelpath;
osg::ref_ptr<osg::Node> submodel;
string subPathStr = sub_props->getStringValue("path");
SGPath submodelPath = SGModelLib::findDataFile(subPathStr,
SGPath submodelPath = SGModelLib::findDataFile(subPathStr,
NULL, modelDir);
if (submodelPath.isNull()) {
@@ -427,16 +419,14 @@ sgLoad3DModel_internal(const SGPath& path,
}
try {
int num_anims;
std::tie(num_anims, submodel) = sgLoad3DModel_internal(submodelPath, options.get(),
submodel = sgLoad3DModel_internal(submodelPath, options.get(),
sub_props->getNode("overlay"));
animationcount += num_anims;
} catch (const sg_exception &t) {
SG_LOG(SG_INPUT, SG_ALERT, "Failed to load submodel: " << t.getFormattedMessage()
<< "\n\tfrom:" << t.getOrigin());
continue;
}
if (!submodel)
continue;
@@ -501,7 +491,7 @@ sgLoad3DModel_internal(const SGPath& path,
if (i==0) {
if (!texturepath.extension().empty())
texturepath = texturepath.dir();
options2->setDatabasePath(texturepath.local8BitStr());
}
group->addChild(Particles::appendParticles(particle_nodes[i],
@@ -520,13 +510,13 @@ sgLoad3DModel_internal(const SGPath& path,
PropertyList effect_nodes = props->getChildren("effect");
PropertyList animation_nodes = props->getChildren("animation");
if (previewMode) {
PropertyList::iterator it;
it = std::remove_if(animation_nodes.begin(), animation_nodes.end(), ExcludeInPreview());
animation_nodes.erase(it, animation_nodes.end());
}
// Some material animations (eventually all) are actually effects.
makeEffectAnimations(animation_nodes, effect_nodes);
{
@@ -553,15 +543,13 @@ sgLoad3DModel_internal(const SGPath& path,
/// OSGFIXME: duh, why not only model?????
SGAnimation::animate(modelData);
}
animationcount += animation_nodes.size();
if (!needTransform && group->getNumChildren() < 2) {
model = group->getChild(0);
group->removeChild(model.get());
if (data.valid())
data->modelLoaded(modelpath.utf8Str(), props, model.get());
return std::make_tuple(animationcount, model.release());
return model.release();
}
if (data.valid())
data->modelLoaded(modelpath.utf8Str(), props, group.get());
@@ -571,7 +559,6 @@ sgLoad3DModel_internal(const SGPath& path,
osgDB::writeNodeFile(*group, outputfile);
}
SG_LOG(SG_GENERAL, SG_DEBUG, "Model " << path << " animation count: " << animationcount);
return std::make_tuple(animationcount, group.release());
return group.release();
}

View File

@@ -41,24 +41,13 @@ SGLoadTexture2D(bool staticTexture, const std::string& path,
const osgDB::Options* options,
bool wrapu, bool wrapv, int)
{
osg::ref_ptr<osg::Image> image;
osg::Image* image;
if (options)
#if OSG_VERSION_LESS_THAN(3,4,0)
image = osgDB::readImageFile(path, options);
#else
image = osgDB::readRefImageFile(path, options);
#endif
image = osgDB::readRefImageFile(path, options);
else
#if OSG_VERSION_LESS_THAN(3,4,0)
image = osgDB::readImageFile(path);
#else
image = osgDB::readRefImageFile(path);
#endif
image = osgDB::readRefImageFile(path);
osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D;
texture->setImage(image);
texture->setMaxAnisotropy(SGSceneFeatures::instance()->getTextureFilter());
if (staticTexture)
texture->setDataVariance(osg::Object::STATIC);
if (wrapu)
@@ -139,45 +128,30 @@ Texture2D* TextureUpdateVisitor::textureReplace(int unit, const StateAttribute*
if (image) {
// The currently loaded file name
fullFilePath = &image->getFileName();
} else {
fullFilePath = &texture->getName();
}
// The short name
string fileName = getSimpleFileName(*fullFilePath);
if (fileName.empty())
return 0;
// The name that should be found with the current database path
string fullLiveryFile = findFileInPath(fileName, _pathList);
// If it is empty or they are identical then there is nothing to do
if (fullLiveryFile.empty() || fullLiveryFile == *fullFilePath)
return 0;
#if OSG_VERSION_LESS_THAN(3,4,0)
Image* newImage = readImageFile(fullLiveryFile);
#else
osg::ref_ptr<Image> newImage = readRefImageFile(fullLiveryFile);
#endif
Image* newImage = readRefImageFile(fullLiveryFile);
if (!newImage)
return 0;
CopyOp copyOp(CopyOp::DEEP_COPY_ALL & ~CopyOp::DEEP_COPY_IMAGES);
Texture2D* newTexture = static_cast<Texture2D*>(copyOp(texture));
if (!newTexture)
if (!newTexture) {
return 0;
newTexture->setImage(newImage);
#if OSG_VERSION_LESS_THAN(3,4,0)
if (newImage->valid())
#else
if (newImage.valid())
#endif
{
newTexture->setMaxAnisotropy(SGSceneFeatures::instance()->getTextureFilter());
} else {
newTexture->setImage(newImage);
return newTexture;
}
return newTexture;
}
StateSet* TextureUpdateVisitor::cloneStateSet(const StateSet* stateSet)

View File

@@ -21,7 +21,6 @@
#include <boost/algorithm/string.hpp>
#include <osg/Version>
#include <osg/PagedLOD>
#include <osg/ProxyNode>
#include <osgDB/ReadFile>
@@ -102,11 +101,7 @@ osg::Node* loadFile(const string& path, SGReaderWriterOptions* options)
options->setInstantiateEffects(true);
}
#if OSG_VERSION_LESS_THAN(3,4,0)
ref_ptr<Node> model = readNodeFile(path, options);
#else
ref_ptr<Node> model = readRefNodeFile(path, options);
#endif
if (!model)
return 0;
else

View File

@@ -1,4 +1,4 @@
// a layer of 3d clouds
// a layer of 3d clouds
//
// Written by Harald JOHNSEN, started April 2005.
//
@@ -78,57 +78,56 @@ bool SGCloudField::reposition( const SGVec3f& p, const SGVec3f& up, double lon,
SGVec3<double> cart;
SGGeod new_pos = SGGeod::fromRadFt(lon, lat, 0.0f);
SGGeodesy::SGGeodToCart(new_pos, cart);
osg::Vec3f osg_pos = toOsg(cart);
osg::Quat orient = toOsg(SGQuatd::fromLonLatRad(lon, lat) * SGQuatd::fromRealImag(0, SGVec3d(0, 1, 0)));
// Always update the altitude transform, as this allows
// the clouds to rise and fall smoothly depending on environment updates.
altitude_transform->setPosition(osg::Vec3f(0.0f, 0.0f, (float) asl));
// Similarly, always determine the effects of the wind
osg::Vec3f wind = osg::Vec3f(-cos((direction + 180)* SGD_DEGREES_TO_RADIANS) * speed * dt,
sin((direction + 180)* SGD_DEGREES_TO_RADIANS) * speed * dt,
0.0f);
osg::Vec3f windosg = field_transform->getAttitude() * wind;
field_transform->setPosition(field_transform->getPosition() + windosg);
if (!wrap) {
// If we're not wrapping the cloudfield, then we make no effort to reposition
return false;
}
if ((old_pos - osg_pos).length() > fieldSize*2) {
// Big movement - reposition centered to current location.
field_transform->setPosition(osg_pos);
field_transform->setAttitude(orient);
old_pos = osg_pos;
} else if ((old_pos - osg_pos).length() > fieldSize*0.1) {
// Smaller, but non-trivial movement - check if any clouds need to be moved
} else {
osg::Quat fta = field_transform->getAttitude();
osg::Quat ftainv = field_transform->getAttitude().inverse();
// delta is the vector from the old position to the new position in cloud-coords
// osg::Vec3f delta = ftainv * (osg_pos - old_pos);
old_pos = osg_pos;
// Check if any of the clouds should be moved.
for(CloudHash::const_iterator itr = cloud_hash.begin(), end = cloud_hash.end();
itr != end;
++itr) {
osg::ref_ptr<osg::PositionAttitudeTransform> pat = itr->second;
if (pat == 0) {
continue;
}
osg::Vec3f currpos = field_transform->getPosition() + fta * pat->getPosition();
// Determine the vector from the new position to the cloud in cloud-space.
osg::Vec3f w = ftainv * (currpos - toOsg(cart));
osg::Vec3f w = ftainv * (currpos - toOsg(cart));
// Determine a course if required. Note that this involves some axis translation.
float x = 0.0;
float y = 0.0;
@@ -136,16 +135,16 @@ bool SGCloudField::reposition( const SGVec3f& p, const SGVec3f& up, double lon,
if (w.x() < -0.6*fieldSize) { y = -fieldSize; }
if (w.y() > 0.6*fieldSize) { x = -fieldSize; }
if (w.y() < -0.6*fieldSize) { x = fieldSize; }
if ((x != 0.0) || (y != 0.0)) {
removeCloudFromTree(pat);
SGGeod p = SGGeod::fromCart(toSG(field_transform->getPosition() +
SGGeod p = SGGeod::fromCart(toSG(field_transform->getPosition() +
fta * pat->getPosition()));
addCloudToTree(pat, p, x, y);
addCloudToTree(pat, p, x, y);
}
}
}
}
// Render the clouds in order from farthest away layer to nearest one.
field_root->getStateSet()->setRenderBinDetails(CLOUDS_BIN, "DepthSortedBin");
return true;
@@ -162,7 +161,7 @@ SGCloudField::SGCloudField() :
osg::StateSet *rootSet = field_root->getOrCreateStateSet();
rootSet->setRenderBinDetails(CLOUDS_BIN, "DepthSortedBin");
rootSet->setAttributeAndModes(getFog());
field_transform->addChild(altitude_transform.get());
placed_root = new osg::Group();
altitude_transform->addChild(placed_root);
@@ -170,7 +169,7 @@ SGCloudField::SGCloudField() :
lodcount = 0;
cloudcount = 0;
}
SGCloudField::~SGCloudField() {
}
@@ -182,7 +181,7 @@ void SGCloudField::clear(void) {
++itr) {
removeCloudFromTree(itr->second);
}
cloud_hash.clear();
}
@@ -199,7 +198,7 @@ void SGCloudField::applyVisAndLoDRange(void)
}
}
}
bool SGCloudField::addCloud(float lon, float lat, float alt, int index, osg::ref_ptr<EffectGeode> geode) {
return addCloud(lon, lat, alt, 0.0f, 0.0f, index, geode);
}
@@ -245,34 +244,34 @@ void SGCloudField::removeCloudFromTree(osg::ref_ptr<osg::PositionAttitudeTransfo
void SGCloudField::addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> transform,
float lon, float lat, float alt, float x, float y) {
// Get the base position
SGGeod loc = SGGeod::fromDegFt(lon, lat, alt);
addCloudToTree(transform, loc, x, y);
addCloudToTree(transform, loc, x, y);
}
void SGCloudField::addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> transform,
SGGeod loc, float x, float y) {
float alt = loc.getElevationFt();
// Determine any shift by x/y
if ((x != 0.0f) || (y != 0.0f)) {
double crs = 90.0 - SG_RADIANS_TO_DEGREES * atan2(y, x);
double crs = 90.0 - SG_RADIANS_TO_DEGREES * atan2(y, x);
double dst = sqrt(x*x + y*y);
double endcrs;
SGGeod base_pos = SGGeod::fromGeodFt(loc, 0.0f);
SGGeod base_pos = SGGeod::fromGeodFt(loc, 0.0f);
SGGeodesy::direct(base_pos, crs, dst, loc, endcrs);
}
// The direct call provides the position at 0 alt, so adjust as required.
loc.setElevationFt(alt);
addCloudToTree(transform, loc);
// The direct call provides the position at 0 alt, so adjust as required.
loc.setElevationFt(alt);
addCloudToTree(transform, loc);
}
void SGCloudField::addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> transform, SGGeod loc) {
void SGCloudField::addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> transform, SGGeod loc) {
// Work out where this cloud should go in OSG coordinates.
SGVec3<double> cart;
SGGeodesy::SGGeodToCart(loc, cart);
@@ -286,17 +285,17 @@ void SGCloudField::addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> t
// Convert to the scenegraph orientation where we just rotate around
// the y axis 180 degrees.
osg::Quat orient = toOsg(SGQuatd::fromLonLatDeg(loc.getLongitudeDeg(), loc.getLatitudeDeg()) * SGQuatd::fromRealImag(0, SGVec3d(0, 1, 0)));
osg::Vec3f osg_pos = toOsg(fieldcenter);
osg::Vec3f osg_pos = toOsg(fieldcenter);
field_transform->setPosition(osg_pos);
field_transform->setAttitude(orient);
old_pos = osg_pos;
}
// Convert position to cloud-coordinates
pos = pos - field_transform->getPosition();
pos = field_transform->getAttitude().inverse() * pos;
pos = field_transform->getAttitude().inverse() * pos;
// We have a two level dynamic quad tree which the cloud will be added
// to. If there are no appropriate nodes in the quad tree, they are
@@ -311,7 +310,7 @@ void SGCloudField::addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> t
if ((lodnode1->getCenter() - pos).length2() < lod1_range*lod1_range) {
// New cloud is within RADIUS_LEVEL_1 of the center of the LOD node.
found = true;
}
}
}
if (!found) {
@@ -319,7 +318,7 @@ void SGCloudField::addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> t
impostornode = new osgSim::Impostor();
impostornode->setImpostorThreshold(impostor_distance);
//impostornode->setImpostorThresholdToBound();
//impostornode->setCenter(pos);
//impostornode->setCenter(pos);
placed_root->addChild(impostornode.get());
lodnode1 = (osg::ref_ptr<osg::LOD>) impostornode;
} else {
@@ -331,8 +330,8 @@ void SGCloudField::addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> t
// Now check if there is a second level LOD node at an appropriate distance
found = false;
for (unsigned int j = 0; (!found) && (j < lodnode1->getNumChildren()); j++) {
for (unsigned int j = 0; (!found) && (j < lodnode1->getNumChildren()); j++) {
lodnode = (osg::LOD*) lodnode1->getChild(j);
if ((lodnode->getCenter() - pos).length2() < lod2_range*lod2_range) {
// We've found the right leaf LOD node
@@ -345,37 +344,37 @@ void SGCloudField::addCloudToTree(osg::ref_ptr<osg::PositionAttitudeTransform> t
lodnode = new osg::LOD();
lodnode1->addChild(lodnode, 0.0f, lod1_range + lod2_range + view_distance + MAX_CLOUD_DEPTH);
lodcount++;
}
}
transform->setPosition(pos);
lodnode->addChild(transform.get(), 0.0f, view_distance + MAX_CLOUD_DEPTH);
cloudcount++;
SG_LOG(SG_ENVIRONMENT, SG_DEBUG, "Impostors: " << impostorcount <<
" LoD: " << lodcount <<
" LoD: " << lodcount <<
" Clouds: " << cloudcount);
lodnode->dirtyBound();
lodnode1->dirtyBound();
field_root->dirtyBound();
}
bool SGCloudField::deleteCloud(int identifier) {
osg::ref_ptr<osg::PositionAttitudeTransform> transform = cloud_hash[identifier];
if (transform == 0) return false;
removeCloudFromTree(transform);
cloud_hash.erase(identifier);
return true;
}
bool SGCloudField::repositionCloud(int identifier, float lon, float lat, float alt) {
return repositionCloud(identifier, lon, lat, alt, 0.0f, 0.0f);
}
bool SGCloudField::repositionCloud(int identifier, float lon, float lat, float alt, float x, float y) {
osg::ref_ptr<osg::PositionAttitudeTransform> transform = cloud_hash[identifier];
if (transform == NULL) return false;
removeCloudFromTree(transform);
@@ -399,3 +398,4 @@ void SGCloudField::updateFog(double visibility, const osg::Vec4f& color) {
fog->setColor(color);
fog->setDensity(sqrt_m_log01 / visibility);
}

View File

@@ -94,7 +94,7 @@ void SGSky::build( double h_radius_m,
planets = new SGStars;
_ephTransform->addChild( planets->build(eph.getNumPlanets(), eph.getPlanets(), h_radius_m) );
stars = new SGStars(property_tree_node);
stars = new SGStars;
_ephTransform->addChild( stars->build(eph.getNumStars(), eph.getStars(), h_radius_m) );
moon = new SGMoon;

View File

@@ -30,11 +30,9 @@
#include <simgear/compiler.h>
#include <simgear/constants.h>
#include <simgear/debug/logstream.hxx>
#include <simgear/props/props.hxx>
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <osg/AlphaFunc>
#include <osg/BlendFunc>
@@ -49,14 +47,9 @@
#include "stars.hxx"
// Constructor
SGStars::SGStars( SGPropertyNode* props ) :
old_phase(-1)
SGStars::SGStars( void ) :
old_phase(-1)
{
if (props) {
// don't create here - if it's not defined, we won't use the cutoff
// from a property
_cutoffProperty = props->getNode("star-magnitude-cutoff");
}
}
@@ -124,102 +117,69 @@ SGStars::build( int num, const SGVec3d star_data[], double star_dist ) {
// 0 degrees = high noon
// 90 degrees = sun rise/set
// 180 degrees = darkest midnight
bool SGStars::repaint( double sun_angle, int num, const SGVec3d star_data[] )
{
bool SGStars::repaint( double sun_angle, int num, const SGVec3d star_data[] ) {
// cout << "repainting stars" << endl;
// double min = 100;
// double max = -100;
double mag, nmag, alpha, factor, cutoff;
/*
maximal magnitudes under dark sky on Earth, from Eq.(90) and (91) of astro-ph/1405.4209
For (18 < musky < 20)
mmax = 0.27 musky + 0.8 - 2.5 * log(F)
For (19.5 µsky 22)
mmax = 0.383 musky - 1.44 - 2.5 * log(F)
Let's take F = 1.4 for healthy young pilot
mudarksky ~ 22 mag/arcsec^2 => mmax=6.2
muastrotwilight ~ 20 mag/arsec^2 => mmax=5.4
mu99deg ~ 17.5 mag/arcsec^2 => mmax=4.7
mu97.5deg ~ 16 mag/arcsec^2 => ? let's keep it rough
*/
double mag_nakedeye = 6.2;
double mag_twilight_astro = 5.4;
double mag_twilight_nautic = 4.7;
// sirius, brightest star (not brightest object)
double mag_min = -1.46;
int phase;
// determine which star structure to draw
if ( sun_angle > (SGD_PI_2 + 18.0 * SGD_DEGREES_TO_RADIANS ) ) {
// deep night, atmosphere is not lighten by the sun
if ( sun_angle > (SGD_PI_2 + 10.0 * SGD_DEGREES_TO_RADIANS ) ) {
// deep night
factor = 1.0;
cutoff = mag_nakedeye;
cutoff = 4.5;
phase = 0;
} else if ( sun_angle > (SGD_PI_2 + 12.0 * SGD_DEGREES_TO_RADIANS ) ) {
// less than 18deg and more than 12deg is astronomical twilight
} else if ( sun_angle > (SGD_PI_2 + 8.8 * SGD_DEGREES_TO_RADIANS ) ) {
factor = 1.0;
cutoff = mag_twilight_astro;
cutoff = 3.8;
phase = 1;
} else if ( sun_angle > (SGD_PI_2 + 9.0 * SGD_DEGREES_TO_RADIANS ) ) {
// less 12deg and more than 6deg is is nautical twilight
factor = 1.0;
cutoff = mag_twilight_nautic;
phase = 2;
} else if ( sun_angle > (SGD_PI_2 + 7.5 * SGD_DEGREES_TO_RADIANS ) ) {
factor = 0.95;
cutoff = 3.1;
phase = 3;
phase = 2;
} else if ( sun_angle > (SGD_PI_2 + 7.0 * SGD_DEGREES_TO_RADIANS ) ) {
factor = 0.9;
cutoff = 2.4;
phase = 4;
phase = 3;
} else if ( sun_angle > (SGD_PI_2 + 6.5 * SGD_DEGREES_TO_RADIANS ) ) {
factor = 0.85;
cutoff = 1.8;
phase = 5;
phase = 4;
} else if ( sun_angle > (SGD_PI_2 + 6.0 * SGD_DEGREES_TO_RADIANS ) ) {
factor = 0.8;
cutoff = 1.2;
phase = 6;
phase = 5;
} else if ( sun_angle > (SGD_PI_2 + 5.5 * SGD_DEGREES_TO_RADIANS ) ) {
factor = 0.75;
cutoff = 0.6;
phase = 7;
phase = 6;
} else {
// early dusk or late dawn
factor = 0.7;
cutoff = 0.0;
phase = 8;
phase = 7;
}
if (_cutoffProperty) {
double propCutoff = _cutoffProperty->getDoubleValue();
cutoff = std::min(propCutoff, cutoff);
}
if ((phase != old_phase) || (cutoff != _cachedCutoff)) {
if( phase != old_phase ) {
// cout << " phase change, repainting stars, num = " << num << endl;
old_phase = phase;
_cachedCutoff = cutoff;
for ( int i = 0; i < num; ++i ) {
// if ( star_data[i][2] < min ) { min = star_data[i][2]; }
// if ( star_data[i][2] > max ) { max = star_data[i][2]; }
// magnitude ranges from -1 (bright) to 6 (dim). The
// magnitude ranges from -1 (bright) to 4 (dim). The
// range of star and planet magnitudes can actually go
// outside of this, but for our purpose, if it is brighter
// that magmin, we'll color it full white/alpha anyway
// that -1, we'll color it full white/alpha anyway and 4
// is a convenient cutoff point which keeps the number of
// stars drawn at about 500.
// color (magnitude)
mag = star_data[i][2];
if ( mag < cutoff ) {
nmag = ( cutoff - mag ) / (cutoff - mag_min); // translate to 0 ... 1.0 scale
nmag = ( 4.5 - mag ) / 5.5; // translate to 0 ... 1.0 scale
alpha = nmag * 0.85 + 0.15; // translate to a 0.15 ... 1.0 scale
alpha *= factor; // dim when the sun is brighter
} else {
@@ -230,8 +190,11 @@ bool SGStars::repaint( double sun_angle, int num, const SGVec3d star_data[] )
if (alpha < 0.0) { alpha = 0.0; }
(*cl)[i] = osg::Vec4(1, 1, 1, alpha);
// cout << "alpha[" << i << "] = " << alpha << endl;
}
cl->dirty();
} else {
// cout << " no phase change, skipping" << endl;
}
// cout << "min = " << min << " max = " << max << " count = " << num

View File

@@ -33,20 +33,18 @@
#include <simgear/math/SGMath.hxx>
#include <simgear/structure/SGReferenced.hxx>
#include <simgear/props/propsfwd.hxx>
class SGStars : public SGReferenced {
osg::ref_ptr<osg::Vec4Array> cl;
int old_phase; // data for optimization
double _cachedCutoff = 0.0;
SGPropertyNode_ptr _cutoffProperty;
public:
// Constructor
SGStars( SGPropertyNode* props = nullptr);
SGStars( void );
// Destructor
~SGStars( void );

View File

@@ -25,7 +25,6 @@
#include <cassert>
#include <osg/Version>
#include <osg/CullFace>
#include <osg/PagedLOD>
#include <osg/MatrixTransform>
@@ -209,11 +208,7 @@ ReaderWriterSPT::readObject(const std::string& fileName, const osgDB::Options* o
imageFileName = osgDB::concatPaths(imageFileName, "Globe");
imageFileName = osgDB::concatPaths(imageFileName, "world.topo.bathy.200407.3x4096x2048.png");
}
#if OSG_VERSION_LESS_THAN(3,4,0)
if (osg::Image* image = osgDB::readImageFile(imageFileName, options)) {
#else
if (osg::Image* image = osgDB::readRefImageFile(imageFileName, options)) {
#endif
osg::Texture2D* texture = new osg::Texture2D;
texture->setImage(image);
texture->setWrap(osg::Texture2D::WRAP_S, osg::Texture2D::REPEAT);
@@ -261,11 +256,7 @@ ReaderWriterSPT::createTree(const BucketBox& bucketBox, const LocalOptions& opti
if (bucketBox.getIsBucketSize()) {
std::string fileName;
fileName = bucketBox.getBucket().gen_index_str() + std::string(".stg");
#if OSG_VERSION_LESS_THAN(3,4,0)
return osgDB::readNodeFile(fileName, options._options);
#else
return osgDB::readRefNodeFile(fileName, options._options);
#endif
} else if (!topLevel && options.isPageLevel(bucketBox.getStartLevel())) {
return createPagedLOD(bucketBox, options);
} else {
@@ -323,11 +314,7 @@ ReaderWriterSPT::createPagedLOD(const BucketBox& bucketBox, const LocalOptions&
std::string fileName = osgDB::findDataFile(lodPath + extensions[i], options._options);
if (fileName.empty())
continue;
#if OSG_VERSION_LESS_THAN(3,4,0)
osg::ref_ptr<osg::Node> node = osgDB::readNodeFile(fileName, options._options);
#else
osg::ref_ptr<osg::Node> node = osgDB::readRefNodeFile(fileName, options._options);
#endif
if (!node.valid())
continue;
pagedLOD->addChild(node.get(), range, std::numeric_limits<float>::max());
@@ -425,11 +412,7 @@ ReaderWriterSPT::getLowLODStateSet(const LocalOptions& options) const
localOptions = static_cast<osgDB::Options*>(options._options->clone(osg::CopyOp()));
localOptions->setObjectCacheHint(osgDB::Options::CACHE_ALL);
#if OSG_VERSION_LESS_THAN(3,4,0)
osg::ref_ptr<osg::Object> object = osgDB::readObjectFile("state.spt", localOptions.get());
#else
osg::ref_ptr<osg::Object> object = osgDB::readRefObjectFile("state.spt", localOptions.get());
#endif
if (!dynamic_cast<osg::StateSet*>(object.get()))
return 0;

View File

@@ -50,8 +50,6 @@
#include <simgear/scene/util/SGReaderWriterOptions.hxx>
#include <simgear/scene/tgdb/apt_signs.hxx>
#include <simgear/scene/tgdb/obj.hxx>
#include <simgear/scene/material/matlib.hxx>
#include <simgear/scene/tgdb/SGBuildingBin.hxx>
#include "SGOceanTile.hxx"
@@ -61,7 +59,6 @@
#define ROAD_DETAILED "OBJECT_ROAD_DETAILED"
#define RAILWAY_ROUGH "OBJECT_RAILWAY_ROUGH"
#define RAILWAY_DETAILED "OBJECT_RAILWAY_DETAILED"
#define BUILDING_LIST "BUILDING_LIST"
namespace simgear {
@@ -124,12 +121,6 @@ struct ReaderWriterSTG::_ModelBin {
double _hdg;
int _size;
};
struct _BuildingList {
_BuildingList() : _lon(0), _lat(0), _elev(0) { }
std::string _filename;
std::string _material_name;
double _lon, _lat, _elev;
};
class DelayLoadReadFileCallback : public OptionsReadFileCallback {
@@ -156,11 +147,7 @@ struct ReaderWriterSTG::_ModelBin {
proxy->setCenterMode(osg::ProxyNode::UNION_OF_BOUNDING_SPHERE_AND_USER_DEFINED);
node = proxy;
} else {
#if OSG_VERSION_LESS_THAN(3,4,0)
node = osgDB::readNodeFile(o._name, o._options.get());
#else
node = osgDB::readRefNodeFile(o._name, o._options.get());
#endif
if (!node.valid()) {
SG_LOG(SG_TERRAIN, SG_ALERT, o._errorLocation << ": Failed to load "
<< o._token << " '" << o._name << "'");
@@ -198,6 +185,8 @@ struct ReaderWriterSTG::_ModelBin {
};
typedef QuadTreeBuilder<osg::LOD*, _ObjectStatic, MakeQuadLeaf, AddModelLOD,
GetModelLODCoord> STGObjectsQuadtree;
public:
virtual osgDB::ReaderWriter::ReadResult
readNode(const std::string&, const osgDB::Options*)
@@ -214,40 +203,12 @@ struct ReaderWriterSTG::_ModelBin {
if (signBuilder.getSignsGroup())
group->addChild(signBuilder.getSignsGroup());
if (_buildingList.size() > 0) {
SGMaterialLibPtr matlib = _options->getMaterialLib();
bool useVBOs = (_options->getPluginStringData("SimGear::USE_VBOS") == "ON");
if (!matlib) {
SG_LOG( SG_TERRAIN, SG_ALERT, "Unable to get materials definition for buildings");
} else {
for (std::list<_BuildingList>::iterator i = _buildingList.begin(); i != _buildingList.end(); ++i) {
// Build buildings for each list of buildings
SGGeod geodPos = SGGeod::fromDegM(i->_lon, i->_lat, 0.0);
SGMaterial* mat = matlib->find(i->_material_name, geodPos);
SGPath path = SGPath(i->_filename);
SGBuildingBin* buildingBin = new SGBuildingBin(path, mat, useVBOs);
SGBuildingBinList buildingBinList;
buildingBinList.push_back(buildingBin);
osg::MatrixTransform* matrixTransform;
matrixTransform = new osg::MatrixTransform(makeZUpFrame(SGGeod::fromDegM(i->_lon, i->_lat, i->_elev)));
matrixTransform->setName("rotateBuildings");
matrixTransform->setDataVariance(osg::Object::STATIC);
matrixTransform->addChild(createRandomBuildings(buildingBinList, osg::Matrix::identity(), _options));
group->addChild(matrixTransform);
}
}
}
return group.release();
}
mt _seed;
std::list<_ObjectStatic> _objectStaticList;
std::list<_Sign> _signList;
std::list<_BuildingList> _buildingList;
/// The original options to use for this bunch of models
osg::ref_ptr<SGReaderWriterOptions> _options;
@@ -352,16 +313,10 @@ struct ReaderWriterSTG::_ModelBin {
return false;
}
// starting with 2018.3 we will use deltas rather than absolutes as it is more intuitive for the user
// and somewhat easier to visualise
double detailedRange = atof(options->getPluginStringData("SimGear::LOD_RANGE_DETAILED").c_str());
double bareRangeDelta = atof(options->getPluginStringData("SimGear::LOD_RANGE_BARE").c_str());
double roughRangeDelta = atof(options->getPluginStringData("SimGear::LOD_RANGE_ROUGH").c_str());
// Determine object ranges. Mesh size of 2000mx2000m needs to be accounted for.
_object_range_detailed = 1414.0f + detailedRange;
_object_range_bare = _object_range_detailed + bareRangeDelta;
_object_range_rough = _object_range_detailed + roughRangeDelta;
_object_range_bare = 1414.0f + atof(options->getPluginStringData("SimGear::LOD_RANGE_BARE").c_str());
_object_range_rough = 1414.0f + atof(options->getPluginStringData("SimGear::LOD_RANGE_ROUGH").c_str());
_object_range_detailed = 1414.0f + atof(options->getPluginStringData("SimGear::LOD_RANGE_DETAILED").c_str());
SG_LOG(SG_TERRAIN, SG_INFO, "Loading stg file " << absoluteFileName);
@@ -528,13 +483,6 @@ struct ReaderWriterSTG::_ModelBin {
obj._options = opt;
checkInsideBucket(absoluteFileName, obj._lon, obj._lat);
_objectStaticList.push_back(obj);
} else if (token == BUILDING_LIST) {
_BuildingList buildinglist;
buildinglist._filename = path.local8BitStr();
in >> buildinglist._material_name >> buildinglist._lon >> buildinglist._lat >> buildinglist._elev;
checkInsideBucket(absoluteFileName, buildinglist._lon, buildinglist._lat);
_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 << "'" );
@@ -557,11 +505,7 @@ struct ReaderWriterSTG::_ModelBin {
if (_foundBase) {
for (auto stgObject : _objectList) {
osg::ref_ptr<osg::Node> node;
#if OSG_VERSION_LESS_THAN(3,4,0)
node = osgDB::readNodeFile(stgObject._name, stgObject._options.get());
#else
node = osgDB::readRefNodeFile(stgObject._name, stgObject._options.get());
#endif
if (!node.valid()) {
SG_LOG(SG_TERRAIN, SG_ALERT, stgObject._errorLocation << ": Failed to load "
<< stgObject._token << " '" << stgObject._name << "'");
@@ -594,7 +538,7 @@ struct ReaderWriterSTG::_ModelBin {
i->_elev += elevation(*terrainGroup, SGGeod::fromDeg(i->_lon, i->_lat));
}
if (_objectStaticList.empty() && _signList.empty() && (_buildingListList.size() == 0)) {
if (_objectStaticList.empty() && _signList.empty()) {
// The simple case, just return the terrain group
return terrainGroup.release();
} else {
@@ -609,7 +553,6 @@ struct ReaderWriterSTG::_ModelBin {
// we just need to know about the read file callback that itself holds the data
osg::ref_ptr<DelayLoadReadFileCallback> readFileCallback = new DelayLoadReadFileCallback;
readFileCallback->_objectStaticList = _objectStaticList;
readFileCallback->_buildingList = _buildingListList;
readFileCallback->_signList = _signList;
readFileCallback->_options = options;
readFileCallback->_bucket = bucket;
@@ -620,11 +563,9 @@ struct ReaderWriterSTG::_ModelBin {
pagedLOD->setFileName(pagedLOD->getNumChildren(), "Dummy name - use the stored data in the read file callback");
// Objects may end up displayed up to 2x the object range.
pagedLOD->setRange(pagedLOD->getNumChildren(), 0, 2.0 * _object_range_rough);
pagedLOD->setRadius(SG_TILE_RADIUS);
pagedLOD->setRange(pagedLOD->getNumChildren(), 0, 2.0 * _object_range_rough + SG_TILE_RADIUS);
SG_LOG( SG_TERRAIN, SG_DEBUG, "Tile PagedLOD Center: " << pagedLOD->getCenter().x() << "," << pagedLOD->getCenter().y() << "," << pagedLOD->getCenter().z() );
SG_LOG( SG_TERRAIN, SG_DEBUG, "Tile PagedLOD Range: " << (2.0 * _object_range_rough));
SG_LOG( SG_TERRAIN, SG_DEBUG, "Tile PagedLOD Radius: " << SG_TILE_RADIUS);
SG_LOG( SG_TERRAIN, SG_DEBUG, "Tile PagedLOD Range: " << (2.0 * _object_range_rough + SG_TILE_RADIUS));
return pagedLOD;
}
}
@@ -636,7 +577,6 @@ struct ReaderWriterSTG::_ModelBin {
std::list<_Object> _objectList;
std::list<_ObjectStatic> _objectStaticList;
std::list<_Sign> _signList;
std::list<_BuildingList> _buildingListList;
};
ReaderWriterSTG::ReaderWriterSTG()

View File

@@ -44,11 +44,12 @@
#include <osgDB/FileUtils>
#include <simgear/debug/logstream.hxx>
#include <simgear/io/iostreams/sgstream.hxx>
#include <simgear/math/SGLimits.hxx>
#include <simgear/math/SGMisc.hxx>
#include <simgear/math/sg_random.h>
#include <simgear/misc/sg_path.hxx>
#include <simgear/scene/material/Effect.hxx>
#include <simgear/scene/material/EffectGeode.hxx>
#include <simgear/scene/model/model.hxx>
#include <simgear/props/props.hxx>
@@ -106,59 +107,6 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const
return bb;
}
// Set up a BuildingBin from a file containing a list of individual building
// positions.
SGBuildingBin::SGBuildingBin(const SGPath& absoluteFileName, const SGMaterial *mat, bool useVBOs) :
SGBuildingBin::SGBuildingBin(mat, useVBOs)
{
if (!absoluteFileName.exists()) {
SG_LOG(SG_TERRAIN, SG_ALERT, "Building list file " << absoluteFileName << " does not exist.");
return;
}
sg_gzifstream stream(absoluteFileName);
if (!stream.is_open()) {
SG_LOG(SG_TERRAIN, SG_ALERT, "Unable to open " << absoluteFileName << " does not exist.");
return;
}
while (!stream.eof()) {
// read a line. Each line defines a single builing position, and may have
// a comment, starting with #
std::string line;
std::getline(stream, line);
// strip comments
std::string::size_type hash_pos = line.find('#');
if (hash_pos != std::string::npos)
line.resize(hash_pos);
// and process further
std::stringstream in(line);
// Line format is X Y Z R T
// where:
// X,Y,Z are the cartesian coordinates of the bottom SW corner of the building. +X is East, +Y is North
// R is the building rotation in degrees centered on the SW corner
// T is the building type [0, 1, 2] for SMALL, MEDIUM, LARGE
float x, y, z, r;
int t;
in >> x >> y >> z >> r >> t;
//SG_LOG(SG_TERRAIN, SG_ALERT, "Building entry " << x << " " << y << " " << z << " " << t );
SGVec3f p = SGVec3f(x,y,z);
BuildingType type = BuildingType::SMALL;
if (t == 1) type = BuildingType::MEDIUM;
if (t == 2) type = BuildingType::LARGE;
// Rotation is in the file as degrees, but in the datastructure normalized
// to 0.0 - 1.0
insert(p, (float) (r / 360.0f), type);
}
stream.close();
};
// Set up the building set based on the material definitions
SGBuildingBin::SGBuildingBin(const SGMaterial *mat, bool useVBOs) {
@@ -217,7 +165,7 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const
if (useVBOs) {
sharedGeometry->setUseVertexBufferObjects(true);
}
for (unsigned int j = 0; j < BUILDING_SET_SIZE; j++) {
float width;
float depth;
@@ -872,8 +820,8 @@ BuildingBoundingBoxCallback::computeBound(const Drawable& drawable) const
};
// This actually returns a MatrixTransform node. If we rotate the whole
// set of buildings into the local Z-up coordinate system we can reuse the
// primitive building geometry for all the buildings of the same type.
// forest into the local Z-up coordinate system we can reuse the
// primitive building geometry for all the forests of the same type.
osg::Group* createRandomBuildings(SGBuildingBinList& buildings, const osg::Matrix& transform,
const SGReaderWriterOptions* options)
{

View File

@@ -36,12 +36,10 @@
#include <osg/Material>
#include <osg/CullFace>
#include <simgear/scene/util/OsgMath.hxx>
#include <simgear/scene/material/mat.hxx>
#include <simgear/scene/material/Effect.hxx>
#include <simgear/scene/material/EffectGeode.hxx>
#include <simgear/scene/util/QuadTreeBuilder.hxx>
#include <simgear/scene/util/RenderConstants.hxx>
#include <simgear/scene/util/StateAttributeFactory.hxx>
@@ -171,7 +169,6 @@ private:
public:
SGBuildingBin(const SGMaterial *mat, bool useVBOs);
SGBuildingBin(const SGPath& absoluteFileName, const SGMaterial *mat, bool useVBOs);
~SGBuildingBin() {
smallBuildings.clear();

View File

@@ -689,13 +689,10 @@ public:
triangleBuildingList.clear();
}
const int numBuildings = (bin) ? bin->getNumBuildings() : 0;
if (numBuildings > 0) {
SG_LOG(SG_TERRAIN, SG_DEBUG, "computed Random Buildings: " << numBuildings);
SG_LOG(SG_TERRAIN, SG_DEBUG, " Dropped due to mask: " << mask_dropped);
SG_LOG(SG_TERRAIN, SG_DEBUG, " Dropped due to random object: " << random_dropped);
SG_LOG(SG_TERRAIN, SG_DEBUG, " Dropped due to other buildings: " << building_dropped);
}
SG_LOG(SG_TERRAIN, SG_DEBUG, "Random Buildings: " << ((bin) ? bin->getNumBuildings() : 0));
SG_LOG(SG_TERRAIN, SG_DEBUG, " Dropped due to mask: " << mask_dropped);
SG_LOG(SG_TERRAIN, SG_DEBUG, " Dropped due to random object: " << random_dropped);
SG_LOG(SG_TERRAIN, SG_DEBUG, " Dropped due to other buildings: " << building_dropped);
}
}

View File

@@ -230,14 +230,12 @@ void addTreeToLeafGeode(Geode* geode, const SGVec3f& p, const SGVec3f& t)
posArray->insert(posArray->end(), 4, pos);
size_t numVerts = posArray->size();
unsigned int imax = 2;
int imax = 2;
if (use_tree_shadows) { imax = 3; }
for (unsigned int i = 0; i < imax; ++i) {
if (i < geom->getNumPrimitiveSets()) {
DrawArrays* primSet = static_cast<DrawArrays*>(geom->getPrimitiveSet(i));
if (primSet != nullptr)
primSet->setCount(numVerts);
}
for (int i = 0; i < imax; ++i) {
DrawArrays* primSet = static_cast<DrawArrays*>(geom->getPrimitiveSet(i));
if(primSet != nullptr)
primSet->setCount(numVerts);
}
}
}

View File

@@ -16,7 +16,6 @@ set(HEADERS
RenderConstants.hxx
SGDebugDrawCallback.hxx
SGEnlargeBoundingBox.hxx
SGImageUtils.hxx
SGNodeMasks.hxx
SGPickCallback.hxx
SGReaderWriterOptions.hxx
@@ -45,7 +44,6 @@ set(SOURCES
PrimitiveUtils.cxx
QuadTreeBuilder.cxx
SGEnlargeBoundingBox.cxx
SGImageUtils.cxx
SGReaderWriterOptions.cxx
SGSceneFeatures.cxx
SGSceneUserData.cxx

File diff suppressed because it is too large Load Diff

View File

@@ -1,542 +0,0 @@
/* -*-c++-*- */
/* ImageUtils: copied from osgEarth - Geospatial SDK for OpenSceneGraph
* Copyright 2018 Pelican Mapping
* http://osgearth.org
*
* osgEarth is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#ifndef SIMGEAR_IMAGEUTILS_H
#define SIMGEAR_IMAGEUTILS_H
#include <osg/Image>
#include <osg/Texture>
#include <osg/GL>
#include <osg/NodeVisitor>
#include <osgDB/ReaderWriter>
#include <vector>
//These formats were not added to OSG until after 2.8.3 so we need to define them to use them.
#ifndef GL_EXT_texture_compression_rgtc
#define GL_COMPRESSED_RED_RGTC1_EXT 0x8DBB
#define GL_COMPRESSED_SIGNED_RED_RGTC1_EXT 0x8DBC
#define GL_COMPRESSED_RED_GREEN_RGTC2_EXT 0x8DBD
#define GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT 0x8DBE
#endif
#ifndef GL_IMG_texture_compression_pvrtc
#define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00
#define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG 0x8C01
#define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02
#define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 0x8C03
#endif
namespace simgear
{
class ImageUtils
{
public:
/**
* Clones an image.
*
* Use this instead of the osg::Image copy construtor, which keeps the referenced to
* its underlying BufferObject around. Calling dirty() on the new clone appears to
* help, but just call this method instead to be sure.
*/
static osg::Image* cloneImage(const osg::Image* image);
/**
* Tweaks an image for consistency. OpenGL allows enums like "GL_RGBA" et.al. to be
* used in the internal texture format, when really "GL_RGBA8" is the proper things
* to use. This method accounts for that. Some parts of osgEarth (like the texture-
* array compositor) rely on the internal texture format being correct.
* (http://http.download.nvidia.com/developer/Papers/2005/Fast_Texture_Transfers/Fast_Texture_Transfers.pdf)
*/
static void fixInternalFormat(osg::Image* image);
/**
* Marks an image as containing un-normalized data values.
*
* Normally the values in an image are "normalized", i.e. scaled so they are in the
* range [0..1]. This is normal for color values. But when the image is being used
* for coverage data (a value lookup table) it is desireable to store the raw
* values instead.
*/
static void markAsUnNormalized(osg::Image* image, bool value);
/** Inverse of above. */
static void markAsNormalized(osg::Image* image, bool value) { markAsUnNormalized(image, !value); }
/**
* Whether the image has been marked as containing un-normalized values.
*/
static bool isUnNormalized(const osg::Image* image);
/**
* Whether the image has been marked as containing normalized values.
*/
static bool isNormalized(const osg::Image* image) { return !isUnNormalized(image); }
/**
* Copys a portion of one image into another.
*/
static bool copyAsSubImage(
const osg::Image* src,
osg::Image* dst,
int dst_start_col, int dst_start_row);
/**
* Resizes an image. Returns a new image, leaving the input image unaltered.
*
* Note. If the output parameter is NULL, this method will allocate a new image and
* resize into that new image. If the output parameter is non-NULL, this method will
* assume that the output image is already allocated to the proper size, and will
* do a resize+copy into that image. In the latter case, it is your responsibility
* to make sure the output image is allocated to the proper size.
*
* If the output parameter is non-NULL, then the mipmapLevel is also considered.
* This lets you resize directly into a particular mipmap level of the output image.
*/
static bool resizeImage(
const osg::Image* input,
unsigned int new_s, unsigned int new_t,
osg::ref_ptr<osg::Image>& output,
unsigned int mipmapLevel = 0, bool bilinear = true);
/**
* Crops the input image to the dimensions provided and returns a
* new image. Returns a new image, leaving the input image unaltered.
* Note: The input destination bounds are modified to reflect the bounds of the
* actual output image. Due to the fact that you cannot crop in the middle of a pixel
* The specified destination extents and the output extents may vary slightly.
*@param src_minx
* The minimum x coordinate of the input image.
*@param src_miny
* The minimum y coordinate of the input image.
*@param src_maxx
* The maximum x coordinate of the input image.
*@param src_maxy
* The maximum y coordinate of the input image.
*@param dst_minx
* The desired minimum x coordinate of the cropped image.
*@param dst_miny
* The desired minimum y coordinate of the cropped image.
*@param dst_maxx
* The desired maximum x coordinate of the cropped image.
*@param dst_maxy
* The desired maximum y coordinate of the cropped image.
*/
static osg::Image* cropImage(
const osg::Image* image,
double src_minx, double src_miny, double src_maxx, double src_maxy,
double &dst_minx, double &dst_miny, double &dst_maxx, double &dst_maxy);
/**
* Creates an Image that "blends" two images into a new image in which "primary"
* occupies mipmap level 0, and "secondary" occupies all the other mipmap levels.
*
* WARNING: this method assumes that primary and seconday are the same exact size
* and the same exact format.
*/
static osg::Image* createMipmapBlendedImage(
const osg::Image* primary,
const osg::Image* secondary);
/**
* Creates a new image containing mipmaps built with nearest-neighbor
* sampling.
*/
static osg::Image* buildNearestNeighborMipmaps(
const osg::Image* image);
/**
* Blends the "src" image into the "dest" image, based on the "a" value.
* The two images must be the same.
*/
static bool mix(osg::Image* dest, const osg::Image* src, float a);
/**
* Creates and returns a copy of the input image after applying a
* sharpening filter. Returns a new image, leaving the input image unaltered.
*/
static osg::Image* createSharpenedImage(const osg::Image* image);
/**
* For each "layer" in the input image (each bitmap in the "r" dimension),
* create a new, separate image with r=1. If the input image is r=1, it is
* simply placed onto the output vector (no copy).
* Returns true upon sucess, false upon failure
*/
static bool flattenImage(osg::Image* image, std::vector<osg::ref_ptr<osg::Image> >& output);
/**
* Gets whether the input image's dimensions are powers of 2.
*/
static bool isPowerOfTwo(const osg::Image* image);
/**
* Gets a transparent, single pixel image used for a placeholder
*/
static osg::Image* createEmptyImage();
/**
* Gets a transparent image used for a placeholder with the specified dimensions
*/
static osg::Image* createEmptyImage(unsigned int s, unsigned int t);
/**
* Creates a one-pixel image.
*/
static osg::Image* createOnePixelImage(const osg::Vec4& color);
/**
* Tests an image to see whether it's "empty", i.e. completely transparent,
* within an alpha threshold.
*/
static bool isEmptyImage(const osg::Image* image, float alphaThreshold = 0.01);
/**
* Tests an image to see whether it's "single color", i.e. completely filled with a single color,
* within an threshold (threshold is tested on each channel).
*/
static bool isSingleColorImage(const osg::Image* image, float threshold = 0.01);
/**
* Returns true if it is possible to convert the image to the specified
* format/datatype specification.
*/
static bool canConvert(const osg::Image* image, GLenum pixelFormat, GLenum dataType);
/**
* Converts an image to the specified format.
*/
static osg::Image* convert(const osg::Image* image, GLenum pixelFormat, GLenum dataType);
/**
*Converts the given image to RGB8
*/
static osg::Image* convertToRGB8(const osg::Image* image);
/**
*Converts the given image to RGBA8
*/
static osg::Image* convertToRGBA8(const osg::Image* image);
/**
* True if the two images are of the same format (pixel format, data type, etc.)
* though not necessarily the same size, depth, etc.
*/
static bool sameFormat(const osg::Image* lhs, const osg::Image* rhs);
/**
* True if the two images have the same format AND size, and can therefore
* be used together in a texture array.
*/
static bool textureArrayCompatible(const osg::Image* lhs, const osg::Image* rhs);
/**
*Compares the image data of two images and determines if they are equivalent
*/
static bool areEquivalent(const osg::Image *lhs, const osg::Image *rhs);
/**
* Whether two colors are roughly equivalent.
*/
static bool areRGBEquivalent(const osg::Vec4& lhs, const osg::Vec4& rhs, float epsilon = 0.01f) {
return
fabs(lhs.r() - rhs.r()) < epsilon &&
fabs(lhs.g() - rhs.g()) < epsilon &&
fabs(lhs.b() - rhs.b()) < epsilon;
}
/**
* Checks whether the image has an alpha component
*/
static bool hasAlphaChannel(const osg::Image* image);
/**
* Checks whether an image has transparency; i.e. whether
* there are any pixels with an alpha component whole value
* falls below the specified threshold.
*/
static bool hasTransparency(const osg::Image* image, float alphaThreshold = 1.0f);
/**
* Finds pixels with alpha less than [maxAlpha] and sets their color
* to match that or neighboring non-alpha pixels. This facilitates multipass
* blending or abutting tiles by overlapping them slightly. Specify "maxAlpha"
* as the maximum value to consider when searching for fully-transparent pixels.
*
* Returns false if there is no reader or writer for the image's format.
*/
static bool featherAlphaRegions(osg::Image* image, float maxAlpha = 0.0f);
/**
* Converts an image (in place) to premultiplied-alpha format.
* Returns False is the conversion fails, e.g., if there is no reader
* or writer for the image format.
*/
static bool convertToPremultipliedAlpha(osg::Image* image);
/**
* Checks whether the given image is compressed
*/
static bool isCompressed(const osg::Image* image);
/**
* Generated a bump map image for the input image
*/
static osg::Image* createBumpMap(const osg::Image* input);
/**
* Is it a floating-point texture format?
*/
static bool isFloatingPointInternalFormat(GLint internalFormat);
/**
* Compute a texture compression format suitable for the image.
*/
static bool computeTextureCompressionMode(
const osg::Image* image,
osg::Texture::InternalFormatMode& out_mode);
/**
* Bicubic upsampling in a quadrant. Target image is already allocated.
*/
static bool bicubicUpsample(
const osg::Image* source,
osg::Image* target,
unsigned quadrant,
unsigned stride);
/**
*
*/
static osg::Image* upSampleNN(const osg::Image* src, int quadrant);
/**
* Activates mipmapping for a texture image if the correct filters exist.
*
* If OSG has an ImageProcessor service installed, this method will use that
* to generate mipmaps. If not, the method will be a NOP and the GPU wil
* generate mipmaps (if necessary) upon GPU transfer.
*/
static void activateMipMaps(osg::Texture* texture);
/**
* Gets an osgDB::ReaderWriter for the given input stream.
* Returns NULL if no ReaderWriter can be found.
*/
static osgDB::ReaderWriter* getReaderWriterForStream(std::istream& stream);
/**
* Reads an osg::Image from the given input stream.
* Returns NULL if the image could not be read.
*/
static osg::Image* readStream(std::istream& stream, const osgDB::Options* options);
/**
* Reads color data out of an image, regardles of its internal pixel format.
*/
class PixelReader
{
public:
/**
* Constructs a pixel reader. "Normalized" means that the values in the source
* image have been scaled to [0..1] and should be denormalized upon reading.
*/
PixelReader(const osg::Image* image);
/** Sets an image to read. */
void setImage(const osg::Image* image);
/** Whether to use bilinear interpolation when reading with u,v coords (default=true) */
void setBilinear(bool value) { _bilinear = value; }
/** Whether PixelReader supports a given format/datatype combiniation. */
static bool supports(GLenum pixelFormat, GLenum dataType);
/** Whether PixelReader can read from the specified image. */
static bool supports(const osg::Image* image) {
return image && supports(image->getPixelFormat(), image->getDataType());
}
/** Reads a color from the image */
osg::Vec4 operator()(int s, int t, int r = 0, int m = 0) const {
return (*_reader)(this, s, t, r, m);
}
/** Reads a color from the image */
osg::Vec4 operator()(unsigned s, unsigned t, unsigned r = 0, int m = 0) const {
return (*_reader)(this, s, t, r, m);
}
/** Reads a color from the image by unit coords [0..1] */
osg::Vec4 operator()(float u, float v, int r = 0, int m = 0) const;
osg::Vec4 operator()(double u, double v, int r = 0, int m = 0) const;
// internals:
const unsigned char* data(int s = 0, int t = 0, int r = 0, int m = 0) const {
return m == 0 ?
_image->data() + s*_colMult + t*_rowMult + r*_imageSize :
_image->getMipmapData(m) + s*_colMult + t*(_rowMult >> m) + r*(_imageSize >> m);
}
typedef osg::Vec4(*ReaderFunc)(const PixelReader* ia, int s, int t, int r, int m);
ReaderFunc _reader;
const osg::Image* _image;
unsigned _colMult;
unsigned _rowMult;
unsigned _imageSize;
bool _normalized;
bool _bilinear;
};
/**
* Writes color data to an image, regardles of its internal pixel format.
*/
class PixelWriter
{
public:
/**
* Constructs a pixel writer. "Normalized" means the values are scaled to [0..1]
* before writing.
*/
PixelWriter(osg::Image* image);
/** Whether PixelWriter can write to an image with the given format/datatype combo. */
static bool supports(GLenum pixelFormat, GLenum dataType);
/** Whether PixelWriter can write to non-const version of an image. */
static bool supports(const osg::Image* image) {
return image && supports(image->getPixelFormat(), image->getDataType());
}
/** Writes a color to a pixel. */
void operator()(const osg::Vec4& c, int s, int t, int r = 0, int m = 0) {
(*_writer)(this, c, s, t, r, m);
}
void f(const osg::Vec4& c, float s, float t, int r = 0, int m = 0) {
this->operator()(c,
(int)(s * (float)(_image->s() - 1)),
(int)(t * (float)(_image->t() - 1)),
r, m);
}
// internals:
osg::Image* _image;
unsigned _colMult;
unsigned _rowMult;
unsigned _imageSize;
bool _normalized;
unsigned char* data(int s = 0, int t = 0, int r = 0, int m = 0) const {
return m == 0 ?
_image->data() + s*_colMult + t*_rowMult + r*_imageSize :
_image->getMipmapData(m) + s*_colMult + t*(_rowMult >> m) + r*(_imageSize >> m);
}
typedef void(*WriterFunc)(const PixelWriter* iw, const osg::Vec4& c, int s, int t, int r, int m);
WriterFunc _writer;
};
/**
* Functor that visits every pixel in an image
*/
template<typename T>
struct PixelVisitor : public T
{
/**
* Traverse an image, and call this method on the superclass:
*
* bool operator(osg::Vec4& pixel);
*
* If that method returns true, write the value back at the same location.
*/
void accept(osg::Image* image) {
PixelReader _reader(image);
PixelWriter _writer(image);
for (int r = 0; r<image->r(); ++r) {
for (int t = 0; t<image->t(); ++t) {
for (int s = 0; s<image->s(); ++s) {
osg::Vec4f pixel = _reader(s, t, r);
if ((*this)(pixel))
_writer(pixel, s, t, r);
}
}
}
}
/**
* Traverse an image, and call this method on the superclass:
*
* bool operator(const osg::Vec4& srcPixel, osg::Vec4& destPixel);
*
* If that method returns true, write destPixel back at the same location
* in the destination image.
*/
void accept(const osg::Image* src, osg::Image* dest) {
PixelReader _readerSrc(src);
PixelReader _readerDest(dest);
PixelWriter _writerDest(dest);
for (int r = 0; r<src->r(); ++r) {
for (int t = 0; t<src->t(); ++t) {
for (int s = 0; s<src->s(); ++s) {
const osg::Vec4f pixelSrc = _readerSrc(s, t, r);
osg::Vec4f pixelDest = _readerDest(s, t, r);
if ((*this)(pixelSrc, pixelDest))
_writerDest(pixelDest, s, t, r);
}
}
}
}
};
/**
* Simple functor to copy pixels from one image to another.
*
* Usage:
* PixelVisitor<CopyImage>().accept( fromImage, toImage );
*/
struct CopyImage {
bool operator()(const osg::Vec4f& src, osg::Vec4f& dest) {
dest = src;
return true;
}
};
};
/** Visitor that finds and operates on textures and images */
class TextureAndImageVisitor : public osg::NodeVisitor
{
public:
TextureAndImageVisitor();
virtual ~TextureAndImageVisitor() { }
public:
/** Visits a texture and, by default, all its components images */
virtual void apply(osg::Texture& texture);
/** Visits an image inside a texture */
virtual void apply(osg::Image& image) { }
public: // osg::NodeVisitor
virtual void apply(osg::Node& node);
virtual void apply(osg::StateSet& stateSet);
};
}
#endif //SIMGEAR_IMAGEUTILS_H

View File

@@ -38,22 +38,12 @@ namespace simgear
class SGReaderWriterOptions : public osgDB::Options {
public:
enum LoadOriginHint
{
ORIGIN_MODEL,
ORIGIN_EFFECTS,
ORIGIN_EFFECTS_NORMALIZED,
};
//SGReaderWriterOptions* cloneOptions(const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY) const { return static_cast<SGReaderWriterOptions*>(clone(copyop)); }
SGReaderWriterOptions() :
_materialLib(0),
_load_panel(0),
_model_data(0),
_instantiateEffects(false),
_instantiateMaterialEffects(false),
_LoadOriginHint(ORIGIN_MODEL)
_instantiateMaterialEffects(false)
{ }
SGReaderWriterOptions(const std::string& str) :
osgDB::Options(str),
@@ -61,8 +51,7 @@ public:
_load_panel(0),
_model_data(0),
_instantiateEffects(false),
_instantiateMaterialEffects(false),
_LoadOriginHint(ORIGIN_MODEL)
_instantiateMaterialEffects(false)
{ }
SGReaderWriterOptions(const osgDB::Options& options,
const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY) :
@@ -71,8 +60,7 @@ public:
_load_panel(0),
_model_data(0),
_instantiateEffects(false),
_instantiateMaterialEffects(false),
_LoadOriginHint(ORIGIN_MODEL)
_instantiateMaterialEffects(false)
{ }
SGReaderWriterOptions(const SGReaderWriterOptions& options,
const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY) :
@@ -87,8 +75,7 @@ public:
_instantiateEffects(options._instantiateEffects),
_instantiateMaterialEffects(options._instantiateMaterialEffects),
_materialName(options._materialName),
_sceneryPathSuffixes(options._sceneryPathSuffixes),
_LoadOriginHint(ORIGIN_MODEL)
_sceneryPathSuffixes(options._sceneryPathSuffixes)
{ }
META_Object(simgear, SGReaderWriterOptions);
@@ -152,13 +139,6 @@ public:
const SGGeod& getLocation() const
{ return _geod; }
// the load origin defines where the load request has come from.
// example usage; to allow the DDS Texture Cache (DTC) to ignore
// any texture that is used in a shader, as these often have special values
// encoded into the channels that aren't suitable for conversion.
void setLoadOriginHint(LoadOriginHint _v) const { _LoadOriginHint = _v; }
LoadOriginHint getLoadOriginHint() const { return _LoadOriginHint; }
protected:
virtual ~SGReaderWriterOptions();
@@ -177,7 +157,6 @@ private:
string _materialName;
string_list _sceneryPathSuffixes;
SGGeod _geod;
mutable LoadOriginHint _LoadOriginHint;
};
}

View File

@@ -40,10 +40,6 @@
SGSceneFeatures::SGSceneFeatures() :
_textureCompression(UseARBCompression),
_MaxTextureSize(4096),
_TextureCacheCompressionActive(true),
_TextureCacheCompressionActiveTransparent(true),
_TextureCacheActive(false),
_shaderLights(true),
_pointSpriteLights(true),
_distanceAttenuationLights(true),

View File

@@ -1,133 +1,102 @@
/* -*-c++-*-
*
* Copyright (C) 2006-2007 Mathias Froehlich
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
*
* Copyright (C) 2006-2007 Mathias Froehlich
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#ifndef SG_SCENE_FEATURES_HXX
#define SG_SCENE_FEATURES_HXX
#include <simgear/structure/SGReferenced.hxx>
#include <string>
#include <simgear/misc/sg_path.hxx>
namespace osg { class Texture; }
class SGSceneFeatures : public SGReferenced {
public:
static SGSceneFeatures* instance();
static SGSceneFeatures* instance();
enum TextureCompression {
DoNotUseCompression,
UseARBCompression,
UseDXT1Compression,
UseDXT3Compression,
UseDXT5Compression
};
int getMaxTextureSize() const { return _MaxTextureSize; }
void setMaxTextureSize(const int maxTextureSize) { _MaxTextureSize = maxTextureSize; }
enum TextureCompression {
DoNotUseCompression,
UseARBCompression,
UseDXT1Compression,
UseDXT3Compression,
UseDXT5Compression
};
SGPath getTextureCompressionPath() const { return _TextureCompressionPath; }
void setTextureCompressionPath(const SGPath path) { _TextureCompressionPath = path; }
void setTextureCompression(TextureCompression textureCompression)
{ _textureCompression = textureCompression; }
TextureCompression getTextureCompression() const
{ return _textureCompression; }
void setTextureCompression(osg::Texture* texture) const;
bool getTextureCacheActive() const { return _TextureCacheActive; }
void setTextureCacheActive(const bool val) { _TextureCacheActive = val; }
void setEnablePointSpriteLights(bool enable)
{ _pointSpriteLights = enable; }
bool getEnablePointSpriteLights() const
{
return _pointSpriteLights;
}
bool getEnablePointSpriteLights(unsigned contextId) const
{
if (!_pointSpriteLights)
return false;
return getHavePointSprites(contextId);
}
bool getTextureCacheCompressionActive() const { return _TextureCacheCompressionActive; }
void setTextureCacheCompressionActive(const bool val) { _TextureCacheCompressionActive = val; }
void setEnableDistanceAttenuationLights(bool enable)
{ _distanceAttenuationLights = enable; }
bool getEnableDistanceAttenuationLights(unsigned contextId) const
{
if (!_distanceAttenuationLights)
return false;
return getHavePointParameters(contextId);
}
bool getTextureCacheCompressionActiveTransparent() const { return _TextureCacheCompressionActiveTransparent; }
void setTextureCacheCompressionActiveTransparent(const bool val) { _TextureCacheCompressionActiveTransparent = val; }
void setTextureCompression(TextureCompression textureCompression) { _textureCompression = textureCompression; }
TextureCompression getTextureCompression() const { return _textureCompression; }
// modify the texture compression on the texture parameter
void setTextureCompression(osg::Texture* texture) const;
void setEnablePointSpriteLights(bool enable)
{
_pointSpriteLights = enable;
}
bool getEnablePointSpriteLights() const
{
return _pointSpriteLights;
}
bool getEnablePointSpriteLights(unsigned contextId) const
{
if (!_pointSpriteLights)
return false;
return getHavePointSprites(contextId);
}
void setEnableDistanceAttenuationLights(bool enable)
{
_distanceAttenuationLights = enable;
}
bool getEnableDistanceAttenuationLights(unsigned contextId) const
{
if (!_distanceAttenuationLights)
return false;
return getHavePointParameters(contextId);
}
void setEnableShaderLights(bool enable)
{
_shaderLights = enable;
}
bool getEnableShaderLights(unsigned contextId) const
{
if (!_shaderLights)
return false;
return getHaveShaderPrograms(contextId);
}
void setTextureFilter(int max)
{
_textureFilter = max;
}
int getTextureFilter() const
{
return _textureFilter;
}
void setEnableShaderLights(bool enable)
{ _shaderLights = enable; }
bool getEnableShaderLights(unsigned contextId) const
{
if (!_shaderLights)
return false;
return getHaveShaderPrograms(contextId);
}
void setTextureFilter(int max)
{ _textureFilter = max; }
int getTextureFilter() const
{ return _textureFilter; }
protected:
bool getHavePointSprites(unsigned contextId) const;
bool getHaveFragmentPrograms(unsigned contextId) const;
bool getHaveVertexPrograms(unsigned contextId) const;
bool getHaveShaderPrograms(unsigned contextId) const;
bool getHavePointParameters(unsigned contextId) const;
bool getHavePointSprites(unsigned contextId) const;
bool getHaveFragmentPrograms(unsigned contextId) const;
bool getHaveVertexPrograms(unsigned contextId) const;
bool getHaveShaderPrograms(unsigned contextId) const;
bool getHavePointParameters(unsigned contextId) const;
private:
SGSceneFeatures();
SGSceneFeatures(const SGSceneFeatures&);
SGSceneFeatures& operator=(const SGSceneFeatures&);
SGSceneFeatures();
SGSceneFeatures(const SGSceneFeatures&);
SGSceneFeatures& operator=(const SGSceneFeatures&);
TextureCompression _textureCompression;
int _MaxTextureSize;
SGPath _TextureCompressionPath;
bool _TextureCacheCompressionActive;
bool _TextureCacheCompressionActiveTransparent;
bool _TextureCacheActive;
bool _shaderLights;
bool _pointSpriteLights;
bool _distanceAttenuationLights;
int _textureFilter;
TextureCompression _textureCompression;
bool _shaderLights;
bool _pointSpriteLights;
bool _distanceAttenuationLights;
int _textureFilter;
};
#endif

View File

@@ -1,17 +0,0 @@
set(HEADERS
ClusteredForward.hxx
Compositor.hxx
CompositorBuffer.hxx
CompositorPass.hxx
CompositorUtil.hxx
)
set(SOURCES
ClusteredForward.cxx
Compositor.cxx
CompositorBuffer.cxx
CompositorPass.cxx
CompositorUtil.cxx
)
simgear_scene_component(viewer scene/viewer "${SOURCES}" "${HEADERS}")

View File

@@ -1,216 +0,0 @@
// Copyright (C) 2018 Fernando García Liñán <fernandogarcialinan@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "ClusteredForward.hxx"
#include <osg/BufferIndexBinding>
#include <osg/BufferObject>
#include <osg/RenderInfo>
#include <osg/Texture3D>
#include <osg/TextureBuffer>
#include <osg/Version>
namespace simgear {
namespace compositor {
///// BEGIN DEBUG
#define DATA_SIZE 24
const GLfloat LIGHT_DATA[DATA_SIZE] = {
0.0, 0.0, -10.0, 1.0, 1.0, 0.0, 0.0, 1.0,
0.0, 0.0, 10.0, 1.0, 0.0, 1.0, 0.0, 1.0,
0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0
};
#define MAX_LIGHT_INDICES 4096
#define MAX_POINT_LIGHTS 256
struct Light {
osg::Vec3 position;
float range;
};
#define NUM_LIGHTS 2
Light LIGHT_LIST[NUM_LIGHTS] = {
{osg::Vec3(0.0f, 0.0f, -10.0f), 10.0f},
{osg::Vec3(0.0f, 0.0f, 5.0f), 1000.0f}
};
///// END DEBUG
ClusteredForwardDrawCallback::ClusteredForwardDrawCallback() :
_initialized(false),
_tile_size(64),
_light_grid(new osg::Image),
_light_indices(new osg::Image),
_light_data(new osg::FloatArray(MAX_POINT_LIGHTS))
{
}
void
ClusteredForwardDrawCallback::operator()(osg::RenderInfo &renderInfo) const
{
osg::Camera *camera = renderInfo.getCurrentCamera();
const osg::Viewport *vp = camera->getViewport();
const int width = vp->width();
const int height = vp->height();
// Round up
int n_htiles = (width + _tile_size - 1) / _tile_size;
int n_vtiles = (height + _tile_size - 1) / _tile_size;
if (!_initialized) {
// Create and associate the light grid 3D texture
_light_grid->allocateImage(n_htiles, n_vtiles, 1,
GL_RGB_INTEGER_EXT, GL_UNSIGNED_SHORT);
_light_grid->setInternalTextureFormat(GL_RGB16UI_EXT);
osg::ref_ptr<osg::Texture3D> light_grid_tex = new osg::Texture3D;
light_grid_tex->setResizeNonPowerOfTwoHint(false);
light_grid_tex->setWrap(osg::Texture3D::WRAP_R, osg::Texture3D::CLAMP_TO_BORDER);
light_grid_tex->setWrap(osg::Texture3D::WRAP_S, osg::Texture3D::CLAMP_TO_BORDER);
light_grid_tex->setWrap(osg::Texture3D::WRAP_T, osg::Texture3D::CLAMP_TO_BORDER);
light_grid_tex->setFilter(osg::Texture3D::MIN_FILTER, osg::Texture3D::NEAREST);
light_grid_tex->setFilter(osg::Texture3D::MAG_FILTER, osg::Texture3D::NEAREST);
light_grid_tex->setImage(0, _light_grid.get());
camera->getOrCreateStateSet()->setTextureAttributeAndModes(
10, light_grid_tex.get(), osg::StateAttribute::ON);
// Create and associate the light indices TBO
_light_indices->allocateImage(4096, 1, 1, GL_RED_INTEGER_EXT, GL_UNSIGNED_SHORT);
osg::ref_ptr<osg::TextureBuffer> light_indices_tbo =
new osg::TextureBuffer;
light_indices_tbo->setInternalFormat(GL_R16UI);
light_indices_tbo->setImage(_light_indices.get());
camera->getOrCreateStateSet()->setTextureAttribute(
11, light_indices_tbo.get());
// Create and associate the light data UBO
osg::ref_ptr<osg::UniformBufferObject> light_data_ubo =
new osg::UniformBufferObject;
_light_data->setBufferObject(light_data_ubo.get());
#if OSG_VERSION_LESS_THAN(3,6,0)
osg::ref_ptr<osg::UniformBufferBinding> light_data_ubb =
new osg::UniformBufferBinding(0, light_data_ubo.get(),
0, MAX_POINT_LIGHTS * 8 * sizeof(GLfloat));
#else
osg::ref_ptr<osg::UniformBufferBinding> light_data_ubb =
new osg::UniformBufferBinding(0, _light_data.get(),
0, MAX_POINT_LIGHTS * 8 * sizeof(GLfloat));
#endif
light_data_ubb->setDataVariance(osg::Object::DYNAMIC);
camera->getOrCreateStateSet()->setAttribute(
light_data_ubb.get(), osg::StateAttribute::ON);
_initialized = true;
}
std::vector<osg::Polytope> subfrustums;
const osg::Matrix &view_matrix = camera->getViewMatrix();
const osg::Matrix &proj_matrix = camera->getProjectionMatrix();
osg::Matrix view_proj_inverse = osg::Matrix::inverse(view_matrix * proj_matrix);
double x_step = (_tile_size / width) * 2.0;
double y_step = (_tile_size / height) * 2.0;
for (int y = 0; y < n_vtiles; ++y) {
for (int x = 0; x < n_htiles; ++x) {
// Create the subfrustum in clip space
double x_min = -1.0 + x_step * x; double x_max = x_min + x_step;
double y_min = -1.0 + y_step * y; double y_max = y_min + y_step;
double z_min = 1.0; double z_max = -1.0;
osg::BoundingBox subfrustum_bb(
x_min, y_min, z_min, x_max, y_max, z_max);
osg::Polytope subfrustum;
subfrustum.setToBoundingBox(subfrustum_bb);
// Transform it to world space
subfrustum.transformProvidingInverse(view_proj_inverse);
subfrustums.push_back(subfrustum);
}
}
GLushort *grid_data = reinterpret_cast<GLushort *>
(_light_grid->data());
GLushort *index_data = reinterpret_cast<GLushort *>
(_light_indices->data());
GLushort global_light_count = 0;
for (size_t i = 0; i < subfrustums.size(); ++i) {
GLushort start_offset = global_light_count;
GLushort local_light_count = 0;
for (GLushort light_list_index = 0;
light_list_index < NUM_LIGHTS;
++light_list_index) {
const Light &light = LIGHT_LIST[light_list_index];
osg::BoundingSphere bs(light.position, light.range);
if (subfrustums[i].contains(bs)) {
index_data[global_light_count] = light_list_index;
++local_light_count;
++global_light_count;
}
}
grid_data[i * 3 + 0] = start_offset;
grid_data[i * 3 + 1] = local_light_count;
grid_data[i * 3 + 2] = 0;
}
_light_grid->dirty();
_light_indices->dirty();
// Upload light data
for (int i = 0; i < DATA_SIZE; ++i) {
(*_light_data)[i] = LIGHT_DATA[i];
}
// DEBUG
/*
if (!_debug) {
for (int y = 0; y < num_vtiles; ++y) {
for (int x = 0; x < num_htiles; ++x) {
std::cout << grid_data[(y * num_htiles + x) * 3 + 0] << ","
<< grid_data[(y * num_htiles + x) * 3 + 1] << " ";
}
std::cout << std::endl;
}
std::cout << "\n\n";
for (int i = 0; i < num_vtiles * num_htiles; ++i) {
std::cout << index_data[i] << " ";
}
std::cout << "\n";
_debug = true;
}
*/
/*
for (int y = 0; y < num_vtiles; ++y) {
for (int x = 0; x < num_htiles; ++x) {
data[(y * num_htiles + x) * 3 + 0] = (unsigned short)x;
data[(y * num_htiles + x) * 3 + 1] = (unsigned short)y;
data[(y * num_htiles + x) * 3 + 2] = 0;
}
}
_light_grid->dirty();
*/
}
} // namespace compositor
} // namespace simgear

View File

@@ -1,40 +0,0 @@
// Copyright (C) 2018 Fernando García Liñán <fernandogarcialinan@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef SG_CLUSTERED_FORWARD_HXX
#define SG_CLUSTERED_FORWARD_HXX
#include <osg/Camera>
namespace simgear {
namespace compositor {
class ClusteredForwardDrawCallback : public osg::Camera::DrawCallback {
public:
ClusteredForwardDrawCallback();
virtual void operator()(osg::RenderInfo &renderInfo) const;
protected:
mutable bool _initialized;
int _tile_size;
osg::ref_ptr<osg::Image> _light_grid;
osg::ref_ptr<osg::Image> _light_indices;
osg::ref_ptr<osg::FloatArray> _light_data;
};
} // namespace compositor
} // namespace simgear
#endif /* SG_CLUSTERED_FORWARD_HXX */

View File

@@ -1,312 +0,0 @@
// Copyright (C) 2018 Fernando García Liñán <fernandogarcialinan@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "Compositor.hxx"
#include <algorithm>
#include <osgUtil/IntersectionVisitor>
#include <osgViewer/Renderer>
#include <osgViewer/Viewer>
#include <simgear/math/SGRect.hxx>
#include <simgear/props/props_io.hxx>
#include <simgear/scene/material/EffectCullVisitor.hxx>
#include <simgear/scene/util/SGReaderWriterOptions.hxx>
#include <simgear/scene/util/RenderConstants.hxx>
#include <simgear/structure/exception.hxx>
#include "CompositorUtil.hxx"
namespace simgear {
namespace compositor {
Compositor *
Compositor::create(osg::View *view,
osg::GraphicsContext *gc,
osg::Viewport *viewport,
const SGPropertyNode *property_list)
{
osg::ref_ptr<Compositor> compositor = new Compositor(view, gc, viewport);
compositor->_name = property_list->getStringValue("name");
// Read all buffers first so passes can use them
PropertyList p_buffers = property_list->getChildren("buffer");
for (auto const &p_buffer : p_buffers) {
if (!checkConditional(p_buffer))
continue;
const std::string &buffer_name = p_buffer->getStringValue("name");
if (buffer_name.empty()) {
SG_LOG(SG_INPUT, SG_ALERT, "Compositor::build: Buffer requires "
"a name to be available to passes. Skipping...");
continue;
}
Buffer *buffer = buildBuffer(compositor.get(), p_buffer);
if (buffer)
compositor->addBuffer(buffer_name, buffer);
}
// Read passes
PropertyList p_passes = property_list->getChildren("pass");
for (auto const &p_pass : p_passes) {
if (!checkConditional(p_pass))
continue;
Pass *pass = buildPass(compositor.get(), p_pass);
if (pass)
compositor->addPass(pass);
}
return compositor.release();
}
Compositor *
Compositor::create(osg::View *view,
osg::GraphicsContext *gc,
osg::Viewport *viewport,
const std::string &name)
{
std::string filename(name);
filename += ".xml";
std::string abs_filename = SGModelLib::findDataFile(filename);
if (abs_filename.empty()) {
SG_LOG(SG_INPUT, SG_ALERT, "Compositor::build: Could not find file '"
<< filename << "'");
return 0;
}
SGPropertyNode_ptr property_list = new SGPropertyNode;
try {
readProperties(abs_filename, property_list.ptr(), 0, true);
} catch (sg_io_exception &e) {
SG_LOG(SG_INPUT, SG_ALERT, "Compositor::build: Failed to parse file '"
<< abs_filename << "'. " << e.getFormattedMessage());
return 0;
}
return create(view, gc, viewport, property_list);
}
Compositor::Compositor(osg::View *view,
osg::GraphicsContext *gc,
osg::Viewport *viewport) :
_view(view),
_gc(gc),
_viewport(viewport),
_uniforms{
new osg::Uniform("fg_ViewportSize", osg::Vec2f()),
new osg::Uniform("fg_ViewMatrix", osg::Matrixf()),
new osg::Uniform("fg_ViewMatrixInverse", osg::Matrixf()),
new osg::Uniform("fg_ProjectionMatrix", osg::Matrixf()),
new osg::Uniform("fg_ProjectionMatrixInverse", osg::Matrixf()),
new osg::Uniform("fg_CameraPositionCart", osg::Vec3f()),
new osg::Uniform("fg_CameraPositionGeod", osg::Vec3f())
}
{
}
Compositor::~Compositor()
{
}
void
Compositor::update(const osg::Matrix &view_matrix,
const osg::Matrix &proj_matrix)
{
for (auto &pass : _passes) {
if (pass->inherit_cull_mask) {
osg::Camera *camera = pass->camera;
osg::Camera *view_camera = _view->getCamera();
camera->setCullMask(pass->cull_mask
& view_camera->getCullMask());
camera->setCullMaskLeft(pass->cull_mask
& view_camera->getCullMaskLeft());
camera->setCullMaskRight(pass->cull_mask
& view_camera->getCullMaskRight());
}
if (pass->update_callback.valid())
pass->update_callback->updatePass(*pass.get(), view_matrix, proj_matrix);
}
// Update uniforms
osg::Matrixd view_inverse = osg::Matrix::inverse(view_matrix);
osg::Vec4d camera_pos = osg::Vec4(0.0, 0.0, 0.0, 1.0) * view_inverse;
SGGeod camera_pos_geod = SGGeod::fromCart(
SGVec3d(camera_pos.x(), camera_pos.y(), camera_pos.z()));
for (int i = 0; i < TOTAL_BUILTIN_UNIFORMS; ++i) {
osg::ref_ptr<osg::Uniform> u = _uniforms[i];
switch (i) {
case VIEWPORT_SIZE:
u->set(osg::Vec2f(_viewport->width(), _viewport->height()));
break;
case VIEW_MATRIX:
u->set(view_matrix);
break;
case VIEW_MATRIX_INV:
u->set(view_inverse);
break;
case PROJECTION_MATRIX:
u->set(proj_matrix);
break;
case PROJECTION_MATRIX_INV:
u->set(osg::Matrix::inverse(proj_matrix));
break;
case CAMERA_POSITION_CART:
u->set(osg::Vec3f(camera_pos.x(), camera_pos.y(), camera_pos.z()));
break;
case CAMERA_POSITION_GEOD:
u->set(osg::Vec3f(camera_pos_geod.getLongitudeRad(),
camera_pos_geod.getLatitudeRad(),
camera_pos_geod.getElevationM()));
break;
default:
// Unknown uniform
break;
}
}
}
void
Compositor::resized()
{
// Cameras attached directly to the framebuffer were already resized by
// osg::GraphicsContext::resizedImplementation(). However, RTT cameras were
// ignored. Here we resize RTT cameras that need to match the physical
// viewport size.
for (const auto &pass : _passes) {
osg::Camera *camera = pass->camera;
if (!camera->isRenderToTextureCamera() ||
pass->viewport_width_scale == 0.0f ||
pass->viewport_height_scale == 0.0f)
continue;
// Resize both the viewport and its texture attachments
camera->resize(pass->viewport_width_scale * _viewport->width(),
pass->viewport_height_scale * _viewport->height());
}
}
bool
Compositor::computeIntersection(
const osg::Vec2d& windowPos,
osgUtil::LineSegmentIntersector::Intersections& intersections)
{
using osgUtil::Intersector;
using osgUtil::LineSegmentIntersector;
osg::Camera *camera = getPass(0)->camera;
const osg::Viewport* viewport = camera->getViewport();
SGRect<double> viewportRect(viewport->x(), viewport->y(),
viewport->x() + viewport->width() - 1.0,
viewport->y() + viewport->height()- 1.0);
double epsilon = 0.5;
if (!viewportRect.contains(windowPos.x(), windowPos.y(), epsilon))
return false;
osg::Vec4d start(windowPos.x(), windowPos.y(), 0.0, 1.0);
osg::Vec4d end(windowPos.x(), windowPos.y(), 1.0, 1.0);
osg::Matrix windowMat = viewport->computeWindowMatrix();
osg::Matrix startPtMat = osg::Matrix::inverse(camera->getProjectionMatrix()
* windowMat);
osg::Matrix endPtMat = startPtMat; // no far camera
start = start * startPtMat;
start /= start.w();
end = end * endPtMat;
end /= end.w();
osg::ref_ptr<LineSegmentIntersector> picker
= new LineSegmentIntersector(Intersector::VIEW,
osg::Vec3d(start.x(), start.y(), start.z()),
osg::Vec3d(end.x(), end.y(), end.z()));
osgUtil::IntersectionVisitor iv(picker.get());
iv.setTraversalMask( simgear::PICK_BIT );
const_cast<osg::Camera*>(camera)->accept(iv);
if (picker->containsIntersections()) {
intersections = picker->getIntersections();
return true;
}
return false;
}
void
Compositor::addBuffer(const std::string &name, Buffer *buffer)
{
_buffers[name] = buffer;
}
void
Compositor::addPass(Pass *pass)
{
if (!_view) {
SG_LOG(SG_GENERAL, SG_ALERT, "Compositor::addPass: Couldn't add camera "
"as a slave to the view. View doesn't exist!");
return;
}
_view->addSlave(pass->camera, pass->useMastersSceneData);
// Install the Effect cull visitor
osgViewer::Renderer* renderer
= static_cast<osgViewer::Renderer*>(pass->camera->getRenderer());
for (int i = 0; i < 2; ++i) {
osgUtil::SceneView* sceneView = renderer->getSceneView(i);
osg::ref_ptr<osgUtil::CullVisitor::Identifier> identifier;
identifier = sceneView->getCullVisitor()->getIdentifier();
sceneView->setCullVisitor(
new EffectCullVisitor(false, pass->effect_override));
sceneView->getCullVisitor()->setIdentifier(identifier.get());
identifier = sceneView->getCullVisitorLeft()->getIdentifier();
sceneView->setCullVisitorLeft(sceneView->getCullVisitor()->clone());
sceneView->getCullVisitorLeft()->setIdentifier(identifier.get());
identifier = sceneView->getCullVisitorRight()->getIdentifier();
sceneView->setCullVisitorRight(sceneView->getCullVisitor()->clone());
sceneView->getCullVisitorRight()->setIdentifier(identifier.get());
}
_passes.push_back(pass);
}
Buffer *
Compositor::getBuffer(const std::string &name) const
{
auto it = _buffers.find(name);
if (it == _buffers.end())
return 0;
return it->second.get();
}
Pass *
Compositor::getPass(const std::string &name) const
{
auto it = std::find_if(_passes.begin(), _passes.end(),
[&name](const osg::ref_ptr<Pass> &p) {
return p->name == name;
});
if (it == _passes.end())
return 0;
return (*it);
}
} // namespace compositor
} // namespace simgear

View File

@@ -1,138 +0,0 @@
// Copyright (C) 2018 Fernando García Liñán <fernandogarcialinan@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef SG_COMPOSITOR_HXX
#define SG_COMPOSITOR_HXX
#include <unordered_map>
#include <vector>
// For osgUtil::LineSegmentIntersector::Intersections, which is a typedef.
#include <osgUtil/LineSegmentIntersector>
#include "CompositorBuffer.hxx"
#include "CompositorPass.hxx"
class SGPropertyNode;
namespace simgear {
namespace compositor {
/**
* A Compositor manages the rendering pipeline of a single physical camera,
* usually via a property tree interface.
*
* The building blocks that define a Compositor are:
* - Buffers. They represent a zone of GPU memory. This is implemented in the
* form of an OpenGL texture, but any type of information can be stored
* (which can be useful in compute shaders for example).
* - Passes. They represent render operations. They can get buffers as input
* and they can output to other buffers. They are also integrated with the
* Effects framework, so the OpenGL internal state is configurable per pass.
*/
class Compositor : public osg::Referenced {
public:
enum BuiltinUniform {
VIEWPORT_SIZE = 0,
VIEW_MATRIX,
VIEW_MATRIX_INV,
PROJECTION_MATRIX,
PROJECTION_MATRIX_INV,
CAMERA_POSITION_CART,
CAMERA_POSITION_GEOD,
TOTAL_BUILTIN_UNIFORMS
};
Compositor(osg::View *view,
osg::GraphicsContext *gc,
osg::Viewport *viewport);
~Compositor();
/**
* \brief Create a Compositor from a property tree.
*
* @param view The View where the passes will be added as slaves.
* @param gc The context where the internal osg::Cameras will draw on.
* @param viewport The viewport position and size inside the window.
* @param property_list A valid property list that describes the Compositor.
* @return A Compositor or a null pointer if there was an error.
*/
static Compositor *create(osg::View *view,
osg::GraphicsContext *gc,
osg::Viewport *viewport,
const SGPropertyNode *property_list);
/**
* \overload
* \brief Create a Compositor from a file.
*
* @param name Name of the compositor. The function will search for a file
* named <name>.xml in $FG_ROOT.
*/
static Compositor *create(osg::View *view,
osg::GraphicsContext *gc,
osg::Viewport *viewport,
const std::string &name);
void update(const osg::Matrix &view_matrix,
const osg::Matrix &proj_matrix);
void resized();
bool computeIntersection(
const osg::Vec2d& windowPos,
osgUtil::LineSegmentIntersector::Intersections& intersections);
const osg::GraphicsContext *getGraphicsContext() const { return _gc; }
const osg::Viewport *getViewport() const { return _viewport; }
typedef std::array<
osg::ref_ptr<osg::Uniform>,
TOTAL_BUILTIN_UNIFORMS> BuiltinUniforms;
const BuiltinUniforms &getUniforms() const { return _uniforms; }
void addBuffer(const std::string &name, Buffer *buffer);
void addPass(Pass *pass);
void setName(const std::string &name) { _name = name; }
const std::string &getName() const { return _name; }
typedef std::unordered_map<std::string, osg::ref_ptr<Buffer>> BufferMap;
const BufferMap & getBufferMap() const { return _buffers; }
Buffer * getBuffer(const std::string &name) const;
typedef std::vector<osg::ref_ptr<Pass>> PassList;
const PassList & getPassList() const { return _passes; }
unsigned int getNumPasses() const { return _passes.size(); }
Pass * getPass(size_t index) const { return _passes[index]; }
Pass * getPass(const std::string &name) const;
protected:
friend class PassBuilder;
osg::View *_view;
osg::GraphicsContext *_gc;
osg::ref_ptr<osg::Viewport> _viewport;
std::string _name;
BufferMap _buffers;
PassList _passes;
BuiltinUniforms _uniforms;
};
} // namespace compositor
} // namespace simgear
#endif /* SG_COMPOSITOR_HXX */

View File

@@ -1,228 +0,0 @@
// Copyright (C) 2018 Fernando García Liñán <fernandogarcialinan@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "CompositorBuffer.hxx"
#include <osg/Texture1D>
#include <osg/Texture2D>
#include <osg/Texture2DArray>
#include <osg/Texture2DMultisample>
#include <osg/Texture3D>
#include <osg/TextureRectangle>
#include <osg/TextureCubeMap>
#include <osg/FrameBufferObject>
#include <simgear/props/props.hxx>
#include <simgear/props/vectorPropTemplates.hxx>
#include <simgear/scene/util/OsgMath.hxx>
#include "Compositor.hxx"
#include "CompositorUtil.hxx"
namespace simgear {
namespace compositor {
struct BufferFormat {
GLint internal_format;
GLenum source_format;
GLenum source_type;
};
PropStringMap<BufferFormat> buffer_format_map {
{"rgb8", {GL_RGB8, GL_RGBA, GL_UNSIGNED_BYTE}},
{"rgba8", {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}},
{"rgb16f", {GL_RGB16F_ARB, GL_RGBA, GL_FLOAT}},
{"rgb32f", {GL_RGB32F_ARB, GL_RGBA, GL_FLOAT}},
{"rgba16f", {GL_RGBA16F_ARB, GL_RGBA, GL_FLOAT}},
{"rgba32f", {GL_RGBA32F_ARB, GL_RGBA, GL_FLOAT}},
{"r32f", {GL_R32F, GL_RED, GL_FLOAT}},
{"rg32f", {GL_RG32F, GL_RG, GL_FLOAT}},
{"depth16", {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}},
{"depth24", {GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_FLOAT}},
{"depth32", {GL_DEPTH_COMPONENT32, GL_DEPTH_COMPONENT, GL_FLOAT}},
{"depth32f", {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT}},
{"depth-stencil", { GL_DEPTH24_STENCIL8_EXT, GL_DEPTH_STENCIL_EXT, GL_FLOAT}}
};
PropStringMap<osg::Texture::WrapMode> wrap_mode_map = {
{"clamp", osg::Texture::CLAMP},
{"clamp-to-edge", osg::Texture::CLAMP_TO_EDGE},
{"clamp-to-border", osg::Texture::CLAMP_TO_BORDER},
{"repeat", osg::Texture::REPEAT},
{"mirror", osg::Texture::MIRROR}
};
PropStringMap<osg::Texture::FilterMode> filter_mode_map = {
{"linear", osg::Texture::LINEAR},
{"linear-mipmap-linear", osg::Texture::LINEAR_MIPMAP_LINEAR},
{"linear-mipmap-nearest", osg::Texture::LINEAR_MIPMAP_NEAREST},
{"nearest", osg::Texture::NEAREST},
{"nearest-mipmap-linear", osg::Texture::NEAREST_MIPMAP_LINEAR},
{"nearest-mipmap-nearest", osg::Texture::NEAREST_MIPMAP_NEAREST}
};
PropStringMap<osg::Texture::ShadowTextureMode> shadow_texture_mode_map = {
{"luminance", osg::Texture::LUMINANCE},
{"intensity", osg::Texture::INTENSITY},
{"alpha", osg::Texture::ALPHA}
};
PropStringMap<osg::Texture::ShadowCompareFunc> shadow_compare_func_map = {
{"never", osg::Texture::NEVER},
{"less", osg::Texture::LESS},
{"equal", osg::Texture::EQUAL},
{"lequal", osg::Texture::LEQUAL},
{"greater", osg::Texture::GREATER},
{"notequal", osg::Texture::NOTEQUAL},
{"gequal", osg::Texture::GEQUAL},
{"always", osg::Texture::ALWAYS}
};
Buffer *
buildBuffer(Compositor *compositor, const SGPropertyNode *node)
{
std::string type = node->getStringValue("type");
if (type.empty()) {
SG_LOG(SG_INPUT, SG_ALERT, "buildBuffer: No type specified");
return 0;
}
osg::ref_ptr<Buffer> buffer = new Buffer;
osg::Texture *texture;
int width = 0;
const SGPropertyNode *p_width = getPropertyChild(node, "width");
if (p_width) {
if (p_width->getStringValue() == std::string("screen")) {
buffer->width_scale = 1.0f;
const SGPropertyNode *p_w_scale = getPropertyChild(node, "screen-width-scale");
if (p_w_scale)
buffer->width_scale = p_w_scale->getFloatValue();
width = buffer->width_scale * compositor->getViewport()->width();
} else {
width = p_width->getIntValue();
}
}
int height = 0;
const SGPropertyNode *p_height = getPropertyChild(node, "height");
if (p_height) {
if (p_height->getStringValue() == std::string("screen")) {
buffer->height_scale = 1.0f;
const SGPropertyNode *p_h_scale = getPropertyChild(node, "screen-height-scale");
if (p_h_scale)
buffer->height_scale = p_h_scale->getFloatValue();
height = buffer->height_scale * compositor->getViewport()->height();
} else {
height = p_height->getIntValue();
}
}
int depth = 0;
const SGPropertyNode *p_depth = getPropertyChild(node, "depth");
if (p_depth)
depth = p_depth->getIntValue();
if (type == "1d") {
osg::Texture1D *tex1D = new osg::Texture1D;
tex1D->setTextureWidth(width);
texture = tex1D;
} else if (type == "2d") {
osg::Texture2D *tex2D = new osg::Texture2D;
tex2D->setTextureSize(width, height);
texture = tex2D;
} else if (type == "2d-array") {
osg::Texture2DArray *tex2D_array = new osg::Texture2DArray;
tex2D_array->setTextureSize(width, height, depth);
texture = tex2D_array;
} else if (type == "2d-multisample") {
osg::Texture2DMultisample *tex2DMS = new osg::Texture2DMultisample;
tex2DMS->setTextureSize(width, height);
tex2DMS->setNumSamples(node->getIntValue("num-samples", 0));
texture = tex2DMS;
} else if (type == "3d") {
osg::Texture3D *tex3D = new osg::Texture3D;
tex3D->setTextureSize(width, height, depth);
texture = tex3D;
} else if (type == "rect") {
osg::TextureRectangle *tex_rect = new osg::TextureRectangle;
tex_rect->setTextureSize(width, height);
texture = tex_rect;
} else if (type == "cubemap") {
osg::TextureCubeMap *tex_cubemap = new osg::TextureCubeMap;
tex_cubemap->setTextureSize(width, height);
texture = tex_cubemap;
} else {
SG_LOG(SG_INPUT, SG_ALERT, "Unknown texture type '" << type << "'");
return 0;
}
buffer->texture = texture;
bool resize_npot = node->getBoolValue("resize-npot", false);
texture->setResizeNonPowerOfTwoHint(resize_npot);
BufferFormat format;
if (findPropString(node, "format", format, buffer_format_map)) {
texture->setInternalFormat(format.internal_format);
texture->setSourceFormat(format.source_format);
texture->setSourceType(format.source_type);
} else {
texture->setInternalFormat(GL_RGBA);
SG_LOG(SG_INPUT, SG_WARN, "Unknown buffer format specified, using RGBA");
}
osg::Texture::FilterMode filter_mode = osg::Texture::LINEAR;
findPropString(node, "min-filter", filter_mode, filter_mode_map);
texture->setFilter(osg::Texture::MIN_FILTER, filter_mode);
findPropString(node, "mag-filter", filter_mode, filter_mode_map);
texture->setFilter(osg::Texture::MAG_FILTER, filter_mode);
osg::Texture::WrapMode wrap_mode = osg::Texture::CLAMP_TO_BORDER;
findPropString(node, "wrap-s", wrap_mode, wrap_mode_map);
texture->setWrap(osg::Texture::WRAP_S, wrap_mode);
findPropString(node, "wrap-t", wrap_mode, wrap_mode_map);
texture->setWrap(osg::Texture::WRAP_T, wrap_mode);
findPropString(node, "wrap-r", wrap_mode, wrap_mode_map);
texture->setWrap(osg::Texture::WRAP_R, wrap_mode);
float anis = node->getFloatValue("anisotropy", 1.0f);
texture->setMaxAnisotropy(anis);
osg::Vec4f border_color(0.0f, 0.0f, 0.0f, 0.0f);
const SGPropertyNode *p_border_color = node->getChild("border-color");
if (p_border_color)
border_color = toOsg(p_border_color->getValue<SGVec4d>());
texture->setBorderColor(border_color);
bool shadow_comparison = node->getBoolValue("shadow-comparison", false);
texture->setShadowComparison(shadow_comparison);
if (shadow_comparison) {
osg::Texture::ShadowTextureMode shadow_texture_mode =
osg::Texture::LUMINANCE;
findPropString(node, "shadow-texture-mode",
shadow_texture_mode, shadow_texture_mode_map);
texture->setShadowTextureMode(shadow_texture_mode);
osg::Texture::ShadowCompareFunc shadow_compare_func =
osg::Texture::LEQUAL;
findPropString(node, "shadow-compare-func",
shadow_compare_func, shadow_compare_func_map);
texture->setShadowCompareFunc(shadow_compare_func);
}
return buffer.release();
}
} // namespace compositor
} // namespace simgear

View File

@@ -1,46 +0,0 @@
// Copyright (C) 2018 Fernando García Liñán <fernandogarcialinan@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef SG_COMPOSITOR_BUFFER_HXX
#define SG_COMPOSITOR_BUFFER_HXX
#include <osg/Texture>
class SGPropertyNode;
namespace simgear {
namespace compositor {
class Compositor;
struct Buffer : public osg::Referenced {
Buffer() : width_scale(0.0f), height_scale(0.0f) {}
osg::ref_ptr<osg::Texture> texture;
/**
* The amount to multiply the size of the default framebuffer.
* A factor of 0.0 means that the buffer has a fixed size.
*/
float width_scale, height_scale;
};
Buffer *buildBuffer(Compositor *compositor, const SGPropertyNode *node);
} // namespace compositor
} // namespace simgear
#endif /* SG_COMPOSITOR_BUFFER_HXX */

View File

@@ -1,694 +0,0 @@
// Copyright (C) 2018 Fernando García Liñán <fernandogarcialinan@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "CompositorPass.hxx"
#include <osg/Depth>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/PolygonMode>
#include <osg/io_utils>
#include <simgear/props/vectorPropTemplates.hxx>
#include <simgear/scene/material/EffectGeode.hxx>
#include <simgear/scene/util/OsgMath.hxx>
#include <simgear/scene/util/SGUpdateVisitor.hxx>
#include <simgear/structure/exception.hxx>
#include "ClusteredForward.hxx"
#include "Compositor.hxx"
#include "CompositorUtil.hxx"
namespace simgear {
namespace compositor {
PropStringMap<osg::Camera::BufferComponent> buffer_component_map = {
{"color", osg::Camera::COLOR_BUFFER},
{"color0", osg::Camera::COLOR_BUFFER0},
{"color1", osg::Camera::COLOR_BUFFER1},
{"color2", osg::Camera::COLOR_BUFFER2},
{"color3", osg::Camera::COLOR_BUFFER3},
{"color4", osg::Camera::COLOR_BUFFER4},
{"color5", osg::Camera::COLOR_BUFFER5},
{"color6", osg::Camera::COLOR_BUFFER6},
{"color7", osg::Camera::COLOR_BUFFER7},
{"depth", osg::Camera::DEPTH_BUFFER},
{"stencil", osg::Camera::STENCIL_BUFFER},
{"packed-depth-stencil", osg::Camera::PACKED_DEPTH_STENCIL_BUFFER}
};
Pass *
PassBuilder::build(Compositor *compositor, const SGPropertyNode *root)
{
// The pass index matches its render order
int render_order = root->getIndex();
osg::ref_ptr<Pass> pass = new Pass;
pass->name = root->getStringValue("name");
if (pass->name.empty()) {
SG_LOG(SG_INPUT, SG_WARN, "PassBuilder::build: Pass " << render_order
<< " has no name. It won't be addressable by name!");
}
pass->type = root->getStringValue("type");
std::string eff_override_file = root->getStringValue("effect-override");
if (!eff_override_file.empty())
pass->effect_override = makeEffect(eff_override_file, true, 0);
osg::Camera *camera = new Camera;
pass->camera = camera;
camera->setName(pass->name);
camera->setGraphicsContext(compositor->_gc);
// Even though this camera will be added as a slave to the view, it will
// always be updated manually in Compositor::update()
camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
// Same with the projection matrix
camera->setProjectionResizePolicy(osg::Camera::FIXED);
camera->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR);
// XXX: Should we make this configurable?
camera->setCullingMode(CullSettings::SMALL_FEATURE_CULLING
| CullSettings::VIEW_FRUSTUM_CULLING);
osg::Node::NodeMask cull_mask =
std::stoul(root->getStringValue("cull-mask", "0xffffffff"), nullptr, 0);
pass->cull_mask = cull_mask;
camera->setCullMask(pass->cull_mask);
camera->setCullMaskLeft(pass->cull_mask);
camera->setCullMaskRight(pass->cull_mask);
osg::Vec4f clear_color(0.0f, 0.0f, 0.0f, 0.0f);
const SGPropertyNode *p_clear_color = root->getChild("clear-color");
if (p_clear_color)
clear_color = toOsg(p_clear_color->getValue<SGVec4d>());
camera->setClearColor(clear_color);
osg::Vec4f clear_accum(0.0f, 0.0f, 0.0f, 0.0f);
const SGPropertyNode *p_clear_accum = root->getChild("clear-accum");
if (p_clear_accum)
clear_accum = toOsg(p_clear_accum->getValue<SGVec4d>());
camera->setClearAccum(clear_accum);
camera->setClearDepth(root->getFloatValue("clear-depth", 1.0f));
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
camera->setClearMask(clear_mask);
PropertyList p_bindings = root->getChildren("binding");
for (auto const &p_binding : p_bindings) {
if (!checkConditional(p_binding))
continue;
try {
std::string buffer_name = p_binding->getStringValue("buffer");
if (buffer_name.empty())
throw sg_exception("No buffer specified");
Buffer *buffer = compositor->getBuffer(buffer_name);
if (!buffer)
throw sg_exception(std::string("Unknown buffer '") +
buffer_name + "'");
osg::Texture *texture = buffer->texture;
int unit = p_binding->getIntValue("unit", -1);
if (unit < 0)
throw sg_exception("No texture unit specified");
// Make the texture available to every child of the pass, overriding
// existing units
camera->getOrCreateStateSet()->setTextureAttributeAndModes(
unit,
texture,
osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
} catch (sg_exception &e) {
SG_LOG(SG_INPUT, SG_ALERT, "PassBuilder::build: Skipping binding "
<< p_binding->getIndex() << " in pass " << render_order
<< ": " << e.what());
}
}
PropertyList p_attachments = root->getChildren("attachment");
if (p_attachments.empty()) {
// If there are no attachments, assume the pass is rendering
// directly to the screen
camera->setRenderOrder(osg::Camera::NESTED_RENDER, render_order * 10);
// OSG cameras use the framebuffer by default, but it is stated
// explicitly anyway
camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER);
camera->setDrawBuffer(GL_BACK);
camera->setReadBuffer(GL_BACK);
// Use the physical viewport. We can't let the user choose the viewport
// size because some parts of the window might not be ours.
camera->setViewport(compositor->_viewport);
} else {
// This is a RTT camera
camera->setRenderOrder(osg::Camera::PRE_RENDER, render_order * 10);
camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
bool viewport_absolute = false;
// The index of the attachment to be used as the size of the viewport.
// The one with index 0 is used by default.
int viewport_attachment = 0;
const SGPropertyNode *p_viewport = root->getChild("viewport");
if (p_viewport) {
// The user has manually specified a viewport size
viewport_absolute = p_viewport->getBoolValue("absolute", false);
if (viewport_absolute) {
camera->setViewport(p_viewport->getIntValue("x"),
p_viewport->getIntValue("y"),
p_viewport->getIntValue("width"),
p_viewport->getIntValue("height"));
}
viewport_attachment = p_viewport->getIntValue("use-attachment", 0);
if (!root->getChild("attachment", viewport_attachment)) {
// Let OSG manage the viewport automatically
camera->setViewport(new osg::Viewport);
SG_LOG(SG_INPUT, SG_WARN, "PassBuilder::build: Can't use attachment "
<< viewport_attachment << " to resize the viewport");
}
}
for (auto const &p_attachment : p_attachments) {
if (!checkConditional(p_attachment))
continue;
try {
std::string buffer_name = p_attachment->getStringValue("buffer");
if (buffer_name.empty())
throw sg_exception("No buffer specified");
Buffer *buffer = compositor->getBuffer(buffer_name);
if (!buffer)
throw sg_exception(std::string("Unknown buffer '") +
buffer_name + "'");
osg::Texture *texture = buffer->texture;
osg::Camera::BufferComponent component = osg::Camera::COLOR_BUFFER;
findPropString(p_attachment, "component", component, buffer_component_map);
unsigned int level = p_attachment->getIntValue("level", 0);
unsigned int face = p_attachment->getIntValue("face", 0);
bool mipmap_generation =
p_attachment->getBoolValue("mipmap-generation", false);
unsigned int multisample_samples =
p_attachment->getIntValue("multisample-samples", 0);
unsigned int multisample_color_samples =
p_attachment->getIntValue("multisample-color-samples", 0);
camera->attach(component,
texture,
level,
face,
mipmap_generation,
multisample_samples,
multisample_color_samples);
if (!viewport_absolute &&
(p_attachment->getIndex() == viewport_attachment)) {
if ((buffer->width_scale == 0.0f) &&
(buffer->height_scale == 0.0f)) {
// This is a fixed size pass. We allow the user to use
// relative coordinates to shape the viewport.
float x = p_viewport->getFloatValue("x", 0.0f);
float y = p_viewport->getFloatValue("y", 0.0f);
float width = p_viewport->getFloatValue("width", 1.0f);
float height = p_viewport->getFloatValue("height", 1.0f);
camera->setViewport(x * texture->getTextureWidth(),
y * texture->getTextureHeight(),
width * texture->getTextureWidth(),
height * texture->getTextureHeight());
} else {
// This is a pass that should match the physical viewport
// size. Store the scales so we can resize the pass later
// if the physical viewport changes size.
pass->viewport_width_scale = buffer->width_scale;
pass->viewport_height_scale = buffer->height_scale;
camera->setViewport(
0,
0,
buffer->width_scale * compositor->_viewport->width(),
buffer->height_scale * compositor->_viewport->height());
}
}
} catch (sg_exception &e) {
SG_LOG(SG_INPUT, SG_ALERT, "PassBuilder::build: Skipping attachment "
<< p_attachment->getIndex() << " in pass " << render_order
<< ": " << e.what());
}
}
}
return pass.release();
}
//------------------------------------------------------------------------------
struct QuadPassBuilder : public PassBuilder {
public:
virtual Pass *build(Compositor *compositor, const SGPropertyNode *root) {
osg::ref_ptr<Pass> pass = PassBuilder::build(compositor, root);
osg::Camera *camera = pass->camera;
camera->setAllowEventFocus(false);
camera->setViewMatrix(osg::Matrix::identity());
camera->setProjectionMatrix(osg::Matrix::ortho2D(0, 1, 0, 1));
float left = 0.0f, bottom = 0.0f, width = 1.0f, height = 1.0f, scale = 1.0f;
const SGPropertyNode *p_geometry = root->getNode("geometry");
if (p_geometry) {
left = p_geometry->getFloatValue("left", left);
bottom = p_geometry->getFloatValue("bottom", bottom);
width = p_geometry->getFloatValue("width", width);
height = p_geometry->getFloatValue("height", height);
scale = p_geometry->getFloatValue("scale", scale);
}
const std::string eff_file = root->getStringValue("effect");
osg::ref_ptr<osg::Geode> quad = createFullscreenQuad(
left, bottom, width, height, scale, eff_file);
camera->addChild(quad);
osg::StateSet *ss = camera->getOrCreateStateSet();
for (const auto &uniform : compositor->getUniforms())
ss->addUniform(uniform);
return pass.release();
}
protected:
osg::Geode *createFullscreenQuad(float left,
float bottom,
float width,
float height,
float scale,
const std::string &eff_file) {
osg::Geometry *geom;
// When the quad is fullscreen, it can be optimized by using a
// a fullscreen triangle instead of a quad to avoid discarding pixels
// in the diagonal. If the desired geometry does not occupy the entire
// viewport, this optimization does not occur and a normal quad is drawn
// instead.
if (left != 0.0f || bottom != 0.0f || width != 1.0f || height != 1.0f
|| scale != 1.0f) {
geom = osg::createTexturedQuadGeometry(
osg::Vec3(left, bottom, 0.0f),
osg::Vec3(width, 0.0f, 0.0f),
osg::Vec3(0.0f, height, 0.0f),
0.0f, 0.0f, scale, scale);
} else {
geom = new osg::Geometry;
osg::Vec3Array *coords = new osg::Vec3Array(3);
(*coords)[0].set(0.0f, 2.0f, 0.0f);
(*coords)[1].set(0.0f, 0.0f, 0.0f);
(*coords)[2].set(2.0f, 0.0f, 0.0f);
geom->setVertexArray(coords);
osg::Vec2Array *tcoords = new osg::Vec2Array(3);
(*tcoords)[0].set(0.0f, 2.0f);
(*tcoords)[1].set(0.0f, 0.0f);
(*tcoords)[2].set(2.0f, 0.0f);
geom->setTexCoordArray(0, tcoords);
osg::Vec4Array *colours = new osg::Vec4Array(1);
(*colours)[0].set(1.0f, 1.0f, 1.0, 1.0f);
geom->setColorArray(colours, osg::Array::BIND_OVERALL);
osg::Vec3Array *normals = new osg::Vec3Array(1);
(*normals)[0].set(0.0f, 0.0f, 1.0f);
geom->setNormalArray(normals, osg::Array::BIND_OVERALL);
geom->addPrimitiveSet(new osg::DrawArrays(
osg::PrimitiveSet::TRIANGLES, 0, 3));
}
osg::ref_ptr<EffectGeode> quad = new EffectGeode;
if (!eff_file.empty()) {
Effect *eff = makeEffect(eff_file, true, 0);
if (eff)
quad->setEffect(eff);
}
quad->addDrawable(geom);
quad->setCullingActive(false);
osg::ref_ptr<osg::StateSet> quad_state = quad->getOrCreateStateSet();
int values = osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED;
quad_state->setAttribute(new osg::PolygonMode(
osg::PolygonMode::FRONT_AND_BACK,
osg::PolygonMode::FILL),
values);
quad_state->setMode(GL_LIGHTING, values);
quad_state->setMode(GL_DEPTH_TEST, values);
return quad.release();
}
};
RegisterPassBuilder<QuadPassBuilder> registerQuadPass("quad");
//------------------------------------------------------------------------------
class LightFinder : public osg::NodeVisitor {
public:
LightFinder(const std::string &name) :
osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
_name(name) {}
virtual void apply(osg::Node &node) {
// Only traverse the scene graph if we haven't found a light yet (or if
// the one we found earlier is no longer valid).
if (getLight().valid())
return;
if (node.getName() == _name) {
osg::LightSource *light_source =
dynamic_cast<osg::LightSource *>(&node);
if (light_source)
_light = light_source->getLight();
}
traverse(node);
}
osg::ref_ptr<osg::Light> getLight() const {
osg::ref_ptr<osg::Light> light_ref;
_light.lock(light_ref);
return light_ref;
}
protected:
std::string _name;
osg::observer_ptr<osg::Light> _light;
};
struct ShadowMapUpdateCallback : public Pass::PassUpdateCallback {
public:
ShadowMapUpdateCallback(const std::string &light_name,
float near_m, float far_m,
const std::string &suffix,
int sm_width, int sm_height) :
_light_finder(new LightFinder(light_name)),
_near_m(near_m),
_far_m(far_m) {
_light_matrix_uniform = new osg::Uniform(
osg::Uniform::FLOAT_MAT4, std::string("fg_LightMatrix_") + suffix);
_half_sm_size = osg::Vec2d((double)sm_width, (double)sm_height) / 2.0;
}
virtual void updatePass(Pass &pass,
const osg::Matrix &view_matrix,
const osg::Matrix &proj_matrix) {
osg::Camera *camera = pass.camera;
// Look for the light
camera->accept(*_light_finder);
osg::ref_ptr<osg::Light> light = _light_finder->getLight();
if (!light) {
// We could not find any light
return;
}
osg::Vec4 light_pos = light->getPosition();
if (light_pos.w() != 0.0) {
// We only support directional light sources for now
return;
}
osg::Vec3 light_dir =
osg::Vec3(light_pos.x(), light_pos.y(), light_pos.z());
// The light direction we've just queried is from the previous frame.
// This is because the position of the osg::LightSource gets updated
// during the update traversal, and this function happens before that
// in the SubsystemMgr update.
// This is not a problem though (for now).
osg::Matrix view_inverse = osg::Matrix::inverse(view_matrix);
// Calculate the light's point of view transformation matrices.
// Taken from Project Rembrandt.
double left, right, bottom, top, zNear, zFar;
proj_matrix.getFrustum(left, right, bottom, top, zNear, zFar);
osg::BoundingSphere bs;
bs.expandBy(osg::Vec3(left, bottom, -zNear) * (_near_m / zNear));
bs.expandBy(osg::Vec3(right, top, -zNear) * (_far_m / zNear));
bs.expandBy(osg::Vec3(left, bottom, -zNear) * (_far_m / zNear));
bs.expandBy(osg::Vec3(right, top, -zNear) * (_near_m / zNear));
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,
aim);
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);
// Do texel snapping to prevent flickering or shimmering.
// We are using double precision vectors and matrices because in FG
// world coordinates are relative to the center of the Earth, which can
// (and will) cause precision issues due to their magnitude.
osg::Vec4d shadow_origin4 = osg::Vec4d(0.0, 0.0, 0.0, 1.0) *
light_view_matrix * light_proj_matrix;
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 rounding = rounded_origin - shadow_origin;
rounding = osg::Vec2d(rounding.x() / _half_sm_size.x(),
rounding.y() / _half_sm_size.y());
osg::Matrixd round_matrix = osg::Matrixd::translate(
rounding.x(), rounding.y(), 0.0);
light_proj_matrix *= round_matrix;
osg::Matrixf light_matrix =
// Include the real camera inverse view matrix because if the shader
// used world coordinates, there would be precision issues.
view_inverse *
camera->getViewMatrix() *
camera->getProjectionMatrix() *
// Bias matrices
osg::Matrix::translate(1.0, 1.0, 1.0) *
osg::Matrix::scale(0.5, 0.5, 0.5);
_light_matrix_uniform->set(light_matrix);
}
osg::Uniform *getLightMatrixUniform() const {
return _light_matrix_uniform.get();
}
protected:
osg::ref_ptr<LightFinder> _light_finder;
float _near_m;
float _far_m;
osg::ref_ptr<osg::Uniform> _light_matrix_uniform;
osg::Vec2d _half_sm_size;
};
struct ShadowMapPassBuilder : public PassBuilder {
virtual Pass *build(Compositor *compositor, const SGPropertyNode *root) {
osg::ref_ptr<Pass> pass = PassBuilder::build(compositor, root);
pass->useMastersSceneData = true;
osg::Camera *camera = pass->camera;
camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT);
std::string light_name = root->getStringValue("light-name");
float near_m = root->getFloatValue("near-m");
float far_m = root->getFloatValue("far-m");
int sm_width = camera->getViewport()->width();
int sm_height = camera->getViewport()->height();
pass->update_callback = new ShadowMapUpdateCallback(
light_name,
near_m, far_m,
pass->name,
sm_width, sm_height);
return pass.release();
}
};
RegisterPassBuilder<ShadowMapPassBuilder> registerShadowMapPass("shadow-map");
//------------------------------------------------------------------------------
class SceneUpdateCallback : public Pass::PassUpdateCallback {
public:
SceneUpdateCallback(int cubemap_face, float zNear, float zFar) :
_cubemap_face(cubemap_face),
_zNear(zNear),
_zFar(zFar) {}
virtual void updatePass(Pass &pass,
const osg::Matrix &view_matrix,
const osg::Matrix &proj_matrix) {
osg::Camera *camera = pass.camera;
if (_cubemap_face < 0) {
camera->setViewMatrix(view_matrix);
camera->setProjectionMatrix(proj_matrix);
} else {
osg::Vec3 camera_pos = osg::Vec3(0.0, 0.0, 0.0) *
osg::Matrix::inverse(view_matrix);
typedef std::pair<osg::Vec3, osg::Vec3> CubemapFace;
const CubemapFace id[] = {
CubemapFace(osg::Vec3( 1, 0, 0), osg::Vec3( 0, -1, 0)), // +X
CubemapFace(osg::Vec3(-1, 0, 0), osg::Vec3( 0, -1, 0)), // -X
CubemapFace(osg::Vec3( 0, 1, 0), osg::Vec3( 0, 0, 1)), // +Y
CubemapFace(osg::Vec3( 0, -1, 0), osg::Vec3( 0, 0, -1)), // -Y
CubemapFace(osg::Vec3( 0, 0, 1), osg::Vec3( 0, -1, 0)), // +Z
CubemapFace(osg::Vec3( 0, 0, -1), osg::Vec3( 0, -1, 0)) // -Z
};
osg::Matrix cubemap_view_matrix;
cubemap_view_matrix.makeLookAt(camera_pos,
camera_pos + id[_cubemap_face].first,
camera_pos + id[_cubemap_face].second);
camera->setViewMatrix(cubemap_view_matrix);
camera->setProjectionMatrixAsFrustum(-1.0, 1.0, -1.0, 1.0,
1.0, 10000.0);
}
if (_zNear != 0.0f && _zFar != 0.0f) {
osg::Matrix new_proj;
makeNewProjMat(camera->getProjectionMatrix(),
_zNear, _zFar, new_proj);
camera->setProjectionMatrix(new_proj);
}
}
protected:
// Given a projection matrix, return a new one with the same frustum
// sides and new near / far values.
void makeNewProjMat(Matrixd& oldProj, double znear,
double zfar, Matrixd& projection) {
projection = oldProj;
// Slightly inflate the near & far planes to avoid objects at the
// extremes being clipped out.
znear *= 0.999;
zfar *= 1.001;
// Clamp the projection matrix z values to the range (near, far)
double epsilon = 1.0e-6;
if (fabs(projection(0,3)) < epsilon &&
fabs(projection(1,3)) < epsilon &&
fabs(projection(2,3)) < epsilon) {
// Projection is Orthographic
epsilon = -1.0/(zfar - znear); // Used as a temp variable
projection(2,2) = 2.0*epsilon;
projection(3,2) = (zfar + znear)*epsilon;
} else {
// Projection is Perspective
double trans_near = (-znear*projection(2,2) + projection(3,2)) /
(-znear*projection(2,3) + projection(3,3));
double trans_far = (-zfar*projection(2,2) + projection(3,2)) /
(-zfar*projection(2,3) + projection(3,3));
double ratio = fabs(2.0/(trans_near - trans_far));
double center = -0.5*(trans_near + trans_far);
projection.postMult(osg::Matrixd(1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, ratio, 0.0,
0.0, 0.0, center*ratio, 1.0));
}
}
int _cubemap_face;
float _zNear;
float _zFar;
};
struct ScenePassBuilder : public PassBuilder {
public:
virtual Pass *build(Compositor *compositor, const SGPropertyNode *root) {
osg::ref_ptr<Pass> pass = PassBuilder::build(compositor, root);
pass->useMastersSceneData = true;
pass->inherit_cull_mask = true;
osg::Camera *camera = pass->camera;
camera->setAllowEventFocus(true);
const SGPropertyNode *clustered = root->getChild("clustered-forward");
if (clustered) {
camera->setInitialDrawCallback(new ClusteredForwardDrawCallback);
}
int cubemap_face = root->getIntValue("cubemap-face", -1);
float zNear = root->getFloatValue("z-near", 0.0f);
float zFar = root->getFloatValue("z-far", 0.0f);
pass->update_callback = new SceneUpdateCallback(cubemap_face, zNear, zFar);
std::string shadow_pass_name = root->getStringValue("use-shadow-pass");
if (!shadow_pass_name.empty()) {
Pass *shadow_pass = compositor->getPass(shadow_pass_name);
if (shadow_pass) {
ShadowMapUpdateCallback *updatecb =
dynamic_cast<ShadowMapUpdateCallback *>(
shadow_pass->update_callback.get());
if (updatecb) {
camera->getOrCreateStateSet()->addUniform(
updatecb->getLightMatrixUniform());
} else {
SG_LOG(SG_INPUT, SG_WARN, "ScenePassBuilder::build: Pass '"
<< shadow_pass_name << "is not a shadow pass");
}
} else {
SG_LOG(SG_INPUT, SG_WARN, "ScenePassBuilder::build: Could not "
"find shadow pass named '" << shadow_pass_name << "'");
}
}
return pass.release();
}
};
RegisterPassBuilder<ScenePassBuilder> registerScenePass("scene");
//------------------------------------------------------------------------------
Pass *
buildPass(Compositor *compositor, const SGPropertyNode *root)
{
std::string type = root->getStringValue("type");
if (type.empty()) {
SG_LOG(SG_INPUT, SG_ALERT, "buildPass: Unspecified pass type");
return 0;
}
PassBuilder *builder = PassBuilder::find(type);
if (!builder) {
SG_LOG(SG_INPUT, SG_ALERT, "buildPass: Unknown pass type '"
<< type << "'");
return 0;
}
return builder->build(compositor, root);
}
} // namespace compositor
} // namespace simgear

View File

@@ -1,133 +0,0 @@
// Copyright (C) 2018 Fernando García Liñán <fernandogarcialinan@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef SG_COMPOSITOR_PASS_HXX
#define SG_COMPOSITOR_PASS_HXX
#include <unordered_map>
#include <osg/Camera>
#include <osg/View>
#include <simgear/scene/material/Effect.hxx>
#include <simgear/structure/Singleton.hxx>
#include <simgear/props/props.hxx>
namespace simgear {
namespace compositor {
class Compositor;
/**
* A Pass encapsulates a single render operation. In an OSG context, this is
* best represented as a Camera attached to the Viewer as a slave camera.
*
* Passes can render directly to the framebuffer or to a texture via FBOs. Also,
* the OpenGL state can be modified via the Effects framework and by exposing RTT
* textures from previous passes.
*
* Every pass can be enabled and disabled via a property tree conditional
* expression. This allows dynamic rendering pipelines where features can be
* enabled or disabled in a coherent way by the user.
*/
struct Pass : public osg::Referenced {
Pass() :
useMastersSceneData(false),
cull_mask(0xffffff),
inherit_cull_mask(false),
viewport_width_scale(0.0f),
viewport_height_scale(0.0f) {}
std::string name;
std::string type;
osg::ref_ptr<osg::Camera> camera;
/** If null, there is no effect override for this pass. */
osg::ref_ptr<Effect> effect_override;
bool useMastersSceneData;
osg::Node::NodeMask cull_mask;
/** Whether the cull mask is ANDed with the view master camera cull mask. */
bool inherit_cull_mask;
float viewport_width_scale;
float viewport_height_scale;
struct PassUpdateCallback : public virtual osg::Referenced {
public:
virtual void updatePass(Pass &pass,
const osg::Matrix &view_matrix,
const osg::Matrix &proj_matrix) = 0;
};
osg::ref_ptr<PassUpdateCallback> update_callback;
};
class PassBuilder : public SGReferenced {
public:
virtual ~PassBuilder() {}
/**
* \brief Build a pass.
*
* By default, this function implements commonly used features such as
* input/output buffers, conditional support etc., but can be safely ignored
* and overrided for more special passes.
*
* @param compositor The Compositor instance that owns the pass.
* @param The root node of the pass property tree.
* @return A Pass or a null pointer if an error occurred.
*/
virtual Pass *build(Compositor *compositor, const SGPropertyNode *root);
static PassBuilder *find(const std::string &type) {
auto itr = PassBuilderMapSingleton::instance()->_map.find(type);
if (itr == PassBuilderMapSingleton::instance()->_map.end())
return 0;
return itr->second.ptr();
}
protected:
typedef std::unordered_map<std::string, SGSharedPtr<PassBuilder>> PassBuilderMap;
struct PassBuilderMapSingleton : public Singleton<PassBuilderMapSingleton> {
PassBuilderMap _map;
};
template <typename T>
friend struct RegisterPassBuilder;
};
/**
* An instance of this type registers a new pass type T with a name.
* A global instance of this class must be created in CompositorPass.cxx to
* register a new pass type.
*/
template <typename T>
struct RegisterPassBuilder {
RegisterPassBuilder(const std::string &name) {
PassBuilder::PassBuilderMapSingleton::instance()->
_map.insert(std::make_pair(name, new T));
}
};
/**
* \brief Create a pass from a property tree definition.
*
* @param comp The Compositor instance that owns the pass.
* @param node The root node of the pass property tree.
* @return A Pass or a null pointer if an error occurred.
*/
Pass *buildPass(Compositor *compositor, const SGPropertyNode *root);
} // namespace compositor
} // namespace simgear
#endif /* SG_COMPOSITOR_PASS_HXX */

View File

@@ -1,62 +0,0 @@
// Copyright (C) 2018 Fernando García Liñán <fernandogarcialinan@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include <simgear/props/condition.hxx>
#include <simgear/props/props.hxx>
#include <simgear/scene/tgdb/userdata.hxx>
#include "CompositorUtil.hxx"
namespace simgear {
namespace compositor {
bool
checkConditional(const SGPropertyNode *node)
{
const SGPropertyNode *p_condition = node->getChild("condition");
if (!p_condition)
return true;
SGSharedPtr<SGCondition> condition =
sgReadCondition(getPropertyRoot(), p_condition);
return !condition || condition->test();
}
const SGPropertyNode *
getPropertyNode(const SGPropertyNode *prop)
{
if (!prop)
return 0;
if (prop->nChildren() > 0) {
const SGPropertyNode *propertyProp = prop->getChild("property");
if (!propertyProp)
return prop;
return getPropertyRoot()->getNode(propertyProp->getStringValue());
}
return prop;
}
const SGPropertyNode *
getPropertyChild(const SGPropertyNode *prop,
const char *name)
{
const SGPropertyNode *child = prop->getChild(name);
if (!child)
return 0;
return getPropertyNode(child);
}
} // namespace compositor
} // namespace simgear

View File

@@ -1,72 +0,0 @@
// Copyright (C) 2018 Fernando García Liñán <fernandogarcialinan@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef SG_COMPOSITOR_UTIL_HXX
#define SG_COMPOSITOR_UTIL_HXX
#include <unordered_map>
namespace simgear {
namespace compositor {
/**
* Lookup table that ties a string property value to a type that cannot be
* represented in the property tree. Useful for OSG or OpenGL enums.
*/
template<class T>
using PropStringMap = std::unordered_map<std::string, T>;
template <class T>
bool findPropString(const std::string &str,
T &value,
const PropStringMap<T> &map)
{
auto itr = map.find(str);
if (itr == map.end())
return false;
value = itr->second;
return true;
}
template <class T>
bool findPropString(const SGPropertyNode *parent,
const std::string &child_name,
T &value,
const PropStringMap<T> &map)
{
const SGPropertyNode *child = parent->getNode(child_name);
if (child) {
if (findPropString<T>(child->getStringValue(), value, map))
return true;
}
return false;
}
/**
* Check if node should be enabled based on a condition tag.
* If no condition tag is found inside or it is malformed, it will be enabled.
*/
bool checkConditional(const SGPropertyNode *node);
const SGPropertyNode *getPropertyNode(const SGPropertyNode *prop);
const SGPropertyNode *getPropertyChild(const SGPropertyNode *prop,
const char *name);
} // namespace compositor
} // namespace simgear
#endif /* SG_COMPOSITOR_UTIL_HXX */

View File

@@ -65,16 +65,16 @@ namespace
else if (numChannels == 2 && bitsPerSample == 16) rv = SG_SAMPLE_STEREO16;
else if (numChannels == 2 && bitsPerSample == 8) rv = SG_SAMPLE_STEREO8;
else {
char msg[81];
snprintf(msg, 80, "Unsupported audio format: tracks: %i, bits/sample: %i", numChannels, bitsPerSample);
char msg[65];
snprintf(msg, 64, "Unsupported audio format: tracks: %i, bits/sample: %i", numChannels, bitsPerSample);
throw sg_exception(msg);
}
} else {
if (numChannels == 1 && bitsPerSample == 4) rv = SG_SAMPLE_ADPCM;
else if (numChannels == 1 && bitsPerSample == 8) rv = SG_SAMPLE_MULAW;
else {
char msg[81];
snprintf(msg, 80, "Unsupported compressed audio format: tracks: %i, bits/sample: %i", numChannels, bitsPerSample);
char msg[65];
snprintf(msg, 64, "Unsupported compressed audio format: tracks: %i, bits/sample: %i", numChannels, bitsPerSample);
throw sg_exception(msg);
}
}

View File

@@ -51,10 +51,7 @@ SGPerformanceMonitor::bind(void)
{
_statiticsSubsystems = _root->getChild("subsystems", 0, true);
_statisticsFlag = _root->getChild("enabled", 0, true);
_timingDetailsFlag = _root->getChild("dump-stats", 0, true);
_timingDetailsFlag->setBoolValue(false);
_statisticsInterval = _root->getChild("interval-s", 0, true);
_maxTimePerFrame_ms = _root->getChild("max-time-per-frame-ms", 0, true);
}
void
@@ -63,7 +60,6 @@ SGPerformanceMonitor::unbind(void)
_statiticsSubsystems = 0;
_statisticsFlag = 0;
_statisticsInterval = 0;
_maxTimePerFrame_ms = 0;
}
void
@@ -87,10 +83,7 @@ SGPerformanceMonitor::update(double dt)
else
_subSysMgr->setReportTimingCb(this,0);
}
if (_timingDetailsFlag->getBoolValue()) {
_subSysMgr->setReportTimingStats(true);
_timingDetailsFlag->setBoolValue(false);
}
if (!_isEnabled)
return;
@@ -101,9 +94,6 @@ SGPerformanceMonitor::update(double dt)
_subSysMgr->reportTiming();
_lastUpdate.stamp();
}
if (_maxTimePerFrame_ms) {
SGSubsystem::maxTimePerFrame_ms = _maxTimePerFrame_ms->getIntValue();
}
}
/** Callback hooked into the subsystem manager. */

View File

@@ -52,10 +52,8 @@ private:
SGSubsystemMgr* _subSysMgr;
SGPropertyNode_ptr _root;
SGPropertyNode_ptr _statiticsSubsystems;
SGPropertyNode_ptr _timingDetailsFlag;
SGPropertyNode_ptr _statisticsFlag;
SGPropertyNode_ptr _statisticsInterval;
SGPropertyNode_ptr _maxTimePerFrame_ms;
bool _isEnabled;
int _count;

View File

@@ -22,11 +22,7 @@
#include "SGSharedPtr.hxx"
#include <boost/type_traits/is_base_of.hpp>
#if BOOST_VERSION >= 105600
#include <boost/core/enable_if.hpp>
#else
#include <boost/utility/enable_if.hpp>
#endif
#ifdef _MSC_VER
# pragma warning(push)

View File

@@ -20,7 +20,7 @@
*/
#include <simgear_config.h>
#include "StateMachine.hxx"
#include <algorithm>
@@ -32,13 +32,13 @@
#include <simgear/props/condition.hxx>
#include <simgear/timing/timestamp.hxx>
#include <simgear/structure/exception.hxx>
namespace simgear
{
typedef std::vector<StateMachine::State_ptr> StatePtrVec;
static void readBindingList(SGPropertyNode* desc, const std::string& name,
static void readBindingList(SGPropertyNode* desc, const std::string& name,
SGPropertyNode* root, SGBindingList& result)
{
for (auto b : desc->getChildren(name)) {
@@ -54,8 +54,8 @@ class StateMachine::State::StatePrivate
{
public:
std::string _name;
SGBindingList _updateBindings,
_entryBindings,
SGBindingList _updateBindings,
_entryBindings,
_exitBindings;
};
@@ -71,14 +71,14 @@ public:
bool _excludeTarget;
SGSharedPtr<SGCondition> _condition;
};
///////////////////////////////////////////////////////////////////////////
class StateMachine::StateMachinePrivate : public SGPropertyChangeListener
{
public:
StateMachinePrivate(StateMachine* p) : _p(p) { }
void computeEligibleTransitions()
{
_eligible.clear();
@@ -88,7 +88,7 @@ public:
}
}
}
StateMachine* _p;
bool _initialised;
State_ptr _currentState;
@@ -96,14 +96,14 @@ public:
std::vector<Transition_ptr> _transitions;
std::vector<Transition*> _eligible;
SGTimeStamp _timeInState;
bool _listenerLockout; ///< block our listener when self-updating props
virtual void valueChanged(SGPropertyNode* changed)
{
if (_listenerLockout) {
return;
}
if (changed == _currentStateIndex) {
State_ptr s = _p->stateByIndex(changed->getIntValue());
_p->changeToState(s);
@@ -111,7 +111,7 @@ public:
_p->changeToStateName(changed->getStringValue());
}
}
// exposed properties
SGPropertyNode_ptr _root;
SGPropertyNode_ptr _currentStateIndex;
@@ -144,12 +144,12 @@ void StateMachine::State::update()
void StateMachine::State::fireEntryBindings()
{
fireBindingList(d->_entryBindings);
}
}
void StateMachine::State::fireExitBindings()
{
fireBindingList(d->_exitBindings);
}
}
void StateMachine::State::addUpdateBinding(SGBinding* aBinding)
{
@@ -184,38 +184,38 @@ StateMachine::Transition::~Transition()
StateMachine::State* StateMachine::Transition::target() const
{
return d->_target;
}
}
void StateMachine::Transition::addSourceState(State* aSource)
{
if (aSource == d->_target) { // should this be disallowed outright?
SG_LOG(SG_GENERAL, SG_WARN, d->_name << ": adding target state as source");
}
d->_sourceStates.insert(aSource);
}
}
bool StateMachine::Transition::applicableForState(State* aCurrent) const
{
if (d->_excludeTarget && (aCurrent == d->_target)) {
return false;
}
if (d->_sourceStates.empty()) {
return true;
}
return d->_sourceStates.count(aCurrent);
}
}
bool StateMachine::Transition::evaluate() const
{
return d->_condition->test();
}
}
void StateMachine::Transition::fireBindings()
{
fireBindingList(d->_bindings);
}
}
std::string StateMachine::Transition::name() const
{
@@ -231,12 +231,12 @@ void StateMachine::Transition::addBinding(SGBinding* aBinding)
{
d->_bindings.push_back(aBinding);
}
void StateMachine::Transition::setExcludeTarget(bool aExclude)
{
d->_excludeTarget = aExclude;
}
///////////////////////////////////////////////////////////////////////////
StateMachine::StateMachine() :
@@ -249,7 +249,7 @@ StateMachine::StateMachine() :
StateMachine::~StateMachine()
{
}
void StateMachine::init()
@@ -257,23 +257,23 @@ void StateMachine::init()
if (d->_initialised) {
return;
}
if (d->_states.empty()) {
throw sg_range_exception("StateMachine::init: no states defined");
}
d->_currentStateIndex = d->_root->getChild("current-index", 0, true);
d->_currentStateIndex->setIntValue(0);
d->_currentStateName = d->_root->getChild("current-name", 0, true);
d->_currentStateName->setStringValue("");
d->_currentStateIndex->addChangeListener(d.get());
d->_currentStateName->addChangeListener(d.get());
d->_timeInStateProp = d->_root->getChild("elapsed-time-msec", 0, true);
d->_timeInStateProp->setIntValue(0);
// TODO go to default state if found
innerChangeState(d->_states[0], NULL);
d->_initialised = true;
@@ -283,23 +283,20 @@ void StateMachine::shutdown()
{
d->_currentStateIndex->removeChangeListener(d.get());
d->_currentStateName->removeChangeListener(d.get());
}
void StateMachine::innerChangeState(State_ptr aState, Transition_ptr aTrans)
{
if (d->_currentState) {
d->_currentState->fireExitBindings();
SG_LOG(SG_GENERAL, SG_INFO, "Changing from state " << d->_currentState->name() << " to state:" << aState->name());
} else {
SG_LOG(SG_GENERAL, SG_INFO, "Initializing to state:" << aState->name());
}
// fire bindings before we change the state, hmmmm
// fire bindings before we change the state, hmmmm
if (aTrans) {
aTrans->fireBindings();
}
// update our private state and properties
d->_listenerLockout = true;
d->_currentState = aState;
@@ -308,11 +305,11 @@ void StateMachine::innerChangeState(State_ptr aState, Transition_ptr aTrans)
d->_currentStateIndex->setIntValue(indexOfState(aState));
d->_timeInStateProp->setIntValue(0);
d->_listenerLockout = false;
// fire bindings
d->_currentState->fireEntryBindings();
d->_currentState->update();
d->computeEligibleTransitions();
}
@@ -322,11 +319,11 @@ void StateMachine::changeToState(State_ptr aState, bool aOnlyIfDifferent)
if (std::find(d->_states.begin(), d->_states.end(), aState) == d->_states.end()) {
throw sg_exception("Requested change to state not in machine");
}
if (aOnlyIfDifferent && (aState == d->_currentState)) {
return;
}
innerChangeState(aState, NULL);
}
@@ -336,7 +333,7 @@ void StateMachine::changeToStateName(const std::string& aName, bool aOnlyIfDiffe
if (!st) {
throw sg_range_exception("unknown state:" + aName);
}
changeToState(st, aOnlyIfDifferent);
}
@@ -344,7 +341,7 @@ StateMachine::State_ptr StateMachine::state() const
{
return d->_currentState;
}
SGPropertyNode* StateMachine::root()
{
return d->_root;
@@ -355,27 +352,27 @@ void StateMachine::update(double aDt)
// do this first, for triggers which depend on time in current state
// (spring-loaded transitions)
d->_timeInStateProp->setIntValue(d->_timeInState.elapsedMSec());
Transition_ptr trigger;
for (auto trans : d->_eligible) {
if (trans->evaluate()) {
if (trigger != Transition_ptr()) {
SG_LOG(SG_GENERAL, SG_WARN, "ambiguous transitions! "
SG_LOG(SG_GENERAL, SG_WARN, "ambiguous transitions! "
<< trans->name() << " or " << trigger->name());
}
trigger = trans;
}
}
if (trigger != Transition_ptr()) {
SG_LOG(SG_GENERAL, SG_DEBUG, "firing transition:" << trigger->name());
innerChangeState(trigger->target(), trigger);
}
d->_currentState->update();
}
}
StateMachine::State_ptr StateMachine::findStateByName(const std::string& aName) const
{
@@ -384,7 +381,7 @@ StateMachine::State_ptr StateMachine::findStateByName(const std::string& aName)
return sp;
}
}
SG_LOG(SG_GENERAL, SG_WARN, "unknown state:" << aName);
return State_ptr();
}
@@ -394,17 +391,17 @@ StateMachine::State_ptr StateMachine::stateByIndex(unsigned int aIndex) const
if (aIndex >= d->_states.size()) {
throw sg_range_exception("invalid state index, out of bounds");
}
return d->_states[aIndex];
}
int StateMachine::indexOfState(State_ptr aState) const
{
StatePtrVec::const_iterator it = std::find(d->_states.begin(), d->_states.end(), aState);
if (it == d->_states.end()) {
return -1;
}
return it - d->_states.begin();
}
@@ -413,7 +410,7 @@ StateMachine::State_ptr StateMachine::createState(const std::string& aName)
if (findStateByName(aName) != NULL) {
throw sg_range_exception("duplicate state name");
}
State_ptr st = new State(aName);
addState(st);
return st;
@@ -434,83 +431,38 @@ void StateMachine::initFromPlist(SGPropertyNode* desc, SGPropertyNode* root)
d->_root = root->getNode(path, 0, true);
assert(d->_root);
}
int stateCount = 0;
for (auto stateDesc : desc->getChildren("state")) {
stateCount++;
std::string nm = stateDesc->getStringValue("name");
if (nm.empty()) {
SG_LOG(SG_GENERAL, SG_ALERT, "No name found for state in branch " << path);
throw sg_exception("No name element in state");
}
State_ptr st(new State(nm));
readBindingList(stateDesc, "enter", root, st->d->_entryBindings);
readBindingList(stateDesc, "update", root, st->d->_updateBindings);
readBindingList(stateDesc, "exit", root, st->d->_exitBindings);
addState(st);
} // of states iteration
if (stateCount < 2) {
SG_LOG(SG_GENERAL, SG_ALERT, "Fewer than two state elements found in branch " << path);
throw sg_exception("Fewer than two state elements found.");
}
for (auto tDesc : desc->getChildren("transition")) {
std::string nm = tDesc->getStringValue("name");
std::string target_id = tDesc->getStringValue("target");
if (nm.empty()) {
SG_LOG(SG_GENERAL, SG_ALERT, "No name found for transition in branch " << path);
throw sg_exception("No name element in transition");
}
if (target_id.empty()) {
SG_LOG(SG_GENERAL, SG_ALERT, "No target element in transition "
<< nm << " in state branch " << path);
throw sg_exception("No target element in transition");
}
State_ptr target = findStateByName(target_id);
if (target == NULL) {
SG_LOG(SG_GENERAL, SG_ALERT, "Unknown target state " << target_id << " in transition "
<< nm << " in state branch " << path);
throw sg_exception("No condition element in transition");
}
if (tDesc->getChild("condition") == NULL) {
SG_LOG(SG_GENERAL, SG_ALERT, "No condition element in transition "
<< nm << " in state branch " << path);
throw sg_exception("No condition element in transition");
}
State_ptr target = findStateByName(tDesc->getStringValue("target"));
SGCondition* cond = sgReadCondition(root, tDesc->getChild("condition"));
Transition_ptr t(new Transition(nm, target));
t->setTriggerCondition(cond);
t->setExcludeTarget(tDesc->getBoolValue("exclude-target", true));
for (auto src : tDesc->getChildren("source")) {
State_ptr srcState = findStateByName(src->getStringValue());
if (srcState == NULL) {
SG_LOG(SG_GENERAL, SG_ALERT, "Unknown source state " << src->getStringValue() << " in transition "
<< nm << " in state branch " << path);
throw sg_exception("No condition element in transition");
}
t->addSourceState(srcState);
}
readBindingList(tDesc, "binding", root, t->d->_bindings);
addTransition(t);
} // of states iteration
init();
}

View File

@@ -47,7 +47,7 @@ SGEventMgr::SGEventMgr() :
_inited(false),
_shutdown(false)
{
_name = "EventMgr";
}
SGEventMgr::~SGEventMgr()
@@ -86,10 +86,10 @@ void SGEventMgr::shutdown()
void SGEventMgr::update(double delta_time_sec)
{
_simQueue.update(delta_time_sec, _timerStats);
_simQueue.update(delta_time_sec);
double rt = _rtProp ? _rtProp->getDoubleValue() : 0;
_rtQueue.update(rt, _timerStats);
_rtQueue.update(rt);
}
void SGEventMgr::removeTask(const std::string& name)
@@ -169,23 +169,19 @@ void SGTimerQueue::clear()
_table[i].timer = 0;
}
}
int maxTimerQueuePerItem_us = 30;
void SGTimerQueue::update(double deltaSecs, std::map<std::string, double> &timingStats)
void SGTimerQueue::update(double deltaSecs)
{
_now += deltaSecs;
while (_numEntries && nextTime() <= _now) {
while(_numEntries && nextTime() <= _now) {
SGTimer* t = remove();
if (t->repeat)
if(t->repeat)
insert(t, t->interval);
// warning: this is not thread safe
// but the entire timer queue isn't either
SGTimeStamp timeStamp;
timeStamp.stamp();
t->running = true;
t->run();
t->running = false;
timingStats[t->name] += timeStamp.elapsedMSec() / 1000.0;
if (!t->repeat)
delete t;
}

View File

@@ -24,8 +24,9 @@ class SGTimerQueue {
public:
SGTimerQueue(int preSize=1);
~SGTimerQueue();
void clear();
void update(double deltaSecs, std::map<std::string, double> &timingStats);
void update(double deltaSecs);
double now() { return _now; }
@@ -77,6 +78,7 @@ public:
virtual void update(double delta_time_sec);
virtual void unbind();
virtual void shutdown();
void setRealtimeProperty(SGPropertyNode* node) { _rtProp = node; }
/**

View File

@@ -45,10 +45,8 @@ using State = SGSubsystem::State;
SGSubsystemTimingCb SGSubsystem::reportTimingCb = NULL;
void* SGSubsystem::reportTimingUserData = NULL;
bool SGSubsystem::reportTimingStatsRequest = false;
int SGSubsystem::maxTimePerFrame_ms = 7;
SGSubsystem::SGSubsystem () : _executionTime(0), _lastExecutionTime(0)
SGSubsystem::SGSubsystem ()
{
}
@@ -204,20 +202,16 @@ std::string SGSubsystem::nameForState(State s)
class SGSubsystemGroup::Member
{
private:
Member(const Member &member);
Member (const Member &member);
public:
Member();
Member ();
~Member ();
void update (double delta_time_sec);
void reportTiming(void) { if (reportTimingCb) reportTimingCb(reportTimingUserData, name, &timeStat); }
void reportTimingStats(TimerStats *_lastValues) {
if (subsystem)
subsystem->reportTimingStats(_lastValues);
}
void updateExecutionTime(double time) { timeStat += time;}
SampleStatistic timeStat;
std::string name;
SGSubsystemRef subsystem;
@@ -226,18 +220,15 @@ public:
bool collectTimeStats;
int exceptionCount;
int initTime;
void mergeTimerStats(SGSubsystem::TimerStats &stats);
};
SGSubsystemGroup::SGSubsystemGroup(const char *name) :
_fixedUpdateTime(-1.0),
_updateTimeRemainder(0.0),
_initPosition(-1)
SGSubsystemGroup::SGSubsystemGroup () :
_fixedUpdateTime(-1.0),
_updateTimeRemainder(0.0),
_initPosition(-1)
{
_name = name;
}
SGSubsystemGroup::~SGSubsystemGroup ()
@@ -290,7 +281,7 @@ SGSubsystemGroup::incrementalInit()
notifyDidChange(m->subsystem, State::INIT);
++_initPosition;
if (_initPosition < static_cast<int>(_members.size())) {
if (_initPosition < _members.size()) {
// start init of the next one
notifyWillChange( _members[_initPosition]->subsystem, State::INIT);
}
@@ -386,145 +377,21 @@ SGSubsystemGroup::update (double delta_time_sec)
const bool recordTime = (reportTimingCb != nullptr);
SGTimeStamp timeStamp;
TimerStats lvTimerStats(_timerStats);
TimerStats overrunItems;
bool overrun = false;
SGTimeStamp outerTimeStamp;
outerTimeStamp.stamp();
while (loopCount-- > 0) {
for (auto member : _members) {
if (recordTime)
timeStamp = SGTimeStamp::now();
timeStamp.stamp();
if (member->subsystem->_timerStats.size()) {
member->subsystem->_lastTimerStats.clear();
member->subsystem->_lastTimerStats.insert(member->subsystem->_timerStats.begin(), member->subsystem->_timerStats.end());
}
member->update(delta_time_sec); // indirect call
if (member->name.size())
_timerStats[member->name] += timeStamp.elapsedMSec() / 1000.0;
if (recordTime && reportTimingCb) {
member->updateExecutionTime(timeStamp.elapsedMSec()*1000);
if (timeStamp.elapsedMSec() > SGSubsystemMgr::maxTimePerFrame_ms) {
overrunItems[member->name] += timeStamp.elapsedMSec();
overrun = true;
}
timeStamp = SGTimeStamp::now() - timeStamp;
member->updateExecutionTime(timeStamp.toUSecs());
}
}
} // of multiple update loop
_lastExecutionTime = _executionTime;
_executionTime += outerTimeStamp.elapsedMSec();
if (overrun) {
for (auto overrunItem : overrunItems) {
SG_LOG(SG_EVENT, SG_ALERT, "Subsystem "
<< overrunItem.first
<< " total "
<< std::setw(6) << std::fixed << std::setprecision(2) << std::right << _timerStats[overrunItem.first]
<< "s overrun "
<< std::setw(6) << std::fixed << std::setprecision(2) << std::right << overrunItem.second
<< "ms");
auto m = std::find_if(_members.begin(), _members.end(), [overrunItem](const Member* m) {
if (m->name == overrunItem.first)
return true;
return false;
});
if (m != _members.end()) {
auto member = *m;
if (overrunItems[member->name]) {
TimerStats sst;
member->reportTimingStats(&_lastTimerStats);
//if (lvTimerStats[member->name] != _timerStats[member->name]) {
// SG_LOG(SG_EVENT, SG_ALERT,
// " +" << std::setw(6) << std::left << (_timerStats[member->name] - lvTimerStats[member->name])
// << " total " << std::setw(6) << std::left << _timerStats[member->name]
// << " " << member->name
// );
//}
}
}
}
}
if (reportTimingStatsRequest) {
reportTimingStats(nullptr);
//for (auto member : _members) {
// member->mergeTimerStats(_timerStats);
// if (_timerStats.size()) {
// SG_LOG(SG_EVENT, SG_ALERT, "" << std::setw(6) << std::fixed << std::setprecision(2) << std::left << _timerStats[member->name]
// << ": " << member->name);
// for (auto item : _timerStats) {
// if (item.second > 0)
// SG_LOG(SG_EVENT, SG_ALERT, " " << std::setw(6) << std::left << item.second << " " << item.first);
// }
// }
//}
}
_lastTimerStats.clear();
_lastTimerStats.insert(_timerStats.begin(), _timerStats.end());
}
void SGSubsystem::reportTimingStats(TimerStats *__lastValues) {
std::string _name = "";
bool reportDeltas = __lastValues != nullptr;
__lastValues = &_lastTimerStats;
std::ostringstream t;
if (reportDeltas) {
auto deltaT = _executionTime - _lastExecutionTime;
if (deltaT != 0) {
t << name() << "(+" << std::setprecision(2) << std::right << deltaT << "ms).";
_name = t.str();
}
}
else {
SG_LOG(SG_EVENT, SG_ALERT, "SubSystem: " << _name << " " << std::setw(6) << std::setprecision(4) << std::right << _executionTime / 1000.0 << "s");
}
for (auto item : _timerStats) {
std::ostringstream output;
if (item.second > 0) {
if (reportDeltas)
{
auto delta = item.second - (*__lastValues)[item.first];
if (delta != 0) {
output
<< " +" << std::setw(6) << std::setprecision(4) << std::left << (delta * 1000.0)
<< _name << item.first
<< " total " << std::setw(6) << std::setprecision(4) << std::right << item.second << "s "
;
}
}
else
output << " " << std::setw(6) << std::setprecision(4) << std::right << item.second << "s " << item.first;
if (output.str().size())
SG_LOG(SG_EVENT, SG_ALERT, output.str());
}
}
}
void SGSubsystemGroup::reportTimingStats(TimerStats *_lastValues) {
SGSubsystem::reportTimingStats(_lastValues);
std::string _name = name();
if (!_name.size())
_name = typeid(this).name();
if (_lastValues) {
auto deltaT = _executionTime - _lastExecutionTime;
if (deltaT != 0) {
SG_LOG(SG_EVENT, SG_ALERT,
" +" << std::setw(6) << std::setprecision(4) << std::right << deltaT << "ms "
<< name() );
}
}
else
SG_LOG(SG_EVENT, SG_ALERT, "SubSystemGroup: " << name() << " " << std::setw(6) << std::setprecision(4) << std::right << _executionTime / 1000.0 << "s");
for (auto member : _members) {
member->reportTimingStats(_lastValues);
}
_lastTimerStats.clear();
_lastTimerStats.insert(_timerStats.begin(), _timerStats.end());
}
void
SGSubsystemGroup::reportTiming(void)
{
@@ -742,7 +609,6 @@ auto SGSubsystemGroup::get_member(const string &name, bool create) -> Member*
Member* m = new Member;
m->name = name;
_timerStats[name] = 0;
_members.push_back(m);
return _members.back();
}
@@ -787,11 +653,6 @@ SGSubsystemGroup::Member::Member (const Member &)
SGSubsystemGroup::Member::~Member ()
{
}
void SGSubsystemGroup::Member::mergeTimerStats(SGSubsystem::TimerStats &stats) {
stats.insert(subsystem->_timerStats.begin(), subsystem->_timerStats.end());
//for (auto ts : subsystem->_timerStats)
// ts.second = 0;
}
void
SGSubsystemGroup::Member::update (double delta_time_sec)
@@ -805,15 +666,10 @@ SGSubsystemGroup::Member::update (double delta_time_sec)
return;
}
SGTimeStamp oTimer;
try {
oTimer.stamp();
subsystem->update(elapsed_sec);
subsystem->_lastExecutionTime = subsystem->_executionTime;
subsystem->_executionTime += oTimer.elapsedMSec();
elapsed_sec = 0;
}
catch (sg_exception& e) {
subsystem->update(elapsed_sec);
elapsed_sec = 0;
} catch (sg_exception& e) {
SG_LOG(SG_GENERAL, SG_ALERT, "caught exception processing subsystem:" << name
<< "\nmessage:" << e.getMessage());
@@ -837,7 +693,7 @@ namespace {
} // end of anonymous namespace
SGSubsystemMgr::SGSubsystemMgr (const char *name) :
SGSubsystemMgr::SGSubsystemMgr () :
_groups(MAX_GROUPS)
{
if (global_defaultSubsystemManager == nullptr) {
@@ -854,7 +710,7 @@ SGSubsystemMgr::SGSubsystemMgr (const char *name) :
#endif
for (int i = 0; i < MAX_GROUPS; i++) {
auto g = new SGSubsystemGroup(name);
auto g = new SGSubsystemGroup;
g->set_manager(this);
_groups[i].reset(g);
}
@@ -890,7 +746,6 @@ SGSubsystemMgr::init ()
_groups[i]->init();
}
SGSubsystem::InitStatus
SGSubsystemMgr::incrementalInit()
{
@@ -947,12 +802,9 @@ SGSubsystemMgr::unbind ()
void
SGSubsystemMgr::update (double delta_time_sec)
{
SGTimeStamp timeStamp;
for (int i = 0; i < MAX_GROUPS; i++) {
_groups[i]->update(delta_time_sec);
}
reportTimingStatsRequest = false;
}
void

View File

@@ -130,8 +130,7 @@ typedef void (*SGSubsystemTimingCb)(void* userData, const std::string& name, Sam
class SGSubsystem : public SGReferenced
{
public:
using TimerStats = std::map<std::string, double>;
/**
/**
* Default constructor.
*/
SGSubsystem ();
@@ -271,7 +270,6 @@ public:
*/
void reportTiming(void);
virtual void reportTimingStats(TimerStats *_lastValues);
/**
* Place time stamps at strategic points in the execution of subsystems
* update() member functions. Predominantly for debugging purposes.
@@ -325,20 +323,6 @@ public:
* debug helper, print a state as a string
*/
static std::string nameForState(State s);
/**
* gets fine grained stats of time elapsed since last clear
* returns map of ident and time
*/
virtual const TimerStats &getTimerStats() {
return _timerStats;
}
/**
* clear fine grained stats that are over the specified value.
*/
virtual void resetTimerStats(double val = 0) { }
protected:
friend class SGSubsystemMgr;
friend class SGSubsystemGroup;
@@ -358,16 +342,9 @@ protected:
static SGSubsystemTimingCb reportTimingCb;
static void* reportTimingUserData;
static bool reportTimingStatsRequest;
static int maxTimePerFrame_ms;
private:
SGSubsystemGroup* _group = nullptr;
protected:
TimerStats _timerStats, _lastTimerStats;
double _executionTime;
double _lastExecutionTime;
};
typedef SGSharedPtr<SGSubsystem> SGSubsystemRef;
@@ -378,7 +355,7 @@ typedef SGSharedPtr<SGSubsystem> SGSubsystemRef;
class SGSubsystemGroup : public SGSubsystem
{
public:
SGSubsystemGroup (const char *name);
SGSubsystemGroup ();
virtual ~SGSubsystemGroup ();
void init() override;
@@ -403,7 +380,6 @@ public:
bool remove_subsystem (const std::string &name);
virtual bool has_subsystem (const std::string &name) const;
void reportTimingStats(TimerStats *_lastValues) override;
/**
* Remove all subsystems.
*/
@@ -501,7 +477,7 @@ public:
MAX_GROUPS
};
SGSubsystemMgr (const char *name);
SGSubsystemMgr ();
virtual ~SGSubsystemMgr ();
void init () override;
@@ -534,8 +510,7 @@ public:
SGSubsystem* get_subsystem(const std::string &name, const std::string& instanceName) const;
void reportTiming();
void setReportTimingCb(void* userData, SGSubsystemTimingCb cb) { reportTimingCb = cb; reportTimingUserData = userData; }
void setReportTimingStats(bool v) { reportTimingStatsRequest = v; }
void setReportTimingCb(void* userData,SGSubsystemTimingCb cb) {reportTimingCb = cb;reportTimingUserData = userData;}
/**
* @brief set the root property node for this subsystem manager

View File

@@ -90,7 +90,7 @@ class InstrumentGroup : public SGSubsystemGroup
{
public:
static const char* subsystemName() { return "instruments"; }
InstrumentGroup() : SGSubsystemGroup(InstrumentGroup::subsystemName()) {}
virtual ~InstrumentGroup()
{
}
@@ -164,7 +164,7 @@ SGSubsystemMgr::InstancedRegistrant<FakeRadioSub> registrant3(SGSubsystemMgr::PO
void testRegistrationAndCreation()
{
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr("TEST1");
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr;
auto anotherSub = manager->create<AnotherSub>();
SG_VERIFY(anotherSub);
@@ -181,7 +181,7 @@ void testRegistrationAndCreation()
void testAddGetRemove()
{
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr("TEST1");
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr;
auto d = new RecorderDelegate;
manager->addDelegate(d);
@@ -228,7 +228,7 @@ void testAddGetRemove()
void testSubGrouping()
{
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr("TEST1");
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr;
auto d = new RecorderDelegate;
manager->addDelegate(d);
@@ -299,7 +299,7 @@ void testSubGrouping()
void testIncrementalInit()
{
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr("TEST");
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr;
auto d = new RecorderDelegate;
manager->addDelegate(d);
@@ -347,7 +347,7 @@ void testEmptyGroup()
// https://sourceforge.net/p/flightgear/codetickets/2043/
// when an empty group is inited, we skipped setting the state
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr("TEST");
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr;
auto d = new RecorderDelegate;
manager->addDelegate(d);
@@ -371,7 +371,7 @@ void testEmptyGroup()
void testSuspendResume()
{
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr("TEST");
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr;
auto d = new RecorderDelegate;
manager->addDelegate(d);
@@ -441,7 +441,7 @@ void testSuspendResume()
void testPropertyRoot()
{
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr("TEST");
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr;
SGPropertyNode_ptr props(new SGPropertyNode);
manager->set_root_node(props);
@@ -467,7 +467,7 @@ void testPropertyRoot()
void testAddRemoveAfterInit()
{
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr("TEST");
SGSharedPtr<SGSubsystemMgr> manager = new SGSubsystemMgr;
auto d = new RecorderDelegate;
manager->addDelegate(d);

View File

@@ -72,33 +72,13 @@ static clockid_t getClockId()
#endif
#ifdef _WIN32
static bool qpc_init = false;
static LARGE_INTEGER s_frequency;
static BOOL s_use_qpc;
#endif
void SGTimeStamp::stamp()
{
#ifdef _WIN32
if (!qpc_init) {
s_use_qpc = QueryPerformanceFrequency(&s_frequency);
qpc_init = true;
}
if (qpc_init && s_use_qpc) {
LARGE_INTEGER now;
QueryPerformanceCounter(&now);
_sec = now.QuadPart / s_frequency.QuadPart;
_nsec = (1000000000LL * (now.QuadPart - _sec * s_frequency.QuadPart)) / s_frequency.QuadPart;
}
else {
unsigned int t;
t = timeGetTime();
_sec = t / 1000;
_nsec = (t - (_sec * 1000)) * 1000 * 1000;
}
unsigned int t;
t = timeGetTime();
_sec = t / 1000;
_nsec = ( t - ( _sec * 1000 ) ) * 1000 * 1000;
#elif defined(_POSIX_TIMERS) && (0 < _POSIX_TIMERS)
struct timespec ts;
clock_gettime(getClockId(), &ts);

View File

@@ -1 +1 @@
2019.1.2
2018.3.4