Compare commits
20 Commits
version/20
...
version/20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b3c048363 | ||
|
|
01f689a0e4 | ||
|
|
b155f2e40f | ||
|
|
10ab8b830a | ||
|
|
8a4871db83 | ||
|
|
9a8c10cb0b | ||
|
|
fd32023437 | ||
|
|
c1ee4a9172 | ||
|
|
a5b32f8eb2 | ||
|
|
4a86368c8f | ||
|
|
3730cc48a5 | ||
|
|
8a55c2f44f | ||
|
|
ef1cbae22b | ||
|
|
61f322f201 | ||
|
|
becbef96f5 | ||
|
|
89b3fadf0f | ||
|
|
4a1a9ea9c1 | ||
|
|
efc609577f | ||
|
|
6ffc501566 | ||
|
|
9785cadbd0 |
@@ -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;
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -160,7 +160,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
int32_t readInt()
|
||||
float readInt()
|
||||
{
|
||||
unsigned int* p = reinterpret_cast<unsigned int*>(ptr + offset);
|
||||
if ( sgIsBigEndian() ) {
|
||||
|
||||
@@ -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 ) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ foreach( mylibfolder
|
||||
tgdb
|
||||
util
|
||||
tsync
|
||||
viewer
|
||||
)
|
||||
|
||||
add_subdirectory(${mylibfolder})
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -111,11 +111,6 @@ public:
|
||||
if (transform->getNumChildren())
|
||||
_group->addChild(transform.get());
|
||||
}
|
||||
virtual void apply(BVHPageNode& leaf)
|
||||
{
|
||||
leaf.traverse(*this);
|
||||
}
|
||||
|
||||
virtual void apply(BVHLineGeometry&)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -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>();
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -40,10 +40,6 @@
|
||||
|
||||
SGSceneFeatures::SGSceneFeatures() :
|
||||
_textureCompression(UseARBCompression),
|
||||
_MaxTextureSize(4096),
|
||||
_TextureCacheCompressionActive(true),
|
||||
_TextureCacheCompressionActiveTransparent(true),
|
||||
_TextureCacheActive(false),
|
||||
_shaderLights(true),
|
||||
_pointSpriteLights(true),
|
||||
_distanceAttenuationLights(true),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}")
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user