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.
This commit is contained in:
Stuart Buchanan 2020-12-05 13:09:35 +00:00
parent 02cf42359d
commit 8641d3616a
7 changed files with 209 additions and 30 deletions

View File

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

View File

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

View File

@ -33,7 +33,7 @@
#include <string>
#include <mutex>
#include <osgDB/Registry>
#include <osgDB/ReadFile>
#include <simgear/debug/logstream.hxx>
#include <simgear/misc/sg_path.hxx>
@ -41,7 +41,9 @@
#include <simgear/props/props.hxx>
#include <simgear/props/props_io.hxx>
#include <simgear/props/condition.hxx>
#include <simgear/scene/model/modellib.hxx>
#include <simgear/scene/tgdb/userdata.hxx>
#include <simgear/scene/util/SGReaderWriterOptions.hxx>
#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<osg::Image> 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());

View File

@ -27,9 +27,10 @@
#include <simgear/compiler.h>
#include <simgear/math/SGMath.hxx>
#include <simgear/structure/SGReferenced.hxx>
#include <simgear/structure/SGSharedPtr.hxx>
#include <simgear/math/SGMath.hxx>
#include <osg/Texture2DArray>
#include <memory>
#include <string> // 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<SGMaterial> > 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<int, int> AtlasIndex;
typedef osg::ref_ptr<osg::Texture2DArray> AtlasImage;
typedef std::pair<AtlasIndex, AtlasImage> Atlas;
// Constructor
SGMaterialCache ( void );
SGMaterialCache (Atlas atlas);
// Insertion
void insert( const std::string& name, SGSharedPtr<SGMaterial> 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<SGMaterial> > 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(); }

View File

@ -55,7 +55,6 @@ typedef std::list<SGDirectionalLightBin> 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

View File

@ -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 <cmath>
#include <osgTerrain/TerrainTile>
#include <osgTerrain/Terrain>
@ -25,6 +27,7 @@
#include <osg/io_utils>
#include <osg/Texture2D>
#include <osg/Texture2DArray>
#include <osg/Texture1D>
#include <osg/Program>
#include <osg/Math>
@ -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<osg::Uniform> 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<osgTerrain::Layer*, osg::Texture*> LayerToTextureMap;
typedef std::map<osgTerrain::Layer*, osg::Texture2DArray*> 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<osg::Texture2D*>(layerToTextureMap[colorLayer]);
osg::Texture2DArray* atlasTexture = dynamic_cast<osg::Texture2DArray*>(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("<<image->s()<<", "<<image->t()<<")"<<std::endl;
SG_LOG(SG_TERRAIN, SG_ALERT, "Disabling mipmapping for non power of two tile size("<<image->s()<<", "<<image->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)

View File

@ -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<osg::Uniform> _filterMatrixUniform;
osg::ref_ptr<SGReaderWriterOptions> _options;
osg::ref_ptr<osg::Image> _atlas;
osg::ref_ptr<SGMaterialCache> _matcache;
};
}