mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-20 07:23:51 +00:00
Merge pull request #2296 from akortunov/blend
Optimize blendmap generation
This commit is contained in:
commit
f0640cbb21
2 changed files with 76 additions and 74 deletions
|
@ -352,11 +352,6 @@ namespace ESMTerrain
|
||||||
|
|
||||||
std::string Storage::getTextureName(UniqueTextureId id)
|
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";
|
static constexpr char defaultTexture[] = "textures\\_land_default.dds";
|
||||||
if (id.first == 0)
|
if (id.first == 0)
|
||||||
return defaultTexture; // Not sure if the default texture really is hardcoded?
|
return defaultTexture; // Not sure if the default texture really is hardcoded?
|
||||||
|
@ -385,72 +380,61 @@ namespace ESMTerrain
|
||||||
|
|
||||||
int rowStart = (origin.x() - cellX) * realTextureSize;
|
int rowStart = (origin.x() - cellX) * realTextureSize;
|
||||||
int colStart = (origin.y() - cellY) * 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;
|
const int blendmapSize = (realTextureSize-1) * chunkSize + 1;
|
||||||
// We need to upscale the blendmap 2x with nearest neighbor sampling to look like Vanilla
|
// We need to upscale the blendmap 2x with nearest neighbor sampling to look like Vanilla
|
||||||
const int imageScaleFactor = 2;
|
const int imageScaleFactor = 2;
|
||||||
const int blendmapImageSize = blendmapSize * imageScaleFactor;
|
const int blendmapImageSize = blendmapSize * imageScaleFactor;
|
||||||
|
|
||||||
for (int i=0; i<numBlendmaps; ++i)
|
LandCache cache;
|
||||||
|
std::map<UniqueTextureId, unsigned int> textureIndicesMap;
|
||||||
|
|
||||||
|
for (int y=0; y<blendmapSize; y++)
|
||||||
|
{
|
||||||
|
for (int x=0; x<blendmapSize; x++)
|
||||||
|
{
|
||||||
|
UniqueTextureId id = getVtexIndexAt(cellX, cellY, x+rowStart, y+colStart, cache);
|
||||||
|
std::map<UniqueTextureId, unsigned int>::iterator found = textureIndicesMap.find(id);
|
||||||
|
if (found == textureIndicesMap.end())
|
||||||
|
{
|
||||||
|
unsigned int layerIndex = layerList.size();
|
||||||
|
Terrain::LayerInfo info = getLayerInfo(getTextureName(id));
|
||||||
|
|
||||||
|
// look for existing diffuse map, which may be present when several plugins use the same texture
|
||||||
|
for (unsigned int i=0; i<layerList.size(); ++i)
|
||||||
|
{
|
||||||
|
if (layerList[i].mDiffuseMap == info.mDiffuseMap)
|
||||||
|
{
|
||||||
|
layerIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
found = textureIndicesMap.emplace(id, layerIndex).first;
|
||||||
|
|
||||||
|
if (layerIndex >= layerList.size())
|
||||||
{
|
{
|
||||||
osg::ref_ptr<osg::Image> image (new osg::Image);
|
osg::ref_ptr<osg::Image> image (new osg::Image);
|
||||||
image->allocateImage(blendmapImageSize, blendmapImageSize, 1, GL_ALPHA, GL_UNSIGNED_BYTE);
|
image->allocateImage(blendmapImageSize, blendmapImageSize, 1, GL_ALPHA, GL_UNSIGNED_BYTE);
|
||||||
unsigned char* pData = image->data();
|
unsigned char* pData = image->data();
|
||||||
|
memset(pData, 0, image->getTotalDataSize());
|
||||||
for (int y=0; y<blendmapSize; ++y)
|
blendmaps.emplace_back(image);
|
||||||
{
|
layerList.emplace_back(info);
|
||||||
for (int x=0; x<blendmapSize; ++x)
|
}
|
||||||
{
|
}
|
||||||
UniqueTextureId id = getVtexIndexAt(cellX, cellY, x+rowStart, y+colStart, cache);
|
unsigned int layerIndex = found->second;
|
||||||
assert(textureIndicesMap.find(id) != textureIndicesMap.end());
|
unsigned char* pData = blendmaps[layerIndex]->data();
|
||||||
int layerIndex = textureIndicesMap.find(id)->second;
|
|
||||||
|
|
||||||
int alpha = (layerIndex == i+1) ? 255 : 0;
|
|
||||||
|
|
||||||
int realY = (blendmapSize - y - 1)*imageScaleFactor;
|
int realY = (blendmapSize - y - 1)*imageScaleFactor;
|
||||||
int realX = x*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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pData[(realY+0)*blendmapImageSize + realX + 0] = alpha;
|
if (blendmaps.size() == 1)
|
||||||
pData[(realY+1)*blendmapImageSize + realX + 0] = alpha;
|
blendmaps.clear(); // If a single texture fills the whole terrain, there is no need to blend
|
||||||
pData[(realY+0)*blendmapImageSize + realX + 1] = alpha;
|
|
||||||
pData[(realY+1)*blendmapImageSize + realX + 1] = alpha;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
blendmaps.push_back(image);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float Storage::getHeightAt(const osg::Vec3f &worldPos)
|
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
|
class BlendFunc
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -124,9 +142,8 @@ namespace
|
||||||
osg::ref_ptr<osg::BlendFunc> mValue;
|
osg::ref_ptr<osg::BlendFunc> mValue;
|
||||||
|
|
||||||
BlendFunc()
|
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);
|
osg::ref_ptr<osg::StateSet> stateset (new osg::StateSet);
|
||||||
|
|
||||||
|
stateset->setMode(GL_BLEND, osg::StateAttribute::ON);
|
||||||
|
|
||||||
if (!firstLayer)
|
if (!firstLayer)
|
||||||
{
|
{
|
||||||
stateset->setMode(GL_BLEND, osg::StateAttribute::ON);
|
|
||||||
stateset->setAttributeAndModes(BlendFunc::value(), osg::StateAttribute::ON);
|
stateset->setAttributeAndModes(BlendFunc::value(), osg::StateAttribute::ON);
|
||||||
stateset->setAttributeAndModes(EqualDepth::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
|
else
|
||||||
if (firstLayer && blendmaps.size() > 0)
|
|
||||||
{
|
{
|
||||||
osg::ref_ptr<osg::Fog> fog (new osg::Fog);
|
stateset->setAttributeAndModes(BlendFuncFirst::value(), osg::StateAttribute::ON);
|
||||||
fog->setStart(10000000);
|
|
||||||
fog->setEnd(10000000);
|
|
||||||
stateset->setAttributeAndModes(fog, osg::StateAttribute::OFF|osg::StateAttribute::OVERRIDE);
|
|
||||||
stateset->setAttributeAndModes(LequalDepth::value(), osg::StateAttribute::ON);
|
stateset->setAttributeAndModes(LequalDepth::value(), osg::StateAttribute::ON);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,7 +207,7 @@ namespace Terrain
|
||||||
|
|
||||||
stateset->addUniform(new osg::Uniform("diffuseMap", texunit));
|
stateset->addUniform(new osg::Uniform("diffuseMap", texunit));
|
||||||
|
|
||||||
if(!firstLayer)
|
if (!blendmaps.empty())
|
||||||
{
|
{
|
||||||
++texunit;
|
++texunit;
|
||||||
osg::ref_ptr<osg::Texture2D> blendmap = blendmaps.at(blendmapIndex++);
|
osg::ref_ptr<osg::Texture2D> blendmap = blendmaps.at(blendmapIndex++);
|
||||||
|
@ -212,7 +226,7 @@ namespace Terrain
|
||||||
|
|
||||||
Shader::ShaderManager::DefineMap defineMap;
|
Shader::ShaderManager::DefineMap defineMap;
|
||||||
defineMap["normalMap"] = (it->mNormalMap) ? "1" : "0";
|
defineMap["normalMap"] = (it->mNormalMap) ? "1" : "0";
|
||||||
defineMap["blendMap"] = !firstLayer ? "1" : "0";
|
defineMap["blendMap"] = (!blendmaps.empty()) ? "1" : "0";
|
||||||
defineMap["specularMap"] = it->mSpecular ? "1" : "0";
|
defineMap["specularMap"] = it->mSpecular ? "1" : "0";
|
||||||
defineMap["parallax"] = (it->mNormalMap && it->mParallax) ? "1" : "0";
|
defineMap["parallax"] = (it->mNormalMap && it->mParallax) ? "1" : "0";
|
||||||
|
|
||||||
|
@ -229,7 +243,17 @@ namespace Terrain
|
||||||
}
|
}
|
||||||
else
|
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++);
|
osg::ref_ptr<osg::Texture2D> blendmap = blendmaps.at(blendmapIndex++);
|
||||||
|
|
||||||
|
@ -242,12 +266,6 @@ namespace Terrain
|
||||||
++texunit;
|
++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");
|
stateset->setRenderBinDetails(passIndex++, "RenderBin");
|
||||||
|
|
Loading…
Reference in a new issue