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

Fixed some minor bugs, a cells terrain can now be rendered as 4 Ogre::Terrain objects, possibly giving a speed increase

This commit is contained in:
Jacob Essex 2012-01-24 13:29:31 +00:00
parent cd0df082df
commit 5e3e6f9165
2 changed files with 179 additions and 35 deletions

View file

@ -1,5 +1,6 @@
#include <OgreTerrain.h>
#include <OgreTerrainGroup.h>
#include <OgreTerrainMaterialGeneratorA.h>
#include "terrain.hpp"
@ -7,64 +8,167 @@
namespace MWRender
{
//----------------------------------------------------------------------------------------------
TerrainManager::TerrainManager(Ogre::SceneManager* mgr)
{
mTerrainGlobals = OGRE_NEW Ogre::TerrainGlobalOptions();
mTerrainGlobals->setMaxPixelError(8);
mTerrainGlobals->setLayerBlendMapSize(SPLIT_TERRAIN ? 1024 : 256);
Ogre::TerrainMaterialGenerator::Profile* const activeProfile =
mTerrainGlobals->getDefaultMaterialGenerator()
->getActiveProfile();
Ogre::TerrainMaterialGeneratorA::SM2Profile* matProfile =
static_cast<Ogre::TerrainMaterialGeneratorA::SM2Profile*>(activeProfile);
matProfile->setLightmapEnabled(false);
matProfile->setReceiveDynamicShadowsEnabled(false);
mLandSize = ESM::Land::LAND_SIZE;
mRealSize = ESM::Land::REAL_SIZE;
if ( SPLIT_TERRAIN )
{
mLandSize = (mLandSize - 1)/2 + 1;
mRealSize /= 2;
}
mTerrainGroup = OGRE_NEW Ogre::TerrainGroup(mgr,
Ogre::Terrain::ALIGN_X_Z, ESM::Land::LAND_SIZE,
ESM::Land::REAL_SIZE);
Ogre::Terrain::ALIGN_X_Z,
mLandSize,
mRealSize);
mTerrainGroup->setOrigin(Ogre::Vector3(ESM::Land::REAL_SIZE/2,
0,
-ESM::Land::REAL_SIZE/2));
mTerrainGroup->setOrigin(Ogre::Vector3(mRealSize/2,
0,
-mRealSize/2));
Ogre::Terrain::ImportData importSettings =
Ogre::Terrain::ImportData& importSettings =
mTerrainGroup->getDefaultImportSettings();
importSettings.terrainSize = ESM::Land::LAND_SIZE;
importSettings.worldSize = ESM::Land::REAL_SIZE;
importSettings.inputBias = 0;
importSettings.terrainSize = mLandSize;
importSettings.worldSize = mRealSize;
importSettings.minBatchSize = 9;
importSettings.maxBatchSize = 33;
importSettings.maxBatchSize = mLandSize;
importSettings.deleteInputData = false;
importSettings.deleteInputData = true;
}
//----------------------------------------------------------------------------------------------
TerrainManager::~TerrainManager()
{
OGRE_DELETE mTerrainGroup;
OGRE_DELETE mTerrainGlobals;
}
//----------------------------------------------------------------------------------------------
void TerrainManager::cellAdded(MWWorld::Ptr::CellStore *store)
{
int x = store->cell->getGridX();
int y = store->cell->getGridY();
const int cellX = store->cell->getGridX();
const int cellY = store->cell->getGridY();
Ogre::Terrain::ImportData terrainData;
Ogre::Terrain::ImportData terrainData =
mTerrainGroup->getDefaultImportSettings();
terrainData.inputBias = 0;
terrainData.inputFloat = store->land[1][1]->landData->heights;
if ( SPLIT_TERRAIN )
{
//split the cell terrain into four segments
const int numTextures = ESM::Land::LAND_TEXTURE_SIZE/2;
std::map<uint16_t, int> indexes;
initTerrainTextures(&terrainData, store, 0, 0,
ESM::Land::LAND_TEXTURE_SIZE, indexes);
mTerrainGroup->defineTerrain(x, y, &terrainData);
for ( int x = 0; x < 2; x++ )
{
for ( int y = 0; y < 2; y++ )
{
const int terrainX = cellX * 2 + x;
const int terrainY = cellY * 2 + y;
terrainData.inputFloat = OGRE_ALLOC_T(float,
mLandSize*mLandSize,
Ogre::MEMCATEGORY_GEOMETRY);
//copy the height data row by row
for ( int terrainCopyY = 0; terrainCopyY < mLandSize; terrainCopyY++ )
{
//the offset of the current segment
const size_t yOffset = y * (mLandSize-1) * ESM::Land::LAND_SIZE +
//offset of the row
terrainCopyY * ESM::Land::LAND_SIZE;
const size_t xOffset = x * (mLandSize-1);
memcpy(&terrainData.inputFloat[terrainCopyY*mLandSize],
&store->land[1][1]->landData->heights[yOffset + xOffset],
mLandSize*sizeof(float));
}
std::map<uint16_t, int> indexes;
initTerrainTextures(&terrainData, store,
x * numTextures, y * numTextures,
numTextures, indexes);
mTerrainGroup->defineTerrain(terrainX, terrainY, &terrainData);
mTerrainGroup->loadTerrain(terrainX, terrainY, true);
Ogre::Terrain* terrain = mTerrainGroup->getTerrain(terrainX, terrainY);
initTerrainBlendMaps(terrain, store,
x * numTextures, y * numTextures,
numTextures, indexes);
}
}
}
else
{
//one cell is one terrain segment
terrainData.inputFloat = OGRE_ALLOC_T(float,
mLandSize*mLandSize,
Ogre::MEMCATEGORY_GEOMETRY);
memcpy(&terrainData.inputFloat[0],
&store->land[1][1]->landData->heights[0],
mLandSize*mLandSize*sizeof(float));
std::map<uint16_t, int> indexes;
initTerrainTextures(&terrainData, store, 0, 0,
ESM::Land::LAND_TEXTURE_SIZE, indexes);
mTerrainGroup->defineTerrain(cellX, cellY, &terrainData);
mTerrainGroup->loadTerrain(cellX, cellY, true);
Ogre::Terrain* terrain = mTerrainGroup->getTerrain(cellX, cellY);
initTerrainBlendMaps(terrain, store, 0, 0,
ESM::Land::LAND_TEXTURE_SIZE, indexes);
}
mTerrainGroup->loadTerrain(x, y, true);
Ogre::Terrain* terrain = mTerrainGroup->getTerrain(x,y);
initTerrainBlendMaps(terrain, store, 0, 0,
ESM::Land::LAND_TEXTURE_SIZE, indexes);
}
//----------------------------------------------------------------------------------------------
void TerrainManager::cellRemoved(MWWorld::Ptr::CellStore *store)
{
mTerrainGroup->removeTerrain(store->cell->getGridX(),
store->cell->getGridY());
if ( SPLIT_TERRAIN )
{
for ( int x = 0; x < 2; x++ )
{
for ( int y = 0; y < 2; y++ )
{
mTerrainGroup->removeTerrain(store->cell->getGridX() * 2 + x,
store->cell->getGridY() * 2 + y);
}
}
}
else
{
mTerrainGroup->removeTerrain(store->cell->getGridX(),
store->cell->getGridY());
}
}
//----------------------------------------------------------------------------------------------
void TerrainManager::initTerrainTextures(Ogre::Terrain::ImportData* terrainData,
MWWorld::Ptr::CellStore* store,
int fromX, int fromY, int size,
@ -122,9 +226,10 @@ namespace MWRender
}
}
}
}
//----------------------------------------------------------------------------------------------
void TerrainManager::initTerrainBlendMaps(Ogre::Terrain* terrain,
MWWorld::Ptr::CellStore* store,
int fromX, int fromY, int size,
@ -157,16 +262,24 @@ namespace MWRender
//covert the ltex data into a set of blend maps
for ( int texY = fromY - 1; texY < fromY + size + 1; texY++ )
{
for ( int texX = fromY - 1; texX < fromY + size + 1; texX++ )
for ( int texX = fromX - 1; texX < fromX + size + 1; texX++ )
{
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;
//this is the ground texture, which is currently the base texture
//so don't alter the splatting map
if ( ltexIndex == 0 ){
continue;
}
assert (indexes.find(ltexIndex) != indexes.end() &&
"Texture layer must exist");
const int layerIndex = indexes.find(ltexIndex)->second;
float* const pBlend = terrain->getLayerBlendMap(layerIndex)
@ -175,11 +288,11 @@ namespace MWRender
const int splatSize = blendSize / 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 startX = std::max(0, relX*splatSize - blendDist);
const int endX = std::min(blendSize, (relX+1)*splatSize + blendDist);
const int startY = std::max(0, texY*splatSize - blendDist);
const int endY = std::min(blendSize, (texY+1)*splatSize + blendDist);
const int startY = std::max(0, relY*splatSize - blendDist);
const int endY = std::min(blendSize, (relY+1)*splatSize + blendDist);
for ( int blendX = startX; blendX < endX; blendX++ )
{
@ -193,16 +306,16 @@ namespace MWRender
//calculate the distance from the edge of the square
// to the point we are shading
int distX = texX*splatSize - blendX;
int distX = relX*splatSize - blendX;
if ( distX < 0 )
{
distX = std::max(0, blendX - (texX+1)*splatSize);
distX = std::max(0, blendX - (relX+1)*splatSize);
}
int distY = texY*splatSize - blendY;
int distY = relY*splatSize - blendY;
if ( distY < 0 )
{
distY = std::max(0, blendY - (texY+1)*splatSize);
distY = std::max(0, blendY - (relY+1)*splatSize);
}
float blendAmount = blendDist - std::sqrt((float)distX*distX + distY*distY);
@ -235,6 +348,7 @@ namespace MWRender
}
//----------------------------------------------------------------------------------------------
int TerrainManager::getLtexIndexAt(MWWorld::Ptr::CellStore* store,
int x, int y)

View file

@ -16,6 +16,15 @@ namespace MWRender{
/**
* Implements the Morrowind terrain using the Ogre Terrain Component
*
* This currently has two options as to how the terrain is rendered, one
* is that one cell is rendered as one Ogre::Terrain and the other that
* it is rendered as 4 Ogre::Terrain segments
*
* Splitting it up into segments has the following advantages
* * Seems to be faster
* * Terrain can now be culled more aggressivly using view frustram culling
* * We don't hit splat limits as much
*/
class TerrainManager{
public:
@ -28,6 +37,27 @@ namespace MWRender{
Ogre::TerrainGlobalOptions* mTerrainGlobals;
Ogre::TerrainGroup* mTerrainGroup;
/**
* Should each cell be split into a further four Ogre::Terrain objects
*
* This has the advantage that it is possible to cull more terrain and
* we are more likly to be able to be able to fit all the required splats
* in (Ogre's default material generator only works with about 6 textures)
*/
static const bool SPLIT_TERRAIN = true;
/**
* The length in verticies of a single terrain block.
* This takes into account the SPLIT_TERRAIN option
*/
int mLandSize;
/**
* The length in game units of a single terrain block.
* This takes into account the SPLIT_TERRAIN option
*/
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