WS30: Improved Atlas encapsulation
This commit is contained in:
parent
2b9a319e76
commit
96a9abfd7a
185
simgear/scene/material/Atlas.cxx
Normal file
185
simgear/scene/material/Atlas.cxx
Normal 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);
|
||||
}
|
||||
|
114
simgear/scene/material/Atlas.hxx
Normal file
114
simgear/scene/material/Atlas.hxx
Normal 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"
|
||||
};
|
||||
};
|
||||
|
||||
}
|
@ -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
|
||||
|
@ -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;
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user