Merge branch 'next' of ssh://git.code.sf.net/p/flightgear/simgear into next

This commit is contained in:
Erik Hofman
2019-01-26 09:13:17 +01:00
19 changed files with 638 additions and 173 deletions

View File

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

View File

@@ -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<osg::Group> 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;

View File

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

View File

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

View File

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

View File

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

View File

@@ -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<SGMutex> g(realizeTechniques_lock);
if (_isRealized)
return true;
PropertyList tniqList = root->getChildren("technique");

View File

@@ -76,7 +76,7 @@ osg::Texture* TextureBuilder::buildFromType(Effect* effect, Pass* pass, const st
typedef boost::tuple<string, Texture::FilterMode, Texture::FilterMode,
Texture::WrapMode, Texture::WrapMode, Texture::WrapMode,
string, MipMapTuple> TexTuple;
string, MipMapTuple, ImageInternalFormat> TexTuple;
EffectNameValue<TexEnv::Mode> 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,15 @@ bool setAttrs(const TexTuple& attrs, Texture* tex,
return false;
osgDB::ReaderWriter::ReadResult result;
result = osgDB::readImageFile(imageName, options);
// 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<osg::Image> image;
if (result.success())
image = result.getImage();
@@ -585,36 +604,40 @@ 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);
}
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);
}
wOpts->setLoadOriginHint(origLOH);
if (itr == _cubemaps.end())
_cubemaps[_tuple] = cubeTexture;
@@ -634,7 +657,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

View File

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

View File

@@ -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<MipMapFunction, MipMapFunction, MipMapFunction, MipMapFunction> MipMapTuple;

View File

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

View File

@@ -60,6 +60,8 @@
#include <simgear/props/props.hxx>
#include <simgear/props/props_io.hxx>
#include <simgear/props/condition.hxx>
#include <simgear/io/sg_file.hxx>
#include <simgear/threads/SGGuard.hxx>
#include "BoundingVolumeBuildVisitor.hxx"
#include "model.hxx"
@@ -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 <map>
#include <list>
#include <utility>
#include <boost/optional.hpp>
template<class Key, class Value>
class lru_cache
{
public:
SGMutex _mutex;
typedef Key key_type;
typedef Value value_type;
typedef std::list<key_type> list_type;
typedef std::map<
key_type,
std::pair<value_type, typename list_type::iterator>
> map_type;
lru_cache(size_t capacity)
: m_capacity(capacity)
{
}
~lru_cache()
{
}
size_t size() const
{
return m_map.size();
}
size_t capacity() const
{
return m_capacity;
}
bool empty() const
{
return m_map.empty();
}
bool contains(const key_type &key)
{
SGGuard<SGMutex> scopeLock(_mutex);
return m_map.find(key) != m_map.end();
}
void insert(const key_type &key, const value_type &value)
{
SGGuard<SGMutex> scopeLock(_mutex);
typename map_type::iterator i = m_map.find(key);
if (i == m_map.end()) {
// insert item into the cache, but first check if it is full
if (size() >= m_capacity) {
// cache is full, evict the least recently used item
evict();
}
// insert the new item
m_list.push_front(key);
m_map[key] = std::make_pair(value, m_list.begin());
}
}
boost::optional<key_type> findValue(const std::string &requiredValue)
{
SGGuard<SGMutex> scopeLock(_mutex);
for (typename map_type::iterator it = m_map.begin(); it != m_map.end(); ++it)
if (it->second.first == requiredValue)
return it->first;
return boost::none;
}
boost::optional<value_type> get(const key_type &key)
{
SGGuard<SGMutex> scopeLock(_mutex);
// lookup value in the cache
typename map_type::iterator i = m_map.find(key);
if (i == m_map.end()) {
// value not in cache
return boost::none;
}
// return the value, but first update its place in the most
// recently used list
typename list_type::iterator j = i->second.second;
if (j != m_list.begin()) {
// move item to the front of the most recently used list
m_list.erase(j);
m_list.push_front(key);
// update iterator in map
j = m_list.begin();
const value_type &value = i->second.first;
m_map[key] = std::make_pair(value, j);
// return the value
return value;
}
else {
// the item is already at the front of the most recently
// used list so just return it
return i->second.first;
}
}
void clear()
{
SGGuard<SGMutex> scopeLock(_mutex);
m_map.clear();
m_list.clear();
}
private:
void evict()
{
SGGuard<SGMutex> scopeLock(_mutex);
// evict item from the end of most recently used list
typename list_type::iterator i = --m_list.end();
m_map.erase(*i);
m_list.erase(i);
}
private:
map_type m_map;
list_type m_list;
size_t m_capacity;
};
lru_cache < std::string, std::string> filename_hash_cache(100000);
lru_cache < std::string, bool> filesCleaned(100000);
static bool refreshCache = false;
ReaderWriter::ReadResult
ModelRegistry::readImage(const string& fileName,
@@ -248,6 +408,8 @@ ModelRegistry::readImage(const string& fileName,
if (cache_active) {
if (fileExtension != "dds" && fileExtension != "gz") {
const SGReaderWriterOptions* sgoptC = dynamic_cast<const SGReaderWriterOptions*>(opt);
std::string root = getPathRoot(absFileName);
std::string prr = getPathRelative(root, absFileName);
std::string cache_root = SGSceneFeatures::instance()->getTextureCompressionPath().c_str();
@@ -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<std::string> cachehash = filename_hash_cache.get(absFileName);
if (cachehash) {
hash = *cachehash;
// SG_LOG(SG_IO, SG_ALERT, "Hash for " + absFileName + " in cache " + hash);
}
else {
// SG_LOG(SG_IO, SG_ALERT, "Creating hash for " + absFileName);
hash = f.computeHash();
filename_hash_cache.insert(absFileName, hash);
boost::optional<std::string> cacheFilename = filename_hash_cache.findValue(hash);
if (res.validImage()) {
osg::ref_ptr<osg::Image> 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<osg::Image> 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<osg::Image> 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<Options> 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<osg::Image> srcImage = res.getImage();
int width = srcImage->s();
bool transparent = srcImage->isImageTranslucent();
bool isNormalMap = false;
bool isEffect = false;
/*
* decide if we need to compress this.
*/
bool can_compress = (transparent && compress_transparent) || (!transparent && compress_solid);
int height = srcImage->t();
// use the new file origin to determine any special processing
// we handle the following
// - normal maps
// - images loaded from effects
if (sgoptC && transparent && sgoptC->getLoadOriginHint() == SGReaderWriterOptions::LoadOriginHint::ORIGIN_EFFECTS_NORMALIZED) {
isNormalMap = true;
}
else if (sgoptC && transparent && sgoptC->getLoadOriginHint() == SGReaderWriterOptions::LoadOriginHint::ORIGIN_EFFECTS) {
SG_LOG(SG_IO, SG_ALERT, "From effects transparent " + absFileName);
isEffect = true;
// can_compress = false;
}
else if (sgoptC && transparent && sgoptC->getLoadOriginHint() == SGReaderWriterOptions::LoadOriginHint::ORIGIN_EFFECTS) {
SG_LOG(SG_IO, SG_ALERT, "From effects " + absFileName);
isEffect = true;
}
if (can_compress)
{
std::string pot_message;
bool resize = false;
if (!isPowerOfTwo(width)) {
width = nearestPowerOfTwo(width);
resize = true;
pot_message += std::string(" not POT: resized width to ") + std::to_string(width);
}
if (!isPowerOfTwo(height)) {
height = nearestPowerOfTwo(height);
resize = true;
pot_message += std::string(" not POT: resized height to ") + std::to_string(height);
}
if (pot_message.size())
SG_LOG(SG_IO, SG_WARN, pot_message << " " << absFileName);
// unlikely that after resizing in height the width will still be outside of the max texture size.
if (height > max_texture_size)
{
SG_LOG(SG_IO, SG_WARN, "Image texture too high (max " << max_texture_size << ") " << width << "," << height << " " << absFileName);
int factor = height / max_texture_size;
height /= factor;
width /= factor;
resize = true;
}
if (width > max_texture_size)
{
SG_LOG(SG_IO, SG_WARN, "Image texture too wide (max " << max_texture_size << ") " << width << "," << height << " " << absFileName);
int factor = width / max_texture_size;
height /= factor;
width /= factor;
resize = true;
}
if (resize) {
osg::ref_ptr<osg::Image> resizedImage;
if (ImageUtils::resizeImage(srcImage, width, height, resizedImage))
srcImage = resizedImage;
}
//
// only cache power of two textures that are of a reasonable size
if (width >= 4 && height >= 4) {
simgear::effect::MipMapTuple mipmapFunctions(simgear::effect::AVERAGE, simgear::effect::AVERAGE, simgear::effect::AVERAGE, simgear::effect::AVERAGE);
SGPath filePath(newName);
filePath.create_dir();
// setup the options string for saving the texture as we don't want OSG to auto flip the texture
// as this complicates loading as it requires a flag to flip it back which will preclude the
// image from being cached because we will have to clone the options to set the flag and thus lose
// the link to the cache in the options from the caller.
osg::ref_ptr<Options> nopt;
nopt = opt->cloneOptions();
std::string optionstring = nopt->getOptionString();
if (!optionstring.empty())
optionstring += " ";
nopt->setOptionString(optionstring + "ddsNoAutoFlipWrite");
//GLenum srcImageType = srcImage->getDataType();
// printf("--- %-80s --> f=%8x t=%8x\n", newName.c_str(), srcImage->getPixelFormat(), srcImageType);
try
{
if (can_compress) {
osg::Texture::InternalFormatMode targetFormat = osg::Texture::USE_S3TC_DXT1_COMPRESSION;
if (isNormalMap) {
if (transparent) {
targetFormat = osg::Texture::USE_S3TC_DXT5_COMPRESSION;
}
else
targetFormat = osg::Texture::USE_S3TC_DXT5_COMPRESSION;
}
else if (isEffect)
{
if (transparent) {
targetFormat = osg::Texture::USE_S3TC_DXT5_COMPRESSION;
}
else
targetFormat = osg::Texture::USE_S3TC_DXT1_COMPRESSION;
}
else{
if (transparent) {
targetFormat = osg::Texture::USE_S3TC_DXT3_COMPRESSION;
}
else
targetFormat = osg::Texture::USE_S3TC_DXT1_COMPRESSION;
}
if (processor)
{
SG_LOG(SG_IO, SG_ALERT, "Creating " << targetFormat << " for " + absFileName);
// normal maps:
// nvdxt.exe - quality_highest - rescaleKaiser - Kaiser - dxt5nm - norm
processor->compress(*srcImage, targetFormat, true, true, osgDB::ImageProcessor::USE_CPU, osgDB::ImageProcessor::PRODUCTION);
SG_LOG(SG_IO, SG_ALERT, "-- finished creating DDS: " + newName);
//processor->generateMipMap(*srcImage, true, osgDB::ImageProcessor::USE_CPU);
}
else {
simgear::effect::MipMapTuple mipmapFunctions(simgear::effect::AVERAGE, simgear::effect::AVERAGE, simgear::effect::AVERAGE, simgear::effect::AVERAGE);
SG_LOG(SG_IO, SG_WARN, "Texture compression plugin (osg_nvtt) not available; storing uncompressed image: " << absFileName);
srcImage = simgear::effect::computeMipmap(srcImage, mipmapFunctions);
}
}
else {
SG_LOG(SG_IO, SG_ALERT, "Creating uncompressed DDS for " + absFileName);
if (processor) {
processor->generateMipMap(*srcImage, true, osgDB::ImageProcessor::USE_CPU);
}
else {
simgear::effect::MipMapTuple mipmapFunctions(simgear::effect::AVERAGE, simgear::effect::AVERAGE, simgear::effect::AVERAGE, simgear::effect::AVERAGE);
srcImage = simgear::effect::computeMipmap(srcImage, mipmapFunctions);
}
}
//}
//else
// printf("--- no compress or mipmap of format %s\n", newName.c_str());
registry->writeImage(*srcImage, newName, nopt);
{
std::string mdlDirectory = cache_root + "/cache-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 \""
@@ -449,19 +755,21 @@ ModelRegistry::readImage(const string& fileName,
}
osg::Node* DefaultCachePolicy::find(const string& fileName,
const Options* opt)
osg::ref_ptr<osg::Node> DefaultCachePolicy::find(const string& fileName, const Options* opt)
{
Registry* registry = Registry::instance();
osg::Node* cached
= dynamic_cast<Node*>(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<osg::Object> cachedObject = registry->getFromObjectCache(fileName);
#else
osg::ref_ptr<osg::Object> cachedObject = registry->getRefFromObjectCache(fileName);
#endif
ref_ptr<osg::Node> cachedNode = dynamic_cast<osg::Node*>(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,

View File

@@ -132,7 +132,7 @@ struct DefaultProcessPolicy {
struct DefaultCachePolicy {
DefaultCachePolicy(const std::string& extension) {}
osg::Node* find(const std::string& fileName,
osg::ref_ptr<osg::Node> find(const std::string& fileName,
const osgDB::Options* opt);
void addToCache(const std::string& filename, osg::Node* node);
};

View File

@@ -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);
@@ -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<SGPropertyNode_ptr> particle_nodes;
particle_nodes = props->getChildren("particlesystem");
for (unsigned i = 0; i < particle_nodes.size(); ++i) {

View File

@@ -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<osg::Texture2D> 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);

View File

@@ -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<osg::TexEnvCombine*>(layer_root->getStateSet()
->getTextureAttribute(1, osg::StateAttribute::TEXENV));
osg::StateAttribute* textureAtt = layer_root->getStateSet()->getTextureAttribute(1, osg::StateAttribute::TEXENV);
osg::TexEnvCombine* combiner = dynamic_cast<osg::TexEnvCombine*>(textureAtt);
if (combiner == nullptr)
return false;
combiner->setConstantColor(combineColor);
// Set the fog color for the 3D clouds too.

View File

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

View File

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

View File

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