WS30: Improved Atlas encapsulation

This commit is contained in:
Stuart Buchanan 2022-10-14 21:36:25 +01:00
parent 2b9a319e76
commit 96a9abfd7a
6 changed files with 347 additions and 240 deletions

View File

@ -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 <osgDB/ReadFile>
#include <simgear/scene/util/SGReaderWriterOptions.hxx>
#include <simgear/scene/util/SGSceneFeatures.hxx>
using namespace simgear;
// Constructor
Atlas::Atlas(osg::ref_ptr<const SGReaderWriterOptions> 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<osg::Image> 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<osg::Image> 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);
}

View File

@ -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 <simgear/compiler.h>
#include <simgear/math/SGMath.hxx>
#include <simgear/structure/SGReferenced.hxx>
#include <simgear/structure/SGSharedPtr.hxx>
#include <simgear/scene/material/mat.hxx>
#include <osg/Texture2DArray>
#include <osg/Texture1D>
#include <memory>
#include <string> // Standard C++ string library
#include <map> // STL associative "array"
#include <vector> // STL "array"
namespace simgear
{
// Atlas of textures
class Atlas : public osg::Referenced
{
public:
// Constructor
Atlas (osg::ref_ptr<const SGReaderWriterOptions> options );
// Mapping of landclass numbers to indexes within the atlas
// materialLookup
typedef std::map<int, int> AtlasIndex;
// Mapping of texture filenames to their index in the Atlas image itself.
typedef std::map<std::string, unsigned int> TextureMap;
// The Texture array itself
typedef osg::ref_ptr<osg::Texture2DArray> AtlasImage;
typedef std::map<int, bool> 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<const SGReaderWriterOptions> _options;
osg::ref_ptr<osg::Uniform> _textureLookup1;
osg::ref_ptr<osg::Uniform> _textureLookup2;
osg::ref_ptr<osg::Uniform> _dimensions;
osg::ref_ptr<osg::Uniform> _ambient;
osg::ref_ptr<osg::Uniform> _diffuse;
osg::ref_ptr<osg::Uniform> _specular;
osg::ref_ptr<osg::Uniform> _materialParams1;
osg::ref_ptr<osg::Uniform> _materialParams2;
osg::ref_ptr<osg::Uniform> _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"
};
};
}

View File

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

View File

@ -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<std::mutex> 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<Atlas> SGMaterialLib::getOrCreateAtlas(SGMaterialLib::landclass_map landclasslib, SGVec2f center, const simgear::SGReaderWriterOptions* const_options) {
osg::ref_ptr<Atlas> atlas;
// Non-VPB does not use the Atlas, so save some effort and return
if (! SGSceneFeatures::instance()->getVPBActive()) return atlas;
std::lock_guard<std::mutex> 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<SGReaderWriterOptions> 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<osg::Image> 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<osg::Image> 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;

View File

@ -32,6 +32,7 @@
#include <simgear/structure/SGSharedPtr.hxx>
#include <osg/Texture2DArray>
#include <osg/Texture1D>
#include "Atlas.hxx"
#include <memory>
#include <string> // 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<int, int> AtlasIndex;
// Mapping of texture filenames to their index in the Atlas image itself.
typedef std::map<std::string, unsigned int> TextureMap;
// The Texture array itself
typedef osg::ref_ptr<osg::Texture2DArray> AtlasImage;
typedef std::map<int, bool> 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<osg::Uniform> textureLookup1;
osg::ref_ptr<osg::Uniform> textureLookup2;
osg::ref_ptr<osg::Uniform> dimensions;
osg::ref_ptr<osg::Uniform> ambient;
osg::ref_ptr<osg::Uniform> diffuse;
osg::ref_ptr<osg::Uniform> specular;
osg::ref_ptr<osg::Uniform> materialParams1;
osg::ref_ptr<osg::Uniform> materialParams2;
osg::ref_ptr<osg::Uniform> materialParams3;
WaterAtlas waterAtlas;
TextureMap textureMap;
} Atlas;
// Constructor
SGMaterialCache (Atlas atlas);
SGMaterialCache();
// Insertion
void insert( const std::string& name, SGSharedPtr<SGMaterial> 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<simgear::Atlas> atlas) { _atlas = atlas; };
osg::ref_ptr<simgear::Atlas> getAtlas() { return _atlas; };
// Destructor
~SGMaterialCache ( void );
@ -130,7 +78,7 @@ public:
private:
typedef std::map < std::string, SGSharedPtr<SGMaterial> > material_cache;
material_cache cache;
Atlas _atlas;
osg::ref_ptr<simgear::Atlas> _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 <const std::string, osg::ref_ptr<simgear::Atlas> > atlas_map;
private:
class MatLibPrivate;
std::unique_ptr<MatLibPrivate> 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<simgear::Atlas> 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;

View File

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