Optimize blendmap generation

pull/541/head
Andrei Kortunov 6 years ago
parent 58f8d1ac04
commit 1da012f6ee

@ -352,11 +352,6 @@ namespace ESMTerrain
std::string Storage::getTextureName(UniqueTextureId id)
{
// Goes under used terrain blend transitions
static constexpr char baseTexture[] = "textures\\tx_black_01.dds";
if (id.first == -1)
return baseTexture;
static constexpr char defaultTexture[] = "textures\\_land_default.dds";
if (id.first == 0)
return defaultTexture; // Not sure if the default texture really is hardcoded?
@ -385,72 +380,44 @@ namespace ESMTerrain
int rowStart = (origin.x() - cellX) * realTextureSize;
int colStart = (origin.y() - cellY) * realTextureSize;
int rowEnd = rowStart + chunkSize * (realTextureSize-1) + 1;
int colEnd = colStart + chunkSize * (realTextureSize-1) + 1;
// Save the used texture indices so we know the total number of textures
// and number of required blend maps
std::set<UniqueTextureId> textureIndices;
// Due to the way the blending works, the base layer will bleed between texture transitions so we want it to be a black texture
// The subsequent passes are added instead of blended, so this gives the correct result
textureIndices.insert(std::make_pair(-1,0)); // -1 goes to tx_black_01
LandCache cache;
for (int y=colStart; y<colEnd; ++y)
for (int x=rowStart; x<rowEnd; ++x)
{
UniqueTextureId id = getVtexIndexAt(cellX, cellY, x, y, cache);
textureIndices.insert(id);
}
// Makes sure the indices are sorted, or rather,
// retrieved as sorted. This is important to keep the splatting order
// consistent across cells.
std::map<UniqueTextureId, int> textureIndicesMap;
for (std::set<UniqueTextureId>::iterator it = textureIndices.begin(); it != textureIndices.end(); ++it)
{
int size = textureIndicesMap.size();
textureIndicesMap[*it] = size;
layerList.push_back(getLayerInfo(getTextureName(*it)));
}
// size-1 since the base layer doesn't need blending
int numBlendmaps = textureIndices.size() - 1;
// Second iteration - create and fill in the blend maps
const int blendmapSize = (realTextureSize-1) * chunkSize + 1;
// We need to upscale the blendmap 2x with nearest neighbor sampling to look like Vanilla
const int imageScaleFactor = 2;
const int blendmapImageSize = blendmapSize * imageScaleFactor;
for (int i=0; i<numBlendmaps; ++i)
{
osg::ref_ptr<osg::Image> image (new osg::Image);
image->allocateImage(blendmapImageSize, blendmapImageSize, 1, GL_ALPHA, GL_UNSIGNED_BYTE);
unsigned char* pData = image->data();
LandCache cache;
std::map<UniqueTextureId, int> textureIndicesMap;
for (int y=0; y<blendmapSize; ++y)
for (int y=0; y<blendmapSize; ++y)
{
for (int x=0; x<blendmapSize; ++x)
{
for (int x=0; x<blendmapSize; ++x)
UniqueTextureId id = getVtexIndexAt(cellX, cellY, x+rowStart, y+colStart, cache);
std::map<UniqueTextureId, int>::iterator found = textureIndicesMap.find(id);
if (found == textureIndicesMap.end())
{
UniqueTextureId id = getVtexIndexAt(cellX, cellY, x+rowStart, y+colStart, cache);
assert(textureIndicesMap.find(id) != textureIndicesMap.end());
int layerIndex = textureIndicesMap.find(id)->second;
int alpha = (layerIndex == i+1) ? 255 : 0;
int realY = (blendmapSize - y - 1)*imageScaleFactor;
int realX = x*imageScaleFactor;
pData[(realY+0)*blendmapImageSize + realX + 0] = alpha;
pData[(realY+1)*blendmapImageSize + realX + 0] = alpha;
pData[(realY+0)*blendmapImageSize + realX + 1] = alpha;
pData[(realY+1)*blendmapImageSize + realX + 1] = alpha;
found = textureIndicesMap.insert(std::make_pair(id, textureIndicesMap.size())).first;
layerList.push_back(getLayerInfo(getTextureName(id)));
osg::ref_ptr<osg::Image> image (new osg::Image);
image->allocateImage(blendmapImageSize, blendmapImageSize, 1, GL_ALPHA, GL_UNSIGNED_BYTE);
unsigned char* pData = image->data();
memset(pData, 0, image->getTotalDataSize());
blendmaps.push_back(image);
}
int layerIndex = found->second;
unsigned char* pData = blendmaps[layerIndex]->data();
int realY = (blendmapSize - y - 1)*imageScaleFactor;
int realX = x*imageScaleFactor;
pData[((realY+0)*blendmapImageSize + realX + 0)] = 255;
pData[((realY+1)*blendmapImageSize + realX + 0)] = 255;
pData[((realY+0)*blendmapImageSize + realX + 1)] = 255;
pData[((realY+1)*blendmapImageSize + realX + 1)] = 255;
}
blendmaps.push_back(image);
}
if (blendmaps.size() == 1)
blendmaps.clear(); // If a single texture fills the whole terrain, there is no need to blend
}
float Storage::getHeightAt(const osg::Vec3f &worldPos)

@ -111,6 +111,24 @@ namespace
}
};
class BlendFuncFirst
{
public:
static const osg::ref_ptr<osg::BlendFunc>& value()
{
static BlendFuncFirst instance;
return instance.mValue;
}
private:
osg::ref_ptr<osg::BlendFunc> mValue;
BlendFuncFirst()
: mValue(new osg::BlendFunc(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ZERO))
{
}
};
class BlendFunc
{
public:
@ -124,9 +142,8 @@ namespace
osg::ref_ptr<osg::BlendFunc> mValue;
BlendFunc()
: mValue(new osg::BlendFunc)
: mValue(new osg::BlendFunc(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE))
{
mValue->setFunction(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE);
}
};
@ -166,19 +183,16 @@ namespace Terrain
osg::ref_ptr<osg::StateSet> stateset (new osg::StateSet);
stateset->setMode(GL_BLEND, osg::StateAttribute::ON);
if (!firstLayer)
{
stateset->setMode(GL_BLEND, osg::StateAttribute::ON);
stateset->setAttributeAndModes(BlendFunc::value(), osg::StateAttribute::ON);
stateset->setAttributeAndModes(EqualDepth::value(), osg::StateAttribute::ON);
}
// disable fog if we're the first layer of several - supposed to be completely black
if (firstLayer && blendmaps.size() > 0)
else
{
osg::ref_ptr<osg::Fog> fog (new osg::Fog);
fog->setStart(10000000);
fog->setEnd(10000000);
stateset->setAttributeAndModes(fog, osg::StateAttribute::OFF|osg::StateAttribute::OVERRIDE);
stateset->setAttributeAndModes(BlendFuncFirst::value(), osg::StateAttribute::ON);
stateset->setAttributeAndModes(LequalDepth::value(), osg::StateAttribute::ON);
}
@ -193,7 +207,7 @@ namespace Terrain
stateset->addUniform(new osg::Uniform("diffuseMap", texunit));
if(!firstLayer)
if (!blendmaps.empty())
{
++texunit;
osg::ref_ptr<osg::Texture2D> blendmap = blendmaps.at(blendmapIndex++);
@ -212,7 +226,7 @@ namespace Terrain
Shader::ShaderManager::DefineMap defineMap;
defineMap["normalMap"] = (it->mNormalMap) ? "1" : "0";
defineMap["blendMap"] = !firstLayer ? "1" : "0";
defineMap["blendMap"] = (!blendmaps.empty()) ? "1" : "0";
defineMap["specularMap"] = it->mSpecular ? "1" : "0";
defineMap["parallax"] = (it->mNormalMap && it->mParallax) ? "1" : "0";
@ -229,7 +243,17 @@ namespace Terrain
}
else
{
if(!firstLayer)
// Add the actual layer texture
osg::ref_ptr<osg::Texture2D> tex = it->mDiffuseMap;
stateset->setTextureAttributeAndModes(texunit, tex.get());
if (layerTileSize != 1.f)
stateset->setTextureAttributeAndModes(texunit, LayerTexMat::value(layerTileSize), osg::StateAttribute::ON);
++texunit;
// Multiply by the alpha map
if (!blendmaps.empty())
{
osg::ref_ptr<osg::Texture2D> blendmap = blendmaps.at(blendmapIndex++);
@ -242,12 +266,6 @@ namespace Terrain
++texunit;
}
// Add the actual layer texture multiplied by the alpha map.
osg::ref_ptr<osg::Texture2D> tex = it->mDiffuseMap;
stateset->setTextureAttributeAndModes(texunit, tex.get());
if (layerTileSize != 1.f)
stateset->setTextureAttributeAndModes(texunit, LayerTexMat::value(layerTileSize), osg::StateAttribute::ON);
}
stateset->setRenderBinDetails(passIndex++, "RenderBin");

Loading…
Cancel
Save