mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-29 21:45:33 +00:00
Optimize blendmap generation
This commit is contained in:
parent
58f8d1ac04
commit
1da012f6ee
2 changed files with 62 additions and 77 deletions
|
@ -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)
|
||||
LandCache cache;
|
||||
std::map<UniqueTextureId, int> textureIndicesMap;
|
||||
|
||||
for (int y=0; y<blendmapSize; ++y)
|
||||
{
|
||||
osg::ref_ptr<osg::Image> image (new osg::Image);
|
||||
image->allocateImage(blendmapImageSize, blendmapImageSize, 1, GL_ALPHA, GL_UNSIGNED_BYTE);
|
||||
unsigned char* pData = image->data();
|
||||
|
||||
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…
Reference in a new issue