diff --git a/CMakeLists.txt b/CMakeLists.txt index 04c20c51..2a2e849f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -125,9 +125,14 @@ option(ENABLE_SOUND "Set to OFF to disable building SimGear's sound support" option(USE_AEONWAVE "Set to ON to use AeonWave instead of OpenAL" ON) option(ENABLE_PKGUTIL "Set to ON to build the sg_pkgutil application (default)" ON) option(ENABLE_DNS "Set to ON to use udns library and DNS service resolver" ON) -option(ENABLE_SIMD "Enable SSE/SSE2 support for x86 compilers" ON) +option(ENABLE_SIMD "Enable SSE/SSE2 support for compilers" ON) +option(ENABLE_SIMD_CODE "Enable SSE/SSE2 support code for compilers" OFF) option(ENABLE_OPENMP "Enable OpenMP compiler support" OFF) +if (NOT ENABLE_SIMD AND ENABLE_SIMD_CODE) + set(ENABLE_SIMD_CODE OFF) +endif() + include (DetectArch) # until the fstream fix is applied and generally available in OSG, @@ -412,8 +417,8 @@ if(CMAKE_COMPILER_IS_GNUCXX) "${CMAKE_CXX_FLAGS} -O0 -fno-omit-frame-pointer -fno-inline") elseif (ENABLE_SIMD) if (X86 OR X86_64) - set(CMAKE_C_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse") - set(CMAKE_CXX_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse") + set(CMAKE_C_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse -ftree-vectorize -ftree-slp-vectorize") + set(CMAKE_CXX_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse -ftree-vectorize -ftree-slp-vectorize") endif() endif() @@ -440,8 +445,8 @@ if (CLANG) "${CMAKE_CXX_FLAGS} -O0 -fno-omit-frame-pointer -fno-inline-functions") elseif (ENABLE_SIMD) if (X86 OR X86_64) - set(CMAKE_C_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse") - set(CMAKE_CXX_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse") + set(CMAKE_C_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse -ftree-vectorize -ftree-slp-vectorize") + set(CMAKE_CXX_FLAGS_RELEASE "-O3 -msse2 -mfpmath=sse -ftree-vectorize -ftree-slp-vectorize") endif() endif() endif() diff --git a/simgear/canvas/ShivaVG/src/shPath.c b/simgear/canvas/ShivaVG/src/shPath.c index 835fcd08..9c2c1ba2 100644 --- a/simgear/canvas/ShivaVG/src/shPath.c +++ b/simgear/canvas/ShivaVG/src/shPath.c @@ -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; + SHuint8 *newSegs, *newData=0; void *userData[4]; SHint segment1, segment2; SHint segindex, s,d,i; diff --git a/simgear/canvas/elements/CanvasText.cxx b/simgear/canvas/elements/CanvasText.cxx index ab226be3..3051045f 100644 --- a/simgear/canvas/elements/CanvasText.cxx +++ b/simgear/canvas/elements/CanvasText.cxx @@ -71,8 +71,12 @@ namespace canvas canvas::Text *_text_element; - void computePositions(unsigned int contextID) const override; - }; +#if OSG_VERSION_LESS_THAN(3,5,6) + void computePositions(unsigned int contextID) const override; +#else + void computePositionsImplementation() override; +#endif +}; class TextLine { @@ -122,6 +126,8 @@ 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); @@ -133,6 +139,10 @@ 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 } //---------------------------------------------------------------------------- @@ -162,33 +172,35 @@ namespace canvas if( empty() ) return pos; #if OSG_VERSION_LESS_THAN(3,3,5) - GlyphQuads::Coords2 const& coords = _quads->_coords; -#else - GlyphQuads::Coords2 refCoords = _quads->_coords; - GlyphQuads::Coords2::element_type &coords = *refCoords.get(); -#endif - size_t global_i = _begin + i; + 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; + 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 - // position at center between characters - pos.x() = 0.5 * (prev_r + cur_l); - } + { + 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); + } +#else +//OSG:TODO: need 3.5.7 version of this. +#endif return pos; } @@ -200,12 +212,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; -#else +#elif OSG_VERSION_LESS_THAN(3,5,6) + 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() @@ -225,6 +237,10 @@ namespace canvas } return cursorPos(i - _begin); +#else +//OSG:TODO: need 3.5.7 version of this. + return cursorPos(0); +#endif } //---------------------------------------------------------------------------- @@ -640,7 +656,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 ) @@ -710,6 +726,14 @@ namespace canvas return osgText::Text::computePositions(contextID); } +#else + void Text::TextOSG::computePositionsImplementation() + { + TextBase::computePositionsImplementation(); + } +#endif + //---------------------------------------------------------------------------- + //---------------------------------------------------------------------------- const std::string Text::TYPE_NAME = "text"; diff --git a/simgear/debug/logstream.cxx b/simgear/debug/logstream.cxx index eeda8901..93ba61b5 100644 --- a/simgear/debug/logstream.cxx +++ b/simgear/debug/logstream.cxx @@ -518,7 +518,7 @@ public: // 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 (p == SG_OSG) return true; + if (static_cast(p) == static_cast(SG_OSG)) return true; p = translatePriority(p); if (p >= SG_INFO) return true; diff --git a/simgear/environment/precipitation.cxx b/simgear/environment/precipitation.cxx index 1a2b8130..97e58d3a 100644 --- a/simgear/environment/precipitation.cxx +++ b/simgear/environment/precipitation.cxx @@ -44,6 +44,10 @@ SGPrecipitation::SGPrecipitation() : void SGPrecipitation::setEnabled( bool value ) { _enabled = value; + if (!_enabled) { + _precipitationEffect->snow(0); + _precipitationEffect->rain(0); + } } void SGPrecipitation::setDropletExternal( bool value ) @@ -64,6 +68,9 @@ bool SGPrecipitation::getEnabled() const */ osg::Group* SGPrecipitation::build(void) { + if (!_enabled) + return nullptr; + osg::ref_ptr group = new osg::Group; _precipitationEffect->snow(0); @@ -227,6 +234,9 @@ 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; diff --git a/simgear/io/HTTPClient.cxx b/simgear/io/HTTPClient.cxx index fe6fa737..0c31354b 100644 --- a/simgear/io/HTTPClient.cxx +++ b/simgear/io/HTTPClient.cxx @@ -273,6 +273,10 @@ 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()) { diff --git a/simgear/io/sg_file.cxx b/simgear/io/sg_file.cxx index be629e7e..fcba988e 100644 --- a/simgear/io/sg_file.cxx +++ b/simgear/io/sg_file.cxx @@ -64,6 +64,31 @@ SGFile::SGFile( int existingFd ) : SGFile::~SGFile() { } +#include +#include +#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(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 ) { diff --git a/simgear/io/sg_file.hxx b/simgear/io/sg_file.hxx index 613f097c..59de6ba7 100644 --- a/simgear/io/sg_file.hxx +++ b/simgear/io/sg_file.hxx @@ -87,6 +87,9 @@ public: /** @return true of eof conditions exists */ virtual bool eof() const { return eof_flag; }; + + std::string computeHash(); + }; class SGBinaryFile : public SGFile { diff --git a/simgear/math/simd.hxx b/simgear/math/simd.hxx index a2847775..162820ac 100644 --- a/simgear/math/simd.hxx +++ b/simgear/math/simd.hxx @@ -309,7 +309,7 @@ inline simd4_t operator*(simd4_t v, T f) { return v; } -#ifdef ENABLE_SIMD +#ifdef ENABLE_SIMD_CODE # ifdef __SSE__ namespace simd4 @@ -1305,7 +1305,7 @@ inline simd4_t max(simd4_t v1, const simd4_t& v2) { # endif -#endif /* ENABLE_SIMD */ +#endif /* ENABLE_SIMD_CODE */ #endif /* __SIMD_H__ */ diff --git a/simgear/math/simd4x4.hxx b/simgear/math/simd4x4.hxx index 69203511..26db560f 100644 --- a/simgear/math/simd4x4.hxx +++ b/simgear/math/simd4x4.hxx @@ -289,7 +289,7 @@ inline simd4x4_t operator*(const simd4x4_t& m1, const simd4x4_t& } -#ifdef ENABLE_SIMD +#ifdef ENABLE_SIMD_CODE # ifdef __SSE__ template<> @@ -1191,7 +1191,7 @@ inline simd4_t transform(const simd4x4_t& m, const simd4_t_type == props::ALIAS) ? p->_value.alias : nullptr)) { + if (p == this) return false; + } + clearValue(); get(target); _value.alias = target; @@ -973,7 +978,7 @@ SGPropertyNode::alias (SGPropertyNode * target) if (!target) { SG_LOG(SG_GENERAL, SG_ALERT, - "Failed to create alias for " << getPath() << ". " + "Failed to set alias " << getPath() << ". " "The target property does not exist."); } else @@ -981,15 +986,15 @@ SGPropertyNode::alias (SGPropertyNode * target) { if (_value.alias == target) return true; // ok, identical alias requested - SG_LOG(SG_GENERAL, SG_ALERT, - "Failed to create alias at " << target->getPath() << ". " - "Source "<< getPath() << " is already aliasing another property."); + SG_LOG(SG_GENERAL, SG_ALERT, "alias(): "<< getPath() << + " is already pointing to " << _value.alias->getPath() << + " so it cannot alias '" << target->getPath() << ". Use unalias() first."); } else if (_tied) { - SG_LOG(SG_GENERAL, SG_ALERT, "Failed to create alias at " << target->getPath() << ". " - "Source " << getPath() << " is a tied property."); + SG_LOG(SG_GENERAL, SG_ALERT, "alias(): " << getPath() << + " is a tied property. It cannot alias " << target->getPath() << "."); } return false; diff --git a/simgear/scene/material/Effect.cxx b/simgear/scene/material/Effect.cxx index 70792a7a..72f5fba1 100644 --- a/simgear/scene/material/Effect.cxx +++ b/simgear/scene/material/Effect.cxx @@ -830,6 +830,9 @@ void reload_shaders() if (!fileName.empty()) { shader->loadShaderSourceFromFile(fileName); } + else + SG_LOG(SG_INPUT, SG_ALERT, "Could not locate shader: " << fileName); + } } @@ -917,8 +920,13 @@ 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); @@ -1405,8 +1413,11 @@ 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 g(realizeTechniques_lock); + if (_isRealized) return true; PropertyList tniqList = root->getChildren("technique"); diff --git a/simgear/scene/material/TextureBuilder.cxx b/simgear/scene/material/TextureBuilder.cxx index fa571912..43343169 100644 --- a/simgear/scene/material/TextureBuilder.cxx +++ b/simgear/scene/material/TextureBuilder.cxx @@ -76,7 +76,7 @@ osg::Texture* TextureBuilder::buildFromType(Effect* effect, Pass* pass, const st typedef boost::tuple TexTuple; + string, MipMapTuple, ImageInternalFormat> TexTuple; EffectNameValue texEnvModesInit[] = { @@ -239,6 +239,17 @@ 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 ); @@ -246,7 +257,7 @@ TexTuple makeTexTuple(Effect* effect, const SGPropertyNode* props, mipmapFunctions = makeMipMapTuple(effect, pMipmapControl, options); return TexTuple(absFileName, minFilter, magFilter, sWrap, tWrap, rWrap, - texType, mipmapFunctions); + texType, mipmapFunctions, iformat); } bool setAttrs(const TexTuple& attrs, Texture* tex, @@ -257,7 +268,19 @@ bool setAttrs(const TexTuple& attrs, Texture* tex, return false; 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 image; if (result.success()) image = result.getImage(); @@ -585,36 +608,64 @@ 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,0) 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,0) 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,0) 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,0) 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,0) 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,0) 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; @@ -634,7 +685,11 @@ Texture* CubeMapBuilder::build(Effect* effect, Pass* pass, const SGPropertyNode* return cubeTexture.release(); osgDB::ReaderWriter::ReadResult result; +#if OSG_VERSION_LESS_THAN(3,4,0) 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 diff --git a/simgear/scene/material/mat.cxx b/simgear/scene/material/mat.cxx index 916d4cbd..0eaa37a6 100644 --- a/simgear/scene/material/mat.cxx +++ b/simgear/scene/material/mat.cxx @@ -227,7 +227,11 @@ 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; diff --git a/simgear/scene/material/mipmap.hxx b/simgear/scene/material/mipmap.hxx index 0a908c59..75b29231 100644 --- a/simgear/scene/material/mipmap.hxx +++ b/simgear/scene/material/mipmap.hxx @@ -31,14 +31,18 @@ class Effect; class SGReaderWriterOptions; namespace effect { -enum MipMapFunction { - AUTOMATIC, - AVERAGE, - SUM, - PRODUCT, - MIN, - MAX -}; + enum MipMapFunction { + AUTOMATIC, + AVERAGE, + SUM, + PRODUCT, + MIN, + MAX + }; + enum ImageInternalFormat { + Unspecified, + Normalized, + }; typedef boost::tuple MipMapTuple; diff --git a/simgear/scene/model/BVHDebugCollectVisitor.hxx b/simgear/scene/model/BVHDebugCollectVisitor.hxx index e99779ff..8e7be665 100644 --- a/simgear/scene/model/BVHDebugCollectVisitor.hxx +++ b/simgear/scene/model/BVHDebugCollectVisitor.hxx @@ -111,6 +111,11 @@ public: if (transform->getNumChildren()) _group->addChild(transform.get()); } + virtual void apply(BVHPageNode& leaf) + { + leaf.traverse(*this); + } + virtual void apply(BVHLineGeometry&) { } diff --git a/simgear/scene/model/BVHPageNodeOSG.cxx b/simgear/scene/model/BVHPageNodeOSG.cxx index e8051847..a2f15bcb 100644 --- a/simgear/scene/model/BVHPageNodeOSG.cxx +++ b/simgear/scene/model/BVHPageNodeOSG.cxx @@ -215,7 +215,11 @@ public: if (pagedLOD.getMinRange(i) <= 0) { osg::ref_ptr 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; @@ -256,7 +260,11 @@ public: osg::ref_ptr options; options = getOptions(proxyNode.getDatabaseOptions(), proxyNode.getDatabasePath()); osg::ref_ptr 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()) @@ -353,7 +361,11 @@ SGSharedPtr BVHPageNodeOSG::load(const std::string& name, const osg::ref_ptr& options) { osg::ref_ptr node; +#if OSG_VERSION_LESS_THAN(3,4,0) + node = osgDB::readNodeFile(name, dynamic_cast(options.get())); +#else node = osgDB::readRefNodeFile(name, dynamic_cast(options.get())); +#endif if (!node.valid()) return SGSharedPtr(); diff --git a/simgear/scene/model/ModelRegistry.cxx b/simgear/scene/model/ModelRegistry.cxx index 4451fd79..ca2beb1f 100644 --- a/simgear/scene/model/ModelRegistry.cxx +++ b/simgear/scene/model/ModelRegistry.cxx @@ -60,6 +60,8 @@ #include #include #include +#include +#include #include "BoundingVolumeBuildVisitor.hxx" #include "model.hxx" @@ -180,9 +182,34 @@ public: } // namespace -static bool isPowerOfTwo(int width, int height) +static int nearestPowerOfTwo(unsigned int _v) { - return (((width & (width - 1)) == 0) && ((height & (height - 1))) == 0); + // 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) @@ -205,6 +232,139 @@ osg::Image* getImageByName(const std::string& filename) return nullptr; } #endif +// a cache which evicts the least recently used item when it is full +#include +#include +#include + +#include +template +class lru_cache +{ +public: + SGMutex _mutex; + + typedef Key key_type; + typedef Value value_type; + typedef std::list list_type; + typedef std::map< + key_type, + std::pair + > 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 scopeLock(_mutex); + return m_map.find(key) != m_map.end(); + } + + void insert(const key_type &key, const value_type &value) + { + SGGuard 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 findValue(const std::string &requiredValue) + { + SGGuard 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 get(const key_type &key) + { + SGGuard 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 scopeLock(_mutex); + m_map.clear(); + m_list.clear(); + } + +private: + void evict() + { + SGGuard 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, @@ -248,6 +408,8 @@ ModelRegistry::readImage(const string& fileName, if (cache_active) { if (fileExtension != "dds" && fileExtension != "gz") { + const SGReaderWriterOptions* sgoptC = dynamic_cast(opt); + std::string root = getPathRoot(absFileName); std::string prr = getPathRelative(root, absFileName); std::string cache_root = SGSceneFeatures::instance()->getTextureCompressionPath().c_str(); @@ -255,99 +417,231 @@ ModelRegistry::readImage(const string& fileName, SGPath file(absFileName); std::stringstream tstream; - tstream << std::hex << file.modTime(); - newName += "." + tstream.str(); - newName += ".cache.dds"; - if (!fileExists(newName)) { - res = registry->readImageImplementation(absFileName, opt); + // 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 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); + hash = f.computeHash(); + filename_hash_cache.insert(absFileName, hash); + boost::optional cacheFilename = filename_hash_cache.findValue(hash); - if (res.validImage()) { - osg::ref_ptr srcImage = res.getImage(); - int width = srcImage->s(); - bool transparent = srcImage->isImageTranslucent(); - int height = srcImage->t(); - - if (height >= max_texture_size) - { - SG_LOG(SG_IO, SG_WARN, "Image texture too high " << width << "," << height << absFileName); - osg::ref_ptr resizedImage; - int factor = height / max_texture_size; - if (ImageUtils::resizeImage(srcImage, width / factor, height / factor, resizedImage)) - srcImage = resizedImage; - width = srcImage->s(); - height = srcImage->t(); - } - if (width >= max_texture_size) - { - SG_LOG(SG_IO, SG_WARN, "Image texture too wide " << width << "," << height << absFileName); - osg::ref_ptr resizedImage; - int factor = width / max_texture_size; - if (ImageUtils::resizeImage(srcImage, width / factor, height / factor, resizedImage)) - srcImage = resizedImage; - width = srcImage->s(); - height = srcImage->t(); - } - - // - // only cache power of two textures that are of a reasonable size - if (width >= 64 && height >= 64 && isPowerOfTwo(width, height)) { - 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 nopt; - nopt = opt->cloneOptions(); - std::string optionstring = nopt->getOptionString(); - - if (!optionstring.empty()) - optionstring += " "; - - nopt->setOptionString(optionstring + "ddsNoAutoFlipWrite"); - - /* - * decide if we need to compress this. - */ - bool compress = (transparent && compress_transparent) || (!transparent && compress_solid); - - if (compress) { - if (processor) - { - if (transparent) - processor->compress(*srcImage, osg::Texture::USE_S3TC_DXT5_COMPRESSION, true, true, osgDB::ImageProcessor::USE_CPU, osgDB::ImageProcessor::PRODUCTION); - else - processor->compress(*srcImage, osg::Texture::USE_S3TC_DXT1_COMPRESSION, true, true, osgDB::ImageProcessor::USE_CPU, osgDB::ImageProcessor::PRODUCTION); - //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: " << newName); - srcImage = simgear::effect::computeMipmap(srcImage, mipmapFunctions); - } - } - else { - 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); - } - } - registry->writeImage(*srcImage, newName, nopt); - absFileName = newName; - } - } + // 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 (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 (!fileExists(newName)) { + res = registry->readImageImplementation(absFileName, opt); + if (res.validImage()) { + osg::ref_ptr 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 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 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 { absFileName = newName; + } } } + res = registry->readImageImplementation(absFileName, opt); if (!res.success()) { @@ -368,6 +662,18 @@ ModelRegistry::readImage(const string& fileName, if (srcImage1->getName().empty()) { srcImage1->setName(absFileName); } + if(cache_active && getFileExtension(absFileName) != "dds") + { + if (processor) { + processor->generateMipMap(*srcImage1, true, osgDB::ImageProcessor::USE_CPU); + SG_LOG(SG_IO, SG_ALERT, "Created nvtt mipmaps DDS for " + absFileName); + } + 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); + } + } if (res.loadedFromCache()) SG_LOG(SG_IO, SG_BULK, "Returning cached image \"" @@ -449,19 +755,21 @@ ModelRegistry::readImage(const string& fileName, } -osg::Node* DefaultCachePolicy::find(const string& fileName, - const Options* opt) +osg::ref_ptr DefaultCachePolicy::find(const string& fileName, const Options* opt) { Registry* registry = Registry::instance(); - osg::Node* cached - = dynamic_cast(registry->getFromObjectCache(fileName)); - if (cached) - SG_LOG(SG_IO, SG_BULK, "Got cached model \"" - << fileName << "\""); +#if OSG_VERSION_LESS_THAN(3,4,0) + osg::ref_ptr cachedObject = registry->getFromObjectCache(fileName); +#else + osg::ref_ptr cachedObject = registry->getRefFromObjectCache(fileName); +#endif + + ref_ptr cachedNode = dynamic_cast(cachedObject.get()); + if (cachedNode.valid()) + SG_LOG(SG_IO, SG_BULK, "Got cached model \"" << fileName << "\""); else - SG_LOG(SG_IO, SG_BULK, "Reading model \"" - << fileName << "\""); - return cached; + SG_LOG(SG_IO, SG_BULK, "Reading model \"" << fileName << "\""); + return cachedNode; } void DefaultCachePolicy::addToCache(const string& fileName, diff --git a/simgear/scene/model/ModelRegistry.hxx b/simgear/scene/model/ModelRegistry.hxx index 8c8e7467..197bb197 100644 --- a/simgear/scene/model/ModelRegistry.hxx +++ b/simgear/scene/model/ModelRegistry.hxx @@ -132,7 +132,7 @@ struct DefaultProcessPolicy { struct DefaultCachePolicy { DefaultCachePolicy(const std::string& extension) {} - osg::Node* find(const std::string& fileName, + osg::ref_ptr find(const std::string& fileName, const osgDB::Options* opt); void addToCache(const std::string& filename, osg::Node* node); }; diff --git a/simgear/scene/model/SGReaderWriterXML.cxx b/simgear/scene/model/SGReaderWriterXML.cxx index 4c6304ac..64ad2754 100644 --- a/simgear/scene/model/SGReaderWriterXML.cxx +++ b/simgear/scene/model/SGReaderWriterXML.cxx @@ -56,7 +56,7 @@ using namespace std; using namespace simgear; using namespace osg; -static osg::Node * +static std::tuple sgLoad3DModel_internal(const SGPath& path, const osgDB::Options* options, SGPropertyNode *overlay = 0); @@ -88,8 +88,10 @@ SGReaderWriterXML::readNode(const std::string& name, if (!p.exists()) { return ReadResult::FILE_NOT_FOUND; } - - result=sgLoad3DModel_internal(p, options); + + static std::tuple retval; + int num_anims; + std::tie(num_anims, 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); @@ -160,13 +162,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; @@ -215,7 +217,7 @@ private: osg::Node::NodeMask nodeMaskSet; osg::Node::NodeMask nodeMaskClear; }; - + } namespace { @@ -229,7 +231,7 @@ namespace { return (typeString == "pick") || (typeString == "knob") || (typeString == "slider") || (typeString == "touch"); } }; - + bool removeNamedNode(osg::Group* aGroup, const std::string& aName) { int nKids = aGroup->getNumChildren(); @@ -240,28 +242,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 osg::Node * +static std::tuple 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 NULL; + return std::make_tuple(0, (osg::Node *) NULL); } osg::ref_ptr options; @@ -270,19 +272,20 @@ sgLoad3DModel_internal(const SGPath& path, SGPath modelpath(path); SGPath texturepath(path); SGPath modelDir(modelpath.dir()); - + int animationcount = 0; + SGSharedPtr 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 data = options->getModelData(); options->setModelData(0); - + osg::ref_ptr model; osg::ref_ptr group; SGPropertyNode_ptr props = new SGPropertyNode; bool previewMode = (dbOptions->getPluginStringData("SimGear::PREVIEW") == "ON"); - + // Check for an XML wrapper if (modelpath.extension() == "xml") { try { @@ -296,9 +299,9 @@ sgLoad3DModel_internal(const SGPath& path, copyProperties(overlay, props); if (previewMode && props->hasChild("nopreview")) { - return NULL; + return std::make_tuple(0, (osg::Node *) NULL); } - + if (props->hasValue("/path")) { string modelPathStr = props->getStringValue("/path"); modelpath = SGModelLib::findDataFile(modelPathStr, NULL, modelDir); @@ -335,7 +338,11 @@ sgLoad3DModel_internal(const SGPath& path, options->setDatabasePath(texturepath.local8BitStr()); osgDB::ReaderWriter::ReadResult modelResult; +#if OSG_VERSION_LESS_THAN(3,4,0) 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); @@ -399,9 +406,9 @@ sgLoad3DModel_internal(const SGPath& path, SGPath submodelpath; osg::ref_ptr submodel; - + string subPathStr = sub_props->getStringValue("path"); - SGPath submodelPath = SGModelLib::findDataFile(subPathStr, + SGPath submodelPath = SGModelLib::findDataFile(subPathStr, NULL, modelDir); if (submodelPath.isNull()) { @@ -419,14 +426,16 @@ sgLoad3DModel_internal(const SGPath& path, } try { - submodel = sgLoad3DModel_internal(submodelPath, options.get(), + int num_anims; + std::tie(num_anims, 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; @@ -481,7 +490,7 @@ sgLoad3DModel_internal(const SGPath& path, } } - if (dbOptions->getPluginStringData("SimGear::PARTICLESYSTEM") != "OFF") { + if (GlobalParticleCallback::getEnabled()){//dbOptions->getPluginStringData("SimGear::PARTICLESYSTEM") != "OFF") { std::vector particle_nodes; particle_nodes = props->getChildren("particlesystem"); for (unsigned i = 0; i < particle_nodes.size(); ++i) { @@ -491,7 +500,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], @@ -510,13 +519,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); { @@ -543,13 +552,15 @@ 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 model.release(); + return std::make_tuple(animationcount, model.release()); } if (data.valid()) data->modelLoaded(modelpath.utf8Str(), props, group.get()); @@ -559,6 +570,7 @@ sgLoad3DModel_internal(const SGPath& path, osgDB::writeNodeFile(*group, outputfile); } - return group.release(); -} + SG_LOG(SG_GENERAL, SG_DEBUG, "Model " << path << " animation count: " << animationcount); + return std::make_tuple(animationcount, group.release()); +} diff --git a/simgear/scene/model/model.cxx b/simgear/scene/model/model.cxx index 6b34ea9c..737c7597 100644 --- a/simgear/scene/model/model.cxx +++ b/simgear/scene/model/model.cxx @@ -43,9 +43,18 @@ SGLoadTexture2D(bool staticTexture, const std::string& path, { osg::Image* image; if (options) - image = osgDB::readImageFile(path, options); +#if OSG_VERSION_LESS_THAN(3,4,0) + image = osgDB::readImageFile(path, options); +#else + image = osgDB::readRefImageFile(path, options); +#endif else - image = osgDB::readImageFile(path); +#if OSG_VERSION_LESS_THAN(3,4,0) + image = osgDB::readImageFile(path); +#else + image = osgDB::readRefImageFile(path); +#endif + osg::ref_ptr texture = new osg::Texture2D; texture->setImage(image); if (staticTexture) @@ -141,7 +150,11 @@ Texture2D* TextureUpdateVisitor::textureReplace(int unit, const StateAttribute* // 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 + Image* newImage = readRefImageFile(fullLiveryFile); +#endif if (!newImage) return 0; CopyOp copyOp(CopyOp::DEEP_COPY_ALL & ~CopyOp::DEEP_COPY_IMAGES); diff --git a/simgear/scene/model/modellib.cxx b/simgear/scene/model/modellib.cxx index 7ce382d3..d11e0d21 100644 --- a/simgear/scene/model/modellib.cxx +++ b/simgear/scene/model/modellib.cxx @@ -101,7 +101,11 @@ osg::Node* loadFile(const string& path, SGReaderWriterOptions* options) options->setInstantiateEffects(true); } +#if OSG_VERSION_LESS_THAN(3,4,0) + ref_ptr model = readNodeFile(path, options); +#else ref_ptr model = readRefNodeFile(path, options); +#endif if (!model) return 0; else diff --git a/simgear/scene/sky/cloud.cxx b/simgear/scene/sky/cloud.cxx index 3c03c514..89a964b7 100644 --- a/simgear/scene/sky/cloud.cxx +++ b/simgear/scene/sky/cloud.cxx @@ -485,9 +485,11 @@ SGCloudLayer::rebuild() // repaint the cloud layer colors bool SGCloudLayer::repaint( const SGVec3f& fog_color ) { osg::Vec4f combineColor(toOsg(fog_color), cloud_alpha); - osg::TexEnvCombine* combiner - = dynamic_cast(layer_root->getStateSet() - ->getTextureAttribute(1, osg::StateAttribute::TEXENV)); + osg::StateAttribute* textureAtt = layer_root->getStateSet()->getTextureAttribute(1, osg::StateAttribute::TEXENV); + osg::TexEnvCombine* combiner = dynamic_cast(textureAtt); + + if (combiner == nullptr) + return false; combiner->setConstantColor(combineColor); // Set the fog color for the 3D clouds too. diff --git a/simgear/scene/tgdb/ReaderWriterSPT.cxx b/simgear/scene/tgdb/ReaderWriterSPT.cxx index cb6f4df1..66fad24b 100644 --- a/simgear/scene/tgdb/ReaderWriterSPT.cxx +++ b/simgear/scene/tgdb/ReaderWriterSPT.cxx @@ -208,7 +208,11 @@ 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); @@ -256,7 +260,11 @@ 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 { @@ -314,7 +322,11 @@ 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 node = osgDB::readNodeFile(fileName, options._options); +#else osg::ref_ptr node = osgDB::readRefNodeFile(fileName, options._options); +#endif if (!node.valid()) continue; pagedLOD->addChild(node.get(), range, std::numeric_limits::max()); @@ -412,7 +424,11 @@ ReaderWriterSPT::getLowLODStateSet(const LocalOptions& options) const localOptions = static_cast(options._options->clone(osg::CopyOp())); localOptions->setObjectCacheHint(osgDB::Options::CACHE_ALL); +#if OSG_VERSION_LESS_THAN(3,4,0) + osg::ref_ptr object = osgDB::readObjectFile("state.spt", localOptions.get()); +#else osg::ref_ptr object = osgDB::readRefObjectFile("state.spt", localOptions.get()); +#endif if (!dynamic_cast(object.get())) return 0; diff --git a/simgear/scene/tgdb/ReaderWriterSTG.cxx b/simgear/scene/tgdb/ReaderWriterSTG.cxx index 46f73ba9..4a3887a4 100644 --- a/simgear/scene/tgdb/ReaderWriterSTG.cxx +++ b/simgear/scene/tgdb/ReaderWriterSTG.cxx @@ -156,7 +156,11 @@ 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 << "'"); @@ -553,7 +557,11 @@ struct ReaderWriterSTG::_ModelBin { if (_foundBase) { for (auto stgObject : _objectList) { osg::ref_ptr 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 << "'"); @@ -586,7 +594,7 @@ struct ReaderWriterSTG::_ModelBin { i->_elev += elevation(*terrainGroup, SGGeod::fromDeg(i->_lon, i->_lat)); } - if (_objectStaticList.empty() && _signList.empty()) { + if (_objectStaticList.empty() && _signList.empty() && (_buildingListList.size() == 0)) { // The simple case, just return the terrain group return terrainGroup.release(); } else { diff --git a/simgear/scene/tgdb/SGTileDetailsCallback.hxx b/simgear/scene/tgdb/SGTileDetailsCallback.hxx index e7612c68..6a16c5da 100644 --- a/simgear/scene/tgdb/SGTileDetailsCallback.hxx +++ b/simgear/scene/tgdb/SGTileDetailsCallback.hxx @@ -689,10 +689,13 @@ public: triangleBuildingList.clear(); } - 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); + 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); + } } } diff --git a/simgear/scene/util/SGReaderWriterOptions.hxx b/simgear/scene/util/SGReaderWriterOptions.hxx index 2760df90..17183944 100644 --- a/simgear/scene/util/SGReaderWriterOptions.hxx +++ b/simgear/scene/util/SGReaderWriterOptions.hxx @@ -38,12 +38,22 @@ 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(clone(copyop)); } + SGReaderWriterOptions() : _materialLib(0), _load_panel(0), _model_data(0), _instantiateEffects(false), - _instantiateMaterialEffects(false) + _instantiateMaterialEffects(false), + _LoadOriginHint(ORIGIN_MODEL) { } SGReaderWriterOptions(const std::string& str) : osgDB::Options(str), @@ -51,7 +61,8 @@ public: _load_panel(0), _model_data(0), _instantiateEffects(false), - _instantiateMaterialEffects(false) + _instantiateMaterialEffects(false), + _LoadOriginHint(ORIGIN_MODEL) { } SGReaderWriterOptions(const osgDB::Options& options, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY) : @@ -60,7 +71,8 @@ public: _load_panel(0), _model_data(0), _instantiateEffects(false), - _instantiateMaterialEffects(false) + _instantiateMaterialEffects(false), + _LoadOriginHint(ORIGIN_MODEL) { } SGReaderWriterOptions(const SGReaderWriterOptions& options, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY) : @@ -75,7 +87,8 @@ public: _instantiateEffects(options._instantiateEffects), _instantiateMaterialEffects(options._instantiateMaterialEffects), _materialName(options._materialName), - _sceneryPathSuffixes(options._sceneryPathSuffixes) + _sceneryPathSuffixes(options._sceneryPathSuffixes), + _LoadOriginHint(ORIGIN_MODEL) { } META_Object(simgear, SGReaderWriterOptions); @@ -139,6 +152,13 @@ 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(); @@ -157,6 +177,7 @@ private: string _materialName; string_list _sceneryPathSuffixes; SGGeod _geod; + mutable LoadOriginHint _LoadOriginHint; }; } diff --git a/simgear/scene/util/SGSceneFeatures.cxx b/simgear/scene/util/SGSceneFeatures.cxx index a3549a80..48aecf22 100644 --- a/simgear/scene/util/SGSceneFeatures.cxx +++ b/simgear/scene/util/SGSceneFeatures.cxx @@ -40,14 +40,14 @@ SGSceneFeatures::SGSceneFeatures() : _textureCompression(UseARBCompression), - _shaderLights(true), - _pointSpriteLights(true), - _distanceAttenuationLights(true), - _textureFilter(1), _MaxTextureSize(4096), _TextureCacheCompressionActive(true), _TextureCacheCompressionActiveTransparent(true), - _TextureCacheActive(false) + _TextureCacheActive(false), + _shaderLights(true), + _pointSpriteLights(true), + _distanceAttenuationLights(true), + _textureFilter(1) { } diff --git a/simgear/simgear_config_cmake.h.in b/simgear/simgear_config_cmake.h.in index de1b69c6..89492869 100644 --- a/simgear/simgear_config_cmake.h.in +++ b/simgear/simgear_config_cmake.h.in @@ -27,4 +27,5 @@ #cmakedefine ENABLE_SOUND #cmakedefine USE_AEONWAVE #cmakedefine ENABLE_SIMD +#cmakedefine ENABLE_SIMD_CODE #cmakedefine ENABLE_GDAL diff --git a/simgear/sound/readwav.cxx b/simgear/sound/readwav.cxx index 005cf193..d5c16cd2 100644 --- a/simgear/sound/readwav.cxx +++ b/simgear/sound/readwav.cxx @@ -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[65]; - snprintf(msg, 64, "Unsupported audio format: tracks: %i, bits/sample: %i", numChannels, bitsPerSample); + char msg[81]; + snprintf(msg, 80, "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[65]; - snprintf(msg, 64, "Unsupported compressed audio format: tracks: %i, bits/sample: %i", numChannels, bitsPerSample); + char msg[81]; + snprintf(msg, 80, "Unsupported compressed audio format: tracks: %i, bits/sample: %i", numChannels, bitsPerSample); throw sg_exception(msg); } } diff --git a/simgear/sound/soundmgr_aeonwave.cxx b/simgear/sound/soundmgr_aeonwave.cxx index b25fe008..111fa10e 100644 --- a/simgear/sound/soundmgr_aeonwave.cxx +++ b/simgear/sound/soundmgr_aeonwave.cxx @@ -8,7 +8,7 @@ // Modified for the new SoundSystem by Erik Hofman, October 2009 // // Copyright (C) 2001 Curtis L. Olson - http://www.flightgear.org/~curt -// Copyright (C) 2009 Erik Hofman +// Copyright (C) 2009-2019 Erik Hofman // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as @@ -418,8 +418,8 @@ void SGSoundMgr::release_source( unsigned int source ) { aax::Emitter& emitter = source_it->second; enum aaxState state = emitter.state(); - if (state == AAX_PLAYING || state == AAX_SUSPENDED) { - TRY( emitter.set(AAX_STOPPED) ); + if (state != AAX_PROCESSED) { + TRY( emitter.set(AAX_PROCESSED) ); TRY( d->_aax.remove(emitter) ); } TRY( emitter.remove_buffer() ); @@ -555,7 +555,6 @@ void SGSoundMgr::sample_play( SGSoundSample *sample ) if (bufid == SGSoundMgr::FAILED_BUFFER || bufid == SGSoundMgr::NO_BUFFER) { -printf("A: release: %i, bufid: %i (%i, %i)\n", sample->get_source(), bufid, SGSoundMgr::FAILED_BUFFER, SGSoundMgr::NO_BUFFER); release_source(sample->get_source()); return; } @@ -590,8 +589,7 @@ void SGSoundMgr::sample_stop( SGSoundSample *sample ) if ( sample->is_looping() && !stopped) { #ifdef ENABLE_SOUND aax::Emitter& emitter = d->get_emitter(source); - TRY( emitter.set(AAX_STOPPED) ); - TRY( d->_aax.remove(emitter) ); + TRY( emitter.set(AAX_PROCESSED) ); #endif stopped = is_sample_stopped(sample); } @@ -610,8 +608,7 @@ void SGSoundMgr::sample_destroy( SGSoundSample *sample ) unsigned int source = sample->get_source(); if ( sample->is_playing() ) { aax::Emitter& emitter = d->get_emitter(source); - TRY( emitter.set(AAX_STOPPED) ); - TRY( d->_aax.remove(emitter) ); + TRY( emitter.set(AAX_PROCESSED) ); } release_source( source ); #endif diff --git a/simgear/structure/subsystem_mgr.cxx b/simgear/structure/subsystem_mgr.cxx index a7294f5f..fc29e883 100644 --- a/simgear/structure/subsystem_mgr.cxx +++ b/simgear/structure/subsystem_mgr.cxx @@ -281,7 +281,7 @@ SGSubsystemGroup::incrementalInit() notifyDidChange(m->subsystem, State::INIT); ++_initPosition; - if (_initPosition < _members.size()) { + if (_initPosition < static_cast(_members.size())) { // start init of the next one notifyWillChange( _members[_initPosition]->subsystem, State::INIT); } diff --git a/simgear/timing/timestamp.cxx b/simgear/timing/timestamp.cxx index 7553d322..067a1b95 100644 --- a/simgear/timing/timestamp.cxx +++ b/simgear/timing/timestamp.cxx @@ -72,13 +72,33 @@ 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 - unsigned int t; - t = timeGetTime(); - _sec = t / 1000; - _nsec = ( t - ( _sec * 1000 ) ) * 1000 * 1000; + 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; + } + #elif defined(_POSIX_TIMERS) && (0 < _POSIX_TIMERS) struct timespec ts; clock_gettime(getClockId(), &ts);