WS30: Improved atlas with multi-texture support.
- Prefix all WS30 Uniforms with "fg_" - fix modelOffset (needs to be passed in as a float) - Create a true atlas with indexes for relevant material textures
This commit is contained in:
@@ -546,6 +546,17 @@ std::string SGMaterial::get_one_texture(int setIndex, int texIndex)
|
||||
return texturePath;
|
||||
}
|
||||
|
||||
std::size_t SGMaterial::get_num_textures(int setIndex)
|
||||
{
|
||||
if (_status.empty()) {
|
||||
SG_LOG( SG_GENERAL, SG_WARN, "No material available.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
SGMaterial::_internal_state st = _status[setIndex % _status.size()];
|
||||
return st.texture_paths.size();
|
||||
}
|
||||
|
||||
void SGMaterial::buildEffectProperties(const SGReaderWriterOptions* options)
|
||||
{
|
||||
using namespace osg;
|
||||
|
||||
@@ -147,11 +147,15 @@ public:
|
||||
*/
|
||||
std::string get_one_texture(int setIndex, int texIndex);
|
||||
|
||||
/**
|
||||
* Get the number of textures defined in this set
|
||||
*/
|
||||
std::size_t get_num_textures(int setIndex);
|
||||
|
||||
/**
|
||||
* Get the number of textures assigned to this material.
|
||||
*/
|
||||
inline int get_num() const { return _status.size(); }
|
||||
inline int get_num_texture_sets() const { return _status.size(); }
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -331,9 +331,10 @@ SGMaterialCache::Atlas SGMaterialLib::getMaterialTextureAtlas(SGVec2f center, co
|
||||
{
|
||||
SGMaterialCache::Atlas atlas;
|
||||
|
||||
// Non-VPB does not use the Atlas, so save some both and return
|
||||
// Non-VPB does not use the Atlas, so save some effort and return
|
||||
if (! SGSceneFeatures::instance()->getVPBActive()) return atlas;
|
||||
|
||||
// A simple key to the atlas is just the list of textures.
|
||||
std::string id;
|
||||
const_landclass_map_iterator lc_iter = landclasslib.begin();
|
||||
for (; lc_iter != landclasslib.end(); ++lc_iter) {
|
||||
@@ -347,7 +348,7 @@ SGMaterialCache::Atlas SGMaterialLib::getMaterialTextureAtlas(SGVec2f center, co
|
||||
|
||||
if (atlas_iter != _atlasCache.end()) return atlas_iter->second;
|
||||
|
||||
// Cache lookup failure - generate a new atlas, but only if we have a change of reading any textures
|
||||
// Cache lookup failure - generate a new atlas, but only if we have a chance of reading any textures
|
||||
if (options == 0) {
|
||||
return atlas;
|
||||
}
|
||||
@@ -356,10 +357,12 @@ SGMaterialCache::Atlas SGMaterialLib::getMaterialTextureAtlas(SGVec2f center, co
|
||||
|
||||
if (landclasslib.size() > 128) SG_LOG(SG_TERRAIN, SG_ALERT, "Too many landclass entries for uniform arrays");
|
||||
|
||||
atlas.dimensions = new osg::Uniform(osg::Uniform::Type::FLOAT_VEC4, "dimensionsArray", 128);
|
||||
atlas.ambient = new osg::Uniform(osg::Uniform::Type::FLOAT_VEC4, "ambientArray", 128);
|
||||
atlas.diffuse = new osg::Uniform(osg::Uniform::Type::FLOAT_VEC4, "diffuseArray", 128);
|
||||
atlas.specular = new osg::Uniform(osg::Uniform::Type::FLOAT_VEC4, "specularArray", 128);
|
||||
atlas.textureLookup1 = new osg::Uniform(osg::Uniform::Type::FLOAT_VEC4, "fg_textureLookup1", 128);
|
||||
atlas.textureLookup2 = new osg::Uniform(osg::Uniform::Type::FLOAT_VEC4, "fg_textureLookup2", 128);
|
||||
atlas.dimensions = new osg::Uniform(osg::Uniform::Type::FLOAT_VEC4, "fg_dimensionsArray", 128);
|
||||
atlas.ambient = new osg::Uniform(osg::Uniform::Type::FLOAT_VEC4, "fg_ambientArray", 128);
|
||||
atlas.diffuse = new osg::Uniform(osg::Uniform::Type::FLOAT_VEC4, "fg_diffuseArray", 128);
|
||||
atlas.specular = new osg::Uniform(osg::Uniform::Type::FLOAT_VEC4, "fg_specularArray", 128);
|
||||
|
||||
atlas.image->setMaxAnisotropy(SGSceneFeatures::instance()->getTextureFilter());
|
||||
atlas.image->setResizeNonPowerOfTwoHint(false);
|
||||
@@ -367,53 +370,85 @@ SGMaterialCache::Atlas SGMaterialLib::getMaterialTextureAtlas(SGVec2f center, co
|
||||
atlas.image->setWrap(osg::Texture::WRAP_S,osg::Texture::REPEAT);
|
||||
atlas.image->setWrap(osg::Texture::WRAP_T,osg::Texture::REPEAT);
|
||||
|
||||
unsigned int index = 0;
|
||||
unsigned int imageIndex = 0u; // Index into the image
|
||||
unsigned int materialLookupIndex = 0u; // Index into the material lookup
|
||||
lc_iter = landclasslib.begin();
|
||||
for (; lc_iter != landclasslib.end(); ++lc_iter) {
|
||||
int i = lc_iter->first;
|
||||
|
||||
int landclass = lc_iter->first;
|
||||
SGMaterial* mat = find(lc_iter->second.first, center);
|
||||
atlas.index[i] = index;
|
||||
atlas.waterAtlas[i] = lc_iter->second.second;
|
||||
atlas.index[landclass] = materialLookupIndex;
|
||||
atlas.waterAtlas[landclass] = lc_iter->second.second;
|
||||
unsigned int textureList[SGMaterialCache::MAX_TEXTURES];
|
||||
|
||||
if (mat != NULL) {
|
||||
|
||||
// Just get the first texture in the first texture-set for the moment.
|
||||
// Should add some variability in texture-set in the future.
|
||||
const std::string texture = mat->get_one_texture(0,0);
|
||||
if (! texture.empty()) {
|
||||
if (texture.empty()) continue;
|
||||
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;
|
||||
}
|
||||
|
||||
SGPath texturePath = SGPath("Textures");
|
||||
//texturePath.append(texture);
|
||||
std::string fullPath = SGModelLib::findDataFile(texture, options, texturePath);
|
||||
atlas.dimensions->setElement(materialLookupIndex, osg::Vec4f(mat->get_xsize(), mat->get_ysize(), mat->get_shininess(), 1.0f));
|
||||
atlas.ambient->setElement(materialLookupIndex, mat->get_ambient());
|
||||
atlas.diffuse->setElement(materialLookupIndex, mat->get_diffuse());
|
||||
atlas.specular->setElement(materialLookupIndex, mat->get_specular());
|
||||
|
||||
if (fullPath.empty()) {
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "Cannot find texture \""
|
||||
<< texture << "\" in Textures folders when creating texture atlas");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Copy the texture into the atlas in the appropriate place
|
||||
osg::ref_ptr<osg::Image> subtexture = osgDB::readRefImageFile(fullPath, options);
|
||||
// Add any missing textures into the atlas image
|
||||
for (std::size_t i = 0; i < mat->get_num_textures(0); ++i) {
|
||||
const std::string texture = mat->get_one_texture(0,i);
|
||||
if (! texture.empty()) {
|
||||
|
||||
if (subtexture && subtexture->valid()) {
|
||||
SGPath texturePath = SGPath("Textures");
|
||||
std::string fullPath = SGModelLib::findDataFile(texture, options, texturePath);
|
||||
|
||||
if ((subtexture->s() != 2048) || (subtexture->t() != 2048)) {
|
||||
subtexture->scaleImage(2048,2048,1);
|
||||
}
|
||||
|
||||
atlas.image->setImage(index,subtexture);
|
||||
atlas.dimensions->setElement(index, osg::Vec4f(mat->get_xsize(), mat->get_ysize(), mat->get_shininess(), 1.0f));
|
||||
atlas.ambient->setElement(index, mat->get_ambient());
|
||||
atlas.diffuse->setElement(index, mat->get_diffuse());
|
||||
atlas.specular->setElement(index, mat->get_specular());
|
||||
if (fullPath.empty()) {
|
||||
SG_LOG(SG_GENERAL, SG_ALERT, "Cannot find texture \""
|
||||
<< texture << "\" in Textures folders when creating texture atlas");
|
||||
textureList[i] = -2;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (atlas.textureMap.find(fullPath) == atlas.textureMap.end()) {
|
||||
// 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];
|
||||
//SG_LOG(SG_TERRAIN, SG_ALERT, "materialLookup Index: " << (materialLookupIndex * SGMaterialCache::MAX_TEXTURES + i) << " " << atlas.textureMap[fullPath] << " " << fullPath);
|
||||
} else {
|
||||
textureList[i] = -2;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill out the rest of the array
|
||||
for (std::size_t i = mat->get_num_textures(0); i < SGMaterialCache::MAX_TEXTURES; ++i) {
|
||||
textureList[i] = -2;
|
||||
}
|
||||
|
||||
// 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), 0.0,0.0));
|
||||
}
|
||||
|
||||
++index;
|
||||
++materialLookupIndex;
|
||||
}
|
||||
|
||||
// Cache for future lookups
|
||||
|
||||
@@ -50,20 +50,38 @@ class SGMaterialCache : public osg::Referenced
|
||||
{
|
||||
public:
|
||||
|
||||
// An atlas, consisting of a landclass index, an atlas image and set
|
||||
// of texture1D containing further data
|
||||
// A texture Atlas with multiple levels of indirection:
|
||||
|
||||
// - A given landclass maps to an index in the materialLookup.
|
||||
// - The materialLookup results in a set of texture indexes that
|
||||
// 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 = 16;
|
||||
|
||||
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;
|
||||
WaterAtlas waterAtlas;
|
||||
TextureMap textureMap;
|
||||
} Atlas;
|
||||
|
||||
// Constructor
|
||||
|
||||
@@ -1257,10 +1257,10 @@ void VPBTechnique::generateGeometry(BufferData& buffer, Locator* masterLocator,
|
||||
|
||||
SG_LOG(SG_TERRAIN, SG_DEBUG, "Tile Level " << _terrainTile->getTileID().level << " width " << buffer._width << " height " << buffer._height);
|
||||
|
||||
osg::ref_ptr<osg::Uniform> twu = new osg::Uniform("tile_width", buffer._width);
|
||||
osg::ref_ptr<osg::Uniform> twu = new osg::Uniform("fg_tileWidth", buffer._width);
|
||||
landStateSet->addUniform(twu);
|
||||
waterStateSet->addUniform(twu);
|
||||
osg::ref_ptr<osg::Uniform> thu = new osg::Uniform("tile_height", buffer._height);
|
||||
osg::ref_ptr<osg::Uniform> thu = new osg::Uniform("fg_tileHeight", buffer._height);
|
||||
landStateSet->addUniform(thu);
|
||||
waterStateSet->addUniform(thu);
|
||||
|
||||
@@ -1322,7 +1322,7 @@ void VPBTechnique::applyColorLayers(BufferData& buffer, Locator* masterLocator)
|
||||
osg::Texture2D* texture = SGLoadTexture2D(SGPath(orthotexture), _options, true, true);
|
||||
osg::StateSet* stateset = buffer._landGeode->getOrCreateStateSet();
|
||||
stateset->setTextureAttributeAndModes(0, texture);
|
||||
stateset->addUniform(new osg::Uniform("photoScenery", true));
|
||||
stateset->addUniform(new osg::Uniform("fg_photoScenery", true));
|
||||
} else {
|
||||
SG_LOG(SG_TERRAIN, SG_DEBUG, "Unable to find " << orthotexture);
|
||||
photoScenery = false;
|
||||
@@ -1354,7 +1354,14 @@ void VPBTechnique::applyColorLayers(BufferData& buffer, Locator* masterLocator)
|
||||
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() );
|
||||
|
||||
// Get texture 0 for this material to save a Uniform lookup in the shader for
|
||||
// maximal performance on single-texture shaders
|
||||
osg::Vec4f texture1;
|
||||
if (! atlas.textureLookup1->getElement(atlasIndex[i], texture1)) {
|
||||
SG_LOG(SG_TERRAIN, SG_ALERT, "Failed to do texture lookup for landclass: " << atlasIndex[i]);
|
||||
}
|
||||
c.set((double) (atlasIndex[i] / 255.0), texture1.r(), texture1.g(), texture1.b());
|
||||
image->setColor(c, s, t);
|
||||
}
|
||||
}
|
||||
@@ -1377,13 +1384,16 @@ 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->addUniform(new osg::Uniform("photoScenery", false));
|
||||
stateset->addUniform(new osg::Uniform("fg_photoScenery", false));
|
||||
stateset->addUniform(atlas.dimensions);
|
||||
stateset->addUniform(atlas.ambient);
|
||||
stateset->addUniform(atlas.diffuse);
|
||||
stateset->addUniform(atlas.specular);
|
||||
stateset->addUniform(new osg::Uniform("zUpTransform", osg::Matrixf(osg::Matrix::inverse(makeZUpFrameRelative(loc)))));
|
||||
stateset->addUniform(new osg::Uniform("modelOffset", buffer._transform->getMatrix().getTrans()));
|
||||
stateset->addUniform(atlas.textureLookup1);
|
||||
stateset->addUniform(atlas.textureLookup2);
|
||||
stateset->addUniform(new osg::Uniform("fg_zUpTransform", osg::Matrixf(osg::Matrix::inverse(makeZUpFrameRelative(loc)))));
|
||||
stateset->addUniform(new osg::Uniform("fg_modelOffset", (osg::Vec3f) buffer._transform->getMatrix().getTrans()));
|
||||
//SG_LOG(SG_TERRAIN, SG_ALERT, "modeOffset:" << buffer._transform->getMatrix().getTrans().length() << " " << buffer._transform->getMatrix().getTrans());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user