Added blended textures to the terrain

pull/21/head
Jacob Essex 13 years ago
parent 18108c02a7
commit 637302fc87

@ -48,9 +48,13 @@ namespace MWRender
terrainData.inputBias = 0;
terrainData.inputFloat = store->land->landData->heights;
std::map<uint16_t, int> indexes;
initTerrainTextures(&terrainData, store, indexes);
mTerrainGroup->defineTerrain(x, y, &terrainData);
mTerrainGroup->loadTerrain(x, y, true);
Ogre::Terrain* terrain = mTerrainGroup->getTerrain(x,y);
initTerrainBlendMaps(terrain, store, indexes);
}
void TerrainManager::cellRemoved(MWWorld::Ptr::CellStore *store)
@ -59,4 +63,150 @@ namespace MWRender
store->cell->getGridY());
}
void TerrainManager::initTerrainTextures(Ogre::Terrain::ImportData* terrainData,
MWWorld::Ptr::CellStore* store,
std::map<uint16_t, int>& indexes)
{
//have a base texture for now, but this is probably not needed on most cells
terrainData->layerList.resize(1);
terrainData->layerList[0].worldSize = 256;
terrainData->layerList[0].textureNames.push_back("textures\\_land_default.dds");
terrainData->layerList[0].textureNames.push_back("textures\\_land_default.dds");
const uint16_t* const textures = store->land->landData->textures;
for ( int y = 0; y < ESM::Land::LAND_TEXTURE_SIZE; y++ )
{
for ( int x = 0; x < ESM::Land::LAND_TEXTURE_SIZE; x++ )
{
const uint16_t ltexIndex = textures[y * ESM::Land::LAND_TEXTURE_SIZE + x];
if ( ltexIndex == 0 )
{
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)ltexIndex - 1 > 0 &&
store->landTextures->ltex.size() > (size_t)ltexIndex - 1 &&
"LAND.VTEX must be within the bounds of the LTEX array");
std::string 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());
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("textures\\" + texture);
indexes[ltexIndex] = position;
}
}
}
}
void TerrainManager::initTerrainBlendMaps(Ogre::Terrain* terrain,
MWWorld::Ptr::CellStore* store,
const std::map<uint16_t, int>& indexes)
{
const int blendSize = terrain->getLayerBlendMapSize();
const int blendDist = TERRAIN_SHADE_DISTANCE *
(blendSize / ESM::Land::LAND_TEXTURE_SIZE);
//zero out every map
std::map<uint16_t, int>::const_iterator iter;
for ( iter = indexes.begin(); iter != indexes.end(); ++iter )
{
float* pBlend = terrain->getLayerBlendMap(iter->second)
->getBlendPointer();
memset(pBlend, 0, sizeof(float) * blendSize * blendSize);
}
//covert the ltex data into a set of blend maps
const uint16_t* const textures = store->land->landData->textures;
for ( int texY = 0; texY < ESM::Land::LAND_TEXTURE_SIZE; texY++ )
{
for ( int texX = 0; texX < ESM::Land::LAND_TEXTURE_SIZE; texX++ )
{
const uint16_t ltexIndex = textures[texY * ESM::Land::LAND_TEXTURE_SIZE + texX];
if ( ltexIndex == 0 ){
continue;
}
const int layerIndex = indexes.find(ltexIndex)->second;
float* const pBlend = terrain->getLayerBlendMap(layerIndex)
->getBlendPointer();
const int splatSize = blendSize / ESM::Land::LAND_TEXTURE_SIZE;
//setup the bounds for the shading of this texture splat
const int startX = std::max(0, texX*splatSize - blendDist);
const int endX = std::min(blendSize, (texX+1)*splatSize + blendDist);
const int startY = std::max(0, texY*splatSize - blendDist);
const int endY = std::min(blendSize, (texY+1)*splatSize + blendDist);
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");
//calculate the distance from the edge of the square
// to the point we are shading
int distX = texX*splatSize - blendX;
if ( distX < 0 )
{
distX = std::max(0, blendX - (texX+1)*splatSize);
}
int distY = texY*splatSize - blendY;
if ( distY < 0 )
{
distY = std::max(0, blendY - (texY+1)*splatSize);
}
float blendAmount = blendDist - std::sqrt((float)distX*distX + distY*distY);
blendAmount /= blendDist;
//this is required as blendDist < sqrt(blendDist*blendDist + blendDist*blendDist)
//this means that the corners are slightly the "wrong" shape but totaly smooth
//shading for the edges
blendAmount = std::max( (float) 0.0, blendAmount);
assert(blendAmount >= 0 && "Blend should never be negative");
//flips the y
const int index = blendSize*(blendSize - 1 - blendY) + blendX;
pBlend[index] += blendAmount;
pBlend[index] = std::min((float)1, pBlend[index]);
}
}
}
}
//update the maps
for ( iter = indexes.begin(); iter != indexes.end(); ++iter )
{
Ogre::TerrainLayerBlendMap* blend = terrain->getLayerBlendMap(iter->second);
blend->dirty();
blend->update();
}
}
}

@ -1,12 +1,15 @@
#ifndef _GAME_RENDER_TERRAIN_H
#define _GAME_RENDER_TERRAIN_H
#include <OgreTerrain.h>
#include "../mwworld/ptr.hpp"
namespace Ogre{
class SceneManager;
class TerrainGroup;
class TerrainGlobalOptions;
class Terrain;
}
namespace MWRender{
@ -24,6 +27,31 @@ namespace MWRender{
private:
Ogre::TerrainGlobalOptions* mTerrainGlobals;
Ogre::TerrainGroup* mTerrainGroup;
/**
* 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.5;
/**
* Setups up the list of textures for the cell
* @param terrainData the terrain data to setup the textures for
* @param indexes a mapping of ltex index to the terrain texture layer that
* can be used by initTerrainBlendMaps
*/
void initTerrainTextures(Ogre::Terrain::ImportData* terrainData,
MWWorld::Ptr::CellStore* store,
std::map<uint16_t, int>& indexes);
/**
* Creates the blend (splatting maps) for the given terrain from the ltex data.
* @param terrain the terrain object for the current cell
* @param indexes the mapping of ltex to blend map produced by initTerrainTextures
*/
void initTerrainBlendMaps(Ogre::Terrain* terrain,
MWWorld::Ptr::CellStore* store,
const std::map<uint16_t, int>& indexes);
};
}

@ -86,6 +86,25 @@ void Land::loadData(ESMReader &esm)
landData->heights[x + y * LAND_SIZE] = tempOffset * HEIGHT_SCALE;
}
}
if (esm.isNextSub("WNAM"))
{
esm.skipHSubSize(81);
}
if (esm.isNextSub("VCLR"))
{
esm.skipHSubSize(12675);
}
//TODO fix magic numbers
uint16_t vtex[512];
esm.getHNExact(&vtex, 512, "VTEX");
int readPos = 0; //bit ugly, but it works
for ( int y1 = 0; y1 < 4; y1++ )
for ( int x1 = 0; x1 < 4; x1++ )
for ( int y2 = 0; y2 < 4; y2++)
for ( int x2 = 0; x2 < 4; x2++ )
landData->textures[(y1*4+y2)*16+(x1*4+x2)] = vtex[readPos++];
}
else
{

@ -34,6 +34,12 @@ struct Land
static const int HEIGHT_SCALE = 8;
//number of textures per side of land
static const int LAND_TEXTURE_SIZE = 16;
//total number of textures per land
static const int LAND_NUM_TEXTURES = LAND_TEXTURE_SIZE * LAND_TEXTURE_SIZE;
#pragma pack(push,1)
struct VHGT
{
@ -51,6 +57,8 @@ struct Land
float heightOffset;
float heights[LAND_NUM_VERTS];
//float normals[LAND_NUM_VERTS * 3];
uint16_t textures[LAND_NUM_TEXTURES];
char colours[3 * LAND_NUM_VERTS];
};
LandData *landData;

@ -126,6 +126,7 @@ namespace ESMS
CellRefList<Weapon, D> weapons;
const Land* land;
const LTexList* landTextures;
void load (const ESMStore &store, ESMReader &esm)
{
@ -141,6 +142,7 @@ namespace ESMS
if ( ! (cell->data.flags & ESM::Cell::Interior) )
{
loadTerrain(cell->data.gridX, cell->data.gridY, store, esm);
landTextures = &store.landTexts;
}
mState = State_Loaded;

Loading…
Cancel
Save