1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-01-22 15:53:54 +00:00

Implemented slightly better texture splatting

This commit is contained in:
Jacob Essex 2012-03-02 20:27:20 +00:00
parent 461ec9f3d6
commit 3ecc427b96
2 changed files with 90 additions and 88 deletions

View file

@ -31,7 +31,7 @@ namespace MWRender
static_cast<TerrainMaterialGeneratorB::SM2Profile*>(activeProfile); static_cast<TerrainMaterialGeneratorB::SM2Profile*>(activeProfile);
mTerrainGlobals->setMaxPixelError(8); mTerrainGlobals->setMaxPixelError(8);
mTerrainGlobals->setLayerBlendMapSize(SPLIT_TERRAIN ? 256 : 1024); mTerrainGlobals->setLayerBlendMapSize(SPLIT_TERRAIN ? 32 : 1024);
mTerrainGlobals->setLightMapSize(SPLIT_TERRAIN ? 256 : 1024); mTerrainGlobals->setLightMapSize(SPLIT_TERRAIN ? 256 : 1024);
mTerrainGlobals->setCompositeMapSize(SPLIT_TERRAIN ? 256 : 1024); mTerrainGlobals->setCompositeMapSize(SPLIT_TERRAIN ? 256 : 1024);
mTerrainGlobals->setDefaultGlobalColourMapSize(256); mTerrainGlobals->setDefaultGlobalColourMapSize(256);
@ -255,62 +255,75 @@ namespace MWRender
fromY+size <= ESM::Land::LAND_TEXTURE_SIZE && fromY+size <= ESM::Land::LAND_TEXTURE_SIZE &&
"Can't get a terrain texture on terrain outside the current cell"); "Can't get a terrain texture on terrain outside the current cell");
//there is one texture that we want to use as a base (i.e. it won't have //this ensures that the ltex indexes are sorted (or retrived as sorted
//a blend map). This holds the ltex index of that base texture so that //which simplifies shading between cells).
//we know not to include it in the output map //
int baseTexture = -1; //If we don't sort the ltex indexes, the splatting order may differ between
//cells which may lead to inconsistent results when shading between cells
std::set<uint16_t> ltexIndexes;
for ( int y = fromY - 1; y < fromY + size + 1; y++ ) for ( int y = fromY - 1; y < fromY + size + 1; y++ )
{ {
for ( int x = fromX - 1; x < fromX + size + 1; x++ ) for ( int x = fromX - 1; x < fromX + size + 1; x++ )
{ {
const uint16_t ltexIndex = getLtexIndexAt(store, x, y); ltexIndexes.insert(getLtexIndexAt(store, x, y));
//this is the base texture, so we can ignore this at present }
if ( ltexIndex == baseTexture ) }
//there is one texture that we want to use as a base (i.e. it won't have
//a blend map). This holds the ltex index of that base texture so that
//we know not to include it in the output map
int baseTexture = -1;
for ( std::set<uint16_t>::iterator iter = ltexIndexes.begin();
iter != ltexIndexes.end();
++iter )
{
const uint16_t ltexIndex = *iter;
//this is the base texture, so we can ignore this at present
if ( ltexIndex == baseTexture )
{
continue;
}
const std::map<uint16_t, int>::const_iterator it = indexes.find(ltexIndex);
if ( it == indexes.end() )
{
//NB: All vtex ids are +1 compared to the ltex ids
assert( (int)store->landTextures->ltex.size() >= (int)ltexIndex - 1 &&
"LAND.VTEX must be within the bounds of the LTEX array");
std::string texture;
if ( ltexIndex == 0 )
{ {
continue; texture = "_land_default.dds";
}
else
{
texture = store->landTextures->ltex[ltexIndex-1].texture;
//TODO this is needed due to MWs messed up texture handling
texture = texture.substr(0, texture.rfind(".")) + ".dds";
} }
const std::map<uint16_t, int>::const_iterator it = indexes.find(ltexIndex); const size_t position = terrainData->layerList.size();
terrainData->layerList.push_back(Ogre::Terrain::LayerInstance());
if ( it == indexes.end() ) Ogre::TexturePtr normDisp = getNormalDisp("textures\\" + texture);
terrainData->layerList[position].worldSize = 256;
terrainData->layerList[position].textureNames.push_back("textures\\" + texture);
//Normal map. This should be removed but it would require alterations to
//the material generator. Another option would be to use a 1x1 blank texture
//terrainData->layerList[position].textureNames.push_back(normDisp->getName());
if ( baseTexture == -1 )
{ {
//NB: All vtex ids are +1 compared to the ltex ids baseTexture = ltexIndex;
}
assert( (int)store->landTextures->ltex.size() >= (int)ltexIndex - 1 && else
"LAND.VTEX must be within the bounds of the LTEX array"); {
indexes[ltexIndex] = position;
std::string texture;
if ( ltexIndex == 0 )
{
texture = "_land_default.dds";
}
else
{
texture = store->landTextures->ltex[ltexIndex-1].texture;
//TODO this is needed due to MWs messed up texture handling
texture = texture.substr(0, texture.rfind(".")) + ".dds";
}
const size_t position = terrainData->layerList.size();
terrainData->layerList.push_back(Ogre::Terrain::LayerInstance());
Ogre::TexturePtr normDisp = getNormalDisp("textures\\" + texture);
terrainData->layerList[position].worldSize = 256;
terrainData->layerList[position].textureNames.push_back("textures\\" + texture);
//Normal map. This should be removed but it would require alterations to
//the material generator. Another option would be to use a 1x1 blank texture
//terrainData->layerList[position].textureNames.push_back(normDisp->getName());
if ( baseTexture == -1 )
{
baseTexture = ltexIndex;
}
else
{
indexes[ltexIndex] = position;
}
} }
} }
} }
@ -335,8 +348,8 @@ namespace MWRender
//that need to result in an integer for correct splatting //that need to result in an integer for correct splatting
assert( (size & (size - 1)) == 0 && "Size must be a power of 2"); assert( (size & (size - 1)) == 0 && "Size must be a power of 2");
const int blendSize = terrain->getLayerBlendMapSize(); const int blendMapSize = terrain->getLayerBlendMapSize();
const int blendDist = TERRAIN_SHADE_DISTANCE * (blendSize / size); const int splatSize = blendMapSize / size;
//zero out every map //zero out every map
std::map<uint16_t, int>::const_iterator iter; std::map<uint16_t, int>::const_iterator iter;
@ -344,7 +357,7 @@ namespace MWRender
{ {
float* pBlend = terrain->getLayerBlendMap(iter->second) float* pBlend = terrain->getLayerBlendMap(iter->second)
->getBlendPointer(); ->getBlendPointer();
memset(pBlend, 0, sizeof(float) * blendSize * blendSize); memset(pBlend, 0, sizeof(float) * blendMapSize * blendMapSize);
} }
//covert the ltex data into a set of blend maps //covert the ltex data into a set of blend maps
@ -354,11 +367,6 @@ namespace MWRender
{ {
const uint16_t ltexIndex = getLtexIndexAt(store, texX, texY); const uint16_t ltexIndex = getLtexIndexAt(store, texX, texY);
//whilte texX is the splat index relative to the entire cell,
//relX is relative to the current segment we are splatting
const int relX = texX - fromX;
const int relY = texY - fromY;
//check if it is the base texture (which isn't in the map) and //check if it is the base texture (which isn't in the map) and
//if it is don't bother altering the blend map for it //if it is don't bother altering the blend map for it
if ( indexes.find(ltexIndex) == indexes.end() ) if ( indexes.find(ltexIndex) == indexes.end() )
@ -366,43 +374,43 @@ namespace MWRender
continue; continue;
} }
//while texX is the splat index relative to the entire cell,
//relX is relative to the current segment we are splatting
const int relX = texX - fromX;
const int relY = texY - fromY;
const int layerIndex = indexes.find(ltexIndex)->second; const int layerIndex = indexes.find(ltexIndex)->second;
float* const pBlend = terrain->getLayerBlendMap(layerIndex) float* const pBlend = terrain->getLayerBlendMap(layerIndex)
->getBlendPointer(); ->getBlendPointer();
const int splatSize = blendSize / size; for ( int y = -1; y < splatSize + 1; y++ ){
for ( int x = -1; x < splatSize + 1; x++ ){
//setup the bounds for the shading of this texture splat //Note: Y is reversed
const int startX = std::max(0, relX*splatSize - blendDist); const int splatY = blendMapSize - 1 - relY * splatSize - y;
const int endX = std::min(blendSize, (relX+1)*splatSize + blendDist); const int splatX = relX * splatSize + x;
const int startY = std::max(0, relY*splatSize - blendDist); if ( splatX >= 0 && splatX < blendMapSize &&
const int endY = std::min(blendSize, (relY+1)*splatSize + blendDist); splatY >= 0 && splatY < blendMapSize )
for ( int blendX = startX; blendX < endX; blendX++ )
{
for ( int blendY = startY; blendY < endY; blendY++ )
{
assert(blendX >= 0 && blendX < blendSize &&
"index must be within texture bounds");
assert(blendY >= 0 && blendY < blendSize &&
"index must be within texture bounds");
const int index = blendSize*(blendSize - 1 - blendY) + blendX;
if ( blendX >= relX*splatSize && blendX < (relX+1)*splatSize &&
blendY >= relY*splatSize && blendY < (relY+1)*splatSize )
{ {
pBlend[index] = 1; const int index = (splatY)*blendMapSize + splatX;
}
else if ( y >= 0 && y < splatSize &&
{ x >= 0 && x < splatSize )
pBlend[index] = std::max((float)pBlend[index], 0.5f); {
pBlend[index] = 1;
}
else
{
//this provides a transition shading but also
//rounds off the corners slightly
pBlend[index] = std::min(1.0f, pBlend[index] + 0.5f);
}
} }
} }
} }
} }
} }

View file

@ -61,12 +61,6 @@ namespace MWRender{
*/ */
int mRealSize; int mRealSize;
/**
* The distance that the current cell should be shaded into the neighbouring
* texture. The distance is in terms of the splat size of a texture
*/
static const float TERRAIN_SHADE_DISTANCE = 0.25f;
/** /**
* Setups up the list of textures for part of a cell, using indexes as * Setups up the list of textures for part of a cell, using indexes as
* an output to create a mapping of MW LtexIndex to the relevant terrain * an output to create a mapping of MW LtexIndex to the relevant terrain