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:
parent
cd0df082df
commit
5e3e6f9165
2 changed files with 179 additions and 35 deletions
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue