From 96a9abfd7a539c6c70926f29a046b9656bb12639 Mon Sep 17 00:00:00 2001 From: Stuart Buchanan Date: Fri, 14 Oct 2022 21:36:25 +0100 Subject: [PATCH] WS30: Improved Atlas encapsulation --- simgear/scene/material/Atlas.cxx | 185 +++++++++++++++++++++++++ simgear/scene/material/Atlas.hxx | 114 ++++++++++++++++ simgear/scene/material/CMakeLists.txt | 2 + simgear/scene/material/matlib.cxx | 188 +++----------------------- simgear/scene/material/matlib.hxx | 73 ++-------- simgear/scene/tgdb/VPBTechnique.cxx | 25 ++-- 6 files changed, 347 insertions(+), 240 deletions(-) create mode 100644 simgear/scene/material/Atlas.cxx create mode 100644 simgear/scene/material/Atlas.hxx diff --git a/simgear/scene/material/Atlas.cxx b/simgear/scene/material/Atlas.cxx new file mode 100644 index 00000000..b943f0b4 --- /dev/null +++ b/simgear/scene/material/Atlas.cxx @@ -0,0 +1,185 @@ +// Atlas.cxx -- class for a material-based texture atlas +// +// Copyright (C) 2022 Stuart Buchanan +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id$ + +#include + +#include +#include + +using namespace simgear; + + +// Constructor +Atlas::Atlas(osg::ref_ptr options) { + + _options = options; + + _textureLookup1 = new osg::Uniform(osg::Uniform::Type::FLOAT_VEC4, "fg_textureLookup1", Atlas::MAX_MATERIALS); + _textureLookup2 = new osg::Uniform(osg::Uniform::Type::FLOAT_VEC4, "fg_textureLookup2", Atlas::MAX_MATERIALS); + _dimensions = new osg::Uniform(osg::Uniform::Type::FLOAT_VEC4, "fg_dimensionsArray", Atlas::MAX_MATERIALS); + _ambient = new osg::Uniform(osg::Uniform::Type::FLOAT_VEC4, "fg_ambientArray", Atlas::MAX_MATERIALS); + _diffuse = new osg::Uniform(osg::Uniform::Type::FLOAT_VEC4, "fg_diffuseArray", Atlas::MAX_MATERIALS); + _specular = new osg::Uniform(osg::Uniform::Type::FLOAT_VEC4, "fg_specularArray", Atlas::MAX_MATERIALS); + _materialParams1 = new osg::Uniform(osg::Uniform::Type::FLOAT_VEC4, "fg_materialParams1", Atlas::MAX_MATERIALS); + _materialParams2 = new osg::Uniform(osg::Uniform::Type::FLOAT_VEC4, "fg_materialParams2", Atlas::MAX_MATERIALS); + _materialParams3 = new osg::Uniform(osg::Uniform::Type::FLOAT_VEC4, "fg_materialParams3", Atlas::MAX_MATERIALS); + + _image = new osg::Texture2DArray(); + _image->setMaxAnisotropy(SGSceneFeatures::instance()->getTextureFilter()); + _image->setResizeNonPowerOfTwoHint(false); + _image->setWrap(osg::Texture::WRAP_S,osg::Texture::REPEAT); + _image->setWrap(osg::Texture::WRAP_T,osg::Texture::REPEAT); + + _imageIndex = 0u; // Index into the image + _materialLookupIndex = 0u; // Index into the material lookup + + // Add hardcoded atlas images. + unsigned int standardTextureCount = size(Atlas::STANDARD_TEXTURES); + for (; _imageIndex < standardTextureCount; _imageIndex++) { + // Copy the texture into the atlas in the appropriate place + osg::ref_ptr subtexture = osgDB::readRefImageFile(Atlas::STANDARD_TEXTURES[_imageIndex], options); + + if (subtexture && subtexture->valid()) { + if ((subtexture->s() != 2048) || (subtexture->t() != 2048)) { + subtexture->scaleImage(2048,2048,1); + } + + _image->setImage(_imageIndex,subtexture); + _textureMap[Atlas::STANDARD_TEXTURES[_imageIndex]] = _imageIndex; + } + } +} + +void Atlas::addMaterial(int landclass, bool isWater, SGMaterial* mat) { + + _index[landclass] = _materialLookupIndex; + _waterAtlas[landclass] = isWater; + unsigned int textureList[Atlas::MAX_TEXTURES]; + + if (mat != NULL) { + + if (mat->get_num_textures(0) > Atlas::MAX_TEXTURES) { + SG_LOG(SG_GENERAL, SG_ALERT, "Unable to build texture atlas for landclass " + << landclass << " aka " << mat->get_names()[0] + << " too many textures: " << mat->get_num_textures(0) + << " (maximum " << Atlas::MAX_TEXTURES << ")"); + return; + } + + _dimensions->setElement(_materialLookupIndex, osg::Vec4f(mat->get_xsize(), mat->get_ysize(), mat->get_shininess(), (double) mat->get_parameter("edge-hardness"))); + _ambient->setElement(_materialLookupIndex, mat->get_ambient()); + _diffuse->setElement(_materialLookupIndex, mat->get_diffuse()); + _specular->setElement(_materialLookupIndex, mat->get_specular()); + + // The following are material parameters that are normally built into the Effect as Uniforms. In the WS30 + // case we need to pass them as an array, indexed against the material. + _materialParams1->setElement(_materialLookupIndex, osg::Vec4f(mat->get_parameter("transition_model"), mat->get_parameter("hires_overlay_bias"), mat->get_parameter("grain_strength"), mat->get_parameter("intrinsic_wetness"))); + _materialParams2->setElement(_materialLookupIndex, osg::Vec4f(mat->get_parameter("dot_density"), mat->get_parameter("dot_size"), mat->get_parameter("dust_resistance"), mat->get_parameter("rock_strata"))); + + float water = 0.0; + if (_waterAtlas[landclass]) { + water = 1.0; + } + + _materialParams3->setElement(_materialLookupIndex, osg::Vec4f(water, mat->get_parameter("waterline-start"), mat->get_parameter("waterline-end"), 0.0)); + + // Similarly, there are specifically 7 textures that are defined in the materials that need to be passed into + // the shader as an array based on the material lookup. + // + // The mapping from terrain-default.eff / terrain-overlay.eff is as follows + // + // TEXTURE NAME texture-unit Material texture index Default value + // Primary texure 0 0 n/a + // gradient_texture 2 13 Textures/Terrain/rock_alt.png + // dot_texture 3 15 Textures/Terrain/sand6.png + // grain_texture 4 14 Textures/Terrain/grain_texture.png + // mix_texture 5 12 Textures/Terrain/void.png + // detail_texture 7 11 Textures/Terrain/void.png + // overlayPrimaryTex 7 20 Textures/Terrain/void.png + // overlaySecondaryTex 8 21 Textures/Terrain/void.png + + for (unsigned int i = 0; i < Atlas::MAX_TEXTURES; i++) { + std::string texture = mat->get_one_texture(0,i); + + if (texture.empty()) { + // This is a rather horrible hardcoded mapping of the default textures defined in + // terrain-default.eff and terrain-overlay.eff which are in effect defaults to + // the material definitions + if (i < 13) texture = std::string("Textures/Terrain/void.png"); + if (i == 13) texture = std::string("Textures/Terrain/rock_alt.png"); + if (i == 14) texture = std::string("Textures/Terrain/grain_texture.png"); + if (i == 15) texture = std::string("Textures/Terrain/sand6.png"); + if (i > 14) texture = std::string("Textures/Terrain/void.png"); + } + + SGPath texturePath = SGPath("Textures"); + 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"); + texture = std::string("Textures/Terrain/void.png"); + fullPath = SGModelLib::findDataFile(texture, _options, texturePath); + } + + if (_textureMap.find(fullPath) == _textureMap.end()) { + // Add any missing textures into the atlas image + // Copy the texture into the atlas in the appropriate place + osg::ref_ptr subtexture = osgDB::readRefImageFile(fullPath, _options); + + if (subtexture && subtexture->valid()) { + + if ((subtexture->s() != 2048) || (subtexture->t() != 2048)) { + subtexture->scaleImage(2048,2048,1); + } + + _image->setImage(_imageIndex,subtexture); + _textureMap[fullPath] = _imageIndex; + ++_imageIndex; + } + } + + // At this point we know that the texture is present in the + // atlas and referenced in the textureMap, so add it to the materialLookup + textureList[i] = _textureMap[fullPath]; + } + + // We now have a textureList containing the full set of textures. Pack the relevant ones into the Vec4 of the index Uniform. + // This is a bit of a hack to maintain compatibility with the WS2.0 material definitions, as the material definitions use the + // 11-15th textures for the various overlay textures for terrain-default.eff, we do the same for ws30.eff + _textureLookup1->setElement(_materialLookupIndex, osg::Vec4f( (float) (textureList[0] / 255.0), (float) (textureList[11] / 255.0), (float) (textureList[12] / 255.0), (float) (textureList[13] / 255.0))); + _textureLookup2->setElement(_materialLookupIndex, osg::Vec4f( (float) (textureList[14] / 255.0), (float) (textureList[15] / 255.0), (float) (textureList[20] / 255.0), (float) (textureList[21] / 255.0))); + } + + ++_materialLookupIndex; +} + +void Atlas::addUniforms(osg::StateSet* stateset) { + stateset->addUniform(_dimensions); + stateset->addUniform(_ambient); + stateset->addUniform(_diffuse); + stateset->addUniform(_specular); + stateset->addUniform(_textureLookup1); + stateset->addUniform(_textureLookup2); + stateset->addUniform(_materialParams1); + stateset->addUniform(_materialParams2); + stateset->addUniform(_materialParams3); +} + diff --git a/simgear/scene/material/Atlas.hxx b/simgear/scene/material/Atlas.hxx new file mode 100644 index 00000000..a53b86d4 --- /dev/null +++ b/simgear/scene/material/Atlas.hxx @@ -0,0 +1,114 @@ +// Atlas.hxx -- class for a material-based texture atlas +// +// Copyright (C) 2022 Stuart Buchanan +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id$ + + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include // Standard C++ string library +#include // STL associative "array" +#include // STL "array" + +namespace simgear +{ + +// Atlas of textures +class Atlas : public osg::Referenced +{ +public: + + // Constructor + Atlas (osg::ref_ptr options ); + + // Mapping of landclass numbers to indexes within the atlas + // materialLookup + typedef std::map AtlasIndex; + + // Mapping of texture filenames to their index in the Atlas image itself. + typedef std::map TextureMap; + + // The Texture array itself + typedef osg::ref_ptr AtlasImage; + typedef std::map WaterAtlas; + + void addUniforms(osg::StateSet* stateset); + void addMaterial(int landclass, bool water, SGMaterial* mat); + + // Maximum number of material entries in the atlas + static const unsigned int MAX_MATERIALS = 64; + + // Lookups into the Atlas from landclass + bool isWater(int landclass) { return _waterAtlas[landclass]; }; + int getIndex(int landclass) { return _index[landclass]; }; + + AtlasImage getImage() { return _image; }; + +private: + AtlasIndex _index; + AtlasImage _image; + + osg::ref_ptr _options; + + osg::ref_ptr _textureLookup1; + osg::ref_ptr _textureLookup2; + osg::ref_ptr _dimensions; + osg::ref_ptr _ambient; + osg::ref_ptr _diffuse; + osg::ref_ptr _specular; + + osg::ref_ptr _materialParams1; + osg::ref_ptr _materialParams2; + osg::ref_ptr _materialParams3; + + unsigned int _imageIndex; // Index into the image + unsigned int _materialLookupIndex; // Index into the material lookup + + WaterAtlas _waterAtlas; + TextureMap _textureMap; + + // Maximum number of textures per texture-set for the Atlas. + static const unsigned int MAX_TEXTURES = 22; + + // Standard textures, used by water shader in particular. + // Indexes are hardcoded in Shaders/ws30-water.frag + inline static const std::string STANDARD_TEXTURES[] = { + "Textures/Terrain/water.png", + "Textures/Water/water-reflection-ws30.png", + "Textures/Water/waves-ver10-nm-ws30.png", + "Textures/Water/water_sine_nmap-ws30.png", + "Textures/Water/water-reflection-grey-ws30.png", + "Textures/Water/sea_foam-ws30.png", + "Textures/Water/perlin-noise-nm.png", + + // The following two textures are large and don't have an alpha channel. Ignoring for now. + //"Textures/Globe/ocean_depth_1.png", + //"Textures/Globe/globe_colors.jpg", + "Textures/Terrain/packice-overlay.png" + }; +}; + +} diff --git a/simgear/scene/material/CMakeLists.txt b/simgear/scene/material/CMakeLists.txt index b72e4edd..00dcd177 100644 --- a/simgear/scene/material/CMakeLists.txt +++ b/simgear/scene/material/CMakeLists.txt @@ -1,4 +1,5 @@ set(HEADERS + Atlas.hxx Effect.hxx EffectBuilder.hxx EffectCullVisitor.hxx @@ -14,6 +15,7 @@ set(HEADERS ) set(SOURCES + Atlas.cxx Effect.cxx EffectBuilder.cxx EffectCullVisitor.cxx diff --git a/simgear/scene/material/matlib.cxx b/simgear/scene/material/matlib.cxx index 360d3fba..59b5baa0 100644 --- a/simgear/scene/material/matlib.cxx +++ b/simgear/scene/material/matlib.cxx @@ -246,10 +246,10 @@ SGMaterial *SGMaterialLib::find( int lc, const SGGeod& center ) const SGMaterialCache *SGMaterialLib::generateMatCache(SGVec2f center, const simgear::SGReaderWriterOptions* options) { - SGMaterialCache* newCache = new SGMaterialCache(getMaterialTextureAtlas(center, options)); + SGMaterialCache* newCache = new SGMaterialCache(); + newCache->setAtlas(SGMaterialLib::getOrCreateAtlas(landclasslib, center, options)); std::lock_guard g(d->mutex); - material_map::const_reverse_iterator it = matlib.rbegin(); for (; it != matlib.rend(); ++it) { newCache->insert(it->first, internalFind(it->first, center)); @@ -295,9 +295,8 @@ const SGMaterial *SGMaterialLib::findMaterial(const osg::Geode* geode) } // Constructor -SGMaterialCache::SGMaterialCache (Atlas atlas) +SGMaterialCache::SGMaterialCache () { - _atlas = atlas; } // Insertion into the material cache @@ -326,14 +325,14 @@ 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* const_options) -{ - SGMaterialCache::Atlas atlas; - +osg::ref_ptr SGMaterialLib::getOrCreateAtlas(SGMaterialLib::landclass_map landclasslib, SGVec2f center, const simgear::SGReaderWriterOptions* const_options) { + + osg::ref_ptr atlas; // Non-VPB does not use the Atlas, so save some effort and return if (! SGSceneFeatures::instance()->getVPBActive()) return atlas; + std::lock_guard g(SGMaterialLib::_atlasCacheMutex); + // A simple key to the atlas is just the list of textures. std::string id; const_landclass_map_iterator lc_iter = landclasslib.begin(); @@ -341,12 +340,11 @@ SGMaterialCache::Atlas SGMaterialLib::getMaterialTextureAtlas(SGVec2f center, co SGMaterial* mat = find(lc_iter->second.first, center); const std::string texture = mat->get_one_texture(0,0); id.append(texture); - id.append(";"); + id.append(";"); } - SGMaterialLib::atlas_map::iterator atlas_iter = _atlasCache.find(id); - - if (atlas_iter != _atlasCache.end()) return atlas_iter->second; + atlas_map::iterator atlas_iter = SGMaterialLib::_atlasCache.find(id); + if (atlas_iter != SGMaterialLib::_atlasCache.end()) return atlas_iter->second; // Cache lookup failure - generate a new atlas, but only if we have a chance of reading any textures if (const_options == 0) { @@ -355,172 +353,30 @@ SGMaterialCache::Atlas SGMaterialLib::getMaterialTextureAtlas(SGVec2f center, co osg::ref_ptr options = SGReaderWriterOptions::copyOrCreate(const_options); options->setLoadOriginHint(SGReaderWriterOptions::LoadOriginHint::ORIGIN_MATERIAL_ATLAS); - - atlas.image = new osg::Texture2DArray(); - SG_LOG(SG_TERRAIN, SG_DEBUG, "Generating atlas of size " << landclasslib.size()); - if (landclasslib.size() > SGMaterialCache::MAX_MATERIALS) SG_LOG(SG_TERRAIN, SG_ALERT, "Too many landclass entries for uniform arrays: " << landclasslib.size() << " > " << SGMaterialCache::MAX_MATERIALS); - - atlas.textureLookup1 = new osg::Uniform(osg::Uniform::Type::FLOAT_VEC4, "fg_textureLookup1", SGMaterialCache::MAX_MATERIALS); - atlas.textureLookup2 = new osg::Uniform(osg::Uniform::Type::FLOAT_VEC4, "fg_textureLookup2", SGMaterialCache::MAX_MATERIALS); - atlas.dimensions = new osg::Uniform(osg::Uniform::Type::FLOAT_VEC4, "fg_dimensionsArray", SGMaterialCache::MAX_MATERIALS); - atlas.ambient = new osg::Uniform(osg::Uniform::Type::FLOAT_VEC4, "fg_ambientArray", SGMaterialCache::MAX_MATERIALS); - atlas.diffuse = new osg::Uniform(osg::Uniform::Type::FLOAT_VEC4, "fg_diffuseArray", SGMaterialCache::MAX_MATERIALS); - atlas.specular = new osg::Uniform(osg::Uniform::Type::FLOAT_VEC4, "fg_specularArray", SGMaterialCache::MAX_MATERIALS); - atlas.materialParams1 = new osg::Uniform(osg::Uniform::Type::FLOAT_VEC4, "fg_materialParams1", SGMaterialCache::MAX_MATERIALS); - atlas.materialParams2 = new osg::Uniform(osg::Uniform::Type::FLOAT_VEC4, "fg_materialParams2", SGMaterialCache::MAX_MATERIALS); - atlas.materialParams3 = new osg::Uniform(osg::Uniform::Type::FLOAT_VEC4, "fg_materialParams3", SGMaterialCache::MAX_MATERIALS); + SG_LOG(SG_TERRAIN, SG_DEBUG, "Generating atlas " << (SGMaterialLib::_atlasCache.size() +1) << " of size " << landclasslib.size()); + if (landclasslib.size() > Atlas::MAX_MATERIALS) SG_LOG(SG_TERRAIN, SG_ALERT, "Too many landclass entries for uniform arrays: " << landclasslib.size() << " > " << Atlas::MAX_MATERIALS); - atlas.image->setMaxAnisotropy(SGSceneFeatures::instance()->getTextureFilter()); - atlas.image->setResizeNonPowerOfTwoHint(false); + atlas = new Atlas(options); - atlas.image->setWrap(osg::Texture::WRAP_S,osg::Texture::REPEAT); - atlas.image->setWrap(osg::Texture::WRAP_T,osg::Texture::REPEAT); - - unsigned int imageIndex = 0u; // Index into the image - - // Add hardcoded atlas images first. - unsigned int standardTextureCount = size(SGMaterialCache::STANDARD_TEXTURES); - for (; imageIndex < standardTextureCount; imageIndex++) { - // Copy the texture into the atlas in the appropriate place - osg::ref_ptr subtexture = osgDB::readRefImageFile(SGMaterialCache::STANDARD_TEXTURES[imageIndex], options); - - if (subtexture && subtexture->valid()) { - if ((subtexture->s() != 2048) || (subtexture->t() != 2048)) { - subtexture->scaleImage(2048,2048,1); - } - - atlas.image->setImage(imageIndex,subtexture); - atlas.textureMap[SGMaterialCache::STANDARD_TEXTURES[imageIndex]] = imageIndex; - } - } - - unsigned int materialLookupIndex = 0u; // Index into the material lookup lc_iter = landclasslib.begin(); for (; lc_iter != landclasslib.end(); ++lc_iter) { - + // Add all the materials to the atlas int landclass = lc_iter->first; + bool water = lc_iter->second.second; SGMaterial* mat = find(lc_iter->second.first, center); - atlas.index[landclass] = materialLookupIndex; - atlas.waterAtlas[landclass] = lc_iter->second.second; - unsigned int textureList[SGMaterialCache::MAX_TEXTURES]; - - if (mat != NULL) { - - if (mat->get_num_textures(0) > SGMaterialCache::MAX_TEXTURES) { - SG_LOG(SG_GENERAL, SG_ALERT, "Unable to build texture atlas for landclass " - << landclass << " aka " << mat->get_names()[0] - << " too many textures: " << mat->get_num_textures(0) - << " (maximum " << SGMaterialCache::MAX_TEXTURES << ")"); - continue; - } - - atlas.dimensions->setElement(materialLookupIndex, osg::Vec4f(mat->get_xsize(), mat->get_ysize(), mat->get_shininess(), (double) mat->get_parameter("edge-hardness"))); - atlas.ambient->setElement(materialLookupIndex, mat->get_ambient()); - atlas.diffuse->setElement(materialLookupIndex, mat->get_diffuse()); - atlas.specular->setElement(materialLookupIndex, mat->get_specular()); - - // The following are material parameters that are normally built into the Effect as Uniforms. In the WS30 - // case we need to pass them as an array, indexed against the material. - atlas.materialParams1->setElement(materialLookupIndex, osg::Vec4f(mat->get_parameter("transition_model"), mat->get_parameter("hires_overlay_bias"), mat->get_parameter("grain_strength"), mat->get_parameter("intrinsic_wetness"))); - atlas.materialParams2->setElement(materialLookupIndex, osg::Vec4f(mat->get_parameter("dot_density"), mat->get_parameter("dot_size"), mat->get_parameter("dust_resistance"), mat->get_parameter("rock_strata"))); - - float water = 0.0; - if (atlas.waterAtlas[landclass]) { - water = 1.0; - } - - atlas.materialParams3->setElement(materialLookupIndex, osg::Vec4f(water, mat->get_parameter("waterline-start"), mat->get_parameter("waterline-end"), 0.0)); - - // Similarly, there are specifically 7 textures that are defined in the materials that need to be passed into - // the shader as an array based on the material lookup. - // - // The mapping from terrain-default.eff / terrain-overlay.eff is as follows - // - // TEXTURE NAME texture-unit Material texture index Default value - // Primary texure 0 0 n/a - // gradient_texture 2 13 Textures/Terrain/rock_alt.png - // dot_texture 3 15 Textures/Terrain/sand6.png - // grain_texture 4 14 Textures/Terrain/grain_texture.png - // mix_texture 5 12 Textures/Terrain/void.png - // detail_texture 7 11 Textures/Terrain/void.png - // overlayPrimaryTex 7 20 Textures/Terrain/void.png - // overlaySecondaryTex 8 21 Textures/Terrain/void.png - - for (unsigned int i = 0; i < SGMaterialCache::MAX_TEXTURES; i++) { - std::string texture = mat->get_one_texture(0,i); - - if (texture.empty()) { - // This is a rather horrible hardcoded mapping of the default textures defined in - // terrain-default.eff and terrain-overlay.eff which are in effect defaults to - // the material definitions - if (i < 13) texture = std::string("Textures/Terrain/void.png"); - if (i == 13) texture = std::string("Textures/Terrain/rock_alt.png"); - if (i == 14) texture = std::string("Textures/Terrain/grain_texture.png"); - if (i == 15) texture = std::string("Textures/Terrain/sand6.png"); - if (i > 14) texture = std::string("Textures/Terrain/void.png"); - } - - SGPath texturePath = SGPath("Textures"); - 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"); - texture = std::string("Textures/Terrain/void.png"); - fullPath = SGModelLib::findDataFile(texture, options, texturePath); - } - - if (atlas.textureMap.find(fullPath) == atlas.textureMap.end()) { - // Add any missing textures into the atlas image - // Copy the texture into the atlas in the appropriate place - osg::ref_ptr subtexture = osgDB::readRefImageFile(fullPath, options); - - if (subtexture && subtexture->valid()) { - - if ((subtexture->s() != 2048) || (subtexture->t() != 2048)) { - subtexture->scaleImage(2048,2048,1); - } - - atlas.image->setImage(imageIndex,subtexture); - atlas.textureMap[fullPath] = imageIndex; - ++imageIndex; - } - } - - // At this point we know that the texture is present in the - // atlas and referenced in the textureMap, so add it to the materialLookup - textureList[i] = atlas.textureMap[fullPath]; - } - - // We now have a textureList containing the full set of textures. Pack the relevant ones into the Vec4 of the index Uniform. - // This is a bit of a hack to maintain compatibility with the WS2.0 material definitions, as the material definitions use the - // 11-15th textures for the various overlay textures for terrain-default.eff, we do the same for ws30.eff - atlas.textureLookup1->setElement(materialLookupIndex, osg::Vec4f( (float) (textureList[0] / 255.0), (float) (textureList[11] / 255.0), (float) (textureList[12] / 255.0), (float) (textureList[13] / 255.0))); - atlas.textureLookup2->setElement(materialLookupIndex, osg::Vec4f( (float) (textureList[14] / 255.0), (float) (textureList[15] / 255.0), (float) (textureList[20] / 255.0), (float) (textureList[21] / 255.0))); - } - - ++materialLookupIndex; + atlas->addMaterial(landclass, water, mat); } - // Cache for future lookups - _atlasCache[id] = atlas; + SGMaterialLib::_atlasCache[id] = atlas; return atlas; } -void SGMaterialCache::addAtlasUniforms(osg::StateSet* stateset) { - stateset->addUniform(_atlas.dimensions); - stateset->addUniform(_atlas.ambient); - stateset->addUniform(_atlas.diffuse); - stateset->addUniform(_atlas.specular); - stateset->addUniform(_atlas.textureLookup1); - stateset->addUniform(_atlas.textureLookup2); - stateset->addUniform(_atlas.materialParams1); - stateset->addUniform(_atlas.materialParams2); - stateset->addUniform(_atlas.materialParams3); -} - // Destructor SGMaterialCache::~SGMaterialCache ( void ) { SG_LOG( SG_TERRAIN, SG_DEBUG, "SGMaterialCache::~SGMaterialCache() size=" << cache.size()); } + +// Initalizer +SGMaterialLib::atlas_map SGMaterialLib::_atlasCache; +std::mutex SGMaterialLib::_atlasCacheMutex; \ No newline at end of file diff --git a/simgear/scene/material/matlib.hxx b/simgear/scene/material/matlib.hxx index 2dfbbfdf..7f65a3b8 100644 --- a/simgear/scene/material/matlib.hxx +++ b/simgear/scene/material/matlib.hxx @@ -32,6 +32,7 @@ #include #include #include +#include "Atlas.hxx" #include #include // Standard C++ string library @@ -57,60 +58,8 @@ public: // represent the textures referenced by the texture-set in the material // - the texture indexes index into the Atlas itself. - // Mapping of landclass numbers to indexes within the atlas - // materialLookup - typedef std::map AtlasIndex; - - // Mapping of texture filenames to their index in the Atlas image itself. - typedef std::map TextureMap; - - // The Texture array itself - typedef osg::ref_ptr AtlasImage; - typedef std::map WaterAtlas; - - // Maximum number of textures per texture-set for the Atlas. - static const unsigned int MAX_TEXTURES = 22; - - // Maximum number of material entries in the atlas - static const unsigned int MAX_MATERIALS = 64; - - // Standard textures, used by water shader in particular. - // Indexes are hardcoded in Shaders/ws30-water.frag - inline static const std::string STANDARD_TEXTURES[] = { - "Textures/Terrain/water.png", - "Textures/Water/water-reflection-ws30.png", - "Textures/Water/waves-ver10-nm-ws30.png", - "Textures/Water/water_sine_nmap-ws30.png", - "Textures/Water/water-reflection-grey-ws30.png", - "Textures/Water/sea_foam-ws30.png", - "Textures/Water/perlin-noise-nm.png", - - // The following two textures are large and don't have an alpha channel. Ignoring for now. - //"Textures/Globe/ocean_depth_1.png", - //"Textures/Globe/globe_colors.jpg", - "Textures/Terrain/packice-overlay.png" - }; - - typedef struct { - AtlasIndex index; - AtlasImage image; - osg::ref_ptr textureLookup1; - osg::ref_ptr textureLookup2; - osg::ref_ptr dimensions; - osg::ref_ptr ambient; - osg::ref_ptr diffuse; - osg::ref_ptr specular; - - osg::ref_ptr materialParams1; - osg::ref_ptr materialParams2; - osg::ref_ptr materialParams3; - - WaterAtlas waterAtlas; - TextureMap textureMap; - } Atlas; - // Constructor - SGMaterialCache (Atlas atlas); + SGMaterialCache(); // Insertion void insert( const std::string& name, SGSharedPtr material ); @@ -120,9 +69,8 @@ public: SGMaterial *find( const std::string& material ) const; SGMaterial *find( int material ) const; - Atlas getAtlas() { return _atlas; }; - - void addAtlasUniforms(osg::StateSet* stateset); + void setAtlas(osg::ref_ptr atlas) { _atlas = atlas; }; + osg::ref_ptr getAtlas() { return _atlas; }; // Destructor ~SGMaterialCache ( void ); @@ -130,7 +78,7 @@ public: private: typedef std::map < std::string, SGSharedPtr > material_cache; material_cache cache; - Atlas _atlas; + osg::ref_ptr _atlas; const std::string getNameFromLandclass(int lc) const { return std::string("WS30_").append(std::to_string(lc)); @@ -141,6 +89,10 @@ private: class SGMaterialLib : public SGReferenced { +public: + typedef std::map > atlas_map; + + private: class MatLibPrivate; std::unique_ptr d; @@ -160,11 +112,10 @@ 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); + osg::ref_ptr getOrCreateAtlas(SGMaterialLib::landclass_map, SGVec2f center, const simgear::SGReaderWriterOptions* const_options); + static atlas_map _atlasCache; + static std::mutex _atlasCacheMutex; SGMaterial* internalFind(const std::string& material, const SGVec2f center) const; diff --git a/simgear/scene/tgdb/VPBTechnique.cxx b/simgear/scene/tgdb/VPBTechnique.cxx index b99ab9f6..99b5e553 100644 --- a/simgear/scene/tgdb/VPBTechnique.cxx +++ b/simgear/scene/tgdb/VPBTechnique.cxx @@ -759,7 +759,7 @@ void VertexNormalGenerator::computeNormals() void VPBTechnique::generateGeometry(BufferData& buffer, Locator* masterLocator, const osg::Vec3d& centerModel) { osg::Image* landclassImage = 0; - SGMaterialCache::Atlas atlas; + osg::ref_ptr atlas; Terrain* terrain = _terrainTile->getTerrain(); osgTerrain::Layer* elevationLayer = _terrainTile->getElevationLayer(); @@ -992,10 +992,10 @@ void VPBTechnique::generateGeometry(BufferData& buffer, Locator* masterLocator, if (separateWaterMesh && landclassImage && matlib) { // If we are generating a separate water mesh, the work out which of the vertices are in appropriate watery materials. - if (i00>=0) w00 = atlas.waterAtlas[int(round(landclassImage->getColor((*texcoords)[i00]).r() * 255.0))]; - if (i01>=0) w01 = atlas.waterAtlas[int(round(landclassImage->getColor((*texcoords)[i01]).r() * 255.0))]; - if (i10>=0) w10 = atlas.waterAtlas[int(round(landclassImage->getColor((*texcoords)[i10]).r() * 255.0))]; - if (i11>=0) w11 = atlas.waterAtlas[int(round(landclassImage->getColor((*texcoords)[i11]).r() * 255.0))]; + if (i00>=0) w00 = atlas->isWater(int(std::round(landclassImage->getColor((*texcoords)[i00]).r() * 255.0))); + if (i01>=0) w01 = atlas->isWater(int(std::round(landclassImage->getColor((*texcoords)[i01]).r() * 255.0))); + if (i10>=0) w10 = atlas->isWater(int(std::round(landclassImage->getColor((*texcoords)[i10]).r() * 255.0))); + if (i11>=0) w11 = atlas->isWater(int(std::round(landclassImage->getColor((*texcoords)[i11]).r() * 255.0))); } unsigned int numValid = 0; @@ -1344,15 +1344,14 @@ void VPBTechnique::applyColorLayers(BufferData& buffer, Locator* masterLocator) SG_LOG(SG_TERRAIN, SG_DEBUG, "Applying VPB material " << loc); SGMaterialCache* matCache = matlib->generateMatCache(loc, _options); - SGMaterialCache::Atlas atlas = matCache->getAtlas(); - SGMaterialCache::AtlasIndex atlasIndex = atlas.index; + osg::ref_ptr atlas = matCache->getAtlas(); - // Set the "g" color channel to an index into the atlas index. + // Set the "g" color channel to an index into the atlas for the landclass. for (unsigned int s = 0; s < (unsigned int) image->s(); s++) { for (unsigned int t = 0; t < (unsigned int) image->t(); t++) { osg::Vec4d c = image->getColor(s, t); - int i = int(round(c.x() * 255.0)); - c.set(c.x(), (double) (atlasIndex[i] / 255.0), c.z(), c.w() ); + int i = int(std::round(c.x() * 255.0)); + c.set(c.x(), (double) (atlas->getIndex(i) / 255.0), c.z(), c.w() ); image->setColor(c, s, t); } } @@ -1383,12 +1382,12 @@ void VPBTechnique::applyColorLayers(BufferData& buffer, Locator* masterLocator) osg::StateSet* stateset = buffer._landGeode->getOrCreateStateSet(); stateset->setTextureAttributeAndModes(0, texture2D, osg::StateAttribute::ON); - stateset->setTextureAttributeAndModes(1, atlas.image, osg::StateAttribute::ON); + stateset->setTextureAttributeAndModes(1, atlas->getImage(), osg::StateAttribute::ON); stateset->setTextureAttributeAndModes(7, waterTexture, osg::StateAttribute::ON); stateset->addUniform(new osg::Uniform(VPBTechnique::PHOTO_SCENERY, false)); stateset->addUniform(new osg::Uniform(VPBTechnique::Z_UP_TRANSFORM, osg::Matrixf(osg::Matrix::inverse(makeZUpFrameRelative(loc))))); stateset->addUniform(new osg::Uniform(VPBTechnique::MODEL_OFFSET, (osg::Vec3f) buffer._transform->getMatrix().getTrans())); - matCache->addAtlasUniforms(stateset); + atlas->addUniforms(stateset); //SG_LOG(SG_TERRAIN, SG_ALERT, "modeOffset:" << buffer._transform->getMatrix().getTrans().length() << " " << buffer._transform->getMatrix().getTrans()); } } @@ -1598,7 +1597,7 @@ void VPBTechnique::applyMaterials(BufferData& buffer, Locator* masterLocator) unsigned int tx = (unsigned int) (image->s() * t.x()) % image->s(); unsigned int ty = (unsigned int) (image->t() * t.y()) % image->t(); const osg::Vec4 tc = image->getColor(tx, ty); - const int land_class = int(round(tc.x() * 255.0)); + const int land_class = int(std::round(tc.x() * 255.0)); if (land_class != current_land_class) { // Use temporal locality to reduce material lookup by caching