diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index 4d13b68893..90dc917313 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -46,15 +46,17 @@ namespace MWRender Ogre::Terrain::ImportData terrainData; terrainData.inputBias = 0; - terrainData.inputFloat = store->land->landData->heights; + terrainData.inputFloat = store->land[1][1]->landData->heights; std::map indexes; - initTerrainTextures(&terrainData, store, indexes); + initTerrainTextures(&terrainData, store, 0, 0, + ESM::Land::LAND_TEXTURE_SIZE, indexes); mTerrainGroup->defineTerrain(x, y, &terrainData); mTerrainGroup->loadTerrain(x, y, true); Ogre::Terrain* terrain = mTerrainGroup->getTerrain(x,y); - initTerrainBlendMaps(terrain, store, indexes); + initTerrainBlendMaps(terrain, store, 0, 0, + ESM::Land::LAND_TEXTURE_SIZE, indexes); } void TerrainManager::cellRemoved(MWWorld::Ptr::CellStore *store) @@ -65,20 +67,29 @@ namespace MWRender void TerrainManager::initTerrainTextures(Ogre::Terrain::ImportData* terrainData, MWWorld::Ptr::CellStore* store, + int fromX, int fromY, int size, std::map& indexes) { + assert(store != NULL && "store must be a valid pointer"); + assert(terrainData != NULL && "Must have valid terrain data"); + assert(fromX >= 0 && fromY >= 0 && + "Can't get a terrain texture on terrain outside the current cell"); + assert(fromX+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"); + //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 y = fromY - 1; y < fromY + size + 1; y++ ) { - for ( int x = 0; x < ESM::Land::LAND_TEXTURE_SIZE; x++ ) + for ( int x = fromX - 1; x < fromX + size + 1; x++ ) { - const uint16_t ltexIndex = textures[y * ESM::Land::LAND_TEXTURE_SIZE + x]; + const uint16_t ltexIndex = getLtexIndexAt(store, x, y); + //this is the base texture, so we can ignore this at present if ( ltexIndex == 0 ) { continue; @@ -89,7 +100,7 @@ namespace MWRender if ( it == indexes.end() ) { //NB: All vtex ids are +1 compared to the ltex ids - assert((int)ltexIndex - 1 > 0 && + assert((int)ltexIndex >= 0 && store->landTextures->ltex.size() > (size_t)ltexIndex - 1 && "LAND.VTEX must be within the bounds of the LTEX array"); @@ -116,11 +127,23 @@ namespace MWRender void TerrainManager::initTerrainBlendMaps(Ogre::Terrain* terrain, MWWorld::Ptr::CellStore* store, + int fromX, int fromY, int size, const std::map& indexes) { + assert(store != NULL && "store must be a valid pointer"); + assert(terrain != NULL && "Must have valid terrain"); + assert(fromX >= 0 && fromY >= 0 && + "Can't get a terrain texture on terrain outside the current cell"); + assert(fromX+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"); + + //size must be a power of 2 as we do divisions with a power of 2 number + //that need to result in an integer for correct splatting + assert( (size & (size - 1)) == 0 && "Size must be a power of 2"); + const int blendSize = terrain->getLayerBlendMapSize(); - const int blendDist = TERRAIN_SHADE_DISTANCE * - (blendSize / ESM::Land::LAND_TEXTURE_SIZE); + const int blendDist = TERRAIN_SHADE_DISTANCE * (blendSize / size); //zero out every map std::map::const_iterator iter; @@ -132,21 +155,24 @@ namespace MWRender } //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 texY = fromY - 1; texY < fromY + size + 1; texY++ ) { - for ( int texX = 0; texX < ESM::Land::LAND_TEXTURE_SIZE; texX++ ) + for ( int texX = fromY - 1; texX < fromY + size + 1; texX++ ) { - const uint16_t ltexIndex = textures[texY * ESM::Land::LAND_TEXTURE_SIZE + texX]; + const uint16_t ltexIndex = getLtexIndexAt(store, texX, texY); + + //this is the ground texture, which is currently the base texture + //so don't alter the splatting map 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; + const int splatSize = blendSize / size; //setup the bounds for the shading of this texture splat const int startX = std::max(0, texX*splatSize - blendDist); @@ -209,4 +235,51 @@ namespace MWRender } + + int TerrainManager::getLtexIndexAt(MWWorld::Ptr::CellStore* store, + int x, int y) + { + //check texture index falls within the 9 cell bounds + //as this function can't cope with anything above that + assert(x >= -ESM::Land::LAND_TEXTURE_SIZE && + y >= -ESM::Land::LAND_TEXTURE_SIZE && + "Trying to get land textures that are out of bounds"); + + assert(x < 2*ESM::Land::LAND_TEXTURE_SIZE && + y < 2*ESM::Land::LAND_TEXTURE_SIZE && + "Trying to get land textures that are out of bounds"); + + assert(store != NULL && "Store pointer must be valid"); + + //default center cell is indexed at (1,1) + int cellX = 1; + int cellY = 1; + + if ( x < 0 ) + { + cellX--; + x += ESM::Land::LAND_TEXTURE_SIZE; + } + else if ( x >= ESM::Land::LAND_TEXTURE_SIZE ) + { + cellX++; + x -= ESM::Land::LAND_TEXTURE_SIZE; + } + + if ( y < 0 ) + { + cellY--; + y += ESM::Land::LAND_TEXTURE_SIZE; + } + else if ( y >= ESM::Land::LAND_TEXTURE_SIZE ) + { + cellY++; + y -= ESM::Land::LAND_TEXTURE_SIZE; + } + + return store->land[cellX][cellY] + ->landData + ->textures[y * ESM::Land::LAND_TEXTURE_SIZE + x]; + } + } diff --git a/apps/openmw/mwrender/terrain.hpp b/apps/openmw/mwrender/terrain.hpp index 127c3f5f2f..c64b741d57 100644 --- a/apps/openmw/mwrender/terrain.hpp +++ b/apps/openmw/mwrender/terrain.hpp @@ -35,23 +35,49 @@ namespace MWRender{ static const float TERRAIN_SHADE_DISTANCE = 0.5; /** - * Setups up the list of textures for the cell + * 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 + * layer + * * @param terrainData the terrain data to setup the textures for + * @param store the cell store for the given terrain cell + * @param fromX the ltex index in the current cell to start making the texture from + * @param fromY the ltex index in the current cell to start making the texture from + * @param size the size (number of splats) to get * @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, + int fromX, int fromY, int size, std::map& indexes); /** * Creates the blend (splatting maps) for the given terrain from the ltex data. + * * @param terrain the terrain object for the current cell + * @param store the cell store for the given terrain cell + * @param fromX the ltex index in the current cell to start making the texture from + * @param fromY the ltex index in the current cell to start making the texture from + * @param size the size (number of splats) to get * @param indexes the mapping of ltex to blend map produced by initTerrainTextures */ void initTerrainBlendMaps(Ogre::Terrain* terrain, MWWorld::Ptr::CellStore* store, + int fromX, int fromY, int size, const std::map& indexes); + + /** + * Gets a LTEX index at the given point, assuming the current cell + * starts at (0,0). This supports getting values from the surrounding + * cells so negative x, y is acceptable + * + * @param store the cell store for the current cell + * @param x, y the splat position of the ltex index to get relative to the + * first splat of the current cell + */ + int getLtexIndexAt(MWWorld::Ptr::CellStore* store, int x, int y); + }; } diff --git a/components/esm_store/cell_store.hpp b/components/esm_store/cell_store.hpp index 5310237fed..951b0736a9 100644 --- a/components/esm_store/cell_store.hpp +++ b/components/esm_store/cell_store.hpp @@ -96,8 +96,8 @@ namespace ESMS State_Unloaded, State_Preloaded, State_Loaded }; - CellStore (const ESM::Cell *cell_) : cell (cell_), mState (State_Unloaded), - land(NULL) {} + CellStore (const ESM::Cell *cell_) : cell (cell_), mState (State_Unloaded) + {} const ESM::Cell *cell; State mState; @@ -125,7 +125,7 @@ namespace ESMS CellRefList statics; CellRefList weapons; - const Land* land; + const Land* land[3][3]; const LTexList* landTextures; void load (const ESMStore &store, ESMReader &esm) @@ -141,7 +141,16 @@ namespace ESMS if ( ! (cell->data.flags & ESM::Cell::Interior) ) { - loadTerrain(cell->data.gridX, cell->data.gridY, store, esm); + for ( size_t x = 0; x < 3; x++ ) + { + for ( size_t y = 0; y < 3; y++ ) + { + land[x][y] = loadTerrain(cell->data.gridX + x - 1, + cell->data.gridY + y - 1, + store, + esm); + } + } landTextures = &store.landTexts; } @@ -190,7 +199,7 @@ namespace ESMS private: - void loadTerrain(int X, int Y, const ESMStore &store, ESMReader &esm) + Land* loadTerrain(int X, int Y, const ESMStore &store, ESMReader &esm) { // load terrain Land *land = store.lands.search(X, Y); @@ -199,18 +208,13 @@ namespace ESMS land->loadData(esm); } - this->land = land; + return land; } void unloadTerrain(int X, int Y, const ESMStore &store) { - Land *land = store.lands.search(X,Y); - // unload terrain - if (land != NULL) - { - land->unloadData(); - } - - this->land = NULL; + assert (false && + "This function is not implemented due to the fact that we now store overlapping land blocks so" && + "we cannot be sure that the land segment is not being used by another CellStore"); } template