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;
|
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)
|
void SGMaterial::buildEffectProperties(const SGReaderWriterOptions* options)
|
||||||
{
|
{
|
||||||
using namespace osg;
|
using namespace osg;
|
||||||
|
|||||||
@@ -147,11 +147,15 @@ public:
|
|||||||
*/
|
*/
|
||||||
std::string get_one_texture(int setIndex, int texIndex);
|
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.
|
* 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;
|
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;
|
if (! SGSceneFeatures::instance()->getVPBActive()) return atlas;
|
||||||
|
|
||||||
|
// A simple key to the atlas is just the list of textures.
|
||||||
std::string id;
|
std::string id;
|
||||||
const_landclass_map_iterator lc_iter = landclasslib.begin();
|
const_landclass_map_iterator lc_iter = landclasslib.begin();
|
||||||
for (; lc_iter != landclasslib.end(); ++lc_iter) {
|
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;
|
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) {
|
if (options == 0) {
|
||||||
return atlas;
|
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");
|
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.textureLookup1 = new osg::Uniform(osg::Uniform::Type::FLOAT_VEC4, "fg_textureLookup1", 128);
|
||||||
atlas.ambient = new osg::Uniform(osg::Uniform::Type::FLOAT_VEC4, "ambientArray", 128);
|
atlas.textureLookup2 = new osg::Uniform(osg::Uniform::Type::FLOAT_VEC4, "fg_textureLookup2", 128);
|
||||||
atlas.diffuse = new osg::Uniform(osg::Uniform::Type::FLOAT_VEC4, "diffuseArray", 128);
|
atlas.dimensions = new osg::Uniform(osg::Uniform::Type::FLOAT_VEC4, "fg_dimensionsArray", 128);
|
||||||
atlas.specular = new osg::Uniform(osg::Uniform::Type::FLOAT_VEC4, "specularArray", 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->setMaxAnisotropy(SGSceneFeatures::instance()->getTextureFilter());
|
||||||
atlas.image->setResizeNonPowerOfTwoHint(false);
|
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_S,osg::Texture::REPEAT);
|
||||||
atlas.image->setWrap(osg::Texture::WRAP_T,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();
|
lc_iter = landclasslib.begin();
|
||||||
for (; lc_iter != landclasslib.end(); ++lc_iter) {
|
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);
|
SGMaterial* mat = find(lc_iter->second.first, center);
|
||||||
atlas.index[i] = index;
|
atlas.index[landclass] = materialLookupIndex;
|
||||||
atlas.waterAtlas[i] = lc_iter->second.second;
|
atlas.waterAtlas[landclass] = lc_iter->second.second;
|
||||||
|
unsigned int textureList[SGMaterialCache::MAX_TEXTURES];
|
||||||
|
|
||||||
if (mat != NULL) {
|
if (mat != NULL) {
|
||||||
|
|
||||||
// Just get the first texture in the first texture-set for the moment.
|
if (mat->get_num_textures(0) > SGMaterialCache::MAX_TEXTURES) {
|
||||||
// Should add some variability in texture-set in the future.
|
SG_LOG(SG_GENERAL, SG_ALERT, "Unable to build texture atlas for landclass "
|
||||||
const std::string texture = mat->get_one_texture(0,0);
|
<< landclass << " aka " << mat->get_names()[0]
|
||||||
if (! texture.empty()) {
|
<< " too many textures: " << mat->get_num_textures(0)
|
||||||
if (texture.empty()) continue;
|
<< " (maximum " << SGMaterialCache::MAX_TEXTURES << ")");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
SGPath texturePath = SGPath("Textures");
|
atlas.dimensions->setElement(materialLookupIndex, osg::Vec4f(mat->get_xsize(), mat->get_ysize(), mat->get_shininess(), 1.0f));
|
||||||
//texturePath.append(texture);
|
atlas.ambient->setElement(materialLookupIndex, mat->get_ambient());
|
||||||
std::string fullPath = SGModelLib::findDataFile(texture, options, texturePath);
|
atlas.diffuse->setElement(materialLookupIndex, mat->get_diffuse());
|
||||||
|
atlas.specular->setElement(materialLookupIndex, mat->get_specular());
|
||||||
|
|
||||||
if (fullPath.empty()) {
|
// Add any missing textures into the atlas image
|
||||||
SG_LOG(SG_GENERAL, SG_ALERT, "Cannot find texture \""
|
for (std::size_t i = 0; i < mat->get_num_textures(0); ++i) {
|
||||||
<< texture << "\" in Textures folders when creating texture atlas");
|
const std::string texture = mat->get_one_texture(0,i);
|
||||||
}
|
if (! texture.empty()) {
|
||||||
else
|
|
||||||
{
|
|
||||||
// Copy the texture into the atlas in the appropriate place
|
|
||||||
osg::ref_ptr<osg::Image> subtexture = osgDB::readRefImageFile(fullPath, options);
|
|
||||||
|
|
||||||
if (subtexture && subtexture->valid()) {
|
SGPath texturePath = SGPath("Textures");
|
||||||
|
std::string fullPath = SGModelLib::findDataFile(texture, options, texturePath);
|
||||||
|
|
||||||
if ((subtexture->s() != 2048) || (subtexture->t() != 2048)) {
|
if (fullPath.empty()) {
|
||||||
subtexture->scaleImage(2048,2048,1);
|
SG_LOG(SG_GENERAL, SG_ALERT, "Cannot find texture \""
|
||||||
}
|
<< texture << "\" in Textures folders when creating texture atlas");
|
||||||
|
textureList[i] = -2;
|
||||||
atlas.image->setImage(index,subtexture);
|
continue;
|
||||||
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 (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
|
// Cache for future lookups
|
||||||
|
|||||||
@@ -50,20 +50,38 @@ class SGMaterialCache : public osg::Referenced
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// An atlas, consisting of a landclass index, an atlas image and set
|
// A texture Atlas with multiple levels of indirection:
|
||||||
// of texture1D containing further data
|
|
||||||
|
// - 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;
|
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 osg::ref_ptr<osg::Texture2DArray> AtlasImage;
|
||||||
typedef std::map<int, bool> WaterAtlas;
|
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 {
|
typedef struct {
|
||||||
AtlasIndex index;
|
AtlasIndex index;
|
||||||
AtlasImage image;
|
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> dimensions;
|
||||||
osg::ref_ptr<osg::Uniform> ambient;
|
osg::ref_ptr<osg::Uniform> ambient;
|
||||||
osg::ref_ptr<osg::Uniform> diffuse;
|
osg::ref_ptr<osg::Uniform> diffuse;
|
||||||
osg::ref_ptr<osg::Uniform> specular;
|
osg::ref_ptr<osg::Uniform> specular;
|
||||||
WaterAtlas waterAtlas;
|
WaterAtlas waterAtlas;
|
||||||
|
TextureMap textureMap;
|
||||||
} Atlas;
|
} Atlas;
|
||||||
|
|
||||||
// Constructor
|
// 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);
|
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);
|
landStateSet->addUniform(twu);
|
||||||
waterStateSet->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);
|
landStateSet->addUniform(thu);
|
||||||
waterStateSet->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::Texture2D* texture = SGLoadTexture2D(SGPath(orthotexture), _options, true, true);
|
||||||
osg::StateSet* stateset = buffer._landGeode->getOrCreateStateSet();
|
osg::StateSet* stateset = buffer._landGeode->getOrCreateStateSet();
|
||||||
stateset->setTextureAttributeAndModes(0, texture);
|
stateset->setTextureAttributeAndModes(0, texture);
|
||||||
stateset->addUniform(new osg::Uniform("photoScenery", true));
|
stateset->addUniform(new osg::Uniform("fg_photoScenery", true));
|
||||||
} else {
|
} else {
|
||||||
SG_LOG(SG_TERRAIN, SG_DEBUG, "Unable to find " << orthotexture);
|
SG_LOG(SG_TERRAIN, SG_DEBUG, "Unable to find " << orthotexture);
|
||||||
photoScenery = false;
|
photoScenery = false;
|
||||||
@@ -1354,7 +1354,14 @@ void VPBTechnique::applyColorLayers(BufferData& buffer, Locator* masterLocator)
|
|||||||
for (unsigned int t = 0; t < (unsigned int) image->t(); t++) {
|
for (unsigned int t = 0; t < (unsigned int) image->t(); t++) {
|
||||||
osg::Vec4d c = image->getColor(s, t);
|
osg::Vec4d c = image->getColor(s, t);
|
||||||
int i = int(round(c.x() * 255.0));
|
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);
|
image->setColor(c, s, t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1377,13 +1384,16 @@ void VPBTechnique::applyColorLayers(BufferData& buffer, Locator* masterLocator)
|
|||||||
osg::StateSet* stateset = buffer._landGeode->getOrCreateStateSet();
|
osg::StateSet* stateset = buffer._landGeode->getOrCreateStateSet();
|
||||||
stateset->setTextureAttributeAndModes(0, texture2D, osg::StateAttribute::ON);
|
stateset->setTextureAttributeAndModes(0, texture2D, osg::StateAttribute::ON);
|
||||||
stateset->setTextureAttributeAndModes(1, atlas.image, 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.dimensions);
|
||||||
stateset->addUniform(atlas.ambient);
|
stateset->addUniform(atlas.ambient);
|
||||||
stateset->addUniform(atlas.diffuse);
|
stateset->addUniform(atlas.diffuse);
|
||||||
stateset->addUniform(atlas.specular);
|
stateset->addUniform(atlas.specular);
|
||||||
stateset->addUniform(new osg::Uniform("zUpTransform", osg::Matrixf(osg::Matrix::inverse(makeZUpFrameRelative(loc)))));
|
stateset->addUniform(atlas.textureLookup1);
|
||||||
stateset->addUniform(new osg::Uniform("modelOffset", buffer._transform->getMatrix().getTrans()));
|
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