From 8641d3616a7a90488cdd9a784a5118ed47949f6d Mon Sep 17 00:00:00 2001 From: Stuart Buchanan Date: Sat, 5 Dec 2020 13:09:35 +0000 Subject: [PATCH] WS30: Texture array texture lookup for landclass - Create a Texture2DArray for a MaterialCache - Adjust the landclass image to index into the above array - Add the array to as a Uniform for rendering. --- simgear/scene/material/mat.cxx | 22 ++++ simgear/scene/material/mat.hxx | 5 + simgear/scene/material/matlib.cxx | 104 +++++++++++++++++-- simgear/scene/material/matlib.hxx | 44 +++++--- simgear/scene/tgdb/SGTileDetailsCallback.hxx | 1 - simgear/scene/tgdb/VPBTechnique.cxx | 59 +++++++++-- simgear/scene/tgdb/VPBTechnique.hxx | 4 +- 7 files changed, 209 insertions(+), 30 deletions(-) diff --git a/simgear/scene/material/mat.cxx b/simgear/scene/material/mat.cxx index b7aa5fa9..af63289d 100644 --- a/simgear/scene/material/mat.cxx +++ b/simgear/scene/material/mat.cxx @@ -496,6 +496,28 @@ osg::Texture2D* SGMaterial::get_one_object_mask(int texIndex) } } +std::string SGMaterial::get_one_texture(int setIndex, int texIndex) +{ + if (_status.empty()) { + SG_LOG( SG_GENERAL, SG_WARN, "No material available."); + return 0; + } + + unsigned int i = setIndex % _status.size(); + + SGMaterial::_internal_state st = _status[i]; + //if (st == NULL) return 0; + + std::string texturePath = ""; + + auto iter = st.texture_paths.begin(); + for (; iter != st.texture_paths.end(); ++iter) { + if (iter->second == texIndex) texturePath = iter->first; + } + + return texturePath; +} + void SGMaterial::buildEffectProperties(const SGReaderWriterOptions* options) { using namespace osg; diff --git a/simgear/scene/material/mat.hxx b/simgear/scene/material/mat.hxx index 21420e94..b245850d 100644 --- a/simgear/scene/material/mat.hxx +++ b/simgear/scene/material/mat.hxx @@ -141,6 +141,11 @@ public: */ osg::Texture2D* get_one_object_mask(int texIndex); + /** + * Get the textured state. + */ + std::string get_one_texture(int setIndex, int texIndex); + /** * Get the number of textures assigned to this material. diff --git a/simgear/scene/material/matlib.cxx b/simgear/scene/material/matlib.cxx index e79fb365..f4008a0c 100644 --- a/simgear/scene/material/matlib.cxx +++ b/simgear/scene/material/matlib.cxx @@ -33,7 +33,7 @@ #include #include -#include +#include #include #include @@ -41,7 +41,9 @@ #include #include #include +#include #include +#include #include "mat.hxx" @@ -50,6 +52,7 @@ #include "matlib.hxx" using std::string; +using namespace simgear; class SGMaterialLib::MatLibPrivate { @@ -217,9 +220,11 @@ SGMaterial *SGMaterialLib::find( int lc, const SGGeod& center ) const } } -SGMaterialCache *SGMaterialLib::generateMatCache(SGVec2f center) +SGMaterialCache *SGMaterialLib::generateMatCache(SGVec2f center, const simgear::SGReaderWriterOptions* options) { - SGMaterialCache* newCache = new SGMaterialCache(); + + + SGMaterialCache* newCache = new SGMaterialCache(getMaterialTextureAtlas(center, options)); material_map::const_reverse_iterator it = matlib.rbegin(); for (; it != matlib.rend(); ++it) { newCache->insert(it->first, find(it->first, center)); @@ -234,10 +239,10 @@ SGMaterialCache *SGMaterialLib::generateMatCache(SGVec2f center) return newCache; } -SGMaterialCache *SGMaterialLib::generateMatCache(SGGeod center) +SGMaterialCache *SGMaterialLib::generateMatCache(SGGeod center, const simgear::SGReaderWriterOptions* options) { SGVec2f c = SGVec2f(center.getLongitudeDeg(), center.getLatitudeDeg()); - return SGMaterialLib::generateMatCache(c); + return SGMaterialLib::generateMatCache(c, options); } @@ -265,8 +270,9 @@ const SGMaterial *SGMaterialLib::findMaterial(const osg::Geode* geode) } // Constructor -SGMaterialCache::SGMaterialCache ( void ) +SGMaterialCache::SGMaterialCache (Atlas atlas) { + _atlas = atlas; } // Insertion into the material cache @@ -295,6 +301,92 @@ SGMaterial *SGMaterialCache::find(int lc) const return find(getNameFromLandclass(lc)); } +// Generate a texture atlas for this location +SGMaterialCache::Atlas SGMaterialLib::getMaterialTextureAtlas(SGVec2f center, const simgear::SGReaderWriterOptions* options) +{ + SG_LOG(SG_TERRAIN, SG_DEBUG, "Getting Texture Atlas for " << center); + + SGMaterialCache::AtlasIndex atlasIndex; + SGMaterialCache::AtlasImage atlasImage; + std::string id; + const_landclass_map_iterator lc_iter = landclasslib.begin(); + for (; lc_iter != landclasslib.end(); ++lc_iter) { + SGMaterial* mat = find(lc_iter->second, center); + const std::string texture = mat->get_one_texture(0,0); + id.append(texture); + id.append(";"); + } + + SGMaterialLib::atlas_map::iterator atlas_iter = _atlasCache.find(id); + + if (atlas_iter != _atlasCache.end()) return atlas_iter->second; + + // Cache lookup failure - generate a new atlas, but only if we have a change of reading any textures + if (options == 0) { + return SGMaterialCache::Atlas(atlasIndex, atlasImage); + } + + atlasImage = new osg::Texture2DArray(); + + atlasImage->setMaxAnisotropy(16.0f); + atlasImage->setResizeNonPowerOfTwoHint(false); + + atlasImage->setFilter(osg::Texture::MIN_FILTER, osg::Texture::NEAREST_MIPMAP_NEAREST); + atlasImage->setFilter(osg::Texture::MAG_FILTER, osg::Texture::NEAREST_MIPMAP_NEAREST); + + atlasImage->setWrap(osg::Texture::WRAP_S,osg::Texture::REPEAT); + atlasImage->setWrap(osg::Texture::WRAP_T,osg::Texture::REPEAT); + + int index = -1; + lc_iter = landclasslib.begin(); + for (; lc_iter != landclasslib.end(); ++lc_iter) { + // index is incremented at the start of the loop so that we have a valid + // mapping based on the lanclassList, even if we fail to process the texture. + ++index; + int i = lc_iter->first; + SGMaterial* mat = find(lc_iter->second, center); + atlasIndex[i] = index; + + SG_LOG(SG_TERRAIN, SG_DEBUG, " Lookup " << i << " " << lc_iter->second); + + + if (mat == NULL) continue; + + SG_LOG(SG_TERRAIN, SG_DEBUG, " Found it!"); + + // Just get the first texture in the first texture-set for the moment. + // Should add some variability in texture-set in the future. + const std::string texture = mat->get_one_texture(0,0); + + if (texture.empty()) continue; + + SGPath texturePath = SGPath("Textures"); + //texturePath.append(texture); + std::string fullPath = SGModelLib::findDataFile(texture, options, texturePath); + + if (fullPath.empty()) { + SG_LOG(SG_GENERAL, SG_ALERT, "Cannot find texture \"" + << texture << "\" in Textures folders when creating texture atlas"); + } + else + { + // Copy the texture into the atlas in the appropriate place + osg::ref_ptr subtexture = osgDB::readRefImageFile(fullPath, options); + + if (subtexture && subtexture->valid()) { + subtexture->scaleImage(2048,2048,1); + SG_LOG(SG_TERRAIN, SG_ALERT, "Adding image " << fullPath << " to texture atlas " << subtexture->getInternalTextureFormat()); + atlasImage->setImage(index,subtexture); + } + } + } + + // Cache for future lookups + SGMaterialCache::Atlas atlas = SGMaterialCache::Atlas(atlasIndex, atlasImage); + _atlasCache[id] = atlas; + return atlas; +} + // Destructor SGMaterialCache::~SGMaterialCache ( void ) { SG_LOG( SG_TERRAIN, SG_DEBUG, "SGMaterialCache::~SGMaterialCache() size=" << cache.size()); diff --git a/simgear/scene/material/matlib.hxx b/simgear/scene/material/matlib.hxx index 826d70c5..439e0b6e 100644 --- a/simgear/scene/material/matlib.hxx +++ b/simgear/scene/material/matlib.hxx @@ -27,9 +27,10 @@ #include +#include #include #include -#include +#include #include #include // Standard C++ string library @@ -40,23 +41,22 @@ class SGMaterial; class SGPropertyNode; class SGPath; -namespace simgear { class Effect; } +namespace simgear { class Effect; class SGReaderWriterOptions; } namespace osg { class Geode; } // Material cache class class SGMaterialCache : public osg::Referenced { -private: - typedef std::map < std::string, SGSharedPtr > material_cache; - material_cache cache; - - const std::string getNameFromLandclass(int lc) const { - return std::string("WS30_").append(std::to_string(lc)); - } - public: + + // An atlas, consisting of a landclass index and an atlas image + typedef std::map AtlasIndex; + typedef osg::ref_ptr AtlasImage; + + typedef std::pair Atlas; + // Constructor - SGMaterialCache ( void ); + SGMaterialCache (Atlas atlas); // Insertion void insert( const std::string& name, SGSharedPtr material ); @@ -66,8 +66,20 @@ public: SGMaterial *find( const std::string& material ) const; SGMaterial *find( int material ) const; + AtlasIndex getAtlasIndex() { return _atlas.first; }; + AtlasImage getAtlasImage() { return _atlas.second; }; + // Destructor ~SGMaterialCache ( void ); + +private: + typedef std::map < std::string, SGSharedPtr > material_cache; + material_cache cache; + Atlas _atlas; + + const std::string getNameFromLandclass(int lc) const { + return std::string("WS30_").append(std::to_string(lc)); + } }; // Material management class @@ -92,7 +104,13 @@ private: material_map matlib; landclass_map landclasslib; + + typedef std::map < std::string, SGMaterialCache::Atlas > atlas_map; + atlas_map _atlasCache; + // Get a (cached) texture atlas for this material cache + SGMaterialCache::Atlas getMaterialTextureAtlas(SGVec2f center, const simgear::SGReaderWriterOptions* options); + public: // Constructor @@ -119,8 +137,8 @@ public: * cache of the valid materials based on the current state and a given position. */ - SGMaterialCache *generateMatCache( SGVec2f center); - SGMaterialCache *generateMatCache( SGGeod center); + SGMaterialCache *generateMatCache( SGVec2f center, const simgear::SGReaderWriterOptions* options=0); + SGMaterialCache *generateMatCache( SGGeod center, const simgear::SGReaderWriterOptions* options=0); material_map_iterator begin() { return matlib.begin(); } const_material_map_iterator begin() const { return matlib.begin(); } diff --git a/simgear/scene/tgdb/SGTileDetailsCallback.hxx b/simgear/scene/tgdb/SGTileDetailsCallback.hxx index b2f6ead8..9a52b208 100644 --- a/simgear/scene/tgdb/SGTileDetailsCallback.hxx +++ b/simgear/scene/tgdb/SGTileDetailsCallback.hxx @@ -55,7 +55,6 @@ typedef std::list SGDirectionalLightListBin; #define SG_SIMPLIFIER_RATIO (0.001) #define SG_SIMPLIFIER_MAX_LENGTH (1000.0) #define SG_SIMPLIFIER_MAX_ERROR (2000.0) -#define SG_TILE_MIN_EXPIRY (180.0) using namespace simgear; // QuadTreeBuilder is used by Random Objects Generator diff --git a/simgear/scene/tgdb/VPBTechnique.cxx b/simgear/scene/tgdb/VPBTechnique.cxx index 24b858c6..33622869 100644 --- a/simgear/scene/tgdb/VPBTechnique.cxx +++ b/simgear/scene/tgdb/VPBTechnique.cxx @@ -16,6 +16,8 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +#include + #include #include @@ -25,6 +27,7 @@ #include #include +#include #include #include #include @@ -151,14 +154,14 @@ void VPBTechnique::init(int dirtyMask, bool assumeMultiThreaded) } else { - applyColorLayers(*buffer); + applyColorLayers(*buffer, masterLocator); applyTransparency(*buffer); } } else { generateGeometry(*buffer, masterLocator, centerModel); - applyColorLayers(*buffer); + applyColorLayers(*buffer, masterLocator); applyTransparency(*buffer); } @@ -1356,7 +1359,7 @@ void VPBTechnique::generateGeometry(BufferData& buffer, Locator* masterLocator, tile_height = 0.5 * (t.length() + v.length()); } - SG_LOG(SG_TERRAIN, SG_ALERT, "Tile Level " << _terrainTile->getTileID().level << " width " << tile_width << " height " << tile_height); + //SG_LOG(SG_TERRAIN, SG_ALERT, "Tile Level " << _terrainTile->getTileID().level << " width " << tile_width << " height " << tile_height); osg::ref_ptr twu = new osg::Uniform("tile_width", tile_width); twu->setDataVariance(osg::Object::STATIC); @@ -1380,10 +1383,12 @@ void VPBTechnique::generateGeometry(BufferData& buffer, Locator* masterLocator, } } -void VPBTechnique::applyColorLayers(BufferData& buffer) -{ +void VPBTechnique::applyColorLayers(BufferData& buffer, Locator* masterLocator) +{ typedef std::map LayerToTextureMap; + typedef std::map LayerToAtlasMap; LayerToTextureMap layerToTextureMap; + LayerToAtlasMap layerToAtlasMap; for(unsigned int layerNum=0; layerNum<_terrainTile->getNumColorLayers(); ++layerNum) { @@ -1413,15 +1418,50 @@ void VPBTechnique::applyColorLayers(BufferData& buffer) osg::StateSet* stateset = buffer._geode->getOrCreateStateSet(); osg::Texture2D* texture2D = dynamic_cast(layerToTextureMap[colorLayer]); + osg::Texture2DArray* atlasTexture = dynamic_cast(layerToAtlasMap[colorLayer]); if (!texture2D) { texture2D = new osg::Texture2D; + + // First time generating this texture, so process to change landclass IDs to texure indexes. + SGPropertyNode_ptr effectProp; + SGMaterialLibPtr matlib = _options->getMaterialLib(); + osg::Vec3d world; + SGGeod loc; + + if (matlib) + { + SG_LOG(SG_TERRAIN, SG_DEBUG, "Generating texture atlas for " << image->getName()); + // Determine the center of the tile, sadly non-trivial. + osg::Vec3d tileloc = computeCenter(buffer, masterLocator); + masterLocator->convertLocalToModel(tileloc, world); + const SGVec3d world2 = SGVec3d(world.x(), world.y(), world.z()); + loc = SGGeod::fromCart(world2); + SG_LOG(SG_TERRAIN, SG_DEBUG, "Applying VPB material " << loc); + + if (_matcache ==0) _matcache = _options->getMaterialLib()->generateMatCache(loc, _options); + + atlasTexture = _matcache->getAtlasImage(); + layerToAtlasMap[colorLayer] = atlasTexture; + SGMaterialCache::AtlasIndex atlasIndex = _matcache->getAtlasIndex(); + + for (int s = 0; s < image->s(); s++) { + for (int t = 0; t < image->t(); t++) { + osg::Vec4 c = image->getColor(s, t); + int i = int(round(c.x() * 255.0)); + c.set(c.x(), (float) (atlasIndex[i] / 255.0), c.z(), c.w() ); + image->setColor(c, s, t); + } + } + + } + texture2D->setImage(image); texture2D->setMaxAnisotropy(16.0f); texture2D->setResizeNonPowerOfTwoHint(false); - texture2D->setFilter(osg::Texture::MIN_FILTER, colorLayer->getMinFilter()); - texture2D->setFilter(osg::Texture::MAG_FILTER, colorLayer->getMagFilter()); + texture2D->setFilter(osg::Texture::MIN_FILTER, osg::Texture::NEAREST); + texture2D->setFilter(osg::Texture::MAG_FILTER, osg::Texture::NEAREST); texture2D->setWrap(osg::Texture::WRAP_S,osg::Texture::CLAMP_TO_EDGE); texture2D->setWrap(osg::Texture::WRAP_T,osg::Texture::CLAMP_TO_EDGE); @@ -1432,7 +1472,7 @@ void VPBTechnique::applyColorLayers(BufferData& buffer) if (mipMapping && (s_NotPowerOfTwo || t_NotPowerOfTwo)) { - OSG_INFO<<"Disabling mipmapping for non power of two tile size("<s()<<", "<t()<<")"<s()<<", "<t()<<")"); texture2D->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); } @@ -1441,7 +1481,8 @@ void VPBTechnique::applyColorLayers(BufferData& buffer) } - stateset->setTextureAttributeAndModes(layerNum, texture2D, osg::StateAttribute::ON); + stateset->setTextureAttributeAndModes(2*layerNum, texture2D, osg::StateAttribute::ON); + stateset->setTextureAttributeAndModes(2*layerNum +1, atlasTexture, osg::StateAttribute::ON); } else if (contourLayer) diff --git a/simgear/scene/tgdb/VPBTechnique.hxx b/simgear/scene/tgdb/VPBTechnique.hxx index 958011f4..244dcde5 100644 --- a/simgear/scene/tgdb/VPBTechnique.hxx +++ b/simgear/scene/tgdb/VPBTechnique.hxx @@ -107,7 +107,7 @@ class VPBTechnique : public TerrainTechnique virtual void generateGeometry(BufferData& buffer, Locator* masterLocator, const osg::Vec3d& centerModel); - virtual void applyColorLayers(BufferData& buffer); + virtual void applyColorLayers(BufferData& buffer, Locator* masterLocator); virtual void applyTransparency(BufferData& buffer); @@ -123,6 +123,8 @@ class VPBTechnique : public TerrainTechnique osg::Matrix3 _filterMatrix; osg::ref_ptr _filterMatrixUniform; osg::ref_ptr _options; + osg::ref_ptr _atlas; + osg::ref_ptr _matcache; }; }