From 64d51b529091525e339bc3a9e548c920ec0cdf6c Mon Sep 17 00:00:00 2001 From: Richard Harrison Date: Fri, 18 Jan 2019 12:04:06 +0100 Subject: [PATCH 01/15] sg_file: Add compute hash method --- simgear/io/sg_file.cxx | 25 +++++++++++++++++++++++++ simgear/io/sg_file.hxx | 3 +++ 2 files changed, 28 insertions(+) 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..267295b0 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 SGFile::computeHash(); + }; class SGBinaryFile : public SGFile { From c433d29171f4926775ea63d4f1a647cefbc6c1a8 Mon Sep 17 00:00:00 2001 From: Richard Harrison Date: Fri, 18 Jan 2019 12:09:10 +0100 Subject: [PATCH 02/15] Win32: use high resolution timers (QueryPerformanceCounter) if available --- simgear/timing/timestamp.cxx | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/simgear/timing/timestamp.cxx b/simgear/timing/timestamp.cxx index 7553d322..b12f7544 100644 --- a/simgear/timing/timestamp.cxx +++ b/simgear/timing/timestamp.cxx @@ -71,14 +71,31 @@ static clockid_t getClockId() } #endif +static bool qpc_init = false; +static LARGE_INTEGER s_frequency; +static BOOL s_use_qpc; 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); From 8bcaa50ba6e8076403ec09fa6f553d870e0ab1f1 Mon Sep 17 00:00:00 2001 From: Richard Harrison Date: Sat, 19 Jan 2019 14:00:16 +0100 Subject: [PATCH 03/15] OSG3.7 changes --- simgear/canvas/elements/CanvasText.cxx | 86 ++++++++++++++++---------- 1 file changed, 55 insertions(+), 31 deletions(-) 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"; From 84046a6717c506f253e4f264b8be01cb3318fbe5 Mon Sep 17 00:00:00 2001 From: Richard Harrison Date: Sat, 19 Jan 2019 14:04:54 +0100 Subject: [PATCH 04/15] Add missing method --- simgear/scene/model/BVHDebugCollectVisitor.hxx | 5 +++++ 1 file changed, 5 insertions(+) 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&) { } From cb024dd82d4c384df0b599640a98e762fbf66688 Mon Sep 17 00:00:00 2001 From: Richard Harrison Date: Sun, 20 Jan 2019 18:05:53 +0100 Subject: [PATCH 05/15] Fix for deleting still referenced object ref https://sourceforge.net/p/flightgear/codetickets/2105/ Use the thread safe versions (getRef) of the objectcache methods --- simgear/scene/material/TextureBuilder.cxx | 15 ++++++++------- simgear/scene/material/mat.cxx | 2 +- simgear/scene/model/SGReaderWriterXML.cxx | 2 +- simgear/scene/model/model.cxx | 6 +++--- simgear/scene/tgdb/ReaderWriterSPT.cxx | 2 +- 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/simgear/scene/material/TextureBuilder.cxx b/simgear/scene/material/TextureBuilder.cxx index fa571912..5634d966 100644 --- a/simgear/scene/material/TextureBuilder.cxx +++ b/simgear/scene/material/TextureBuilder.cxx @@ -257,7 +257,8 @@ bool setAttrs(const TexTuple& attrs, Texture* tex, return false; osgDB::ReaderWriter::ReadResult result; - result = osgDB::readImageFile(imageName, options); + + result = osgDB::readRefImageFile(imageName, options); osg::ref_ptr image; if (result.success()) image = result.getImage(); @@ -590,27 +591,27 @@ Texture* CubeMapBuilder::build(Effect* effect, Pass* pass, const SGPropertyNode* osg::Image* image = result.getImage(); cubeTexture->setImage(TextureCubeMap::POSITIVE_X, image); } - result = osgDB::readImageFile(_tuple.get<1>(), options); + result = osgDB::readRefImageFile(_tuple.get<1>(), options); if(result.success()) { osg::Image* image = result.getImage(); cubeTexture->setImage(TextureCubeMap::NEGATIVE_X, image); } - result = osgDB::readImageFile(_tuple.get<2>(), options); + result = osgDB::readRefImageFile(_tuple.get<2>(), options); if(result.success()) { osg::Image* image = result.getImage(); cubeTexture->setImage(TextureCubeMap::POSITIVE_Y, image); } - result = osgDB::readImageFile(_tuple.get<3>(), options); + result = osgDB::readRefImageFile(_tuple.get<3>(), options); if(result.success()) { osg::Image* image = result.getImage(); cubeTexture->setImage(TextureCubeMap::NEGATIVE_Y, image); } - result = osgDB::readImageFile(_tuple.get<4>(), options); + result = osgDB::readRefImageFile(_tuple.get<4>(), options); if(result.success()) { osg::Image* image = result.getImage(); cubeTexture->setImage(TextureCubeMap::POSITIVE_Z, image); } - result = osgDB::readImageFile(_tuple.get<5>(), options); + result = osgDB::readRefImageFile(_tuple.get<5>(), options); if(result.success()) { osg::Image* image = result.getImage(); cubeTexture->setImage(TextureCubeMap::NEGATIVE_Z, image); @@ -634,7 +635,7 @@ Texture* CubeMapBuilder::build(Effect* effect, Pass* pass, const SGPropertyNode* return cubeTexture.release(); osgDB::ReaderWriter::ReadResult result; - result = osgDB::readImageFile(texname, options); + result = osgDB::readRefImageFile(texname, options); 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..ba9d63f7 100644 --- a/simgear/scene/material/mat.cxx +++ b/simgear/scene/material/mat.cxx @@ -227,7 +227,7 @@ SGMaterial::read_properties(const SGReaderWriterOptions* options, } else { - osg::Image* image = osgDB::readImageFile(fullMaskPath, options); + osg::Image* image = osgDB::readRefImageFile(fullMaskPath, options); if (image && image->valid()) { Texture2DRef object_mask = new osg::Texture2D; diff --git a/simgear/scene/model/SGReaderWriterXML.cxx b/simgear/scene/model/SGReaderWriterXML.cxx index 3cc13eb9..105a5528 100644 --- a/simgear/scene/model/SGReaderWriterXML.cxx +++ b/simgear/scene/model/SGReaderWriterXML.cxx @@ -338,7 +338,7 @@ sgLoad3DModel_internal(const SGPath& path, options->setDatabasePath(texturepath.local8BitStr()); osgDB::ReaderWriter::ReadResult modelResult; - modelResult = osgDB::readNodeFile(modelpath.local8BitStr(), options.get()); + modelResult = osgDB::readRefNodeFile(modelpath.local8BitStr(), options.get()); if (!modelResult.validNode()) throw sg_io_exception("Failed to load 3D model:" + modelResult.message(), modelpath); diff --git a/simgear/scene/model/model.cxx b/simgear/scene/model/model.cxx index 6b34ea9c..0b86f701 100644 --- a/simgear/scene/model/model.cxx +++ b/simgear/scene/model/model.cxx @@ -43,9 +43,9 @@ SGLoadTexture2D(bool staticTexture, const std::string& path, { osg::Image* image; if (options) - image = osgDB::readImageFile(path, options); + image = osgDB::readRefImageFile(path, options); else - image = osgDB::readImageFile(path); + image = osgDB::readRefImageFile(path); osg::ref_ptr texture = new osg::Texture2D; texture->setImage(image); if (staticTexture) @@ -141,7 +141,7 @@ 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; - Image* newImage = readImageFile(fullLiveryFile); + Image* newImage = readRefImageFile(fullLiveryFile); if (!newImage) return 0; CopyOp copyOp(CopyOp::DEEP_COPY_ALL & ~CopyOp::DEEP_COPY_IMAGES); diff --git a/simgear/scene/tgdb/ReaderWriterSPT.cxx b/simgear/scene/tgdb/ReaderWriterSPT.cxx index cb6f4df1..39facca1 100644 --- a/simgear/scene/tgdb/ReaderWriterSPT.cxx +++ b/simgear/scene/tgdb/ReaderWriterSPT.cxx @@ -208,7 +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::Image* image = osgDB::readImageFile(imageFileName, options)) { + if (osg::Image* image = osgDB::readRefImageFile(imageFileName, options)) { osg::Texture2D* texture = new osg::Texture2D; texture->setImage(image); texture->setWrap(osg::Texture2D::WRAP_S, osg::Texture2D::REPEAT); From 8707005a97cb5bfbebbecfed1f8a1cbce5e295a8 Mon Sep 17 00:00:00 2001 From: Richard Harrison Date: Sun, 20 Jan 2019 18:07:57 +0100 Subject: [PATCH 06/15] Fix particles active even when disabled during load. Possibly this could be fixed better by using the plugin string data - but there is nothing that currently set this; and it seems easier to use the particle callback enabled flag. --- simgear/scene/model/SGReaderWriterXML.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simgear/scene/model/SGReaderWriterXML.cxx b/simgear/scene/model/SGReaderWriterXML.cxx index 105a5528..51a9817e 100644 --- a/simgear/scene/model/SGReaderWriterXML.cxx +++ b/simgear/scene/model/SGReaderWriterXML.cxx @@ -486,7 +486,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) { From 8e57a61aed437f75b8af7df60c18e26b877a4e7d Mon Sep 17 00:00:00 2001 From: Richard Harrison Date: Sun, 20 Jan 2019 18:08:34 +0100 Subject: [PATCH 07/15] Fix null ref during load. This happened a few times --- simgear/scene/sky/cloud.cxx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) 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. From 4f6c8f7784a12e4351bc7e5f142a53d975764d18 Mon Sep 17 00:00:00 2001 From: Richard Harrison Date: Sun, 20 Jan 2019 18:08:57 +0100 Subject: [PATCH 08/15] Better error handling on load failure --- simgear/scene/material/Effect.cxx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) 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"); From 07036ac48f0d2005f77d966aaf5991a9aea52d4c Mon Sep 17 00:00:00 2001 From: Richard Harrison Date: Sun, 20 Jan 2019 18:10:20 +0100 Subject: [PATCH 09/15] Add load origin hint to options This is to allow the DDS cache to handle the image load differently depending on the origin. --- simgear/scene/material/TextureBuilder.cxx | 28 +++++++++++++++++-- simgear/scene/material/mipmap.hxx | 20 ++++++++------ simgear/scene/util/SGReaderWriterOptions.hxx | 29 +++++++++++++++++--- 3 files changed, 62 insertions(+), 15 deletions(-) diff --git a/simgear/scene/material/TextureBuilder.cxx b/simgear/scene/material/TextureBuilder.cxx index 5634d966..0d2a60c7 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, @@ -258,7 +269,14 @@ 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); result = osgDB::readRefImageFile(imageName, options); + options->setLoadOriginHint(origLOH); osg::ref_ptr image; if (result.success()) image = result.getImage(); @@ -586,7 +604,10 @@ Texture* CubeMapBuilder::build(Effect* effect, Pass* pass, const SGPropertyNode* cubeTexture->setWrap(osg::Texture3D::WRAP_R, osg::Texture::CLAMP_TO_EDGE); osgDB::ReaderWriter::ReadResult result; - result = osgDB::readImageFile(_tuple.get<0>(), options); + SGReaderWriterOptions* wOpts = (SGReaderWriterOptions*)options; + SGReaderWriterOptions::LoadOriginHint origLOH = wOpts->getLoadOriginHint(); + wOpts->setLoadOriginHint(SGReaderWriterOptions::LoadOriginHint::ORIGIN_EFFECTS); + result = osgDB::readRefImageFile(_tuple.get<0>(), options); if(result.success()) { osg::Image* image = result.getImage(); cubeTexture->setImage(TextureCubeMap::POSITIVE_X, image); @@ -616,6 +637,7 @@ Texture* CubeMapBuilder::build(Effect* effect, Pass* pass, const SGPropertyNode* osg::Image* image = result.getImage(); cubeTexture->setImage(TextureCubeMap::NEGATIVE_Z, image); } + wOpts->setLoadOriginHint(origLOH); if (itr == _cubemaps.end()) _cubemaps[_tuple] = cubeTexture; 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/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; }; } From 105d63a69787ba04bcdd7a42c6aa94f01960fd43 Mon Sep 17 00:00:00 2001 From: Scott Giese Date: Sun, 20 Jan 2019 20:35:08 -0600 Subject: [PATCH 10/15] Simgear: LARGE_INTEGER is defined in Windows.h Fixes build issues on Linux. --- simgear/io/sg_file.hxx | 2 +- simgear/timing/timestamp.cxx | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/simgear/io/sg_file.hxx b/simgear/io/sg_file.hxx index 267295b0..59de6ba7 100644 --- a/simgear/io/sg_file.hxx +++ b/simgear/io/sg_file.hxx @@ -88,7 +88,7 @@ public: /** @return true of eof conditions exists */ virtual bool eof() const { return eof_flag; }; - std::string SGFile::computeHash(); + std::string computeHash(); }; diff --git a/simgear/timing/timestamp.cxx b/simgear/timing/timestamp.cxx index b12f7544..067a1b95 100644 --- a/simgear/timing/timestamp.cxx +++ b/simgear/timing/timestamp.cxx @@ -71,9 +71,12 @@ 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() { From 61ebee12ad692da424ac86a561749f9402e406af Mon Sep 17 00:00:00 2001 From: James Turner Date: Tue, 22 Jan 2019 10:18:25 +0100 Subject: [PATCH 11/15] Set curl VERBOSE based on current SG log settings --- simgear/io/HTTPClient.cxx | 4 ++++ 1 file changed, 4 insertions(+) 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()) { From df6ec4f94c90c59a30f5a3e3106ab4a402189c9b Mon Sep 17 00:00:00 2001 From: James Turner Date: Tue, 22 Jan 2019 10:30:34 +0100 Subject: [PATCH 12/15] Property alias loop protection Patch by Henning Stahlke --- simgear/props/props.cxx | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/simgear/props/props.cxx b/simgear/props/props.cxx index 1c21ecee..8b599632 100644 --- a/simgear/props/props.cxx +++ b/simgear/props/props.cxx @@ -963,6 +963,11 @@ 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; @@ -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; From a5d8eec25fb129db5f96e365d67e364715d78c67 Mon Sep 17 00:00:00 2001 From: Richard Harrison Date: Thu, 24 Jan 2019 16:10:58 +0100 Subject: [PATCH 13/15] Fix for deleting referenced object from model registry This should have been in the previous commit - However I managed to mess up the merging of this module due to other changes related to the DDS texture cache. --- simgear/scene/model/ModelRegistry.cxx | 22 ++++++++++++---------- simgear/scene/model/ModelRegistry.hxx | 2 +- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/simgear/scene/model/ModelRegistry.cxx b/simgear/scene/model/ModelRegistry.cxx index 4451fd79..3756fae7 100644 --- a/simgear/scene/model/ModelRegistry.cxx +++ b/simgear/scene/model/ModelRegistry.cxx @@ -449,19 +449,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); }; From 8b35c9a4b121c425caa264d27d0497d93313485d Mon Sep 17 00:00:00 2001 From: Richard Harrison Date: Thu, 24 Jan 2019 18:05:37 +0100 Subject: [PATCH 14/15] Improvements to the DDS texture cache - use a file contents hash instead of filepath. - add a local lru cache for filepath->hash (for performance improvements) - calculate and resize to nearest power of two - handle normal maps and images from effects differently. - when cache is active any image that can't be converted to a dds will have a mipmap generated (which still helps the loading process); although this may be responsible for introducing purple into transparent images.. --- simgear/scene/model/ModelRegistry.cxx | 482 +++++++++++++++++++++----- 1 file changed, 394 insertions(+), 88 deletions(-) diff --git a/simgear/scene/model/ModelRegistry.cxx b/simgear/scene/model/ModelRegistry.cxx index 3756fae7..b5bbc7b9 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-i ndex.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 \"" From 0d5afe83a22a042ddaf3d6dfb8ebcfd1edd6c474 Mon Sep 17 00:00:00 2001 From: Richard Harrison Date: Thu, 24 Jan 2019 18:06:18 +0100 Subject: [PATCH 15/15] No need to set precipitation (rain/snow) each frame when it is turned off. --- simgear/environment/precipitation.cxx | 10 ++++++++++ 1 file changed, 10 insertions(+) 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;