From 18108c02a7e7a965252da74526536b3306ba1e8a Mon Sep 17 00:00:00 2001 From: Jacob Essex Date: Sat, 21 Jan 2012 16:59:08 +0000 Subject: [PATCH 01/39] Merge of corristo's terrain rendering and fixes so that the terrain is correctly positioned and rendered --- CMakeLists.txt | 11 +++ apps/openmw/CMakeLists.txt | 3 +- apps/openmw/mwrender/renderingmanager.cpp | 4 + apps/openmw/mwrender/renderingmanager.hpp | 2 + apps/openmw/mwrender/terrain.cpp | 62 ++++++++++++++++ apps/openmw/mwrender/terrain.hpp | 31 ++++++++ cmake/FindOGRE.cmake | 15 ++++ components/esm/loadcell.hpp | 15 ++++ components/esm/loadland.cpp | 89 +++++++++++++++++++++-- components/esm/loadland.hpp | 44 +++++++++++ components/esm_store/cell_store.hpp | 33 ++++++++- components/esm_store/reclists.hpp | 2 +- components/esm_store/store.hpp | 2 +- files/plugins.cfg.linux | 2 +- files/plugins.cfg.mac | 2 +- 15 files changed, 306 insertions(+), 11 deletions(-) create mode 100644 apps/openmw/mwrender/terrain.cpp create mode 100644 apps/openmw/mwrender/terrain.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 723d10b34..015ffd7ce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -188,6 +188,7 @@ find_package(OpenAL REQUIRED) find_package(Bullet REQUIRED) include_directories("." ${OGRE_INCLUDE_DIR} ${OGRE_INCLUDE_DIR}/Ogre ${OGRE_INCLUDE_DIR}/OGRE + ${OGRE_Terrain_INCLUDE_DIR} ${OIS_INCLUDE_DIR} ${Boost_INCLUDE_DIR} ${PLATFORM_INCLUDE_DIR} ${CMAKE_HOME_DIRECTORY}/extern/caelum/include @@ -259,6 +260,16 @@ if (APPLE) configure_file(${OGRE_PLUGIN_DIR}/Plugin_ParticleFX.dylib "${APP_BUNDLE_DIR}/Contents/Plugins/Plugin_ParticleFX.dylib" COPYONLY) + # prepare components + configure_file(${OGRE_LIB_DIR}/libOgrePaging.dylib + "${APP_BUNDLE_DIR}/Contents/Components/libOgrePaging.dylib" COPYONLY) + + configure_file(${OGRE_LIB_DIR}/libOgreTerrain.dylib + "${APP_BUNDLE_DIR}/Contents/Components/libOgreTerrain.dylib" COPYONLY) + + configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg + "${APP_BUNDLE_DIR}/Contents/MacOS/openmw.cfg") + endif (APPLE) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index e17a2cb25..663a10787 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -16,7 +16,7 @@ set(GAME_HEADER source_group(game FILES ${GAME} ${GAME_HEADER}) add_openmw_dir (mwrender - renderingmanager debugging sky player npcs creatures objects renderinginterface + renderingmanager debugging sky terrain player npcs creatures objects renderinginterface ) add_openmw_dir (mwinput @@ -75,6 +75,7 @@ add_definitions(${SOUND_DEFINE}) target_link_libraries(openmw ${OGRE_LIBRARIES} + ${OGRE_Terrain_LIBRARY} ${OIS_LIBRARIES} ${Boost_LIBRARIES} ${OPENAL_LIBRARY} diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 06339cdd4..b0b7c88c4 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -26,6 +26,7 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const { rend.createScene("PlayerCam", 55, 5); mSkyManager = MWRender::SkyManager::create(rend.getWindow(), rend.getCamera(), resDir); + mTerrainManager = new TerrainManager(rend.getScene()); // Set default mipmap level (NB some APIs ignore this) TextureManager::getSingleton().setDefaultNumMipmaps(5); @@ -59,6 +60,7 @@ RenderingManager::~RenderingManager () { delete mPlayer; delete mSkyManager; + delete mTerrainManager; } MWRender::Npcs& RenderingManager::getNPCs(){ @@ -76,11 +78,13 @@ MWRender::Player& RenderingManager::getPlayer(){ void RenderingManager::removeCell (MWWorld::Ptr::CellStore *store){ objects.removeCell(store); + mTerrainManager->cellRemoved(store); } void RenderingManager::cellAdded (MWWorld::Ptr::CellStore *store) { objects.buildStaticGeometry (*store); + mTerrainManager->cellAdded(store); } void RenderingManager::addObject (const MWWorld::Ptr& ptr){ diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 8d8c98232..5afbd9b78 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -3,6 +3,7 @@ #include "sky.hpp" +#include "terrain.hpp" #include "debugging.hpp" #include "../mwworld/class.hpp" @@ -97,6 +98,7 @@ class RenderingManager: private RenderingInterface { void setAmbientMode(); SkyManager* mSkyManager; + TerrainManager* mTerrainManager; OEngine::Render::OgreRenderer &rend; Ogre::Camera* camera; MWRender::Npcs npcs; diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp new file mode 100644 index 000000000..44c499130 --- /dev/null +++ b/apps/openmw/mwrender/terrain.cpp @@ -0,0 +1,62 @@ +#include +#include + +#include "terrain.hpp" + +#include "components/esm/loadland.hpp" + +namespace MWRender +{ + TerrainManager::TerrainManager(Ogre::SceneManager* mgr) + { + mTerrainGlobals = OGRE_NEW Ogre::TerrainGlobalOptions(); + + mTerrainGlobals->setMaxPixelError(8); + + mTerrainGroup = OGRE_NEW Ogre::TerrainGroup(mgr, + Ogre::Terrain::ALIGN_X_Z, ESM::Land::LAND_SIZE, + ESM::Land::REAL_SIZE); + + mTerrainGroup->setOrigin(Ogre::Vector3(ESM::Land::REAL_SIZE/2, + 0, + -ESM::Land::REAL_SIZE/2)); + + Ogre::Terrain::ImportData importSettings = + mTerrainGroup->getDefaultImportSettings(); + + importSettings.terrainSize = ESM::Land::LAND_SIZE; + importSettings.worldSize = ESM::Land::REAL_SIZE; + importSettings.minBatchSize = 9; + importSettings.maxBatchSize = 33; + + importSettings.deleteInputData = false; + } + + 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(); + + Ogre::Terrain::ImportData terrainData; + + terrainData.inputBias = 0; + terrainData.inputFloat = store->land->landData->heights; + + mTerrainGroup->defineTerrain(x, y, &terrainData); + + mTerrainGroup->loadTerrain(x, y, true); + } + + void TerrainManager::cellRemoved(MWWorld::Ptr::CellStore *store) + { + mTerrainGroup->removeTerrain(store->cell->getGridX(), + store->cell->getGridY()); + } + +} diff --git a/apps/openmw/mwrender/terrain.hpp b/apps/openmw/mwrender/terrain.hpp new file mode 100644 index 000000000..0a8b1ace0 --- /dev/null +++ b/apps/openmw/mwrender/terrain.hpp @@ -0,0 +1,31 @@ +#ifndef _GAME_RENDER_TERRAIN_H +#define _GAME_RENDER_TERRAIN_H + +#include "../mwworld/ptr.hpp" + +namespace Ogre{ + class SceneManager; + class TerrainGroup; + class TerrainGlobalOptions; +} + +namespace MWRender{ + + /** + * Implements the Morrowind terrain using the Ogre Terrain Component + */ + class TerrainManager{ + public: + TerrainManager(Ogre::SceneManager*); + virtual ~TerrainManager(); + + void cellAdded(MWWorld::Ptr::CellStore* store); + void cellRemoved(MWWorld::Ptr::CellStore* store); + private: + Ogre::TerrainGlobalOptions* mTerrainGlobals; + Ogre::TerrainGroup* mTerrainGroup; + }; + +} + +#endif // _GAME_RENDER_TERRAIN_H diff --git a/cmake/FindOGRE.cmake b/cmake/FindOGRE.cmake index ce3993805..c3b471bb2 100644 --- a/cmake/FindOGRE.cmake +++ b/cmake/FindOGRE.cmake @@ -96,10 +96,25 @@ IF (OGRE_INCLUDE_DIR AND OGRE_LIBRARIES) ENDIF (OGRE_INCLUDE_DIR AND OGRE_LIBRARIES) IF (OGRE_FOUND) + # find terrain component + find_path(OGRE_Terrain_INCLUDE_DIR NAMES OgreTerrain.h HINTS ${OGRE_INCLUDE_DIR} PATH_SUFFIXES Terrain Components/Terrain/include) + set(OGRE_Terrain_LIBRARY_NAMES "OgreTerrain") + find_library(OGRE_Terrain_LIBRARY NAMES ${OGRE_Terrain_LIBRARY_NAMES} HINTS ${OGRE_LIB_DIR} PATH_SUFFIXES "" "release" "relwithdebinfo" "minsizerel") + if(OGRE_Terrain_INCLUDE_DIR AND OGRE_Terrain_LIBRARY) + SET(OGRE_Terrain_FOUND TRUE) + endif(OGRE_Terrain_INCLUDE_DIR AND OGRE_Terrain_LIBRARY) + IF (NOT OGRE_FIND_QUIETLY) MESSAGE(STATUS " libraries : ${OGRE_LIBRARIES} from ${OGRE_LIB_DIR}") MESSAGE(STATUS " includes : ${OGRE_INCLUDE_DIR}") MESSAGE(STATUS " plugins : ${OGRE_PLUGIN_DIR}") + IF (OGRE_Terrain_FOUND) + MESSAGE(STATUS "Ogre Terrain component found:") + MESSAGE(STATUS " include : ${OGRE_Terrain_INCLUDE_DIR}") + MESSAGE(STATUS " library : ${OGRE_Terrain_LIBRARY}") + ELSE (OGRE_Terrain_FOUND) + MESSAGE(FATAL_ERROR "Required Ogre terrain component not found") + ENDIF (OGRE_Terrain_FOUND) ENDIF (NOT OGRE_FIND_QUIETLY) ELSE (OGRE_FOUND) IF (OGRE_FIND_REQUIRED) diff --git a/components/esm/loadcell.hpp b/components/esm/loadcell.hpp index 671f702ca..a5d61f930 100644 --- a/components/esm/loadcell.hpp +++ b/components/esm/loadcell.hpp @@ -119,6 +119,21 @@ struct Cell void load(ESMReader &esm); + bool isExterior() const + { + return !(data.flags & Interior); + } + + int getGridX() const + { + return data.gridX; + } + + int getGridY() const + { + return data.gridY; + } + // Restore the given reader to the stored position. Will try to open // the file matching the stored file name. If you want to read from // somewhere other than the file system, you need to pre-open the diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index 740d15a40..b8de98f0a 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -19,14 +19,93 @@ void Land::load(ESMReader &esm) int cnt = 0; // Skip these here. Load the actual data when the cell is loaded. - if(esm.isNextSub("VNML")) {esm.skipHSubSize(12675);cnt++;} - if(esm.isNextSub("VHGT")) {esm.skipHSubSize(4232);cnt++;} - if(esm.isNextSub("WNAM")) esm.skipHSubSize(81); - if(esm.isNextSub("VCLR")) esm.skipHSubSize(12675); - if(esm.isNextSub("VTEX")) {esm.skipHSubSize(512);cnt++;} + if (esm.isNextSub("VNML")) + { + esm.skipHSubSize(12675); + cnt++; + } + if (esm.isNextSub("VHGT")) + { + esm.skipHSubSize(4232); + cnt++; + } + if (esm.isNextSub("WNAM")) + { + esm.skipHSubSize(81); + } + if (esm.isNextSub("VCLR")) + { + esm.skipHSubSize(12675); + } + if (esm.isNextSub("VTEX")) + { + esm.skipHSubSize(512); + cnt++; + } // We need all three of VNML, VHGT and VTEX in order to use the // landscape. hasData = (cnt == 3); + + dataLoaded = false; + landData = NULL; } + +void Land::loadData(ESMReader &esm) +{ + if (dataLoaded) + { + return; + } + + landData = new LandData; + + if (hasData) + { + esm.restoreContext(context); + + //esm.getHNExact(landData->normals, sizeof(VNML), "VNML"); + if (esm.isNextSub("VNML")) + { + esm.skipHSubSize(12675); + } + + VHGT rawHeights; + + esm.getHNExact(&rawHeights, sizeof(VHGT), "VHGT"); + int currentHeightOffset = rawHeights.heightOffset; + for (int y = 0; y < LAND_SIZE; y++) + { + currentHeightOffset += rawHeights.heightData[y * LAND_SIZE]; + landData->heights[y * LAND_SIZE] = currentHeightOffset * HEIGHT_SCALE; + + int tempOffset = currentHeightOffset; + for (int x = 1; x < LAND_SIZE; x++) + { + tempOffset += rawHeights.heightData[y * LAND_SIZE + x]; + landData->heights[x + y * LAND_SIZE] = tempOffset * HEIGHT_SCALE; + } + } + } + else + { + for (int i = 0; i < LAND_NUM_VERTS; i++) + { + landData->heights[i] = -256.0f * HEIGHT_SCALE; + } + } + + dataLoaded = true; +} + +void Land::unloadData() +{ + if (dataLoaded) + { + delete landData; + landData = NULL; + dataLoaded = false; + } +} + } diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index af91850ac..898e7f529 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -21,7 +21,51 @@ struct Land bool hasData; + bool dataLoaded; + + // number of vertices per side + static const int LAND_SIZE = 65; + + // cell terrain size in world coords + static const int REAL_SIZE = 8192; + + // total number of vertices + static const int LAND_NUM_VERTS = LAND_SIZE * LAND_SIZE; + + static const int HEIGHT_SCALE = 8; + +#pragma pack(push,1) + struct VHGT + { + float heightOffset; + int8_t heightData[LAND_NUM_VERTS]; + short unknown1; + char unknown2; + }; +#pragma pack(pop) + + typedef uint8_t VNML[LAND_NUM_VERTS * 3]; + + struct LandData + { + float heightOffset; + float heights[LAND_NUM_VERTS]; + //float normals[LAND_NUM_VERTS * 3]; + }; + + LandData *landData; + void load(ESMReader &esm); + + /** + * Actually loads data + */ + void loadData(ESMReader &esm); + + /** + * Frees memory allocated for land data + */ + void unloadData(); }; } #endif diff --git a/components/esm_store/cell_store.hpp b/components/esm_store/cell_store.hpp index d064312f1..7c2ee48fb 100644 --- a/components/esm_store/cell_store.hpp +++ b/components/esm_store/cell_store.hpp @@ -96,7 +96,8 @@ namespace ESMS State_Unloaded, State_Preloaded, State_Loaded }; - CellStore (const ESM::Cell *cell_) : cell (cell_), mState (State_Unloaded) {} + CellStore (const ESM::Cell *cell_) : cell (cell_), mState (State_Unloaded), + land(NULL) {} const ESM::Cell *cell; State mState; @@ -124,6 +125,8 @@ namespace ESMS CellRefList statics; CellRefList weapons; + const Land* land; + void load (const ESMStore &store, ESMReader &esm) { if (mState!=State_Loaded) @@ -135,6 +138,11 @@ namespace ESMS loadRefs (store, esm); + if ( ! (cell->data.flags & ESM::Cell::Interior) ) + { + loadTerrain(cell->data.gridX, cell->data.gridY, store, esm); + } + mState = State_Loaded; } } @@ -180,6 +188,29 @@ namespace ESMS private: + void loadTerrain(int X, int Y, const ESMStore &store, ESMReader &esm) + { + // load terrain + Land *land = store.lands.search(X, Y); + if (land != NULL) + { + land->loadData(esm); + } + + this->land = 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; + } + template bool forEachImp (Functor& functor, List& list) { diff --git a/components/esm_store/reclists.hpp b/components/esm_store/reclists.hpp index 20a2e8ff9..e150f1085 100644 --- a/components/esm_store/reclists.hpp +++ b/components/esm_store/reclists.hpp @@ -235,7 +235,7 @@ namespace ESMS virtual void listIdentifier (std::vector& identifier) const {} // Find land for the given coordinates. Return null if no data. - const Land *search(int x, int y) const + Land *search(int x, int y) const { Lands::const_iterator it = lands.find(x); if(it==lands.end()) diff --git a/components/esm_store/store.hpp b/components/esm_store/store.hpp index e3bbf9e82..83cd5ab16 100644 --- a/components/esm_store/store.hpp +++ b/components/esm_store/store.hpp @@ -115,7 +115,7 @@ namespace ESMS recLists[REC_GLOB] = &globals; recLists[REC_GMST] = &gameSettings; recLists[REC_INGR] = &ingreds; - //recLists[REC_LAND] = &lands; + recLists[REC_LAND] = &lands; recLists[REC_LEVC] = &creatureLists; recLists[REC_LEVI] = &itemLists; recLists[REC_LIGH] = &lights; diff --git a/files/plugins.cfg.linux b/files/plugins.cfg.linux index b6e104351..2921153b4 100644 --- a/files/plugins.cfg.linux +++ b/files/plugins.cfg.linux @@ -7,6 +7,6 @@ PluginFolder=${OGRE_PLUGIN_DIR} Plugin=RenderSystem_GL Plugin=Plugin_ParticleFX Plugin=Plugin_OctreeSceneManager -# Plugin=Plugin_CgProgramManager +Plugin=Plugin_CgProgramManager diff --git a/files/plugins.cfg.mac b/files/plugins.cfg.mac index baaca4479..0c16bddaf 100644 --- a/files/plugins.cfg.mac +++ b/files/plugins.cfg.mac @@ -7,6 +7,6 @@ PluginFolder= Plugin=RenderSystem_GL.dylib Plugin=Plugin_ParticleFX.dylib Plugin=Plugin_OctreeSceneManager.dylib -# Plugin=Plugin_CgProgramManager +Plugin=Plugin_CgProgramManager From 637302fc87a85fb3c67574861baef406734a7076 Mon Sep 17 00:00:00 2001 From: Jacob Essex Date: Sat, 21 Jan 2012 17:59:12 +0000 Subject: [PATCH 02/39] Added blended textures to the terrain --- apps/openmw/mwrender/terrain.cpp | 150 ++++++++++++++++++++++++++++ apps/openmw/mwrender/terrain.hpp | 28 ++++++ components/esm/loadland.cpp | 19 ++++ components/esm/loadland.hpp | 8 ++ components/esm_store/cell_store.hpp | 2 + 5 files changed, 207 insertions(+) diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index 44c499130..4d13b6889 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -48,9 +48,13 @@ namespace MWRender terrainData.inputBias = 0; terrainData.inputFloat = store->land->landData->heights; + std::map 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& 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::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& indexes) + { + const int blendSize = terrain->getLayerBlendMapSize(); + const int blendDist = TERRAIN_SHADE_DISTANCE * + (blendSize / ESM::Land::LAND_TEXTURE_SIZE); + + //zero out every map + std::map::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(); + } + + } + } diff --git a/apps/openmw/mwrender/terrain.hpp b/apps/openmw/mwrender/terrain.hpp index 0a8b1ace0..127c3f5f2 100644 --- a/apps/openmw/mwrender/terrain.hpp +++ b/apps/openmw/mwrender/terrain.hpp @@ -1,12 +1,15 @@ #ifndef _GAME_RENDER_TERRAIN_H #define _GAME_RENDER_TERRAIN_H +#include + #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& 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& indexes); }; } diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index b8de98f0a..1d670036e 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -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 { diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index 898e7f529..4219f3eb6 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -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; diff --git a/components/esm_store/cell_store.hpp b/components/esm_store/cell_store.hpp index 7c2ee48fb..5310237fe 100644 --- a/components/esm_store/cell_store.hpp +++ b/components/esm_store/cell_store.hpp @@ -126,6 +126,7 @@ namespace ESMS CellRefList 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; From 1b1ce485024ddca13ad354ff4ee38c349dc00222 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Sun, 22 Jan 2012 20:20:12 +0400 Subject: [PATCH 03/39] Now terrain manager donesn't try to load terrain for interiors --- apps/openmw/mwrender/renderingmanager.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index b0b7c88c4..471ce5c41 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -78,13 +78,15 @@ MWRender::Player& RenderingManager::getPlayer(){ void RenderingManager::removeCell (MWWorld::Ptr::CellStore *store){ objects.removeCell(store); - mTerrainManager->cellRemoved(store); + if (store->cell->isExterior()) + mTerrainManager->cellRemoved(store); } void RenderingManager::cellAdded (MWWorld::Ptr::CellStore *store) { objects.buildStaticGeometry (*store); - mTerrainManager->cellAdded(store); + if (store->cell->isExterior()) + mTerrainManager->cellAdded(store); } void RenderingManager::addObject (const MWWorld::Ptr& ptr){ From cd0df082df5c83cca1170651d7b74068e701d146 Mon Sep 17 00:00:00 2001 From: Jacob Essex Date: Mon, 23 Jan 2012 17:19:54 +0000 Subject: [PATCH 04/39] Textures are now blended between cells and the texture blending functions are now more abstract --- apps/openmw/mwrender/terrain.cpp | 103 ++++++++++++++++++++++++---- apps/openmw/mwrender/terrain.hpp | 28 +++++++- components/esm_store/cell_store.hpp | 32 +++++---- 3 files changed, 133 insertions(+), 30 deletions(-) diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index 4d13b6889..90dc91731 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 127c3f5f2..c64b741d5 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 5310237fe..951b0736a 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 From 5e3e6f916562ddeb8a7e3928551a0a810718e232 Mon Sep 17 00:00:00 2001 From: Jacob Essex Date: Tue, 24 Jan 2012 13:29:31 +0000 Subject: [PATCH 05/39] Fixed some minor bugs, a cells terrain can now be rendered as 4 Ogre::Terrain objects, possibly giving a speed increase --- apps/openmw/mwrender/terrain.cpp | 184 +++++++++++++++++++++++++------ apps/openmw/mwrender/terrain.hpp | 30 +++++ 2 files changed, 179 insertions(+), 35 deletions(-) diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index 90dc91731..70ecdee73 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -1,5 +1,6 @@ #include #include +#include #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(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 = + mTerrainGroup->getDefaultImportSettings(); + + if ( SPLIT_TERRAIN ) + { + //split the cell terrain into four segments + const int numTextures = ESM::Land::LAND_TEXTURE_SIZE/2; + + 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 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); - Ogre::Terrain::ImportData terrainData; + memcpy(&terrainData.inputFloat[0], + &store->land[1][1]->landData->heights[0], + mLandSize*mLandSize*sizeof(float)); - terrainData.inputBias = 0; - terrainData.inputFloat = store->land[1][1]->landData->heights; + std::map indexes; + initTerrainTextures(&terrainData, store, 0, 0, + ESM::Land::LAND_TEXTURE_SIZE, indexes); - std::map indexes; - initTerrainTextures(&terrainData, store, 0, 0, - ESM::Land::LAND_TEXTURE_SIZE, indexes); - mTerrainGroup->defineTerrain(x, y, &terrainData); + 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) diff --git a/apps/openmw/mwrender/terrain.hpp b/apps/openmw/mwrender/terrain.hpp index c64b741d5..195741b0f 100644 --- a/apps/openmw/mwrender/terrain.hpp +++ b/apps/openmw/mwrender/terrain.hpp @@ -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 From 9cc3af34e5dc8ebd8b90cee9a3e1bd3da48321b0 Mon Sep 17 00:00:00 2001 From: Jacob Essex Date: Tue, 7 Feb 2012 12:41:08 +0000 Subject: [PATCH 06/39] Removed composite maps, fixes to texture sizes and unloading --- apps/openmw/mwrender/terrain.cpp | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index 70ecdee73..a468a4406 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -16,7 +16,20 @@ namespace MWRender mTerrainGlobals = OGRE_NEW Ogre::TerrainGlobalOptions(); mTerrainGlobals->setMaxPixelError(8); - mTerrainGlobals->setLayerBlendMapSize(SPLIT_TERRAIN ? 1024 : 256); + mTerrainGlobals->setLayerBlendMapSize(SPLIT_TERRAIN ? 256 : 1024); + mTerrainGlobals->setLightMapSize(SPLIT_TERRAIN ? 256 : 1024); + mTerrainGlobals->setCompositeMapSize(SPLIT_TERRAIN ? 256 : 1024); + mTerrainGlobals->setDefaultGlobalColourMapSize(256); + + //10 (default) didn't seem to be quite enough + mTerrainGlobals->setSkirtSize(32); + + /* + * Here we are pushing the composite map distance beyond the edge + * of the rendered terrain due to light issues (the lighting differs + * so much that it is very noticable + */ + mTerrainGlobals->setCompositeMapDistance(ESM::Land::REAL_SIZE*4); Ogre::TerrainMaterialGenerator::Profile* const activeProfile = mTerrainGlobals->getDefaultMaterialGenerator() @@ -27,6 +40,7 @@ namespace MWRender matProfile->setLightmapEnabled(false); matProfile->setReceiveDynamicShadowsEnabled(false); + //scale the land size if required mLandSize = ESM::Land::LAND_SIZE; mRealSize = ESM::Land::REAL_SIZE; if ( SPLIT_TERRAIN ) @@ -109,6 +123,8 @@ namespace MWRender x * numTextures, y * numTextures, numTextures, indexes); + assert( mTerrainGroup->getTerrain(cellX, cellY) == NULL && + "The terrain for this cell already existed" ); mTerrainGroup->defineTerrain(terrainX, terrainY, &terrainData); mTerrainGroup->loadTerrain(terrainX, terrainY, true); @@ -143,6 +159,7 @@ namespace MWRender ESM::Land::LAND_TEXTURE_SIZE, indexes); } + mTerrainGroup->freeTemporaryResources(); } //---------------------------------------------------------------------------------------------- @@ -155,14 +172,14 @@ namespace MWRender { for ( int y = 0; y < 2; y++ ) { - mTerrainGroup->removeTerrain(store->cell->getGridX() * 2 + x, + mTerrainGroup->unloadTerrain(store->cell->getGridX() * 2 + x, store->cell->getGridY() * 2 + y); } } } else { - mTerrainGroup->removeTerrain(store->cell->getGridX(), + mTerrainGroup->unloadTerrain(store->cell->getGridX(), store->cell->getGridY()); } } From 286701e3a5ace50f60d60e0f8dcd9078b043f893 Mon Sep 17 00:00:00 2001 From: Jacob Essex Date: Sat, 11 Feb 2012 20:54:29 +0000 Subject: [PATCH 07/39] Removed the flawed shading, updated textures, removed the need for a unused base texture --- apps/openmw/mwrender/terrain.cpp | 146 +++++++++++++++++++------------ apps/openmw/mwrender/terrain.hpp | 13 ++- 2 files changed, 104 insertions(+), 55 deletions(-) diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index a468a4406..710a3ad05 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -22,12 +22,11 @@ namespace MWRender mTerrainGlobals->setDefaultGlobalColourMapSize(256); //10 (default) didn't seem to be quite enough - mTerrainGlobals->setSkirtSize(32); + mTerrainGlobals->setSkirtSize(128); /* * Here we are pushing the composite map distance beyond the edge - * of the rendered terrain due to light issues (the lighting differs - * so much that it is very noticable + * of the rendered terrain due to not having setup lighting */ mTerrainGlobals->setCompositeMapDistance(ESM::Land::REAL_SIZE*4); @@ -38,6 +37,10 @@ namespace MWRender static_cast(activeProfile); matProfile->setLightmapEnabled(false); + matProfile->setLayerSpecularMappingEnabled(false); + + matProfile->setLayerParallaxMappingEnabled(false); + matProfile->setReceiveDynamicShadowsEnabled(false); //scale the land size if required @@ -85,8 +88,6 @@ namespace MWRender const int cellX = store->cell->getGridX(); const int cellY = store->cell->getGridY(); - Ogre::Terrain::ImportData terrainData = - mTerrainGroup->getDefaultImportSettings(); if ( SPLIT_TERRAIN ) { @@ -97,6 +98,9 @@ namespace MWRender { for ( int y = 0; y < 2; y++ ) { + Ogre::Terrain::ImportData terrainData = + mTerrainGroup->getDefaultImportSettings(); + const int terrainX = cellX * 2 + x; const int terrainY = cellY * 2 + y; @@ -138,6 +142,9 @@ namespace MWRender } else { + Ogre::Terrain::ImportData terrainData = + mTerrainGroup->getDefaultImportSettings(); + //one cell is one terrain segment terrainData.inputFloat = OGRE_ALLOC_T(float, mLandSize*mLandSize, @@ -199,19 +206,17 @@ namespace MWRender 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"); - + //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 ( int y = fromY - 1; y < fromY + size + 1; y++ ) { for ( int x = fromX - 1; x < fromX + size + 1; x++ ) { const uint16_t ltexIndex = getLtexIndexAt(store, x, y); //this is the base texture, so we can ignore this at present - if ( ltexIndex == 0 ) + if ( ltexIndex == baseTexture ) { continue; } @@ -225,21 +230,35 @@ namespace MWRender 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"; + 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()); - terrainData->layerList[position].worldSize = 256; - terrainData->layerList[position].textureNames.push_back("textures\\" + texture); + Ogre::TexturePtr normDisp = getNormalDisp("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].worldSize = 256; terrainData->layerList[position].textureNames.push_back("textures\\" + texture); + terrainData->layerList[position].textureNames.push_back(normDisp->getName()); - indexes[ltexIndex] = position; + if ( baseTexture == -1 ) + { + baseTexture = ltexIndex; + } + else + { + indexes[ltexIndex] = position; + } } } } @@ -271,9 +290,9 @@ namespace MWRender std::map::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); + float* pBlend = terrain->getLayerBlendMap(iter->second) + ->getBlendPointer(); + memset(pBlend, 0, sizeof(float) * blendSize * blendSize); } //covert the ltex data into a set of blend maps @@ -288,15 +307,13 @@ namespace MWRender 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 ){ + //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 ( indexes.find(ltexIndex) == indexes.end() ) + { continue; } - assert (indexes.find(ltexIndex) != indexes.end() && - "Texture layer must exist"); - const int layerIndex = indexes.find(ltexIndex)->second; float* const pBlend = terrain->getLayerBlendMap(layerIndex) @@ -310,7 +327,7 @@ namespace MWRender 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++ ) { for ( int blendY = startY; blendY < endY; blendY++ ) @@ -321,34 +338,16 @@ namespace MWRender 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 = relX*splatSize - blendX; - if ( distX < 0 ) + const int index = blendSize*(blendSize - 1 - blendY) + blendX; + if ( blendX >= relX*splatSize && blendX < (relX+1)*splatSize && + blendY >= relY*splatSize && blendY < (relY+1)*splatSize ) { - distX = std::max(0, blendX - (relX+1)*splatSize); + pBlend[index] = 1; } - - int distY = relY*splatSize - blendY; - if ( distY < 0 ) + else { - distY = std::max(0, blendY - (relY+1)*splatSize); + pBlend[index] = std::max((float)pBlend[index], 0.5f); } - - 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]); } } @@ -413,4 +412,43 @@ namespace MWRender ->textures[y * ESM::Land::LAND_TEXTURE_SIZE + x]; } + //---------------------------------------------------------------------------------------------- + + Ogre::TexturePtr TerrainManager::getNormalDisp(const std::string& fileName) + { + Ogre::TextureManager* const texMgr = Ogre::TextureManager::getSingletonPtr(); + const std::string normalTextureName = fileName.substr(0, fileName.rfind(".")) + + "_n.dds"; + if ( !texMgr->getByName(normalTextureName).isNull() ) + { + return texMgr->getByName(normalTextureName); + } + + const std::string textureName = "default_terrain_normal"; + if ( !texMgr->getByName(textureName).isNull() ) + { + return texMgr->getByName(textureName); + } + + Ogre::TexturePtr tex = texMgr->createManual( + textureName, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + Ogre::TEX_TYPE_2D, 1, 1, 0, Ogre::PF_BYTE_BGRA); + + Ogre::HardwarePixelBufferSharedPtr pixelBuffer = tex->getBuffer(); + + pixelBuffer->lock(Ogre::HardwareBuffer::HBL_NORMAL); + const Ogre::PixelBox& pixelBox = pixelBuffer->getCurrentLock(); + + Ogre::uint8* pDest = static_cast(pixelBox.data); + + *pDest++ = 128; // B + *pDest++ = 128; // G + *pDest++ = 128; // R + *pDest++ = 0; // A + + pixelBuffer->unlock(); + + return tex; + } + } diff --git a/apps/openmw/mwrender/terrain.hpp b/apps/openmw/mwrender/terrain.hpp index 195741b0f..548d00eab 100644 --- a/apps/openmw/mwrender/terrain.hpp +++ b/apps/openmw/mwrender/terrain.hpp @@ -62,7 +62,7 @@ namespace MWRender{ * 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; + static const float TERRAIN_SHADE_DISTANCE = 0.25f; /** * Setups up the list of textures for part of a cell, using indexes as @@ -107,6 +107,17 @@ namespace MWRender{ * first splat of the current cell */ int getLtexIndexAt(MWWorld::Ptr::CellStore* store, int x, int y); + + /** + * Retrives the texture that is the normal and parallax map for the + * terrain. If it doesn't exist a blank texture is used + * + * The file name of the texture should be the same as the file name of + * the base diffuse texture, but with _n appended before the extension + * + * @param fileName the name of the *diffuse* texture + */ + Ogre::TexturePtr getNormalDisp(const std::string& fileName); }; From f34b2c73c5f81f15a02ee9a4fe878ff005ff4e37 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 27 Feb 2012 19:44:52 +0100 Subject: [PATCH 08/39] committing the terrain material generator from ogre 1.7.4, it seems to be almost impossible to make a derived class of TerrainMaterialGeneratorA because of the many classes it contains and the inter-relationships between them; just adding the whole source makes it a lot easier to modify if we decide to update this source from OGRE upstream at any point (which seems unlikely), we can take the diff from this commit on to see the changes we did to the material generator --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwrender/terrain.cpp | 18 +- apps/openmw/mwrender/terrainmaterial.cpp | 1656 ++++++++++++++++++++++ apps/openmw/mwrender/terrainmaterial.hpp | 261 ++++ 4 files changed, 1932 insertions(+), 5 deletions(-) create mode 100644 apps/openmw/mwrender/terrainmaterial.cpp create mode 100644 apps/openmw/mwrender/terrainmaterial.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 663a10787..8ee865a0b 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -16,7 +16,7 @@ set(GAME_HEADER source_group(game FILES ${GAME} ${GAME_HEADER}) add_openmw_dir (mwrender - renderingmanager debugging sky terrain player npcs creatures objects renderinginterface + renderingmanager debugging sky terrain terrainmaterial player npcs creatures objects renderinginterface ) add_openmw_dir (mwinput diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index 70ecdee73..568ed3741 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -1,11 +1,13 @@ #include #include -#include +#include "terrainmaterial.hpp" #include "terrain.hpp" #include "components/esm/loadland.hpp" +using namespace Ogre; + namespace MWRender { @@ -18,15 +20,23 @@ namespace MWRender mTerrainGlobals->setMaxPixelError(8); mTerrainGlobals->setLayerBlendMapSize(SPLIT_TERRAIN ? 1024 : 256); + Ogre::TerrainMaterialGeneratorPtr matGen; + TerrainMaterialGeneratorB* matGenP = new TerrainMaterialGeneratorB(); + matGen.bind(matGenP); + mTerrainGlobals->setDefaultMaterialGenerator(matGen); + Ogre::TerrainMaterialGenerator::Profile* const activeProfile = mTerrainGlobals->getDefaultMaterialGenerator() ->getActiveProfile(); - Ogre::TerrainMaterialGeneratorA::SM2Profile* matProfile = - static_cast(activeProfile); + TerrainMaterialGeneratorB::SM2Profile* matProfile = + static_cast(activeProfile); matProfile->setLightmapEnabled(false); matProfile->setReceiveDynamicShadowsEnabled(false); - + matProfile->setLayerNormalMappingEnabled(false); + //matProfile->setLayerParallaxMappingEnabled(false); + matProfile->setLayerSpecularMappingEnabled(false); + mLandSize = ESM::Land::LAND_SIZE; mRealSize = ESM::Land::REAL_SIZE; if ( SPLIT_TERRAIN ) diff --git a/apps/openmw/mwrender/terrainmaterial.cpp b/apps/openmw/mwrender/terrainmaterial.cpp new file mode 100644 index 000000000..313a4a90c --- /dev/null +++ b/apps/openmw/mwrender/terrainmaterial.cpp @@ -0,0 +1,1656 @@ +/* +----------------------------------------------------------------------------- +This source file is part of OGRE +(Object-oriented Graphics Rendering Engine) +For the latest info, see http://www.ogre3d.org/ + +Copyright (c) 2000-2011 Torus Knot Software Ltd + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +----------------------------------------------------------------------------- +*/ +#include "terrainmaterial.hpp" +#include "OgreTerrain.h" +#include "OgreMaterialManager.h" +#include "OgreTechnique.h" +#include "OgrePass.h" +#include "OgreTextureUnitState.h" +#include "OgreGpuProgramManager.h" +#include "OgreHighLevelGpuProgramManager.h" +#include "OgreHardwarePixelBuffer.h" +#include "OgreShadowCameraSetupPSSM.h" + +namespace Ogre +{ + //--------------------------------------------------------------------- + TerrainMaterialGeneratorB::TerrainMaterialGeneratorB() + { + // define the layers + // We expect terrain textures to have no alpha, so we use the alpha channel + // in the albedo texture to store specular reflection + // similarly we double-up the normal and height (for parallax) + mLayerDecl.samplers.push_back(TerrainLayerSampler("albedo_specular", PF_BYTE_RGBA)); + mLayerDecl.samplers.push_back(TerrainLayerSampler("normal_height", PF_BYTE_RGBA)); + + mLayerDecl.elements.push_back( + TerrainLayerSamplerElement(0, TLSS_ALBEDO, 0, 3)); + mLayerDecl.elements.push_back( + TerrainLayerSamplerElement(0, TLSS_SPECULAR, 3, 1)); + mLayerDecl.elements.push_back( + TerrainLayerSamplerElement(1, TLSS_NORMAL, 0, 3)); + mLayerDecl.elements.push_back( + TerrainLayerSamplerElement(1, TLSS_HEIGHT, 3, 1)); + + + mProfiles.push_back(OGRE_NEW SM2Profile(this, "SM2", "Profile for rendering on Shader Model 2 capable cards")); + // TODO - check hardware capabilities & use fallbacks if required (more profiles needed) + setActiveProfile("SM2"); + + } + //--------------------------------------------------------------------- + TerrainMaterialGeneratorB::~TerrainMaterialGeneratorB() + { + + } + //--------------------------------------------------------------------- + //--------------------------------------------------------------------- + TerrainMaterialGeneratorB::SM2Profile::SM2Profile(TerrainMaterialGenerator* parent, const String& name, const String& desc) + : Profile(parent, name, desc) + , mShaderGen(0) + , mLayerNormalMappingEnabled(true) + , mLayerParallaxMappingEnabled(true) + , mLayerSpecularMappingEnabled(true) + , mGlobalColourMapEnabled(true) + , mLightmapEnabled(true) + , mCompositeMapEnabled(true) + , mReceiveDynamicShadows(true) + , mPSSM(0) + , mDepthShadows(false) + , mLowLodShadows(false) + { + + } + //--------------------------------------------------------------------- + TerrainMaterialGeneratorB::SM2Profile::~SM2Profile() + { + OGRE_DELETE mShaderGen; + } + //--------------------------------------------------------------------- + void TerrainMaterialGeneratorB::SM2Profile::requestOptions(Terrain* terrain) + { + terrain->_setMorphRequired(true); + terrain->_setNormalMapRequired(true); + terrain->_setLightMapRequired(mLightmapEnabled, true); + terrain->_setCompositeMapRequired(mCompositeMapEnabled); + } + //--------------------------------------------------------------------- + void TerrainMaterialGeneratorB::SM2Profile::setLayerNormalMappingEnabled(bool enabled) + { + if (enabled != mLayerNormalMappingEnabled) + { + mLayerNormalMappingEnabled = enabled; + mParent->_markChanged(); + } + } + //--------------------------------------------------------------------- + void TerrainMaterialGeneratorB::SM2Profile::setLayerParallaxMappingEnabled(bool enabled) + { + if (enabled != mLayerParallaxMappingEnabled) + { + mLayerParallaxMappingEnabled = enabled; + mParent->_markChanged(); + } + } + //--------------------------------------------------------------------- + void TerrainMaterialGeneratorB::SM2Profile::setLayerSpecularMappingEnabled(bool enabled) + { + if (enabled != mLayerSpecularMappingEnabled) + { + mLayerSpecularMappingEnabled = enabled; + mParent->_markChanged(); + } + } + //--------------------------------------------------------------------- + void TerrainMaterialGeneratorB::SM2Profile::setGlobalColourMapEnabled(bool enabled) + { + if (enabled != mGlobalColourMapEnabled) + { + mGlobalColourMapEnabled = enabled; + mParent->_markChanged(); + } + } + //--------------------------------------------------------------------- + void TerrainMaterialGeneratorB::SM2Profile::setLightmapEnabled(bool enabled) + { + if (enabled != mLightmapEnabled) + { + mLightmapEnabled = enabled; + mParent->_markChanged(); + } + } + //--------------------------------------------------------------------- + void TerrainMaterialGeneratorB::SM2Profile::setCompositeMapEnabled(bool enabled) + { + if (enabled != mCompositeMapEnabled) + { + mCompositeMapEnabled = enabled; + mParent->_markChanged(); + } + } + //--------------------------------------------------------------------- + void TerrainMaterialGeneratorB::SM2Profile::setReceiveDynamicShadowsEnabled(bool enabled) + { + if (enabled != mReceiveDynamicShadows) + { + mReceiveDynamicShadows = enabled; + mParent->_markChanged(); + } + } + //--------------------------------------------------------------------- + void TerrainMaterialGeneratorB::SM2Profile::setReceiveDynamicShadowsPSSM(PSSMShadowCameraSetup* pssmSettings) + { + if (pssmSettings != mPSSM) + { + mPSSM = pssmSettings; + mParent->_markChanged(); + } + } + //--------------------------------------------------------------------- + void TerrainMaterialGeneratorB::SM2Profile::setReceiveDynamicShadowsDepth(bool enabled) + { + if (enabled != mDepthShadows) + { + mDepthShadows = enabled; + mParent->_markChanged(); + } + + } + //--------------------------------------------------------------------- + void TerrainMaterialGeneratorB::SM2Profile::setReceiveDynamicShadowsLowLod(bool enabled) + { + if (enabled != mLowLodShadows) + { + mLowLodShadows = enabled; + mParent->_markChanged(); + } + } + //--------------------------------------------------------------------- + uint8 TerrainMaterialGeneratorB::SM2Profile::getMaxLayers(const Terrain* terrain) const + { + // count the texture units free + uint8 freeTextureUnits = 16; + // lightmap + --freeTextureUnits; + // normalmap + --freeTextureUnits; + // colourmap + if (terrain->getGlobalColourMapEnabled()) + --freeTextureUnits; + if (isShadowingEnabled(HIGH_LOD, terrain)) + { + uint numShadowTextures = 1; + if (getReceiveDynamicShadowsPSSM()) + { + numShadowTextures = getReceiveDynamicShadowsPSSM()->getSplitCount(); + } + freeTextureUnits -= numShadowTextures; + } + + // each layer needs 2.25 units (1xdiffusespec, 1xnormalheight, 0.25xblend) + return static_cast(freeTextureUnits / 2.25f); + + + } + //--------------------------------------------------------------------- + MaterialPtr TerrainMaterialGeneratorB::SM2Profile::generate(const Terrain* terrain) + { + // re-use old material if exists + MaterialPtr mat = terrain->_getMaterial(); + if (mat.isNull()) + { + MaterialManager& matMgr = MaterialManager::getSingleton(); + + // it's important that the names are deterministic for a given terrain, so + // use the terrain pointer as an ID + const String& matName = terrain->getMaterialName(); + mat = matMgr.getByName(matName); + if (mat.isNull()) + { + mat = matMgr.create(matName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); + } + } + // clear everything + mat->removeAllTechniques(); + + // Automatically disable normal & parallax mapping if card cannot handle it + // We do this rather than having a specific technique for it since it's simpler + GpuProgramManager& gmgr = GpuProgramManager::getSingleton(); + if (!gmgr.isSyntaxSupported("ps_3_0") && !gmgr.isSyntaxSupported("ps_2_x") + && !gmgr.isSyntaxSupported("fp40") && !gmgr.isSyntaxSupported("arbfp1")) + { + setLayerNormalMappingEnabled(false); + setLayerParallaxMappingEnabled(false); + } + + addTechnique(mat, terrain, HIGH_LOD); + + // LOD + if(mCompositeMapEnabled) + { + addTechnique(mat, terrain, LOW_LOD); + Material::LodValueList lodValues; + lodValues.push_back(TerrainGlobalOptions::getSingleton().getCompositeMapDistance()); + mat->setLodLevels(lodValues); + Technique* lowLodTechnique = mat->getTechnique(1); + lowLodTechnique->setLodIndex(1); + } + + updateParams(mat, terrain); + + return mat; + + } + //--------------------------------------------------------------------- + MaterialPtr TerrainMaterialGeneratorB::SM2Profile::generateForCompositeMap(const Terrain* terrain) + { + // re-use old material if exists + MaterialPtr mat = terrain->_getCompositeMapMaterial(); + if (mat.isNull()) + { + MaterialManager& matMgr = MaterialManager::getSingleton(); + + // it's important that the names are deterministic for a given terrain, so + // use the terrain pointer as an ID + const String& matName = terrain->getMaterialName() + "/comp"; + mat = matMgr.getByName(matName); + if (mat.isNull()) + { + mat = matMgr.create(matName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); + } + } + // clear everything + mat->removeAllTechniques(); + + addTechnique(mat, terrain, RENDER_COMPOSITE_MAP); + + updateParamsForCompositeMap(mat, terrain); + + return mat; + + } + //--------------------------------------------------------------------- + void TerrainMaterialGeneratorB::SM2Profile::addTechnique( + const MaterialPtr& mat, const Terrain* terrain, TechniqueType tt) + { + + Technique* tech = mat->createTechnique(); + + // Only supporting one pass + Pass* pass = tech->createPass(); + + GpuProgramManager& gmgr = GpuProgramManager::getSingleton(); + HighLevelGpuProgramManager& hmgr = HighLevelGpuProgramManager::getSingleton(); + if (!mShaderGen) + { + bool check2x = mLayerNormalMappingEnabled || mLayerParallaxMappingEnabled; + if (hmgr.isLanguageSupported("cg")) + mShaderGen = OGRE_NEW ShaderHelperCg(); + else if (hmgr.isLanguageSupported("hlsl") && + ((check2x && gmgr.isSyntaxSupported("ps_2_x")) || + (!check2x && gmgr.isSyntaxSupported("ps_2_0")))) + mShaderGen = OGRE_NEW ShaderHelperHLSL(); + else if (hmgr.isLanguageSupported("glsl")) + mShaderGen = OGRE_NEW ShaderHelperGLSL(); + else + { + // todo + } + + // check SM3 features + mSM3Available = GpuProgramManager::getSingleton().isSyntaxSupported("ps_3_0"); + + } + HighLevelGpuProgramPtr vprog = mShaderGen->generateVertexProgram(this, terrain, tt); + HighLevelGpuProgramPtr fprog = mShaderGen->generateFragmentProgram(this, terrain, tt); + + pass->setVertexProgram(vprog->getName()); + pass->setFragmentProgram(fprog->getName()); + + if (tt == HIGH_LOD || tt == RENDER_COMPOSITE_MAP) + { + // global normal map + TextureUnitState* tu = pass->createTextureUnitState(); + tu->setTextureName(terrain->getTerrainNormalMap()->getName()); + tu->setTextureAddressingMode(TextureUnitState::TAM_CLAMP); + + // global colour map + if (terrain->getGlobalColourMapEnabled() && isGlobalColourMapEnabled()) + { + tu = pass->createTextureUnitState(terrain->getGlobalColourMap()->getName()); + tu->setTextureAddressingMode(TextureUnitState::TAM_CLAMP); + } + + // light map + if (isLightmapEnabled()) + { + tu = pass->createTextureUnitState(terrain->getLightmap()->getName()); + tu->setTextureAddressingMode(TextureUnitState::TAM_CLAMP); + } + + // blend maps + uint maxLayers = getMaxLayers(terrain); + uint numBlendTextures = std::min(terrain->getBlendTextureCount(maxLayers), terrain->getBlendTextureCount()); + uint numLayers = std::min(maxLayers, static_cast(terrain->getLayerCount())); + for (uint i = 0; i < numBlendTextures; ++i) + { + tu = pass->createTextureUnitState(terrain->getBlendTextureName(i)); + tu->setTextureAddressingMode(TextureUnitState::TAM_CLAMP); + } + + // layer textures + for (uint i = 0; i < numLayers; ++i) + { + // diffuse / specular + tu = pass->createTextureUnitState(terrain->getLayerTextureName(i, 0)); + // normal / height + tu = pass->createTextureUnitState(terrain->getLayerTextureName(i, 1)); + } + + } + else + { + // LOW_LOD textures + // composite map + TextureUnitState* tu = pass->createTextureUnitState(); + tu->setTextureName(terrain->getCompositeMap()->getName()); + tu->setTextureAddressingMode(TextureUnitState::TAM_CLAMP); + + // That's it! + + } + + // Add shadow textures (always at the end) + if (isShadowingEnabled(tt, terrain)) + { + uint numTextures = 1; + if (getReceiveDynamicShadowsPSSM()) + { + numTextures = getReceiveDynamicShadowsPSSM()->getSplitCount(); + } + for (uint i = 0; i < numTextures; ++i) + { + TextureUnitState* tu = pass->createTextureUnitState(); + tu->setContentType(TextureUnitState::CONTENT_SHADOW); + tu->setTextureAddressingMode(TextureUnitState::TAM_BORDER); + tu->setTextureBorderColour(ColourValue::White); + } + } + + } + //--------------------------------------------------------------------- + bool TerrainMaterialGeneratorB::SM2Profile::isShadowingEnabled(TechniqueType tt, const Terrain* terrain) const + { + return getReceiveDynamicShadowsEnabled() && tt != RENDER_COMPOSITE_MAP && + (tt != LOW_LOD || mLowLodShadows) && + terrain->getSceneManager()->isShadowTechniqueTextureBased(); + + } + //--------------------------------------------------------------------- + void TerrainMaterialGeneratorB::SM2Profile::updateParams(const MaterialPtr& mat, const Terrain* terrain) + { + mShaderGen->updateParams(this, mat, terrain, false); + + } + //--------------------------------------------------------------------- + void TerrainMaterialGeneratorB::SM2Profile::updateParamsForCompositeMap(const MaterialPtr& mat, const Terrain* terrain) + { + mShaderGen->updateParams(this, mat, terrain, true); + } + //--------------------------------------------------------------------- + //--------------------------------------------------------------------- + HighLevelGpuProgramPtr + TerrainMaterialGeneratorB::SM2Profile::ShaderHelper::generateVertexProgram( + const SM2Profile* prof, const Terrain* terrain, TechniqueType tt) + { + HighLevelGpuProgramPtr ret = createVertexProgram(prof, terrain, tt); + + StringUtil::StrStreamType sourceStr; + generateVertexProgramSource(prof, terrain, tt, sourceStr); + ret->setSource(sourceStr.str()); + ret->load(); + defaultVpParams(prof, terrain, tt, ret); +#if OGRE_DEBUG_MODE + LogManager::getSingleton().stream(LML_TRIVIAL) << "*** Terrain Vertex Program: " + << ret->getName() << " ***\n" << ret->getSource() << "\n*** ***"; +#endif + + return ret; + + } + //--------------------------------------------------------------------- + HighLevelGpuProgramPtr + TerrainMaterialGeneratorB::SM2Profile::ShaderHelper::generateFragmentProgram( + const SM2Profile* prof, const Terrain* terrain, TechniqueType tt) + { + HighLevelGpuProgramPtr ret = createFragmentProgram(prof, terrain, tt); + + StringUtil::StrStreamType sourceStr; + generateFragmentProgramSource(prof, terrain, tt, sourceStr); + ret->setSource(sourceStr.str()); + ret->load(); + defaultFpParams(prof, terrain, tt, ret); + +#if OGRE_DEBUG_MODE + LogManager::getSingleton().stream(LML_TRIVIAL) << "*** Terrain Fragment Program: " + << ret->getName() << " ***\n" << ret->getSource() << "\n*** ***"; +#endif + + return ret; + } + //--------------------------------------------------------------------- + void TerrainMaterialGeneratorB::SM2Profile::ShaderHelper::generateVertexProgramSource( + const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) + { + generateVpHeader(prof, terrain, tt, outStream); + + if (tt != LOW_LOD) + { + uint maxLayers = prof->getMaxLayers(terrain); + uint numLayers = std::min(maxLayers, static_cast(terrain->getLayerCount())); + + for (uint i = 0; i < numLayers; ++i) + generateVpLayer(prof, terrain, tt, i, outStream); + } + + generateVpFooter(prof, terrain, tt, outStream); + + } + //--------------------------------------------------------------------- + void TerrainMaterialGeneratorB::SM2Profile::ShaderHelper::generateFragmentProgramSource( + const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) + { + generateFpHeader(prof, terrain, tt, outStream); + + if (tt != LOW_LOD) + { + uint maxLayers = prof->getMaxLayers(terrain); + uint numLayers = std::min(maxLayers, static_cast(terrain->getLayerCount())); + + for (uint i = 0; i < numLayers; ++i) + generateFpLayer(prof, terrain, tt, i, outStream); + } + + generateFpFooter(prof, terrain, tt, outStream); + } + //--------------------------------------------------------------------- + void TerrainMaterialGeneratorB::SM2Profile::ShaderHelper::defaultVpParams( + const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, const HighLevelGpuProgramPtr& prog) + { + GpuProgramParametersSharedPtr params = prog->getDefaultParameters(); + params->setIgnoreMissingParams(true); + params->setNamedAutoConstant("worldMatrix", GpuProgramParameters::ACT_WORLD_MATRIX); + params->setNamedAutoConstant("viewProjMatrix", GpuProgramParameters::ACT_VIEWPROJ_MATRIX); + params->setNamedAutoConstant("lodMorph", GpuProgramParameters::ACT_CUSTOM, + Terrain::LOD_MORPH_CUSTOM_PARAM); + params->setNamedAutoConstant("fogParams", GpuProgramParameters::ACT_FOG_PARAMS); + + if (prof->isShadowingEnabled(tt, terrain)) + { + uint numTextures = 1; + if (prof->getReceiveDynamicShadowsPSSM()) + { + numTextures = prof->getReceiveDynamicShadowsPSSM()->getSplitCount(); + } + for (uint i = 0; i < numTextures; ++i) + { + params->setNamedAutoConstant("texViewProjMatrix" + StringConverter::toString(i), + GpuProgramParameters::ACT_TEXTURE_VIEWPROJ_MATRIX, i); + if (prof->getReceiveDynamicShadowsDepth()) + { + params->setNamedAutoConstant("depthRange" + StringConverter::toString(i), + GpuProgramParameters::ACT_SHADOW_SCENE_DEPTH_RANGE, i); + } + } + } + + + } + //--------------------------------------------------------------------- + void TerrainMaterialGeneratorB::SM2Profile::ShaderHelper::defaultFpParams( + const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, const HighLevelGpuProgramPtr& prog) + { + GpuProgramParametersSharedPtr params = prog->getDefaultParameters(); + params->setIgnoreMissingParams(true); + + params->setNamedAutoConstant("ambient", GpuProgramParameters::ACT_AMBIENT_LIGHT_COLOUR); + params->setNamedAutoConstant("lightPosObjSpace", GpuProgramParameters::ACT_LIGHT_POSITION_OBJECT_SPACE, 0); + params->setNamedAutoConstant("lightDiffuseColour", GpuProgramParameters::ACT_LIGHT_DIFFUSE_COLOUR, 0); + params->setNamedAutoConstant("lightSpecularColour", GpuProgramParameters::ACT_LIGHT_SPECULAR_COLOUR, 0); + params->setNamedAutoConstant("eyePosObjSpace", GpuProgramParameters::ACT_CAMERA_POSITION_OBJECT_SPACE); + params->setNamedAutoConstant("fogColour", GpuProgramParameters::ACT_FOG_COLOUR); + + if (prof->isShadowingEnabled(tt, terrain)) + { + uint numTextures = 1; + if (prof->getReceiveDynamicShadowsPSSM()) + { + PSSMShadowCameraSetup* pssm = prof->getReceiveDynamicShadowsPSSM(); + numTextures = pssm->getSplitCount(); + Vector4 splitPoints; + const PSSMShadowCameraSetup::SplitPointList& splitPointList = pssm->getSplitPoints(); + // Populate from split point 1, not 0, since split 0 isn't useful (usually 0) + for (uint i = 1; i < numTextures; ++i) + { + splitPoints[i-1] = splitPointList[i]; + } + params->setNamedConstant("pssmSplitPoints", splitPoints); + } + + if (prof->getReceiveDynamicShadowsDepth()) + { + size_t samplerOffset = (tt == HIGH_LOD) ? mShadowSamplerStartHi : mShadowSamplerStartLo; + for (uint i = 0; i < numTextures; ++i) + { + params->setNamedAutoConstant("inverseShadowmapSize" + StringConverter::toString(i), + GpuProgramParameters::ACT_INVERSE_TEXTURE_SIZE, i + samplerOffset); + } + } + } + + + } + //--------------------------------------------------------------------- + void TerrainMaterialGeneratorB::SM2Profile::ShaderHelper::updateParams( + const SM2Profile* prof, const MaterialPtr& mat, const Terrain* terrain, bool compositeMap) + { + Pass* p = mat->getTechnique(0)->getPass(0); + if (compositeMap) + { + updateVpParams(prof, terrain, RENDER_COMPOSITE_MAP, p->getVertexProgramParameters()); + updateFpParams(prof, terrain, RENDER_COMPOSITE_MAP, p->getFragmentProgramParameters()); + } + else + { + // high lod + updateVpParams(prof, terrain, HIGH_LOD, p->getVertexProgramParameters()); + updateFpParams(prof, terrain, HIGH_LOD, p->getFragmentProgramParameters()); + + if(prof->isCompositeMapEnabled()) + { + // low lod + p = mat->getTechnique(1)->getPass(0); + updateVpParams(prof, terrain, LOW_LOD, p->getVertexProgramParameters()); + updateFpParams(prof, terrain, LOW_LOD, p->getFragmentProgramParameters()); + } + } + } + //--------------------------------------------------------------------- + void TerrainMaterialGeneratorB::SM2Profile::ShaderHelper::updateVpParams( + const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, const GpuProgramParametersSharedPtr& params) + { + params->setIgnoreMissingParams(true); + uint maxLayers = prof->getMaxLayers(terrain); + uint numLayers = std::min(maxLayers, static_cast(terrain->getLayerCount())); + uint numUVMul = numLayers / 4; + if (numLayers % 4) + ++numUVMul; + for (uint i = 0; i < numUVMul; ++i) + { + Vector4 uvMul( + terrain->getLayerUVMultiplier(i * 4), + terrain->getLayerUVMultiplier(i * 4 + 1), + terrain->getLayerUVMultiplier(i * 4 + 2), + terrain->getLayerUVMultiplier(i * 4 + 3) + ); + params->setNamedConstant("uvMul" + StringConverter::toString(i), uvMul); + } + + } + //--------------------------------------------------------------------- + void TerrainMaterialGeneratorB::SM2Profile::ShaderHelper::updateFpParams( + const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, const GpuProgramParametersSharedPtr& params) + { + params->setIgnoreMissingParams(true); + // TODO - parameterise this? + Vector4 scaleBiasSpecular(0.03, -0.04, 32, 1); + params->setNamedConstant("scaleBiasSpecular", scaleBiasSpecular); + + } + //--------------------------------------------------------------------- + String TerrainMaterialGeneratorB::SM2Profile::ShaderHelper::getChannel(uint idx) + { + uint rem = idx % 4; + switch(rem) + { + case 0: + default: + return "r"; + case 1: + return "g"; + case 2: + return "b"; + case 3: + return "a"; + }; + } + //--------------------------------------------------------------------- + String TerrainMaterialGeneratorB::SM2Profile::ShaderHelper::getVertexProgramName( + const SM2Profile* prof, const Terrain* terrain, TechniqueType tt) + { + String progName = terrain->getMaterialName() + "/sm2/vp"; + + switch(tt) + { + case HIGH_LOD: + progName += "/hlod"; + break; + case LOW_LOD: + progName += "/llod"; + break; + case RENDER_COMPOSITE_MAP: + progName += "/comp"; + break; + } + + return progName; + + } + //--------------------------------------------------------------------- + String TerrainMaterialGeneratorB::SM2Profile::ShaderHelper::getFragmentProgramName( + const SM2Profile* prof, const Terrain* terrain, TechniqueType tt) + { + + String progName = terrain->getMaterialName() + "/sm2/fp"; + + switch(tt) + { + case HIGH_LOD: + progName += "/hlod"; + break; + case LOW_LOD: + progName += "/llod"; + break; + case RENDER_COMPOSITE_MAP: + progName += "/comp"; + break; + } + + return progName; + } + //--------------------------------------------------------------------- + //--------------------------------------------------------------------- + HighLevelGpuProgramPtr + TerrainMaterialGeneratorB::SM2Profile::ShaderHelperCg::createVertexProgram( + const SM2Profile* prof, const Terrain* terrain, TechniqueType tt) + { + HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton(); + String progName = getVertexProgramName(prof, terrain, tt); + HighLevelGpuProgramPtr ret = mgr.getByName(progName); + if (ret.isNull()) + { + ret = mgr.createProgram(progName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + "cg", GPT_VERTEX_PROGRAM); + } + else + { + ret->unload(); + } + + ret->setParameter("profiles", "vs_3_0 vs_2_0 arbvp1"); + ret->setParameter("entry_point", "main_vp"); + + return ret; + + } + //--------------------------------------------------------------------- + HighLevelGpuProgramPtr + TerrainMaterialGeneratorB::SM2Profile::ShaderHelperCg::createFragmentProgram( + const SM2Profile* prof, const Terrain* terrain, TechniqueType tt) + { + HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton(); + String progName = getFragmentProgramName(prof, terrain, tt); + + HighLevelGpuProgramPtr ret = mgr.getByName(progName); + if (ret.isNull()) + { + ret = mgr.createProgram(progName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + "cg", GPT_FRAGMENT_PROGRAM); + } + else + { + ret->unload(); + } + + if(prof->isLayerNormalMappingEnabled() || prof->isLayerParallaxMappingEnabled()) + ret->setParameter("profiles", "ps_3_0 ps_2_x fp40 arbfp1"); + else + ret->setParameter("profiles", "ps_3_0 ps_2_0 fp30 arbfp1"); + ret->setParameter("entry_point", "main_fp"); + + return ret; + + } + //--------------------------------------------------------------------- + void TerrainMaterialGeneratorB::SM2Profile::ShaderHelperCg::generateVpHeader( + const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) + { + outStream << + "void main_vp(\n" + "float4 pos : POSITION,\n" + "float2 uv : TEXCOORD0,\n"; + if (tt != RENDER_COMPOSITE_MAP) + outStream << "float2 delta : TEXCOORD1,\n"; // lodDelta, lodThreshold + + outStream << + "uniform float4x4 worldMatrix,\n" + "uniform float4x4 viewProjMatrix,\n" + "uniform float2 lodMorph,\n"; // morph amount, morph LOD target + + // uv multipliers + uint maxLayers = prof->getMaxLayers(terrain); + uint numLayers = std::min(maxLayers, static_cast(terrain->getLayerCount())); + uint numUVMultipliers = (numLayers / 4); + if (numLayers % 4) + ++numUVMultipliers; + for (uint i = 0; i < numUVMultipliers; ++i) + outStream << "uniform float4 uvMul" << i << ", \n"; + + outStream << + "out float4 oPos : POSITION,\n" + "out float4 oPosObj : TEXCOORD0 \n"; + + uint texCoordSet = 1; + outStream << + ", out float4 oUVMisc : TEXCOORD" << texCoordSet++ <<" // xy = uv, z = camDepth\n"; + + // layer UV's premultiplied, packed as xy/zw + uint numUVSets = numLayers / 2; + if (numLayers % 2) + ++numUVSets; + if (tt != LOW_LOD) + { + for (uint i = 0; i < numUVSets; ++i) + { + outStream << + ", out float4 oUV" << i << " : TEXCOORD" << texCoordSet++ << "\n"; + } + } + + if (prof->getParent()->getDebugLevel() && tt != RENDER_COMPOSITE_MAP) + { + outStream << ", out float2 lodInfo : TEXCOORD" << texCoordSet++ << "\n"; + } + + bool fog = terrain->getSceneManager()->getFogMode() != FOG_NONE && tt != RENDER_COMPOSITE_MAP; + if (fog) + { + outStream << + ", uniform float4 fogParams\n" + ", out float fogVal : COLOR\n"; + } + + if (prof->isShadowingEnabled(tt, terrain)) + { + texCoordSet = generateVpDynamicShadowsParams(texCoordSet, prof, terrain, tt, outStream); + } + + // check we haven't exceeded texture coordinates + if (texCoordSet > 8) + { + OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, + "Requested options require too many texture coordinate sets! Try reducing the number of layers.", + __FUNCTION__); + } + + outStream << + ")\n" + "{\n" + " float4 worldPos = mul(worldMatrix, pos);\n" + " oPosObj = pos;\n"; + + if (tt != RENDER_COMPOSITE_MAP) + { + // determine whether to apply the LOD morph to this vertex + // we store the deltas against all vertices so we only want to apply + // the morph to the ones which would disappear. The target LOD which is + // being morphed to is stored in lodMorph.y, and the LOD at which + // the vertex should be morphed is stored in uv.w. If we subtract + // the former from the latter, and arrange to only morph if the + // result is negative (it will only be -1 in fact, since after that + // the vertex will never be indexed), we will achieve our aim. + // sign(vertexLOD - targetLOD) == -1 is to morph + outStream << + " float toMorph = -min(0, sign(delta.y - lodMorph.y));\n"; + // this will either be 1 (morph) or 0 (don't morph) + if (prof->getParent()->getDebugLevel()) + { + // x == LOD level (-1 since value is target level, we want to display actual) + outStream << "lodInfo.x = (lodMorph.y - 1) / " << terrain->getNumLodLevels() << ";\n"; + // y == LOD morph + outStream << "lodInfo.y = toMorph * lodMorph.x;\n"; + } + + // morph + switch (terrain->getAlignment()) + { + case Terrain::ALIGN_X_Y: + outStream << " worldPos.z += delta.x * toMorph * lodMorph.x;\n"; + break; + case Terrain::ALIGN_X_Z: + outStream << " worldPos.y += delta.x * toMorph * lodMorph.x;\n"; + break; + case Terrain::ALIGN_Y_Z: + outStream << " worldPos.x += delta.x * toMorph * lodMorph.x;\n"; + break; + }; + } + + + // generate UVs + if (tt != LOW_LOD) + { + for (uint i = 0; i < numUVSets; ++i) + { + uint layer = i * 2; + uint uvMulIdx = layer / 4; + + outStream << + " oUV" << i << ".xy = " << " uv.xy * uvMul" << uvMulIdx << "." << getChannel(layer) << ";\n"; + outStream << + " oUV" << i << ".zw = " << " uv.xy * uvMul" << uvMulIdx << "." << getChannel(layer+1) << ";\n"; + + } + + } + + + } + //--------------------------------------------------------------------- + void TerrainMaterialGeneratorB::SM2Profile::ShaderHelperCg::generateFpHeader( + const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) + { + + // Main header + outStream << + // helpers + "float4 expand(float4 v)\n" + "{ \n" + " return v * 2 - 1;\n" + "}\n\n\n"; + + if (prof->isShadowingEnabled(tt, terrain)) + generateFpDynamicShadowsHelpers(prof, terrain, tt, outStream); + + + outStream << + "float4 main_fp(\n" + "float4 position : TEXCOORD0,\n"; + + uint texCoordSet = 1; + outStream << + "float4 uvMisc : TEXCOORD" << texCoordSet++ << ",\n"; + + // UV's premultiplied, packed as xy/zw + uint maxLayers = prof->getMaxLayers(terrain); + uint numBlendTextures = std::min(terrain->getBlendTextureCount(maxLayers), terrain->getBlendTextureCount()); + uint numLayers = std::min(maxLayers, static_cast(terrain->getLayerCount())); + uint numUVSets = numLayers / 2; + if (numLayers % 2) + ++numUVSets; + if (tt != LOW_LOD) + { + for (uint i = 0; i < numUVSets; ++i) + { + outStream << + "float4 layerUV" << i << " : TEXCOORD" << texCoordSet++ << ", \n"; + } + + } + if (prof->getParent()->getDebugLevel() && tt != RENDER_COMPOSITE_MAP) + { + outStream << "float2 lodInfo : TEXCOORD" << texCoordSet++ << ", \n"; + } + + bool fog = terrain->getSceneManager()->getFogMode() != FOG_NONE && tt != RENDER_COMPOSITE_MAP; + if (fog) + { + outStream << + "uniform float3 fogColour, \n" + "float fogVal : COLOR,\n"; + } + + uint currentSamplerIdx = 0; + + outStream << + // Only 1 light supported in this version + // deferred shading profile / generator later, ok? :) + "uniform float3 ambient,\n" + "uniform float4 lightPosObjSpace,\n" + "uniform float3 lightDiffuseColour,\n" + "uniform float3 lightSpecularColour,\n" + "uniform float3 eyePosObjSpace,\n" + // pack scale, bias and specular + "uniform float4 scaleBiasSpecular,\n"; + + if (tt == LOW_LOD) + { + // single composite map covers all the others below + outStream << + "uniform sampler2D compositeMap : register(s" << currentSamplerIdx++ << ")\n"; + } + else + { + outStream << + "uniform sampler2D globalNormal : register(s" << currentSamplerIdx++ << ")\n"; + + + if (terrain->getGlobalColourMapEnabled() && prof->isGlobalColourMapEnabled()) + { + outStream << ", uniform sampler2D globalColourMap : register(s" + << currentSamplerIdx++ << ")\n"; + } + if (prof->isLightmapEnabled()) + { + outStream << ", uniform sampler2D lightMap : register(s" + << currentSamplerIdx++ << ")\n"; + } + // Blend textures - sampler definitions + for (uint i = 0; i < numBlendTextures; ++i) + { + outStream << ", uniform sampler2D blendTex" << i + << " : register(s" << currentSamplerIdx++ << ")\n"; + } + + // Layer textures - sampler definitions & UV multipliers + for (uint i = 0; i < numLayers; ++i) + { + outStream << ", uniform sampler2D difftex" << i + << " : register(s" << currentSamplerIdx++ << ")\n"; + outStream << ", uniform sampler2D normtex" << i + << " : register(s" << currentSamplerIdx++ << ")\n"; + } + } + + if (prof->isShadowingEnabled(tt, terrain)) + { + generateFpDynamicShadowsParams(&texCoordSet, ¤tSamplerIdx, prof, terrain, tt, outStream); + } + + // check we haven't exceeded samplers + if (currentSamplerIdx > 16) + { + OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, + "Requested options require too many texture samplers! Try reducing the number of layers.", + __FUNCTION__); + } + + outStream << + ") : COLOR\n" + "{\n" + " float4 outputCol;\n" + " float shadow = 1.0;\n" + " float2 uv = uvMisc.xy;\n" + // base colour + " outputCol = float4(0,0,0,1);\n"; + + if (tt != LOW_LOD) + { + outStream << + // global normal + " float3 normal = expand(tex2D(globalNormal, uv)).rgb;\n"; + + } + + outStream << + " float3 lightDir = \n" + " lightPosObjSpace.xyz - (position.xyz * lightPosObjSpace.w);\n" + " float3 eyeDir = eyePosObjSpace - position.xyz;\n" + + // set up accumulation areas + " float3 diffuse = float3(0,0,0);\n" + " float specular = 0;\n"; + + + if (tt == LOW_LOD) + { + // we just do a single calculation from composite map + outStream << + " float4 composite = tex2D(compositeMap, uv);\n" + " diffuse = composite.rgb;\n"; + // TODO - specular; we'll need normals for this! + } + else + { + // set up the blend values + for (uint i = 0; i < numBlendTextures; ++i) + { + outStream << " float4 blendTexVal" << i << " = tex2D(blendTex" << i << ", uv);\n"; + } + + if (prof->isLayerNormalMappingEnabled()) + { + // derive the tangent space basis + // we do this in the pixel shader because we don't have per-vertex normals + // because of the LOD, we use a normal map + // tangent is always +x or -z in object space depending on alignment + switch(terrain->getAlignment()) + { + case Terrain::ALIGN_X_Y: + case Terrain::ALIGN_X_Z: + outStream << " float3 tangent = float3(1, 0, 0);\n"; + break; + case Terrain::ALIGN_Y_Z: + outStream << " float3 tangent = float3(0, 0, -1);\n"; + break; + }; + + outStream << " float3 binormal = normalize(cross(tangent, normal));\n"; + // note, now we need to re-cross to derive tangent again because it wasn't orthonormal + outStream << " tangent = normalize(cross(normal, binormal));\n"; + // derive final matrix + outStream << " float3x3 TBN = float3x3(tangent, binormal, normal);\n"; + + // set up lighting result placeholders for interpolation + outStream << " float4 litRes, litResLayer;\n"; + outStream << " float3 TSlightDir, TSeyeDir, TShalfAngle, TSnormal;\n"; + if (prof->isLayerParallaxMappingEnabled()) + outStream << " float displacement;\n"; + // move + outStream << " TSlightDir = normalize(mul(TBN, lightDir));\n"; + outStream << " TSeyeDir = normalize(mul(TBN, eyeDir));\n"; + + } + else + { + // simple per-pixel lighting with no normal mapping + outStream << " lightDir = normalize(lightDir);\n"; + outStream << " eyeDir = normalize(eyeDir);\n"; + outStream << " float3 halfAngle = normalize(lightDir + eyeDir);\n"; + outStream << " float4 litRes = lit(dot(lightDir, normal), dot(halfAngle, normal), scaleBiasSpecular.z);\n"; + + } + } + + + } + //--------------------------------------------------------------------- + void TerrainMaterialGeneratorB::SM2Profile::ShaderHelperCg::generateVpLayer( + const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, uint layer, StringUtil::StrStreamType& outStream) + { + // nothing to do + } + //--------------------------------------------------------------------- + void TerrainMaterialGeneratorB::SM2Profile::ShaderHelperCg::generateFpLayer( + const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, uint layer, StringUtil::StrStreamType& outStream) + { + uint uvIdx = layer / 2; + String uvChannels = layer % 2 ? ".zw" : ".xy"; + uint blendIdx = (layer-1) / 4; + String blendChannel = getChannel(layer-1); + String blendWeightStr = String("blendTexVal") + StringConverter::toString(blendIdx) + + "." + blendChannel; + + // generate early-out conditional + /* Disable - causing some issues even when trying to force the use of texldd + if (layer && prof->_isSM3Available()) + outStream << " if (" << blendWeightStr << " > 0.0003)\n { \n"; + */ + + // generate UV + outStream << " float2 uv" << layer << " = layerUV" << uvIdx << uvChannels << ";\n"; + + // calculate lighting here if normal mapping + if (prof->isLayerNormalMappingEnabled()) + { + if (prof->isLayerParallaxMappingEnabled() && tt != RENDER_COMPOSITE_MAP) + { + // modify UV - note we have to sample an extra time + outStream << " displacement = tex2D(normtex" << layer << ", uv" << layer << ").a\n" + " * scaleBiasSpecular.x + scaleBiasSpecular.y;\n"; + outStream << " uv" << layer << " += TSeyeDir.xy * displacement;\n"; + } + + // access TS normal map + outStream << " TSnormal = expand(tex2D(normtex" << layer << ", uv" << layer << ")).rgb;\n"; + outStream << " TShalfAngle = normalize(TSlightDir + TSeyeDir);\n"; + outStream << " litResLayer = lit(dot(TSlightDir, TSnormal), dot(TShalfAngle, TSnormal), scaleBiasSpecular.z);\n"; + if (!layer) + outStream << " litRes = litResLayer;\n"; + else + outStream << " litRes = lerp(litRes, litResLayer, " << blendWeightStr << ");\n"; + + } + + // sample diffuse texture + outStream << " float4 diffuseSpecTex" << layer + << " = tex2D(difftex" << layer << ", uv" << layer << ");\n"; + + // apply to common + if (!layer) + { + outStream << " diffuse = diffuseSpecTex0.rgb;\n"; + if (prof->isLayerSpecularMappingEnabled()) + outStream << " specular = diffuseSpecTex0.a;\n"; + } + else + { + outStream << " diffuse = lerp(diffuse, diffuseSpecTex" << layer + << ".rgb, " << blendWeightStr << ");\n"; + if (prof->isLayerSpecularMappingEnabled()) + outStream << " specular = lerp(specular, diffuseSpecTex" << layer + << ".a, " << blendWeightStr << ");\n"; + + } + + // End early-out + /* Disable - causing some issues even when trying to force the use of texldd + if (layer && prof->_isSM3Available()) + outStream << " } // early-out blend value\n"; + */ + } + //--------------------------------------------------------------------- + void TerrainMaterialGeneratorB::SM2Profile::ShaderHelperCg::generateVpFooter( + const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) + { + + outStream << + " oPos = mul(viewProjMatrix, worldPos);\n" + " oUVMisc.xy = uv.xy;\n"; + + bool fog = terrain->getSceneManager()->getFogMode() != FOG_NONE && tt != RENDER_COMPOSITE_MAP; + if (fog) + { + if (terrain->getSceneManager()->getFogMode() == FOG_LINEAR) + { + outStream << + " fogVal = saturate((oPos.z - fogParams.y) * fogParams.w);\n"; + } + else + { + outStream << + " fogVal = saturate(1 / (exp(oPos.z * fogParams.x)));\n"; + } + } + + if (prof->isShadowingEnabled(tt, terrain)) + generateVpDynamicShadows(prof, terrain, tt, outStream); + + outStream << + "}\n"; + + + } + //--------------------------------------------------------------------- + void TerrainMaterialGeneratorB::SM2Profile::ShaderHelperCg::generateFpFooter( + const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) + { + + if (tt == LOW_LOD) + { + if (prof->isShadowingEnabled(tt, terrain)) + { + generateFpDynamicShadows(prof, terrain, tt, outStream); + outStream << + " outputCol.rgb = diffuse * rtshadow;\n"; + } + else + { + outStream << + " outputCol.rgb = diffuse;\n"; + } + } + else + { + if (terrain->getGlobalColourMapEnabled() && prof->isGlobalColourMapEnabled()) + { + // sample colour map and apply to diffuse + outStream << " diffuse *= tex2D(globalColourMap, uv).rgb;\n"; + } + if (prof->isLightmapEnabled()) + { + // sample lightmap + outStream << " shadow = tex2D(lightMap, uv).r;\n"; + } + + if (prof->isShadowingEnabled(tt, terrain)) + { + generateFpDynamicShadows(prof, terrain, tt, outStream); + } + + // diffuse lighting + outStream << " outputCol.rgb += ambient * diffuse + litRes.y * lightDiffuseColour * diffuse * shadow;\n"; + + // specular default + if (!prof->isLayerSpecularMappingEnabled()) + outStream << " specular = 1.0;\n"; + + if (tt == RENDER_COMPOSITE_MAP) + { + // Lighting embedded in alpha + outStream << + " outputCol.a = shadow;\n"; + + } + else + { + // Apply specular + outStream << " outputCol.rgb += litRes.z * lightSpecularColour * specular * shadow;\n"; + + if (prof->getParent()->getDebugLevel()) + { + outStream << " outputCol.rg += lodInfo.xy;\n"; + } + } + } + + bool fog = terrain->getSceneManager()->getFogMode() != FOG_NONE && tt != RENDER_COMPOSITE_MAP; + if (fog) + { + outStream << " outputCol.rgb = lerp(outputCol.rgb, fogColour, fogVal);\n"; + } + + // Final return + outStream << " return outputCol;\n" + << "}\n"; + + } + //--------------------------------------------------------------------- + void TerrainMaterialGeneratorB::SM2Profile::ShaderHelperCg::generateFpDynamicShadowsHelpers( + const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) + { + // TODO make filtering configurable + outStream << + "// Simple PCF \n" + "// Number of samples in one dimension (square for total samples) \n" + "#define NUM_SHADOW_SAMPLES_1D 2.0 \n" + "#define SHADOW_FILTER_SCALE 1 \n" + + "#define SHADOW_SAMPLES NUM_SHADOW_SAMPLES_1D*NUM_SHADOW_SAMPLES_1D \n" + + "float4 offsetSample(float4 uv, float2 offset, float invMapSize) \n" + "{ \n" + " return float4(uv.xy + offset * invMapSize * uv.w, uv.z, uv.w); \n" + "} \n"; + + if (prof->getReceiveDynamicShadowsDepth()) + { + outStream << + "float calcDepthShadow(sampler2D shadowMap, float4 uv, float invShadowMapSize) \n" + "{ \n" + " // 4-sample PCF \n" + + " float shadow = 0.0; \n" + " float offset = (NUM_SHADOW_SAMPLES_1D/2 - 0.5) * SHADOW_FILTER_SCALE; \n" + " for (float y = -offset; y <= offset; y += SHADOW_FILTER_SCALE) \n" + " for (float x = -offset; x <= offset; x += SHADOW_FILTER_SCALE) \n" + " { \n" + " float4 newUV = offsetSample(uv, float2(x, y), invShadowMapSize);\n" + " // manually project and assign derivatives \n" + " // to avoid gradient issues inside loops \n" + " newUV = newUV / newUV.w; \n" + " float depth = tex2D(shadowMap, newUV.xy, 1, 1).x; \n" + " if (depth >= 1 || depth >= uv.z)\n" + " shadow += 1.0;\n" + " } \n" + + " shadow /= SHADOW_SAMPLES; \n" + + " return shadow; \n" + "} \n"; + } + else + { + outStream << + "float calcSimpleShadow(sampler2D shadowMap, float4 shadowMapPos) \n" + "{ \n" + " return tex2Dproj(shadowMap, shadowMapPos).x; \n" + "} \n"; + + } + + if (prof->getReceiveDynamicShadowsPSSM()) + { + uint numTextures = prof->getReceiveDynamicShadowsPSSM()->getSplitCount(); + + + if (prof->getReceiveDynamicShadowsDepth()) + { + outStream << + "float calcPSSMDepthShadow("; + } + else + { + outStream << + "float calcPSSMSimpleShadow("; + } + + outStream << "\n "; + for (uint i = 0; i < numTextures; ++i) + outStream << "sampler2D shadowMap" << i << ", "; + outStream << "\n "; + for (uint i = 0; i < numTextures; ++i) + outStream << "float4 lsPos" << i << ", "; + if (prof->getReceiveDynamicShadowsDepth()) + { + outStream << "\n "; + for (uint i = 0; i < numTextures; ++i) + outStream << "float invShadowmapSize" << i << ", "; + } + outStream << "\n" + " float4 pssmSplitPoints, float camDepth) \n" + "{ \n" + " float shadow; \n" + " // calculate shadow \n"; + + for (uint i = 0; i < numTextures; ++i) + { + if (!i) + outStream << " if (camDepth <= pssmSplitPoints." << ShaderHelper::getChannel(i) << ") \n"; + else if (i < numTextures - 1) + outStream << " else if (camDepth <= pssmSplitPoints." << ShaderHelper::getChannel(i) << ") \n"; + else + outStream << " else \n"; + + outStream << + " { \n"; + if (prof->getReceiveDynamicShadowsDepth()) + { + outStream << + " shadow = calcDepthShadow(shadowMap" << i << ", lsPos" << i << ", invShadowmapSize" << i << "); \n"; + } + else + { + outStream << + " shadow = calcSimpleShadow(shadowMap" << i << ", lsPos" << i << "); \n"; + } + outStream << + " } \n"; + + } + + outStream << + " return shadow; \n" + "} \n\n\n"; + } + + + } + //--------------------------------------------------------------------- + uint TerrainMaterialGeneratorB::SM2Profile::ShaderHelperCg::generateVpDynamicShadowsParams( + uint texCoord, const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) + { + // out semantics & params + uint numTextures = 1; + if (prof->getReceiveDynamicShadowsPSSM()) + { + numTextures = prof->getReceiveDynamicShadowsPSSM()->getSplitCount(); + } + for (uint i = 0; i < numTextures; ++i) + { + outStream << + ", out float4 oLightSpacePos" << i << " : TEXCOORD" << texCoord++ << " \n" << + ", uniform float4x4 texViewProjMatrix" << i << " \n"; + if (prof->getReceiveDynamicShadowsDepth()) + { + outStream << + ", uniform float4 depthRange" << i << " // x = min, y = max, z = range, w = 1/range \n"; + } + } + + return texCoord; + + } + //--------------------------------------------------------------------- + void TerrainMaterialGeneratorB::SM2Profile::ShaderHelperCg::generateVpDynamicShadows( + const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) + { + uint numTextures = 1; + if (prof->getReceiveDynamicShadowsPSSM()) + { + numTextures = prof->getReceiveDynamicShadowsPSSM()->getSplitCount(); + } + + // Calculate the position of vertex in light space + for (uint i = 0; i < numTextures; ++i) + { + outStream << + " oLightSpacePos" << i << " = mul(texViewProjMatrix" << i << ", worldPos); \n"; + if (prof->getReceiveDynamicShadowsDepth()) + { + // make linear + outStream << + "oLightSpacePos" << i << ".z = (oLightSpacePos" << i << ".z - depthRange" << i << ".x) * depthRange" << i << ".w;\n"; + + } + } + + + if (prof->getReceiveDynamicShadowsPSSM()) + { + outStream << + " // pass cam depth\n" + " oUVMisc.z = oPos.z;\n"; + } + + } + //--------------------------------------------------------------------- + void TerrainMaterialGeneratorB::SM2Profile::ShaderHelperCg::generateFpDynamicShadowsParams( + uint* texCoord, uint* sampler, const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) + { + if (tt == HIGH_LOD) + mShadowSamplerStartHi = *sampler; + else if (tt == LOW_LOD) + mShadowSamplerStartLo = *sampler; + + // in semantics & params + uint numTextures = 1; + if (prof->getReceiveDynamicShadowsPSSM()) + { + numTextures = prof->getReceiveDynamicShadowsPSSM()->getSplitCount(); + outStream << + ", uniform float4 pssmSplitPoints \n"; + } + for (uint i = 0; i < numTextures; ++i) + { + outStream << + ", float4 lightSpacePos" << i << " : TEXCOORD" << *texCoord << " \n" << + ", uniform sampler2D shadowMap" << i << " : register(s" << *sampler << ") \n"; + *sampler = *sampler + 1; + *texCoord = *texCoord + 1; + if (prof->getReceiveDynamicShadowsDepth()) + { + outStream << + ", uniform float inverseShadowmapSize" << i << " \n"; + } + } + + } + //--------------------------------------------------------------------- + void TerrainMaterialGeneratorB::SM2Profile::ShaderHelperCg::generateFpDynamicShadows( + const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) + { + if (prof->getReceiveDynamicShadowsPSSM()) + { + uint numTextures = prof->getReceiveDynamicShadowsPSSM()->getSplitCount(); + outStream << + " float camDepth = uvMisc.z;\n"; + + if (prof->getReceiveDynamicShadowsDepth()) + { + outStream << + " float rtshadow = calcPSSMDepthShadow("; + } + else + { + outStream << + " float rtshadow = calcPSSMSimpleShadow("; + } + for (uint i = 0; i < numTextures; ++i) + outStream << "shadowMap" << i << ", "; + outStream << "\n "; + + for (uint i = 0; i < numTextures; ++i) + outStream << "lightSpacePos" << i << ", "; + if (prof->getReceiveDynamicShadowsDepth()) + { + outStream << "\n "; + for (uint i = 0; i < numTextures; ++i) + outStream << "inverseShadowmapSize" << i << ", "; + } + outStream << "\n" << + " pssmSplitPoints, camDepth);\n"; + + } + else + { + if (prof->getReceiveDynamicShadowsDepth()) + { + outStream << + " float rtshadow = calcDepthShadow(shadowMap0, lightSpacePos0, inverseShadowmapSize0);"; + } + else + { + outStream << + " float rtshadow = calcSimpleShadow(shadowMap0, lightSpacePos0);"; + } + } + + outStream << + " shadow = min(shadow, rtshadow);\n"; + + } + //--------------------------------------------------------------------- + //--------------------------------------------------------------------- + HighLevelGpuProgramPtr + TerrainMaterialGeneratorB::SM2Profile::ShaderHelperHLSL::createVertexProgram( + const SM2Profile* prof, const Terrain* terrain, TechniqueType tt) + { + HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton(); + String progName = getVertexProgramName(prof, terrain, tt); + + HighLevelGpuProgramPtr ret = mgr.getByName(progName); + if (ret.isNull()) + { + ret = mgr.createProgram(progName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + "hlsl", GPT_VERTEX_PROGRAM); + } + else + { + ret->unload(); + } + + if (prof->_isSM3Available()) + ret->setParameter("target", "vs_3_0"); + else + ret->setParameter("target", "vs_2_0"); + ret->setParameter("entry_point", "main_vp"); + + return ret; + + } + //--------------------------------------------------------------------- + HighLevelGpuProgramPtr + TerrainMaterialGeneratorB::SM2Profile::ShaderHelperHLSL::createFragmentProgram( + const SM2Profile* prof, const Terrain* terrain, TechniqueType tt) + { + HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton(); + String progName = getFragmentProgramName(prof, terrain, tt); + + + HighLevelGpuProgramPtr ret = mgr.getByName(progName); + if (ret.isNull()) + { + ret = mgr.createProgram(progName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + "hlsl", GPT_FRAGMENT_PROGRAM); + } + else + { + ret->unload(); + } + + if (prof->_isSM3Available()) + ret->setParameter("target", "ps_3_0"); + else + ret->setParameter("target", "ps_2_x"); + ret->setParameter("entry_point", "main_fp"); + + return ret; + + } + //--------------------------------------------------------------------- + //--------------------------------------------------------------------- + HighLevelGpuProgramPtr + TerrainMaterialGeneratorB::SM2Profile::ShaderHelperGLSL::createVertexProgram( + const SM2Profile* prof, const Terrain* terrain, TechniqueType tt) + { + HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton(); + String progName = getVertexProgramName(prof, terrain, tt); + + switch(tt) + { + case HIGH_LOD: + progName += "/hlod"; + break; + case LOW_LOD: + progName += "/llod"; + break; + case RENDER_COMPOSITE_MAP: + progName += "/comp"; + break; + } + + HighLevelGpuProgramPtr ret = mgr.getByName(progName); + if (ret.isNull()) + { + ret = mgr.createProgram(progName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + "glsl", GPT_VERTEX_PROGRAM); + } + else + { + ret->unload(); + } + + return ret; + + } + //--------------------------------------------------------------------- + HighLevelGpuProgramPtr + TerrainMaterialGeneratorB::SM2Profile::ShaderHelperGLSL::createFragmentProgram( + const SM2Profile* prof, const Terrain* terrain, TechniqueType tt) + { + HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton(); + String progName = getFragmentProgramName(prof, terrain, tt); + + HighLevelGpuProgramPtr ret = mgr.getByName(progName); + if (ret.isNull()) + { + ret = mgr.createProgram(progName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + "glsl", GPT_FRAGMENT_PROGRAM); + } + else + { + ret->unload(); + } + + return ret; + + } + + +} diff --git a/apps/openmw/mwrender/terrainmaterial.hpp b/apps/openmw/mwrender/terrainmaterial.hpp new file mode 100644 index 000000000..eaa5572ee --- /dev/null +++ b/apps/openmw/mwrender/terrainmaterial.hpp @@ -0,0 +1,261 @@ +/* +----------------------------------------------------------------------------- +This source file is part of OGRE +(Object-oriented Graphics Rendering Engine) +For the latest info, see http://www.ogre3d.org/ + +Copyright (c) 2000-2011 Torus Knot Software Ltd + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +----------------------------------------------------------------------------- +*/ + +#ifndef __Ogre_TerrainMaterialGeneratorB_H__ +#define __Ogre_TerrainMaterialGeneratorB_H__ + +#include "OgreTerrainPrerequisites.h" +#include "OgreTerrainMaterialGenerator.h" +#include "OgreGpuProgramParams.h" + +namespace Ogre +{ + class PSSMShadowCameraSetup; + + /** \addtogroup Optional Components + * @{ + */ + /** \addtogroup Terrain + * Some details on the terrain component + * @{ + */ + + + /** A TerrainMaterialGenerator which can cope with normal mapped, specular mapped + terrain. + @note Requires the Cg plugin to render correctly + */ + class _OgreTerrainExport TerrainMaterialGeneratorB : public TerrainMaterialGenerator + { + public: + TerrainMaterialGeneratorB(); + ~TerrainMaterialGeneratorB(); + + /** Shader model 2 profile target. + */ + class _OgreTerrainExport SM2Profile : public TerrainMaterialGenerator::Profile + { + public: + SM2Profile(TerrainMaterialGenerator* parent, const String& name, const String& desc); + ~SM2Profile(); + MaterialPtr generate(const Terrain* terrain); + MaterialPtr generateForCompositeMap(const Terrain* terrain); + uint8 getMaxLayers(const Terrain* terrain) const; + void updateParams(const MaterialPtr& mat, const Terrain* terrain); + void updateParamsForCompositeMap(const MaterialPtr& mat, const Terrain* terrain); + void requestOptions(Terrain* terrain); + + /** Whether to support normal mapping per layer in the shader (default true). + */ + bool isLayerNormalMappingEnabled() const { return mLayerNormalMappingEnabled; } + /** Whether to support normal mapping per layer in the shader (default true). + */ + void setLayerNormalMappingEnabled(bool enabled); + /** Whether to support parallax mapping per layer in the shader (default true). + */ + bool isLayerParallaxMappingEnabled() const { return mLayerParallaxMappingEnabled; } + /** Whether to support parallax mapping per layer in the shader (default true). + */ + void setLayerParallaxMappingEnabled(bool enabled); + /** Whether to support specular mapping per layer in the shader (default true). + */ + bool isLayerSpecularMappingEnabled() const { return mLayerSpecularMappingEnabled; } + /** Whether to support specular mapping per layer in the shader (default true). + */ + void setLayerSpecularMappingEnabled(bool enabled); + /** Whether to support a global colour map over the terrain in the shader, + if it's present (default true). + */ + bool isGlobalColourMapEnabled() const { return mGlobalColourMapEnabled; } + /** Whether to support a global colour map over the terrain in the shader, + if it's present (default true). + */ + void setGlobalColourMapEnabled(bool enabled); + /** Whether to support a light map over the terrain in the shader, + if it's present (default true). + */ + bool isLightmapEnabled() const { return mLightmapEnabled; } + /** Whether to support a light map over the terrain in the shader, + if it's present (default true). + */ + void setLightmapEnabled(bool enabled); + /** Whether to use the composite map to provide a lower LOD technique + in the distance (default true). + */ + bool isCompositeMapEnabled() const { return mCompositeMapEnabled; } + /** Whether to use the composite map to provide a lower LOD technique + in the distance (default true). + */ + void setCompositeMapEnabled(bool enabled); + /** Whether to support dynamic texture shadows received from other + objects, on the terrain (default true). + */ + bool getReceiveDynamicShadowsEnabled() const { return mReceiveDynamicShadows; } + /** Whether to support dynamic texture shadows received from other + objects, on the terrain (default true). + */ + void setReceiveDynamicShadowsEnabled(bool enabled); + + /** Whether to use PSSM support dynamic texture shadows, and if so the + settings to use (default 0). + */ + void setReceiveDynamicShadowsPSSM(PSSMShadowCameraSetup* pssmSettings); + /** Whether to use PSSM support dynamic texture shadows, and if so the + settings to use (default 0). + */ + PSSMShadowCameraSetup* getReceiveDynamicShadowsPSSM() const { return mPSSM; } + /** Whether to use depth shadows (default false). + */ + void setReceiveDynamicShadowsDepth(bool enabled); + /** Whether to use depth shadows (default false). + */ + bool getReceiveDynamicShadowsDepth() const { return mDepthShadows; } + /** Whether to use shadows on low LOD material rendering (when using composite map) (default false). + */ + void setReceiveDynamicShadowsLowLod(bool enabled); + /** Whether to use shadows on low LOD material rendering (when using composite map) (default false). + */ + bool getReceiveDynamicShadowsLowLod() const { return mLowLodShadows; } + + /// Internal + bool _isSM3Available() const { return mSM3Available; } + + protected: + + enum TechniqueType + { + HIGH_LOD, + LOW_LOD, + RENDER_COMPOSITE_MAP + }; + void addTechnique(const MaterialPtr& mat, const Terrain* terrain, TechniqueType tt); + + /// Interface definition for helper class to generate shaders + class _OgreTerrainExport ShaderHelper : public TerrainAlloc + { + public: + ShaderHelper() {} + virtual ~ShaderHelper() {} + virtual HighLevelGpuProgramPtr generateVertexProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt); + virtual HighLevelGpuProgramPtr generateFragmentProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt); + virtual void updateParams(const SM2Profile* prof, const MaterialPtr& mat, const Terrain* terrain, bool compositeMap); + protected: + virtual String getVertexProgramName(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt); + virtual String getFragmentProgramName(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt); + virtual HighLevelGpuProgramPtr createVertexProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt) = 0; + virtual HighLevelGpuProgramPtr createFragmentProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt) = 0; + virtual void generateVertexProgramSource(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream); + virtual void generateFragmentProgramSource(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream); + virtual void generateVpHeader(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) = 0; + virtual void generateFpHeader(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) = 0; + virtual void generateVpLayer(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, uint layer, StringUtil::StrStreamType& outStream) = 0; + virtual void generateFpLayer(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, uint layer, StringUtil::StrStreamType& outStream) = 0; + virtual void generateVpFooter(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) = 0; + virtual void generateFpFooter(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) = 0; + virtual void defaultVpParams(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, const HighLevelGpuProgramPtr& prog); + virtual void defaultFpParams(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, const HighLevelGpuProgramPtr& prog); + virtual void updateVpParams(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, const GpuProgramParametersSharedPtr& params); + virtual void updateFpParams(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, const GpuProgramParametersSharedPtr& params); + static String getChannel(uint idx); + + size_t mShadowSamplerStartHi; + size_t mShadowSamplerStartLo; + + }; + + /// Utility class to help with generating shaders for Cg / HLSL. + class _OgreTerrainExport ShaderHelperCg : public ShaderHelper + { + protected: + HighLevelGpuProgramPtr createVertexProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt); + HighLevelGpuProgramPtr createFragmentProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt); + void generateVpHeader(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream); + void generateFpHeader(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream); + void generateVpLayer(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, uint layer, StringUtil::StrStreamType& outStream); + void generateFpLayer(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, uint layer, StringUtil::StrStreamType& outStream); + void generateVpFooter(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream); + void generateFpFooter(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream); + uint generateVpDynamicShadowsParams(uint texCoordStart, const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream); + void generateVpDynamicShadows(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream); + void generateFpDynamicShadowsHelpers(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream); + void generateFpDynamicShadowsParams(uint* texCoord, uint* sampler, const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream); + void generateFpDynamicShadows(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream); + }; + + class _OgreTerrainExport ShaderHelperHLSL : public ShaderHelperCg + { + protected: + HighLevelGpuProgramPtr createVertexProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt); + HighLevelGpuProgramPtr createFragmentProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt); + }; + + /// Utility class to help with generating shaders for GLSL. + class _OgreTerrainExport ShaderHelperGLSL : public ShaderHelper + { + protected: + HighLevelGpuProgramPtr createVertexProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt); + HighLevelGpuProgramPtr createFragmentProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt); + void generateVpHeader(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) {} + void generateFpHeader(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) {} + void generateVpLayer(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, uint layer, StringUtil::StrStreamType& outStream) {} + void generateFpLayer(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, uint layer, StringUtil::StrStreamType& outStream) {} + void generateVpFooter(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) {} + void generateFpFooter(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) {} + }; + + ShaderHelper* mShaderGen; + bool mLayerNormalMappingEnabled; + bool mLayerParallaxMappingEnabled; + bool mLayerSpecularMappingEnabled; + bool mGlobalColourMapEnabled; + bool mLightmapEnabled; + bool mCompositeMapEnabled; + bool mReceiveDynamicShadows; + PSSMShadowCameraSetup* mPSSM; + bool mDepthShadows; + bool mLowLodShadows; + bool mSM3Available; + + bool isShadowingEnabled(TechniqueType tt, const Terrain* terrain) const; + + }; + + + + + }; + + + + /** @} */ + /** @} */ + + +} + +#endif From dbd2d390fd4e4858d672c9c11f882010988f96f0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 27 Feb 2012 21:12:16 +0100 Subject: [PATCH 09/39] terrain material fixes: - disable normal and parallax mapping - disable specular mapping and specular --- apps/openmw/mwrender/terrain.cpp | 10 +++---- apps/openmw/mwrender/terrainmaterial.cpp | 36 ++++++++++++++---------- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index 568ed3741..f3104ef59 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -31,10 +31,10 @@ namespace MWRender TerrainMaterialGeneratorB::SM2Profile* matProfile = static_cast(activeProfile); - matProfile->setLightmapEnabled(false); + matProfile->setLightmapEnabled(false); // this doesn't disable the lightmap, although it should ?? matProfile->setReceiveDynamicShadowsEnabled(false); matProfile->setLayerNormalMappingEnabled(false); - //matProfile->setLayerParallaxMappingEnabled(false); + matProfile->setLayerParallaxMappingEnabled(false); matProfile->setLayerSpecularMappingEnabled(false); mLandSize = ESM::Land::LAND_SIZE; @@ -195,8 +195,8 @@ namespace MWRender //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"); + terrainData->layerList[0].textureNames.push_back("textures\\_land_default.dds"); // diffuseSpec + //terrainData->layerList[0].textureNames.push_back("textures\\_land_default.dds"); // normalHeight for ( int y = fromY - 1; y < fromY + size + 1; y++ ) { @@ -230,7 +230,7 @@ namespace MWRender //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); + //terrainData->layerList[position].textureNames.push_back("textures\\" + texture); indexes[ltexIndex] = position; } diff --git a/apps/openmw/mwrender/terrainmaterial.cpp b/apps/openmw/mwrender/terrainmaterial.cpp index 313a4a90c..61a7cdb19 100644 --- a/apps/openmw/mwrender/terrainmaterial.cpp +++ b/apps/openmw/mwrender/terrainmaterial.cpp @@ -46,16 +46,16 @@ namespace Ogre // in the albedo texture to store specular reflection // similarly we double-up the normal and height (for parallax) mLayerDecl.samplers.push_back(TerrainLayerSampler("albedo_specular", PF_BYTE_RGBA)); - mLayerDecl.samplers.push_back(TerrainLayerSampler("normal_height", PF_BYTE_RGBA)); + //mLayerDecl.samplers.push_back(TerrainLayerSampler("normal_height", PF_BYTE_RGBA)); mLayerDecl.elements.push_back( TerrainLayerSamplerElement(0, TLSS_ALBEDO, 0, 3)); - mLayerDecl.elements.push_back( - TerrainLayerSamplerElement(0, TLSS_SPECULAR, 3, 1)); - mLayerDecl.elements.push_back( - TerrainLayerSamplerElement(1, TLSS_NORMAL, 0, 3)); - mLayerDecl.elements.push_back( - TerrainLayerSamplerElement(1, TLSS_HEIGHT, 3, 1)); + //mLayerDecl.elements.push_back( + // TerrainLayerSamplerElement(0, TLSS_SPECULAR, 3, 1)); + //mLayerDecl.elements.push_back( + // TerrainLayerSamplerElement(1, TLSS_NORMAL, 0, 3)); + //mLayerDecl.elements.push_back( + // TerrainLayerSamplerElement(1, TLSS_HEIGHT, 3, 1)); mProfiles.push_back(OGRE_NEW SM2Profile(this, "SM2", "Profile for rendering on Shader Model 2 capable cards")); @@ -212,8 +212,8 @@ namespace Ogre freeTextureUnits -= numShadowTextures; } - // each layer needs 2.25 units (1xdiffusespec, 1xnormalheight, 0.25xblend) - return static_cast(freeTextureUnits / 2.25f); + // each layer needs 2.25 units (1xdiffusespec, (1xnormalheight), 0.25xblend) + return static_cast(freeTextureUnits / (1.25f + (mLayerNormalMappingEnabled||mLayerParallaxMappingEnabled))); } @@ -368,8 +368,10 @@ namespace Ogre { // diffuse / specular tu = pass->createTextureUnitState(terrain->getLayerTextureName(i, 0)); + // normal / height - tu = pass->createTextureUnitState(terrain->getLayerTextureName(i, 1)); + if (mLayerNormalMappingEnabled || mLayerParallaxMappingEnabled) + tu = pass->createTextureUnitState(terrain->getLayerTextureName(i, 1)); } } @@ -739,8 +741,10 @@ namespace Ogre if(prof->isLayerNormalMappingEnabled() || prof->isLayerParallaxMappingEnabled()) ret->setParameter("profiles", "ps_3_0 ps_2_x fp40 arbfp1"); - else - ret->setParameter("profiles", "ps_3_0 ps_2_0 fp30 arbfp1"); + //else + //ret->setParameter("profiles", "ps_3_0 ps_2_0 fp30 arbfp1"); + else // fp30 doesn't work (black terrain) + ret->setParameter("profiles", "ps_3_0 ps_2_x fp40 arbfp1"); ret->setParameter("entry_point", "main_fp"); return ret; @@ -982,7 +986,9 @@ namespace Ogre { outStream << ", uniform sampler2D difftex" << i << " : register(s" << currentSamplerIdx++ << ")\n"; - outStream << ", uniform sampler2D normtex" << i + + if (prof->mLayerNormalMappingEnabled || prof->mLayerParallaxMappingEnabled) + outStream << ", uniform sampler2D normtex" << i << " : register(s" << currentSamplerIdx++ << ")\n"; } } @@ -1238,7 +1244,7 @@ namespace Ogre // specular default if (!prof->isLayerSpecularMappingEnabled()) - outStream << " specular = 1.0;\n"; + outStream << " specular = 0.0;\n"; if (tt == RENDER_COMPOSITE_MAP) { @@ -1266,7 +1272,7 @@ namespace Ogre } // Final return - outStream << " return outputCol;\n" + outStream << " return outputCol;\n" << "}\n"; } From 5b5e8ba4fdb0fdd6bf0c62787b8cb32736bca7f9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 28 Feb 2012 16:20:57 +0100 Subject: [PATCH 10/39] fix a typo that caused SPLIT_TERRAIN=true blendmap to be too detailed (causing insane loading times) --- apps/openmw/mwrender/terrain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index f3104ef59..e25b4b189 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -18,7 +18,7 @@ namespace MWRender mTerrainGlobals = OGRE_NEW Ogre::TerrainGlobalOptions(); mTerrainGlobals->setMaxPixelError(8); - mTerrainGlobals->setLayerBlendMapSize(SPLIT_TERRAIN ? 1024 : 256); + mTerrainGlobals->setLayerBlendMapSize(SPLIT_TERRAIN ? 256 : 1024); Ogre::TerrainMaterialGeneratorPtr matGen; TerrainMaterialGeneratorB* matGenP = new TerrainMaterialGeneratorB(); From abcf25ead21909d6f9f0f6ac33ed98b31526d686 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 28 Feb 2012 16:41:05 +0100 Subject: [PATCH 11/39] terrain shader performance improvement (tested on opengl only, please test on directx) --- apps/openmw/mwrender/terrainmaterial.cpp | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwrender/terrainmaterial.cpp b/apps/openmw/mwrender/terrainmaterial.cpp index 61a7cdb19..2b1eb2604 100644 --- a/apps/openmw/mwrender/terrainmaterial.cpp +++ b/apps/openmw/mwrender/terrainmaterial.cpp @@ -1113,10 +1113,17 @@ namespace Ogre "." + blendChannel; // generate early-out conditional - /* Disable - causing some issues even when trying to force the use of texldd + // Disable - causing some issues even when trying to force the use of texldd + + // comment by scrawl: + // on a NVIDIA card in opengl mode, didn't produce any problems, + // while increasing FPS from 170 to 185 (!!!) in the same area + // so let's try this out - if something does cause problems for + // someone else (with a different card / renderer) we can just + // add a vendor-specific check here if (layer && prof->_isSM3Available()) outStream << " if (" << blendWeightStr << " > 0.0003)\n { \n"; - */ + // generate UV outStream << " float2 uv" << layer << " = layerUV" << uvIdx << uvChannels << ";\n"; @@ -1165,10 +1172,17 @@ namespace Ogre } // End early-out - /* Disable - causing some issues even when trying to force the use of texldd + // Disable - causing some issues even when trying to force the use of texldd + + // comment by scrawl: + // on a NVIDIA card in opengl mode, didn't produce any problems, + // while increasing FPS from 170 to 185 (!!!) in the same area + // so let's try this out - if something does cause problems for + // someone else (with a different card / renderer) we can just + // add a vendor-specific check here if (layer && prof->_isSM3Available()) outStream << " } // early-out blend value\n"; - */ + } //--------------------------------------------------------------------- void TerrainMaterialGeneratorB::SM2Profile::ShaderHelperCg::generateVpFooter( From f55905609f21036cfbf265194171acc9de47cedb Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 28 Feb 2012 17:18:07 +0100 Subject: [PATCH 12/39] fixed the terrain normals - they had to be adjusted because we use Morrowind's coordinate system --- apps/openmw/mwrender/terrain.cpp | 2 +- apps/openmw/mwrender/terrainmaterial.cpp | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index e25b4b189..d5f768428 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -31,7 +31,7 @@ namespace MWRender TerrainMaterialGeneratorB::SM2Profile* matProfile = static_cast(activeProfile); - matProfile->setLightmapEnabled(false); // this doesn't disable the lightmap, although it should ?? + matProfile->setLightmapEnabled(false); matProfile->setReceiveDynamicShadowsEnabled(false); matProfile->setLayerNormalMappingEnabled(false); matProfile->setLayerParallaxMappingEnabled(false); diff --git a/apps/openmw/mwrender/terrainmaterial.cpp b/apps/openmw/mwrender/terrainmaterial.cpp index 2b1eb2604..541fcb91e 100644 --- a/apps/openmw/mwrender/terrainmaterial.cpp +++ b/apps/openmw/mwrender/terrainmaterial.cpp @@ -196,7 +196,8 @@ namespace Ogre // count the texture units free uint8 freeTextureUnits = 16; // lightmap - --freeTextureUnits; + if (mLightmapEnabled) + --freeTextureUnits; // normalmap --freeTextureUnits; // colourmap @@ -1019,7 +1020,8 @@ namespace Ogre { outStream << // global normal - " float3 normal = expand(tex2D(globalNormal, uv)).rgb;\n"; + " float3 normal = expand(tex2D(globalNormal, uv)).rgb;\n" + " normal = float3(normal.x, normal.z, -normal.y); \n"; // convert Ogre to MW coordinate system } From be5555956ced6012bba5937accc9043bfbc425bd Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 28 Feb 2012 22:04:44 +0100 Subject: [PATCH 13/39] improved composite map, now adapts to the ambient/diffuse light --- apps/openmw/mwrender/renderingmanager.cpp | 8 +++++--- apps/openmw/mwrender/terrain.cpp | 14 ++++++++++++++ apps/openmw/mwrender/terrain.hpp | 3 +++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 04ddc2cf8..7fcfc2a8d 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -232,17 +232,17 @@ void RenderingManager::setAmbientMode() { case 0: - mRendering.getScene()->setAmbientLight(mAmbientColor); + setAmbientColour(mAmbientColor); break; case 1: - mRendering.getScene()->setAmbientLight(0.7f*mAmbientColor + 0.3f*ColourValue(1,1,1)); + setAmbientColour(0.7f*mAmbientColor + 0.3f*ColourValue(1,1,1)); break; case 2: - mRendering.getScene()->setAmbientLight(ColourValue(1,1,1)); + setAmbientColour(ColourValue(1,1,1)); break; } } @@ -297,11 +297,13 @@ void RenderingManager::skipAnimation (const MWWorld::Ptr& ptr) void RenderingManager::setSunColour(const Ogre::ColourValue& colour) { mSun->setDiffuseColour(colour); + mTerrainManager->setDiffuse(colour); } void RenderingManager::setAmbientColour(const Ogre::ColourValue& colour) { mRendering.getScene()->setAmbientLight(colour); + mTerrainManager->setAmbient(colour); } void RenderingManager::sunEnable() diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index d5f768428..dd05bf333 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -73,6 +73,20 @@ namespace MWRender OGRE_DELETE mTerrainGroup; OGRE_DELETE mTerrainGlobals; } + + //---------------------------------------------------------------------------------------------- + + void TerrainManager::setDiffuse(const Ogre::ColourValue& diffuse) + { + mTerrainGlobals->setCompositeMapDiffuse(diffuse); + } + + //---------------------------------------------------------------------------------------------- + + void TerrainManager::setAmbient(const Ogre::ColourValue& ambient) + { + mTerrainGlobals->setCompositeMapAmbient(ambient); + } //---------------------------------------------------------------------------------------------- diff --git a/apps/openmw/mwrender/terrain.hpp b/apps/openmw/mwrender/terrain.hpp index 195741b0f..3f2633ff9 100644 --- a/apps/openmw/mwrender/terrain.hpp +++ b/apps/openmw/mwrender/terrain.hpp @@ -31,6 +31,9 @@ namespace MWRender{ TerrainManager(Ogre::SceneManager*); virtual ~TerrainManager(); + void setDiffuse(const Ogre::ColourValue& diffuse); + void setAmbient(const Ogre::ColourValue& ambient); + void cellAdded(MWWorld::Ptr::CellStore* store); void cellRemoved(MWWorld::Ptr::CellStore* store); private: From f2b807142a8f1be856c88a62eab9cfaec70f213c Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 29 Feb 2012 11:08:25 +0100 Subject: [PATCH 14/39] update mangle --- libs/mangle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/mangle b/libs/mangle index f3c9694bf..14b2851e7 160000 --- a/libs/mangle +++ b/libs/mangle @@ -1 +1 @@ -Subproject commit f3c9694bf249a34eae05f0304e6bfc120014ce8c +Subproject commit 14b2851e72f610ae81dd296598867e6fb0babd2a From 1d0ae9c8d122ea168b9515bb696a74e6d8c9aa05 Mon Sep 17 00:00:00 2001 From: Jacob Essex Date: Wed, 29 Feb 2012 23:05:22 +0000 Subject: [PATCH 15/39] Start of a basic implementation of fake vertex colours --- apps/openmw/mwrender/terrain.cpp | 146 ++++++++++++++++++++++++++++--- apps/openmw/mwrender/terrain.hpp | 26 +++++- components/esm/loadland.cpp | 5 +- components/esm/loadland.hpp | 2 + 4 files changed, 167 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index 710a3ad05..89c362c4c 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -2,6 +2,8 @@ #include #include +#include + #include "terrain.hpp" #include "components/esm/loadland.hpp" @@ -104,6 +106,9 @@ namespace MWRender const int terrainX = cellX * 2 + x; const int terrainY = cellY * 2 + y; + //it makes far more sense to reallocate the memory here, + //and let Ogre deal with it due to the issues with deleting + //it at the wrong time if using threads (Which Ogre::Terrain does) terrainData.inputFloat = OGRE_ALLOC_T(float, mLandSize*mLandSize, Ogre::MEMCATEGORY_GEOMETRY); @@ -122,10 +127,16 @@ namespace MWRender mLandSize*sizeof(float)); } + //this should really be 33*33 + Ogre::uchar* vertexColourAlpha = OGRE_ALLOC_T(Ogre::uchar, + mLandSize*mLandSize, + Ogre::MEMCATEGORY_GENERAL); + std::map indexes; initTerrainTextures(&terrainData, store, x * numTextures, y * numTextures, - numTextures, indexes); + numTextures, vertexColourAlpha, + indexes); assert( mTerrainGroup->getTerrain(cellX, cellY) == NULL && "The terrain for this cell already existed" ); @@ -133,9 +144,20 @@ namespace MWRender mTerrainGroup->loadTerrain(terrainX, terrainY, true); Ogre::Terrain* terrain = mTerrainGroup->getTerrain(terrainX, terrainY); + + Ogre::Image vertexColourBlendMap = Ogre::Image(); + vertexColourBlendMap.loadDynamicImage(vertexColourAlpha, + mLandSize, mLandSize, 1, + Ogre::PF_A8, true) + .resize(mTerrainGlobals->getLayerBlendMapSize(), + mTerrainGlobals->getLayerBlendMapSize(), + Ogre::Image::FILTER_BOX); + initTerrainBlendMaps(terrain, store, x * numTextures, y * numTextures, - numTextures, indexes); + numTextures, + vertexColourBlendMap.getData(), + indexes); } } @@ -154,16 +176,30 @@ namespace MWRender &store->land[1][1]->landData->heights[0], mLandSize*mLandSize*sizeof(float)); + Ogre::uchar* vertexColourAlpha = OGRE_ALLOC_T(Ogre::uchar, + mLandSize*mLandSize, + Ogre::MEMCATEGORY_GENERAL); + std::map indexes; initTerrainTextures(&terrainData, store, 0, 0, - ESM::Land::LAND_TEXTURE_SIZE, indexes); + ESM::Land::LAND_TEXTURE_SIZE, vertexColourAlpha, indexes); mTerrainGroup->defineTerrain(cellX, cellY, &terrainData); mTerrainGroup->loadTerrain(cellX, cellY, true); Ogre::Terrain* terrain = mTerrainGroup->getTerrain(cellX, cellY); + + Ogre::Image vertexColourBlendMap = Ogre::Image(); + vertexColourBlendMap.loadDynamicImage(vertexColourAlpha, + mLandSize, mLandSize, 1, + Ogre::PF_A8, true) + .resize(mTerrainGlobals->getLayerBlendMapSize(), + mTerrainGlobals->getLayerBlendMapSize(), + Ogre::Image::FILTER_BOX); + initTerrainBlendMaps(terrain, store, 0, 0, - ESM::Land::LAND_TEXTURE_SIZE, indexes); + ESM::Land::LAND_TEXTURE_SIZE, + vertexColourBlendMap.getData(), indexes); } mTerrainGroup->freeTemporaryResources(); @@ -196,6 +232,7 @@ namespace MWRender void TerrainManager::initTerrainTextures(Ogre::Terrain::ImportData* terrainData, MWWorld::Ptr::CellStore* store, int fromX, int fromY, int size, + Ogre::uchar* vertexColourAlpha, std::map& indexes) { assert(store != NULL && "store must be a valid pointer"); @@ -227,7 +264,7 @@ namespace MWRender { //NB: All vtex ids are +1 compared to the ltex ids assert((int)ltexIndex >= 0 && - store->landTextures->ltex.size() > (size_t)ltexIndex - 1 && + (int)store->landTextures->ltex.size() >= (int)ltexIndex - 1 && "LAND.VTEX must be within the bounds of the LTEX array"); std::string texture; @@ -262,6 +299,17 @@ namespace MWRender } } } + + //add the vertex colour overlay + //TODO sort the *4 bit + Ogre::TexturePtr vclr = getVertexColours(store, vertexColourAlpha, fromX*4, fromY*4, mLandSize); + Ogre::TexturePtr normDisp = getNormalDisp("DOES_NOT_EXIST"); + + const size_t position = terrainData->layerList.size(); + terrainData->layerList.push_back(Ogre::Terrain::LayerInstance()); + terrainData->layerList[position].worldSize = mRealSize; + terrainData->layerList[position].textureNames.push_back(vclr->getName()); + terrainData->layerList[position].textureNames.push_back(normDisp->getName()); } //---------------------------------------------------------------------------------------------- @@ -269,6 +317,7 @@ namespace MWRender void TerrainManager::initTerrainBlendMaps(Ogre::Terrain* terrain, MWWorld::Ptr::CellStore* store, int fromX, int fromY, int size, + Ogre::uchar* vertexColourAlpha, const std::map& indexes) { assert(store != NULL && "store must be a valid pointer"); @@ -294,6 +343,15 @@ namespace MWRender ->getBlendPointer(); memset(pBlend, 0, sizeof(float) * blendSize * blendSize); } + //except the overlay, which we will just splat over the top + { + //the overlay is always the last texture layer + float* pBlend = terrain->getLayerBlendMap(terrain->getLayerCount() - 1) + ->getBlendPointer(); + for ( int i = 0; i < blendSize * blendSize; i++ ){ + *pBlend++ = (*vertexColourAlpha++)/255.0f; + } + } //covert the ltex data into a set of blend maps for ( int texY = fromY - 1; texY < fromY + size + 1; texY++ ) @@ -354,10 +412,9 @@ namespace MWRender } } - //update the maps - for ( iter = indexes.begin(); iter != indexes.end(); ++iter ) + for ( int i = 1; i < terrain->getLayerCount(); i++ ) { - Ogre::TerrainLayerBlendMap* blend = terrain->getLayerBlendMap(iter->second); + Ogre::TerrainLayerBlendMap* blend = terrain->getLayerBlendMap(i); blend->dirty(); blend->update(); } @@ -424,8 +481,7 @@ namespace MWRender return texMgr->getByName(normalTextureName); } - const std::string textureName = "default_terrain_normal"; - if ( !texMgr->getByName(textureName).isNull() ) + const std::string textureName = "default_terrain_normal"; if ( !texMgr->getByName(textureName).isNull() ) { return texMgr->getByName(textureName); } @@ -451,4 +507,74 @@ namespace MWRender return tex; } + //---------------------------------------------------------------------------------------------- + + Ogre::TexturePtr TerrainManager::getVertexColours(MWWorld::Ptr::CellStore* store, + Ogre::uchar* alpha, + int fromX, int fromY, int size) + { + Ogre::TextureManager* const texMgr = Ogre::TextureManager::getSingletonPtr(); + const char* const colours = store->land[1][1]->landData->colours; + + const std::string colourTextureName = "VtexColours_" + + boost::lexical_cast(store->cell->getGridX()) + + "_" + + boost::lexical_cast(store->cell->getGridY()) + + "_" + + boost::lexical_cast(fromX) + + "_" + + boost::lexical_cast(fromY); + + Ogre::TexturePtr tex = texMgr->getByName(colourTextureName); + if ( !tex.isNull() ) + { + return tex; + } + + tex = texMgr->createManual(colourTextureName, + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + Ogre::TEX_TYPE_2D, size, size, 0, Ogre::PF_BYTE_BGRA); + + Ogre::HardwarePixelBufferSharedPtr pixelBuffer = tex->getBuffer(); + + pixelBuffer->lock(Ogre::HardwareBuffer::HBL_NORMAL); + const Ogre::PixelBox& pixelBox = pixelBuffer->getCurrentLock(); + + Ogre::uint8* pDest = static_cast(pixelBox.data); + + for ( int y = 0; y < size; y++ ) + { + for ( int x = 0; x < size; x++ ) + { + const size_t colourOffset = (y+fromY)*3*65 + (x+fromX)*3; + + assert( colourOffset >= 0 && colourOffset < 65*65*3 && + "Colour offset is out of the expected bounds of record" ); + + const unsigned char r = colours[colourOffset + 0]; + const unsigned char g = colours[colourOffset + 1]; + const unsigned char b = colours[colourOffset + 2]; + + //as is the case elsewhere we need to flip the y + const size_t imageOffset = (size - 1 - y)*size*4 + x*4; + pDest[imageOffset + 0] = b; + pDest[imageOffset + 1] = g; + pDest[imageOffset + 2] = r; + pDest[imageOffset + 3] = 255; //Alpha, TODO this needs to be removed + + const size_t alphaOffset = (size - 1 - y)*size + x; + if ( r == 255 && g == 255 && b == 255 ){ + alpha[alphaOffset] = 0; + }else{ + alpha[alphaOffset] = 128; + } + + } + } + + pixelBuffer->unlock(); + + return tex; + } + } diff --git a/apps/openmw/mwrender/terrain.hpp b/apps/openmw/mwrender/terrain.hpp index 548d00eab..a201fdb61 100644 --- a/apps/openmw/mwrender/terrain.hpp +++ b/apps/openmw/mwrender/terrain.hpp @@ -74,12 +74,16 @@ namespace MWRender{ * @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 vertexColourAlpha this should be an empty array containing the number of + * vertexes in this terrain segment. It is filled with the + * alpha values for the vertex colours * @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, + Ogre::uchar* vertexColourAlpha, std::map& indexes); /** @@ -90,11 +94,16 @@ namespace MWRender{ * @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 vertexColourAlpha this should be an array containing the alpha values + * for the vertex colours. NOTE: This should be the + * size of a splat map, which is NOT the same size + * as retured from initTerrainTextures. * @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, + Ogre::uchar* vertexColourAlpha, const std::map& indexes); /** @@ -118,7 +127,22 @@ namespace MWRender{ * @param fileName the name of the *diffuse* texture */ Ogre::TexturePtr getNormalDisp(const std::string& fileName); - + + /** + * Due to the fact that Ogre terrain doesn't support vertex colours + * we have to generate them manually + * + * @param store the cell store for the given terrain cell + * @param vertexColourAlpha this should be an empty array containing the number of + * vertexes in this terrain segment. It is filled with the + * alpha values for the vertex colours + * @param fromX the *vertex* index in the current cell to start making texture from + * @param fromY the *vertex* index in the current cell to start making the texture from + * @param size the size (number of vertexes) to get + */ + Ogre::TexturePtr getVertexColours(MWWorld::Ptr::CellStore* store, + Ogre::uchar* alpha, + int fromX, int fromY, int size); }; } diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index 1d670036e..3bca8692b 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -93,7 +93,10 @@ void Land::loadData(ESMReader &esm) } if (esm.isNextSub("VCLR")) { - esm.skipHSubSize(12675); + landData->usingColours = true; + esm.getHExact(&landData->colours, 3*LAND_NUM_VERTS); + }else{ + landData->usingColours = false; } //TODO fix magic numbers uint16_t vtex[512]; diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index 4219f3eb6..eeb198e90 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -58,6 +58,8 @@ struct Land float heights[LAND_NUM_VERTS]; //float normals[LAND_NUM_VERTS * 3]; uint16_t textures[LAND_NUM_TEXTURES]; + + bool usingColours; char colours[3 * LAND_NUM_VERTS]; }; From 69243486960bb9b4ab8bed03d591dcbf04a0f085 Mon Sep 17 00:00:00 2001 From: Jacob Essex Date: Wed, 29 Feb 2012 23:33:54 +0000 Subject: [PATCH 16/39] Fixed some texture splatting issues with loading empty terrain cells --- apps/openmw/mwrender/terrain.cpp | 3 +-- components/esm/loadland.cpp | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index 89c362c4c..0e6fdff01 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -263,8 +263,7 @@ namespace MWRender if ( it == indexes.end() ) { //NB: All vtex ids are +1 compared to the ltex ids - assert((int)ltexIndex >= 0 && - (int)store->landTextures->ltex.size() >= (int)ltexIndex - 1 && + assert( (int)store->landTextures->ltex.size() >= (int)ltexIndex - 1 && "LAND.VTEX must be within the bounds of the LTEX array"); std::string texture; diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index 3bca8692b..4fe6279d3 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -111,6 +111,8 @@ void Land::loadData(ESMReader &esm) } else { + landData->usingColours = false; + memset(&landData->textures, 0, 512 * sizeof(uint16_t)); for (int i = 0; i < LAND_NUM_VERTS; i++) { landData->heights[i] = -256.0f * HEIGHT_SCALE; From 611f336d0731bdff53203c6aa93fa5b9af087522 Mon Sep 17 00:00:00 2001 From: Jacob Essex Date: Wed, 29 Feb 2012 23:38:21 +0000 Subject: [PATCH 17/39] Now doesn't attempt to use fake vertex colours with cells that don't have vertex colours --- apps/openmw/mwrender/terrain.cpp | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index 0e6fdff01..59f2f2082 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -300,15 +300,18 @@ namespace MWRender } //add the vertex colour overlay - //TODO sort the *4 bit - Ogre::TexturePtr vclr = getVertexColours(store, vertexColourAlpha, fromX*4, fromY*4, mLandSize); - Ogre::TexturePtr normDisp = getNormalDisp("DOES_NOT_EXIST"); - - const size_t position = terrainData->layerList.size(); - terrainData->layerList.push_back(Ogre::Terrain::LayerInstance()); - terrainData->layerList[position].worldSize = mRealSize; - terrainData->layerList[position].textureNames.push_back(vclr->getName()); - terrainData->layerList[position].textureNames.push_back(normDisp->getName()); + if ( store->land[1][1]->landData->usingColours ) + { + //TODO sort the *4 bit + Ogre::TexturePtr vclr = getVertexColours(store, vertexColourAlpha, fromX*4, fromY*4, mLandSize); + Ogre::TexturePtr normDisp = getNormalDisp("DOES_NOT_EXIST"); + + const size_t position = terrainData->layerList.size(); + terrainData->layerList.push_back(Ogre::Terrain::LayerInstance()); + terrainData->layerList[position].worldSize = mRealSize; + terrainData->layerList[position].textureNames.push_back(vclr->getName()); + terrainData->layerList[position].textureNames.push_back(normDisp->getName()); + } } //---------------------------------------------------------------------------------------------- @@ -343,6 +346,7 @@ namespace MWRender memset(pBlend, 0, sizeof(float) * blendSize * blendSize); } //except the overlay, which we will just splat over the top + if ( store->land[1][1]->landData->usingColours ) { //the overlay is always the last texture layer float* pBlend = terrain->getLayerBlendMap(terrain->getLayerCount() - 1) From fddf7b4dd059eae8c0b0a536d8914471d7869642 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 1 Mar 2012 14:35:39 +0100 Subject: [PATCH 18/39] terrain material now supports point lights --- apps/openmw/mwrender/terrain.cpp | 10 +-- apps/openmw/mwrender/terrainmaterial.cpp | 93 ++++++++++++++++++++---- apps/openmw/mwrender/terrainmaterial.hpp | 2 + 3 files changed, 84 insertions(+), 21 deletions(-) diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index 30e0823f9..df5bba7c4 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -29,10 +29,10 @@ namespace MWRender static_cast(activeProfile); mTerrainGlobals->setMaxPixelError(8); - //mTerrainGlobals->setLayerBlendMapSize(SPLIT_TERRAIN ? 256 : 1024); - //mTerrainGlobals->setLightMapSize(SPLIT_TERRAIN ? 256 : 1024); - //mTerrainGlobals->setCompositeMapSize(SPLIT_TERRAIN ? 256 : 1024); - //mTerrainGlobals->setDefaultGlobalColourMapSize(256); + mTerrainGlobals->setLayerBlendMapSize(SPLIT_TERRAIN ? 256 : 1024); + mTerrainGlobals->setLightMapSize(SPLIT_TERRAIN ? 256 : 1024); + mTerrainGlobals->setCompositeMapSize(SPLIT_TERRAIN ? 256 : 1024); + mTerrainGlobals->setDefaultGlobalColourMapSize(256); //10 (default) didn't seem to be quite enough mTerrainGlobals->setSkirtSize(128); @@ -266,7 +266,7 @@ namespace MWRender Ogre::TexturePtr normDisp = getNormalDisp("textures\\" + texture); - //terrainData->layerList[position].worldSize = 256; + 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 diff --git a/apps/openmw/mwrender/terrainmaterial.cpp b/apps/openmw/mwrender/terrainmaterial.cpp index 8985fc8ae..9121da793 100644 --- a/apps/openmw/mwrender/terrainmaterial.cpp +++ b/apps/openmw/mwrender/terrainmaterial.cpp @@ -36,6 +36,8 @@ THE SOFTWARE. #include "OgreHardwarePixelBuffer.h" #include "OgreShadowCameraSetupPSSM.h" +#define POINTLIGHTS + namespace Ogre { //--------------------------------------------------------------------- @@ -218,6 +220,22 @@ namespace Ogre } + int TerrainMaterialGeneratorB::SM2Profile::getNumberOfLightsSupported() const + { + #ifndef POINTLIGHTS + return 1; + #else + // number of supported lights depends on the number of available constant registers, + // which in turn depends on the shader profile used + if (GpuProgramManager::getSingleton().isSyntaxSupported("ps_3_0") + || GpuProgramManager::getSingleton().isSyntaxSupported("ps_4_0") + || GpuProgramManager::getSingleton().isSyntaxSupported("fp40") + ) + return 32; + else + return 8; + #endif + } //--------------------------------------------------------------------- MaterialPtr TerrainMaterialGeneratorB::SM2Profile::generate(const Terrain* terrain) { @@ -542,9 +560,15 @@ namespace Ogre params->setIgnoreMissingParams(true); params->setNamedAutoConstant("ambient", GpuProgramParameters::ACT_AMBIENT_LIGHT_COLOUR); - params->setNamedAutoConstant("lightPosObjSpace", GpuProgramParameters::ACT_LIGHT_POSITION_OBJECT_SPACE, 0); - params->setNamedAutoConstant("lightDiffuseColour", GpuProgramParameters::ACT_LIGHT_DIFFUSE_COLOUR, 0); - //params->setNamedAutoConstant("lightSpecularColour", GpuProgramParameters::ACT_LIGHT_SPECULAR_COLOUR, 0); + + for (int i=0; igetNumberOfLightsSupported(); ++i) + { + params->setNamedAutoConstant("lightPosObjSpace"+StringConverter::toString(i), GpuProgramParameters::ACT_LIGHT_POSITION_OBJECT_SPACE, i); + params->setNamedAutoConstant("lightDiffuseColour"+StringConverter::toString(i), GpuProgramParameters::ACT_LIGHT_DIFFUSE_COLOUR, i); + params->setNamedAutoConstant("lightAttenuation"+StringConverter::toString(i), GpuProgramParameters::ACT_LIGHT_ATTENUATION, i); + //params->setNamedAutoConstant("lightSpecularColour"+StringConverter::toString(i), GpuProgramParameters::ACT_LIGHT_SPECULAR_COLOUR, i); + } + params->setNamedAutoConstant("eyePosObjSpace", GpuProgramParameters::ACT_CAMERA_POSITION_OBJECT_SPACE); params->setNamedAutoConstant("fogColour", GpuProgramParameters::ACT_FOG_COLOUR); @@ -945,10 +969,25 @@ namespace Ogre outStream << // Only 1 light supported in this version // deferred shading profile / generator later, ok? :) - "uniform float3 ambient,\n" - "uniform float4 lightPosObjSpace,\n" - "uniform float3 lightDiffuseColour,\n" - //"uniform float3 lightSpecularColour,\n" + "uniform float3 ambient,\n"; + + + for (int i=0; igetNumberOfLightsSupported(); ++i) + { + outStream << + "uniform float4 lightPosObjSpace"<getNumberOfLightsSupported(); ++i) + outStream << + " float3 lightDir"<getNumberOfLightsSupported(); ++i) + { + outStream << " float3 halfAngle"<getNumberOfLightsSupported(); ++i) + outStream << " outputCol.rgb += litRes"<isLayerSpecularMappingEnabled()) diff --git a/apps/openmw/mwrender/terrainmaterial.hpp b/apps/openmw/mwrender/terrainmaterial.hpp index eaa5572ee..7c0b87ce4 100644 --- a/apps/openmw/mwrender/terrainmaterial.hpp +++ b/apps/openmw/mwrender/terrainmaterial.hpp @@ -142,6 +142,8 @@ namespace Ogre */ bool getReceiveDynamicShadowsLowLod() const { return mLowLodShadows; } + int getNumberOfLightsSupported() const; + /// Internal bool _isSM3Available() const { return mSM3Available; } From 562cc7aa30f96cabf3d8380696f777f622dcd45c Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 1 Mar 2012 15:03:43 +0100 Subject: [PATCH 19/39] fixed 2 assertions that caused crashes --- apps/openmw/mwrender/terrain.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index df5bba7c4..5925ad82a 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -146,16 +146,16 @@ namespace MWRender x * numTextures, y * numTextures, numTextures, indexes); - assert( mTerrainGroup->getTerrain(cellX, cellY) == NULL && - "The terrain for this cell already existed" ); - 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); - + if (mTerrainGroup->getTerrain(cellX, cellY) == NULL) + { + 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); + } } } } @@ -246,7 +246,7 @@ namespace MWRender { //NB: All vtex ids are +1 compared to the ltex ids assert((int)ltexIndex >= 0 && - store->landTextures->ltex.size() > (size_t)ltexIndex - 1 && + (int)store->landTextures->ltex.size() > (int)ltexIndex - 1 && "LAND.VTEX must be within the bounds of the LTEX array"); std::string texture; From 8dd6e75ae1437f9db6db52e0d9fab5498f78624b Mon Sep 17 00:00:00 2001 From: Jacob Essex Date: Fri, 2 Mar 2012 11:56:51 +0000 Subject: [PATCH 20/39] Vertex colours now uses the Ogre::Terrain colour map --- apps/openmw/mwrender/terrain.cpp | 134 +++++++------------------------ apps/openmw/mwrender/terrain.hpp | 17 +--- 2 files changed, 34 insertions(+), 117 deletions(-) diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index 59f2f2082..2d433d52f 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -2,8 +2,6 @@ #include #include -#include - #include "terrain.hpp" #include "components/esm/loadland.hpp" @@ -127,38 +125,29 @@ namespace MWRender mLandSize*sizeof(float)); } - //this should really be 33*33 - Ogre::uchar* vertexColourAlpha = OGRE_ALLOC_T(Ogre::uchar, - mLandSize*mLandSize, - Ogre::MEMCATEGORY_GENERAL); - std::map indexes; initTerrainTextures(&terrainData, store, x * numTextures, y * numTextures, - numTextures, vertexColourAlpha, - indexes); + numTextures, indexes); assert( mTerrainGroup->getTerrain(cellX, cellY) == NULL && "The terrain for this cell already existed" ); mTerrainGroup->defineTerrain(terrainX, terrainY, &terrainData); mTerrainGroup->loadTerrain(terrainX, terrainY, true); - Ogre::Terrain* terrain = mTerrainGroup->getTerrain(terrainX, terrainY); - - Ogre::Image vertexColourBlendMap = Ogre::Image(); - vertexColourBlendMap.loadDynamicImage(vertexColourAlpha, - mLandSize, mLandSize, 1, - Ogre::PF_A8, true) - .resize(mTerrainGlobals->getLayerBlendMapSize(), - mTerrainGlobals->getLayerBlendMapSize(), - Ogre::Image::FILTER_BOX); + Ogre::Terrain* terrain = mTerrainGroup->getTerrain(terrainX, terrainY); initTerrainBlendMaps(terrain, store, x * numTextures, y * numTextures, numTextures, - vertexColourBlendMap.getData(), indexes); - + + if ( store->land[1][1]->landData->usingColours ) + { + Ogre::Image vertex = getVertexColours(store, x*32, y*32, mLandSize); + terrain->setGlobalColourMapEnabled(true); + terrain->getGlobalColourMap()->loadImage(vertex); + } } } } @@ -176,30 +165,25 @@ namespace MWRender &store->land[1][1]->landData->heights[0], mLandSize*mLandSize*sizeof(float)); - Ogre::uchar* vertexColourAlpha = OGRE_ALLOC_T(Ogre::uchar, - mLandSize*mLandSize, - Ogre::MEMCATEGORY_GENERAL); - std::map indexes; initTerrainTextures(&terrainData, store, 0, 0, - ESM::Land::LAND_TEXTURE_SIZE, vertexColourAlpha, indexes); + ESM::Land::LAND_TEXTURE_SIZE, indexes); mTerrainGroup->defineTerrain(cellX, cellY, &terrainData); mTerrainGroup->loadTerrain(cellX, cellY, true); Ogre::Terrain* terrain = mTerrainGroup->getTerrain(cellX, cellY); - Ogre::Image vertexColourBlendMap = Ogre::Image(); - vertexColourBlendMap.loadDynamicImage(vertexColourAlpha, - mLandSize, mLandSize, 1, - Ogre::PF_A8, true) - .resize(mTerrainGlobals->getLayerBlendMapSize(), - mTerrainGlobals->getLayerBlendMapSize(), - Ogre::Image::FILTER_BOX); - initTerrainBlendMaps(terrain, store, 0, 0, ESM::Land::LAND_TEXTURE_SIZE, - vertexColourBlendMap.getData(), indexes); + indexes); + + if ( store->land[1][1]->landData->usingColours ) + { + Ogre::Image vertex = getVertexColours(store, 0, 0, mLandSize); + terrain->setGlobalColourMapEnabled(true); + terrain->getGlobalColourMap()->loadImage(vertex); + } } mTerrainGroup->freeTemporaryResources(); @@ -232,7 +216,6 @@ namespace MWRender void TerrainManager::initTerrainTextures(Ogre::Terrain::ImportData* terrainData, MWWorld::Ptr::CellStore* store, int fromX, int fromY, int size, - Ogre::uchar* vertexColourAlpha, std::map& indexes) { assert(store != NULL && "store must be a valid pointer"); @@ -298,20 +281,6 @@ namespace MWRender } } } - - //add the vertex colour overlay - if ( store->land[1][1]->landData->usingColours ) - { - //TODO sort the *4 bit - Ogre::TexturePtr vclr = getVertexColours(store, vertexColourAlpha, fromX*4, fromY*4, mLandSize); - Ogre::TexturePtr normDisp = getNormalDisp("DOES_NOT_EXIST"); - - const size_t position = terrainData->layerList.size(); - terrainData->layerList.push_back(Ogre::Terrain::LayerInstance()); - terrainData->layerList[position].worldSize = mRealSize; - terrainData->layerList[position].textureNames.push_back(vclr->getName()); - terrainData->layerList[position].textureNames.push_back(normDisp->getName()); - } } //---------------------------------------------------------------------------------------------- @@ -319,7 +288,6 @@ namespace MWRender void TerrainManager::initTerrainBlendMaps(Ogre::Terrain* terrain, MWWorld::Ptr::CellStore* store, int fromX, int fromY, int size, - Ogre::uchar* vertexColourAlpha, const std::map& indexes) { assert(store != NULL && "store must be a valid pointer"); @@ -345,16 +313,6 @@ namespace MWRender ->getBlendPointer(); memset(pBlend, 0, sizeof(float) * blendSize * blendSize); } - //except the overlay, which we will just splat over the top - if ( store->land[1][1]->landData->usingColours ) - { - //the overlay is always the last texture layer - float* pBlend = terrain->getLayerBlendMap(terrain->getLayerCount() - 1) - ->getBlendPointer(); - for ( int i = 0; i < blendSize * blendSize; i++ ){ - *pBlend++ = (*vertexColourAlpha++)/255.0f; - } - } //covert the ltex data into a set of blend maps for ( int texY = fromY - 1; texY < fromY + size + 1; texY++ ) @@ -484,7 +442,8 @@ namespace MWRender return texMgr->getByName(normalTextureName); } - const std::string textureName = "default_terrain_normal"; if ( !texMgr->getByName(textureName).isNull() ) + const std::string textureName = "default_terrain_normal"; + if ( !texMgr->getByName(textureName).isNull() ) { return texMgr->getByName(textureName); } @@ -512,38 +471,14 @@ namespace MWRender //---------------------------------------------------------------------------------------------- - Ogre::TexturePtr TerrainManager::getVertexColours(MWWorld::Ptr::CellStore* store, - Ogre::uchar* alpha, + Ogre::Image TerrainManager::getVertexColours(MWWorld::Ptr::CellStore* store, int fromX, int fromY, int size) { - Ogre::TextureManager* const texMgr = Ogre::TextureManager::getSingletonPtr(); const char* const colours = store->land[1][1]->landData->colours; - const std::string colourTextureName = "VtexColours_" + - boost::lexical_cast(store->cell->getGridX()) + - "_" + - boost::lexical_cast(store->cell->getGridY()) + - "_" + - boost::lexical_cast(fromX) + - "_" + - boost::lexical_cast(fromY); - - Ogre::TexturePtr tex = texMgr->getByName(colourTextureName); - if ( !tex.isNull() ) - { - return tex; - } - - tex = texMgr->createManual(colourTextureName, - Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - Ogre::TEX_TYPE_2D, size, size, 0, Ogre::PF_BYTE_BGRA); - - Ogre::HardwarePixelBufferSharedPtr pixelBuffer = tex->getBuffer(); - - pixelBuffer->lock(Ogre::HardwareBuffer::HBL_NORMAL); - const Ogre::PixelBox& pixelBox = pixelBuffer->getCurrentLock(); - - Ogre::uint8* pDest = static_cast(pixelBox.data); + Ogre::uchar* imgData = OGRE_ALLOC_T(Ogre::uchar, + size*size*sizeof(Ogre::uchar)*3, + Ogre::MEMCATEGORY_GENERAL); for ( int y = 0; y < size; y++ ) { @@ -559,25 +494,18 @@ namespace MWRender const unsigned char b = colours[colourOffset + 2]; //as is the case elsewhere we need to flip the y - const size_t imageOffset = (size - 1 - y)*size*4 + x*4; - pDest[imageOffset + 0] = b; - pDest[imageOffset + 1] = g; - pDest[imageOffset + 2] = r; - pDest[imageOffset + 3] = 255; //Alpha, TODO this needs to be removed - - const size_t alphaOffset = (size - 1 - y)*size + x; - if ( r == 255 && g == 255 && b == 255 ){ - alpha[alphaOffset] = 0; - }else{ - alpha[alphaOffset] = 128; - } + const size_t imageOffset = (size - 1 - y)*size*3 + x*3; + imgData[imageOffset + 0] = r; + imgData[imageOffset + 1] = g; + imgData[imageOffset + 2] = b; } } - pixelBuffer->unlock(); + Ogre::Image img; + img.loadDynamicImage(imgData, size, size, 1, Ogre::PF_R8G8B8, true); - return tex; + return img; } } diff --git a/apps/openmw/mwrender/terrain.hpp b/apps/openmw/mwrender/terrain.hpp index a201fdb61..232c1d103 100644 --- a/apps/openmw/mwrender/terrain.hpp +++ b/apps/openmw/mwrender/terrain.hpp @@ -74,16 +74,12 @@ namespace MWRender{ * @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 vertexColourAlpha this should be an empty array containing the number of - * vertexes in this terrain segment. It is filled with the - * alpha values for the vertex colours * @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, - Ogre::uchar* vertexColourAlpha, std::map& indexes); /** @@ -94,16 +90,11 @@ namespace MWRender{ * @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 vertexColourAlpha this should be an array containing the alpha values - * for the vertex colours. NOTE: This should be the - * size of a splat map, which is NOT the same size - * as retured from initTerrainTextures. * @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, - Ogre::uchar* vertexColourAlpha, const std::map& indexes); /** @@ -133,15 +124,13 @@ namespace MWRender{ * we have to generate them manually * * @param store the cell store for the given terrain cell - * @param vertexColourAlpha this should be an empty array containing the number of - * vertexes in this terrain segment. It is filled with the - * alpha values for the vertex colours * @param fromX the *vertex* index in the current cell to start making texture from * @param fromY the *vertex* index in the current cell to start making the texture from * @param size the size (number of vertexes) to get + * + * @TODO FIXME the return of this function possibly copies the image data */ - Ogre::TexturePtr getVertexColours(MWWorld::Ptr::CellStore* store, - Ogre::uchar* alpha, + Ogre::Image getVertexColours(MWWorld::Ptr::CellStore* store, int fromX, int fromY, int size); }; From 2bea4c47242420b8d0b5db4661745a410186a727 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 2 Mar 2012 15:29:12 +0100 Subject: [PATCH 21/39] fixed the global colour map, apparently Ogre::Terrain was corrupting it somehow, now we are just bypassing Ogre::Terrain for the colour map and passing it directly to the material, which makes more sense performance-wise anyway --- apps/openmw/mwrender/terrain.cpp | 68 +++++++++++++++++------- apps/openmw/mwrender/terrain.hpp | 4 +- apps/openmw/mwrender/terrainmaterial.cpp | 10 ++-- 3 files changed, 55 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index 4dd03c097..8d82cb296 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -6,6 +6,8 @@ #include "components/esm/loadland.hpp" +#include + using namespace Ogre; namespace MWRender @@ -48,6 +50,7 @@ namespace MWRender matProfile->setLayerNormalMappingEnabled(false); matProfile->setLayerParallaxMappingEnabled(false); matProfile->setReceiveDynamicShadowsEnabled(false); + matProfile->setGlobalColourMapEnabled(true); mLandSize = ESM::Land::LAND_SIZE; mRealSize = ESM::Land::REAL_SIZE; @@ -163,9 +166,12 @@ namespace MWRender if ( store->land[1][1]->landData->usingColours ) { - Ogre::Image vertex = getVertexColours(store, x*32, y*32, mLandSize); - terrain->setGlobalColourMapEnabled(true); - terrain->getGlobalColourMap()->loadImage(vertex); + Ogre::TexturePtr vertex = getVertexColours(store, x*32, y*32, mLandSize); + + MaterialPtr mat = terrain->_getMaterial(); + mat->getTechnique(0)->getPass(0)->getTextureUnitState(1)->setTextureName( vertex->getName() ); + mat = terrain->_getCompositeMapMaterial(); + mat->getTechnique(0)->getPass(0)->getTextureUnitState(1)->setTextureName( vertex->getName() ); } } } @@ -200,9 +206,12 @@ namespace MWRender if ( store->land[1][1]->landData->usingColours ) { - Ogre::Image vertex = getVertexColours(store, 0, 0, mLandSize); - terrain->setGlobalColourMapEnabled(true); - terrain->getGlobalColourMap()->loadImage(vertex); + Ogre::TexturePtr vertex = getVertexColours(store, 0, 0, mLandSize); + + MaterialPtr mat = terrain->_getMaterial(); + mat->getTechnique(0)->getPass(0)->getTextureUnitState(1)->setTextureName( vertex->getName() ); + mat = terrain->_getCompositeMapMaterial(); + mat->getTechnique(0)->getPass(0)->getTextureUnitState(1)->setTextureName( vertex->getName() ); } } @@ -495,14 +504,37 @@ namespace MWRender //---------------------------------------------------------------------------------------------- - Ogre::Image TerrainManager::getVertexColours(MWWorld::Ptr::CellStore* store, - int fromX, int fromY, int size) + Ogre::TexturePtr TerrainManager::getVertexColours(MWWorld::Ptr::CellStore* store, + int fromX, int fromY, int size) { + Ogre::TextureManager* const texMgr = Ogre::TextureManager::getSingletonPtr(); const char* const colours = store->land[1][1]->landData->colours; - Ogre::uchar* imgData = OGRE_ALLOC_T(Ogre::uchar, - size*size*sizeof(Ogre::uchar)*3, - Ogre::MEMCATEGORY_GENERAL); + const std::string colourTextureName = "VtexColours_" + + boost::lexical_cast(store->cell->getGridX()) + + "_" + + boost::lexical_cast(store->cell->getGridY()) + + "_" + + boost::lexical_cast(fromX) + + "_" + + boost::lexical_cast(fromY); + + Ogre::TexturePtr tex = texMgr->getByName(colourTextureName); + if ( !tex.isNull() ) + { + return tex; + } + + tex = texMgr->createManual(colourTextureName, + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + Ogre::TEX_TYPE_2D, size, size, 0, Ogre::PF_BYTE_BGRA); + + Ogre::HardwarePixelBufferSharedPtr pixelBuffer = tex->getBuffer(); + + pixelBuffer->lock(Ogre::HardwareBuffer::HBL_DISCARD); + const Ogre::PixelBox& pixelBox = pixelBuffer->getCurrentLock(); + + Ogre::uint8* pDest = static_cast(pixelBox.data); for ( int y = 0; y < size; y++ ) { @@ -518,18 +550,16 @@ namespace MWRender const unsigned char b = colours[colourOffset + 2]; //as is the case elsewhere we need to flip the y - const size_t imageOffset = (size - 1 - y)*size*3 + x*3; - imgData[imageOffset + 0] = r; - imgData[imageOffset + 1] = g; - imgData[imageOffset + 2] = b; - + const size_t imageOffset = (size - 1 - y)*size*4 + x*4; + pDest[imageOffset + 0] = b; + pDest[imageOffset + 1] = g; + pDest[imageOffset + 2] = r; } } - Ogre::Image img; - img.loadDynamicImage(imgData, size, size, 1, Ogre::PF_R8G8B8, true); + pixelBuffer->unlock(); - return img; + return tex; } } diff --git a/apps/openmw/mwrender/terrain.hpp b/apps/openmw/mwrender/terrain.hpp index 923f4fc2b..a0a48fb37 100644 --- a/apps/openmw/mwrender/terrain.hpp +++ b/apps/openmw/mwrender/terrain.hpp @@ -130,10 +130,8 @@ namespace MWRender{ * @param fromX the *vertex* index in the current cell to start making texture from * @param fromY the *vertex* index in the current cell to start making the texture from * @param size the size (number of vertexes) to get - * - * @TODO FIXME the return of this function possibly copies the image data */ - Ogre::Image getVertexColours(MWWorld::Ptr::CellStore* store, + Ogre::TexturePtr getVertexColours(MWWorld::Ptr::CellStore* store, int fromX, int fromY, int size); }; diff --git a/apps/openmw/mwrender/terrainmaterial.cpp b/apps/openmw/mwrender/terrainmaterial.cpp index 9121da793..0edc23a71 100644 --- a/apps/openmw/mwrender/terrainmaterial.cpp +++ b/apps/openmw/mwrender/terrainmaterial.cpp @@ -203,7 +203,7 @@ namespace Ogre // normalmap --freeTextureUnits; // colourmap - if (terrain->getGlobalColourMapEnabled()) + //if (terrain->getGlobalColourMapEnabled()) --freeTextureUnits; if (isShadowingEnabled(HIGH_LOD, terrain)) { @@ -359,9 +359,9 @@ namespace Ogre tu->setTextureAddressingMode(TextureUnitState::TAM_CLAMP); // global colour map - if (terrain->getGlobalColourMapEnabled() && isGlobalColourMapEnabled()) + //if (terrain->getGlobalColourMapEnabled() && isGlobalColourMapEnabled()) { - tu = pass->createTextureUnitState(terrain->getGlobalColourMap()->getName()); + tu = pass->createTextureUnitState(""); tu->setTextureAddressingMode(TextureUnitState::TAM_CLAMP); } @@ -1004,7 +1004,7 @@ namespace Ogre "uniform sampler2D globalNormal : register(s" << currentSamplerIdx++ << ")\n"; - if (terrain->getGlobalColourMapEnabled() && prof->isGlobalColourMapEnabled()) + //if (terrain->getGlobalColourMapEnabled() && prof->isGlobalColourMapEnabled()) { outStream << ", uniform sampler2D globalColourMap : register(s" << currentSamplerIdx++ << ")\n"; @@ -1299,7 +1299,7 @@ namespace Ogre } else { - if (terrain->getGlobalColourMapEnabled() && prof->isGlobalColourMapEnabled()) + //if (terrain->getGlobalColourMapEnabled() && prof->isGlobalColourMapEnabled()) { // sample colour map and apply to diffuse outStream << " diffuse *= tex2D(globalColourMap, uv).rgb;\n"; From 0d4e3b8e758ba916f83e3b4ddc496348df3d79e1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 2 Mar 2012 18:33:05 +0100 Subject: [PATCH 22/39] remove alpha channel from colour map --- apps/openmw/mwrender/terrain.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index 8d82cb296..325892563 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -527,7 +527,7 @@ namespace MWRender tex = texMgr->createManual(colourTextureName, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - Ogre::TEX_TYPE_2D, size, size, 0, Ogre::PF_BYTE_BGRA); + Ogre::TEX_TYPE_2D, size, size, 0, Ogre::PF_BYTE_BGR); Ogre::HardwarePixelBufferSharedPtr pixelBuffer = tex->getBuffer(); @@ -550,7 +550,7 @@ namespace MWRender const unsigned char b = colours[colourOffset + 2]; //as is the case elsewhere we need to flip the y - const size_t imageOffset = (size - 1 - y)*size*4 + x*4; + const size_t imageOffset = (size - 1 - y)*size*3 + x*3; pDest[imageOffset + 0] = b; pDest[imageOffset + 1] = g; pDest[imageOffset + 2] = r; From 461ec9f3d6ddfb3642644d85af925c2c7518e642 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 2 Mar 2012 18:50:55 +0100 Subject: [PATCH 23/39] fix regarding last commit --- apps/openmw/mwrender/terrain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index 325892563..ee7175465 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -550,7 +550,7 @@ namespace MWRender const unsigned char b = colours[colourOffset + 2]; //as is the case elsewhere we need to flip the y - const size_t imageOffset = (size - 1 - y)*size*3 + x*3; + const size_t imageOffset = (size - 1 - y)*size*4 + x*4; pDest[imageOffset + 0] = b; pDest[imageOffset + 1] = g; pDest[imageOffset + 2] = r; From 3ecc427b9606f8f7d85adee4b528c0dc6bb12a80 Mon Sep 17 00:00:00 2001 From: Jacob Essex Date: Fri, 2 Mar 2012 20:27:20 +0000 Subject: [PATCH 24/39] Implemented slightly better texture splatting --- apps/openmw/mwrender/terrain.cpp | 162 ++++++++++++++++--------------- apps/openmw/mwrender/terrain.hpp | 6 -- 2 files changed, 85 insertions(+), 83 deletions(-) diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index ee7175465..39aeb6437 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -31,7 +31,7 @@ namespace MWRender static_cast(activeProfile); mTerrainGlobals->setMaxPixelError(8); - mTerrainGlobals->setLayerBlendMapSize(SPLIT_TERRAIN ? 256 : 1024); + mTerrainGlobals->setLayerBlendMapSize(SPLIT_TERRAIN ? 32 : 1024); mTerrainGlobals->setLightMapSize(SPLIT_TERRAIN ? 256 : 1024); mTerrainGlobals->setCompositeMapSize(SPLIT_TERRAIN ? 256 : 1024); mTerrainGlobals->setDefaultGlobalColourMapSize(256); @@ -255,62 +255,75 @@ namespace MWRender fromY+size <= ESM::Land::LAND_TEXTURE_SIZE && "Can't get a terrain texture on terrain outside the current cell"); + //this ensures that the ltex indexes are sorted (or retrived as sorted + //which simplifies shading between cells). + // + //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 ltexIndexes; + for ( int y = fromY - 1; y < fromY + size + 1; y++ ) + { + for ( int x = fromX - 1; x < fromX + size + 1; x++ ) + { + ltexIndexes.insert(getLtexIndexAt(store, x, y)); + } + } + //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 ( int y = fromY - 1; y < fromY + size + 1; y++ ) + for ( std::set::iterator iter = ltexIndexes.begin(); + iter != ltexIndexes.end(); + ++iter ) { - for ( int x = fromX - 1; x < fromX + size + 1; x++ ) + const uint16_t ltexIndex = *iter; + //this is the base texture, so we can ignore this at present + if ( ltexIndex == baseTexture ) { - const uint16_t ltexIndex = getLtexIndexAt(store, x, y); - //this is the base texture, so we can ignore this at present - if ( ltexIndex == baseTexture ) - { - continue; - } + continue; + } - const std::map::const_iterator it = indexes.find(ltexIndex); + const std::map::const_iterator it = indexes.find(ltexIndex); - if ( it == indexes.end() ) - { - //NB: All vtex ids are +1 compared to the ltex ids + 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 ) - { - 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"; - } + 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 ) + { + 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()); + const size_t position = terrainData->layerList.size(); + terrainData->layerList.push_back(Ogre::Terrain::LayerInstance()); - Ogre::TexturePtr normDisp = getNormalDisp("textures\\" + texture); + Ogre::TexturePtr normDisp = getNormalDisp("textures\\" + texture); - terrainData->layerList[position].worldSize = 256; - terrainData->layerList[position].textureNames.push_back("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()); + //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; - } + 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 assert( (size & (size - 1)) == 0 && "Size must be a power of 2"); - const int blendSize = terrain->getLayerBlendMapSize(); - const int blendDist = TERRAIN_SHADE_DISTANCE * (blendSize / size); + const int blendMapSize = terrain->getLayerBlendMapSize(); + const int splatSize = blendMapSize / size; //zero out every map std::map::const_iterator iter; @@ -344,7 +357,7 @@ namespace MWRender { float* pBlend = terrain->getLayerBlendMap(iter->second) ->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 @@ -354,11 +367,6 @@ namespace MWRender { 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 //if it is don't bother altering the blend map for it if ( indexes.find(ltexIndex) == indexes.end() ) @@ -366,43 +374,43 @@ namespace MWRender 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; float* const pBlend = terrain->getLayerBlendMap(layerIndex) ->getBlendPointer(); - const int splatSize = blendSize / size; - - //setup the bounds for the shading of this texture splat - 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, relY*splatSize - blendDist); - const int endY = std::min(blendSize, (relY+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"); + for ( int y = -1; y < splatSize + 1; y++ ){ + for ( int x = -1; x < splatSize + 1; x++ ){ - assert(blendY >= 0 && blendY < blendSize && - "index must be within texture bounds"); + //Note: Y is reversed + const int splatY = blendMapSize - 1 - relY * splatSize - y; + const int splatX = relX * splatSize + x; - 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; - } - else + if ( splatX >= 0 && splatX < blendMapSize && + splatY >= 0 && splatY < blendMapSize ) { - pBlend[index] = std::max((float)pBlend[index], 0.5f); + const int index = (splatY)*blendMapSize + splatX; + + if ( y >= 0 && y < splatSize && + x >= 0 && x < splatSize ) + { + 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); + } } + } } - } } diff --git a/apps/openmw/mwrender/terrain.hpp b/apps/openmw/mwrender/terrain.hpp index a0a48fb37..c298b7fc1 100644 --- a/apps/openmw/mwrender/terrain.hpp +++ b/apps/openmw/mwrender/terrain.hpp @@ -61,12 +61,6 @@ namespace MWRender{ */ 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 * an output to create a mapping of MW LtexIndex to the relevant terrain From 0f705eaca580da7bf11bc773529b9fc54c9d2b28 Mon Sep 17 00:00:00 2001 From: Jacob Essex Date: Fri, 2 Mar 2012 21:03:53 +0000 Subject: [PATCH 25/39] Removed the option of not splitting terrain as it was slower and adding to code complexity. The only real reason for keeping it, which was that it made debugging some texture issues easier is now gone. --- apps/openmw/mwrender/terrain.cpp | 179 +++++++++++-------------------- apps/openmw/mwrender/terrain.hpp | 25 +---- 2 files changed, 67 insertions(+), 137 deletions(-) diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index 39aeb6437..113a4a831 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -31,9 +31,9 @@ namespace MWRender static_cast(activeProfile); mTerrainGlobals->setMaxPixelError(8); - mTerrainGlobals->setLayerBlendMapSize(SPLIT_TERRAIN ? 32 : 1024); - mTerrainGlobals->setLightMapSize(SPLIT_TERRAIN ? 256 : 1024); - mTerrainGlobals->setCompositeMapSize(SPLIT_TERRAIN ? 256 : 1024); + mTerrainGlobals->setLayerBlendMapSize(32); + mTerrainGlobals->setLightMapSize(256); + mTerrainGlobals->setCompositeMapSize(256); mTerrainGlobals->setDefaultGlobalColourMapSize(256); //10 (default) didn't seem to be quite enough @@ -52,29 +52,21 @@ namespace MWRender matProfile->setReceiveDynamicShadowsEnabled(false); matProfile->setGlobalColourMapEnabled(true); - 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, mLandSize, - mRealSize); + mWorldSize); - mTerrainGroup->setOrigin(Ogre::Vector3(mRealSize/2, + mTerrainGroup->setOrigin(Ogre::Vector3(mWorldSize/2, 0, - -mRealSize/2)); + -mWorldSize/2)); Ogre::Terrain::ImportData& importSettings = mTerrainGroup->getDefaultImportSettings(); importSettings.inputBias = 0; importSettings.terrainSize = mLandSize; - importSettings.worldSize = mRealSize; + importSettings.worldSize = mWorldSize; importSettings.minBatchSize = 9; importSettings.maxBatchSize = mLandSize; @@ -111,109 +103,72 @@ namespace MWRender const int cellY = store->cell->getGridY(); - if ( SPLIT_TERRAIN ) - { - //split the cell terrain into four segments - const int numTextures = ESM::Land::LAND_TEXTURE_SIZE/2; + //split the cell terrain into four segments + const int numTextures = ESM::Land::LAND_TEXTURE_SIZE/2; - for ( int x = 0; x < 2; x++ ) + for ( int x = 0; x < 2; x++ ) + { + for ( int y = 0; y < 2; y++ ) { - for ( int y = 0; y < 2; y++ ) - { - Ogre::Terrain::ImportData terrainData = - mTerrainGroup->getDefaultImportSettings(); - - const int terrainX = cellX * 2 + x; - const int terrainY = cellY * 2 + y; + Ogre::Terrain::ImportData terrainData = + mTerrainGroup->getDefaultImportSettings(); - //it makes far more sense to reallocate the memory here, - //and let Ogre deal with it due to the issues with deleting - //it at the wrong time if using threads (Which Ogre::Terrain does) - terrainData.inputFloat = OGRE_ALLOC_T(float, - mLandSize*mLandSize, - Ogre::MEMCATEGORY_GEOMETRY); + const int terrainX = cellX * 2 + x; + const int terrainY = cellY * 2 + y; - //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)); - } + //it makes far more sense to reallocate the memory here, + //and let Ogre deal with it due to the issues with deleting + //it at the wrong time if using threads (Which Ogre::Terrain does) + terrainData.inputFloat = OGRE_ALLOC_T(float, + mLandSize*mLandSize, + Ogre::MEMCATEGORY_GEOMETRY); - std::map indexes; - initTerrainTextures(&terrainData, store, - x * numTextures, y * numTextures, - numTextures, indexes); + //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)); + } - if (mTerrainGroup->getTerrain(cellX, cellY) == NULL) - { - mTerrainGroup->defineTerrain(terrainX, terrainY, &terrainData); + std::map indexes; + initTerrainTextures(&terrainData, store, + x * numTextures, y * numTextures, + numTextures, indexes); - mTerrainGroup->loadTerrain(terrainX, terrainY, true); + if (mTerrainGroup->getTerrain(cellX, cellY) == NULL) + { + mTerrainGroup->defineTerrain(terrainX, terrainY, &terrainData); - Ogre::Terrain* terrain = mTerrainGroup->getTerrain(terrainX, terrainY); - initTerrainBlendMaps(terrain, store, - x * numTextures, y * numTextures, - numTextures, - indexes); + mTerrainGroup->loadTerrain(terrainX, terrainY, true); - if ( store->land[1][1]->landData->usingColours ) - { - Ogre::TexturePtr vertex = getVertexColours(store, x*32, y*32, mLandSize); + Ogre::Terrain* terrain = mTerrainGroup->getTerrain(terrainX, terrainY); + initTerrainBlendMaps(terrain, store, + x * numTextures, y * numTextures, + numTextures, + indexes); - MaterialPtr mat = terrain->_getMaterial(); - mat->getTechnique(0)->getPass(0)->getTextureUnitState(1)->setTextureName( vertex->getName() ); - mat = terrain->_getCompositeMapMaterial(); - mat->getTechnique(0)->getPass(0)->getTextureUnitState(1)->setTextureName( vertex->getName() ); - } + if ( store->land[1][1]->landData->usingColours ) + { + Ogre::TexturePtr vertex = getVertexColours(store, + x*(mLandSize-1), + y*(mLandSize-1), + mLandSize); + + MaterialPtr mat = terrain->_getMaterial(); + mat->getTechnique(0)->getPass(0)->getTextureUnitState(1)->setTextureName( vertex->getName() ); + mat = terrain->_getCompositeMapMaterial(); + mat->getTechnique(0)->getPass(0)->getTextureUnitState(1)->setTextureName( vertex->getName() ); } } } } - else - { - Ogre::Terrain::ImportData terrainData = - mTerrainGroup->getDefaultImportSettings(); - - //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 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); - - if ( store->land[1][1]->landData->usingColours ) - { - Ogre::TexturePtr vertex = getVertexColours(store, 0, 0, mLandSize); - - MaterialPtr mat = terrain->_getMaterial(); - mat->getTechnique(0)->getPass(0)->getTextureUnitState(1)->setTextureName( vertex->getName() ); - mat = terrain->_getCompositeMapMaterial(); - mat->getTechnique(0)->getPass(0)->getTextureUnitState(1)->setTextureName( vertex->getName() ); - } - } mTerrainGroup->freeTemporaryResources(); } @@ -222,22 +177,14 @@ namespace MWRender void TerrainManager::cellRemoved(MWWorld::Ptr::CellStore *store) { - if ( SPLIT_TERRAIN ) + for ( int x = 0; x < 2; x++ ) { - for ( int x = 0; x < 2; x++ ) + for ( int y = 0; y < 2; y++ ) { - for ( int y = 0; y < 2; y++ ) - { - mTerrainGroup->unloadTerrain(store->cell->getGridX() * 2 + x, - store->cell->getGridY() * 2 + y); - } + mTerrainGroup->unloadTerrain(store->cell->getGridX() * 2 + x, + store->cell->getGridY() * 2 + y); } } - else - { - mTerrainGroup->unloadTerrain(store->cell->getGridX(), - store->cell->getGridY()); - } } //---------------------------------------------------------------------------------------------- diff --git a/apps/openmw/mwrender/terrain.hpp b/apps/openmw/mwrender/terrain.hpp index c298b7fc1..eba437a2d 100644 --- a/apps/openmw/mwrender/terrain.hpp +++ b/apps/openmw/mwrender/terrain.hpp @@ -17,14 +17,8 @@ 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 + * Each terrain cell is split into four blocks as this leads to an increase + * in performance and means we don't hit splat limits quite as much */ class TerrainManager{ public: @@ -40,26 +34,15 @@ 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; + static const int mLandSize = (ESM::Land::LAND_SIZE - 1)/2 + 1; /** * The length in game units of a single terrain block. - * This takes into account the SPLIT_TERRAIN option */ - int mRealSize; + static const int mWorldSize = ESM::Land::REAL_SIZE/2; /** * Setups up the list of textures for part of a cell, using indexes as From 07a2e5a5be7edea0b0a4e14807202b74d91bbd01 Mon Sep 17 00:00:00 2001 From: Jacob Essex Date: Fri, 2 Mar 2012 21:20:12 +0000 Subject: [PATCH 26/39] Removed dead code, fixed a error, added comments and tweaked some constants (after trying to find their min/max values) --- apps/openmw/mwrender/terrain.cpp | 65 ++++++-------------------------- apps/openmw/mwrender/terrain.hpp | 11 ------ 2 files changed, 12 insertions(+), 64 deletions(-) diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index 113a4a831..9cc0a9f37 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -30,7 +30,11 @@ namespace MWRender TerrainMaterialGeneratorB::SM2Profile* matProfile = static_cast(activeProfile); - mTerrainGlobals->setMaxPixelError(8); + //The pixel error should be as high as possible without it being noticed + //as it governs how fast mesh quality decreases. 16 was just about Ok + //when tested at the small swamp pond in Seyda Neen + mTerrainGlobals->setMaxPixelError(16); + mTerrainGlobals->setLayerBlendMapSize(32); mTerrainGlobals->setLightMapSize(256); mTerrainGlobals->setCompositeMapSize(256); @@ -39,11 +43,9 @@ namespace MWRender //10 (default) didn't seem to be quite enough mTerrainGlobals->setSkirtSize(128); - /* - * Here we are pushing the composite map distance beyond the edge - * of the rendered terrain due to not having setup lighting - */ - //mTerrainGlobals->setCompositeMapDistance(ESM::Land::REAL_SIZE*4); + //due to the sudden flick between composite and non composite textures, + //this seemed the distance where it wasn't too noticeable + mTerrainGlobals->setCompositeMapDistance(mWorldSize*2); matProfile->setLightmapEnabled(false); matProfile->setLayerSpecularMappingEnabled(false); @@ -142,7 +144,7 @@ namespace MWRender x * numTextures, y * numTextures, numTextures, indexes); - if (mTerrainGroup->getTerrain(cellX, cellY) == NULL) + if (mTerrainGroup->getTerrain(terrainX, terrainY) == NULL) { mTerrainGroup->defineTerrain(terrainX, terrainY, &terrainData); @@ -161,6 +163,8 @@ namespace MWRender y*(mLandSize-1), mLandSize); + //this is a hack to get around the fact that Ogre seems to + //corrupt the composite map leading to rendering errors MaterialPtr mat = terrain->_getMaterial(); mat->getTechnique(0)->getPass(0)->getTextureUnitState(1)->setTextureName( vertex->getName() ); mat = terrain->_getCompositeMapMaterial(); @@ -255,15 +259,9 @@ namespace MWRender 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; @@ -418,52 +416,12 @@ namespace MWRender ->textures[y * ESM::Land::LAND_TEXTURE_SIZE + x]; } - //---------------------------------------------------------------------------------------------- - - Ogre::TexturePtr TerrainManager::getNormalDisp(const std::string& fileName) - { - Ogre::TextureManager* const texMgr = Ogre::TextureManager::getSingletonPtr(); - const std::string normalTextureName = fileName.substr(0, fileName.rfind(".")) - + "_n.dds"; - if ( !texMgr->getByName(normalTextureName).isNull() ) - { - return texMgr->getByName(normalTextureName); - } - - const std::string textureName = "default_terrain_normal"; - if ( !texMgr->getByName(textureName).isNull() ) - { - return texMgr->getByName(textureName); - } - - Ogre::TexturePtr tex = texMgr->createManual( - textureName, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - Ogre::TEX_TYPE_2D, 1, 1, 0, Ogre::PF_BYTE_BGRA); - - Ogre::HardwarePixelBufferSharedPtr pixelBuffer = tex->getBuffer(); - - pixelBuffer->lock(Ogre::HardwareBuffer::HBL_NORMAL); - const Ogre::PixelBox& pixelBox = pixelBuffer->getCurrentLock(); - - Ogre::uint8* pDest = static_cast(pixelBox.data); - - *pDest++ = 128; // B - *pDest++ = 128; // G - *pDest++ = 128; // R - *pDest++ = 0; // A - - pixelBuffer->unlock(); - - return tex; - } - //---------------------------------------------------------------------------------------------- Ogre::TexturePtr TerrainManager::getVertexColours(MWWorld::Ptr::CellStore* store, int fromX, int fromY, int size) { Ogre::TextureManager* const texMgr = Ogre::TextureManager::getSingletonPtr(); - const char* const colours = store->land[1][1]->landData->colours; const std::string colourTextureName = "VtexColours_" + boost::lexical_cast(store->cell->getGridX()) + @@ -491,6 +449,7 @@ namespace MWRender Ogre::uint8* pDest = static_cast(pixelBox.data); + const char* const colours = store->land[1][1]->landData->colours; for ( int y = 0; y < size; y++ ) { for ( int x = 0; x < size; x++ ) diff --git a/apps/openmw/mwrender/terrain.hpp b/apps/openmw/mwrender/terrain.hpp index eba437a2d..6d9865540 100644 --- a/apps/openmw/mwrender/terrain.hpp +++ b/apps/openmw/mwrender/terrain.hpp @@ -88,17 +88,6 @@ namespace MWRender{ */ int getLtexIndexAt(MWWorld::Ptr::CellStore* store, int x, int y); - /** - * Retrives the texture that is the normal and parallax map for the - * terrain. If it doesn't exist a blank texture is used - * - * The file name of the texture should be the same as the file name of - * the base diffuse texture, but with _n appended before the extension - * - * @param fileName the name of the *diffuse* texture - */ - Ogre::TexturePtr getNormalDisp(const std::string& fileName); - /** * Due to the fact that Ogre terrain doesn't support vertex colours * we have to generate them manually From 3c934e3e44ae0a3cc87beb51a2dc2e1fe378bb33 Mon Sep 17 00:00:00 2001 From: Jacob Essex Date: Fri, 2 Mar 2012 22:31:17 +0000 Subject: [PATCH 27/39] Fixed a bug with fog not being applied to initially created terrain when starting in exteriors. --- apps/openmw/mwrender/renderingmanager.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index e204b5856..1c7edb7da 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -25,6 +25,10 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const mRendering.createScene("PlayerCam", 55, 5); mTerrainManager = new TerrainManager(mRendering.getScene()); + //The fog type must be set before any terrain objects are created as if the + //fog type is set to FOG_NONE then the initially created terrain won't have any fog + configureFog(1, ColourValue(1,1,1)); + // Set default mipmap level (NB some APIs ignore this) TextureManager::getSingleton().setDefaultNumMipmaps(5); From b882c03adc95d5ed9a6b888d69084aff2b5bc2d2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 2 Mar 2012 23:36:48 +0100 Subject: [PATCH 28/39] fixed terrain regions without vertexcolours, tweaked maxPixelError --- apps/openmw/mwrender/terrain.cpp | 32 +++++++++++++++--------- apps/openmw/mwrender/terrain.hpp | 3 +++ apps/openmw/mwrender/terrainmaterial.cpp | 8 +++--- 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index 9cc0a9f37..ca218388f 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -27,13 +27,12 @@ namespace MWRender Ogre::TerrainMaterialGenerator::Profile* const activeProfile = mTerrainGlobals->getDefaultMaterialGenerator() ->getActiveProfile(); - TerrainMaterialGeneratorB::SM2Profile* matProfile = + mActiveProfile = static_cast(activeProfile); //The pixel error should be as high as possible without it being noticed - //as it governs how fast mesh quality decreases. 16 was just about Ok - //when tested at the small swamp pond in Seyda Neen - mTerrainGlobals->setMaxPixelError(16); + //as it governs how fast mesh quality decreases. + mTerrainGlobals->setMaxPixelError(8); mTerrainGlobals->setLayerBlendMapSize(32); mTerrainGlobals->setLightMapSize(256); @@ -47,12 +46,11 @@ namespace MWRender //this seemed the distance where it wasn't too noticeable mTerrainGlobals->setCompositeMapDistance(mWorldSize*2); - matProfile->setLightmapEnabled(false); - matProfile->setLayerSpecularMappingEnabled(false); - matProfile->setLayerNormalMappingEnabled(false); - matProfile->setLayerParallaxMappingEnabled(false); - matProfile->setReceiveDynamicShadowsEnabled(false); - matProfile->setGlobalColourMapEnabled(true); + mActiveProfile->setLightmapEnabled(false); + mActiveProfile->setLayerSpecularMappingEnabled(false); + mActiveProfile->setLayerNormalMappingEnabled(false); + mActiveProfile->setLayerParallaxMappingEnabled(false); + mActiveProfile->setReceiveDynamicShadowsEnabled(false); mTerrainGroup = OGRE_NEW Ogre::TerrainGroup(mgr, Ogre::Terrain::ALIGN_X_Z, @@ -156,15 +154,25 @@ namespace MWRender numTextures, indexes); + // disable or enable global colour map (depends on available vertex colours) if ( store->land[1][1]->landData->usingColours ) - { + mActiveProfile->setGlobalColourMapEnabled(true); + else + mActiveProfile->setGlobalColourMapEnabled(false); + + /// \todo are we possibly generating the materials twice? + mActiveProfile->generate(terrain); + mActiveProfile->generateForCompositeMap(terrain); + + if ( store->land[1][1]->landData->usingColours ) + { Ogre::TexturePtr vertex = getVertexColours(store, x*(mLandSize-1), y*(mLandSize-1), mLandSize); //this is a hack to get around the fact that Ogre seems to - //corrupt the composite map leading to rendering errors + //corrupt the global colour map leading to rendering errors MaterialPtr mat = terrain->_getMaterial(); mat->getTechnique(0)->getPass(0)->getTextureUnitState(1)->setTextureName( vertex->getName() ); mat = terrain->_getCompositeMapMaterial(); diff --git a/apps/openmw/mwrender/terrain.hpp b/apps/openmw/mwrender/terrain.hpp index 6d9865540..397c0c473 100644 --- a/apps/openmw/mwrender/terrain.hpp +++ b/apps/openmw/mwrender/terrain.hpp @@ -2,6 +2,7 @@ #define _GAME_RENDER_TERRAIN_H #include +#include "terrainmaterial.hpp" #include "../mwworld/ptr.hpp" @@ -34,6 +35,8 @@ namespace MWRender{ Ogre::TerrainGlobalOptions* mTerrainGlobals; Ogre::TerrainGroup* mTerrainGroup; + Ogre::TerrainMaterialGeneratorB::SM2Profile* mActiveProfile; + /** * The length in verticies of a single terrain block. */ diff --git a/apps/openmw/mwrender/terrainmaterial.cpp b/apps/openmw/mwrender/terrainmaterial.cpp index 0edc23a71..67ebf45af 100644 --- a/apps/openmw/mwrender/terrainmaterial.cpp +++ b/apps/openmw/mwrender/terrainmaterial.cpp @@ -134,7 +134,7 @@ namespace Ogre if (enabled != mGlobalColourMapEnabled) { mGlobalColourMapEnabled = enabled; - mParent->_markChanged(); + //mParent->_markChanged(); } } //--------------------------------------------------------------------- @@ -359,7 +359,7 @@ namespace Ogre tu->setTextureAddressingMode(TextureUnitState::TAM_CLAMP); // global colour map - //if (terrain->getGlobalColourMapEnabled() && isGlobalColourMapEnabled()) + if (isGlobalColourMapEnabled()) { tu = pass->createTextureUnitState(""); tu->setTextureAddressingMode(TextureUnitState::TAM_CLAMP); @@ -1004,7 +1004,7 @@ namespace Ogre "uniform sampler2D globalNormal : register(s" << currentSamplerIdx++ << ")\n"; - //if (terrain->getGlobalColourMapEnabled() && prof->isGlobalColourMapEnabled()) + if (prof->isGlobalColourMapEnabled()) { outStream << ", uniform sampler2D globalColourMap : register(s" << currentSamplerIdx++ << ")\n"; @@ -1299,7 +1299,7 @@ namespace Ogre } else { - //if (terrain->getGlobalColourMapEnabled() && prof->isGlobalColourMapEnabled()) + if (prof->isGlobalColourMapEnabled()) { // sample colour map and apply to diffuse outStream << " diffuse *= tex2D(globalColourMap, uv).rgb;\n"; From a8939bc3180a8475be9dd38878228c924a31e9b0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 4 Mar 2012 00:08:56 +0100 Subject: [PATCH 29/39] disabled the composite map, improves loading time a lot --- apps/openmw/mwrender/terrain.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index ca218388f..a66b4ad3f 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -51,6 +51,7 @@ namespace MWRender mActiveProfile->setLayerNormalMappingEnabled(false); mActiveProfile->setLayerParallaxMappingEnabled(false); mActiveProfile->setReceiveDynamicShadowsEnabled(false); + mActiveProfile->setCompositeMapEnabled(false); mTerrainGroup = OGRE_NEW Ogre::TerrainGroup(mgr, Ogre::Terrain::ALIGN_X_Z, @@ -160,10 +161,6 @@ namespace MWRender else mActiveProfile->setGlobalColourMapEnabled(false); - /// \todo are we possibly generating the materials twice? - mActiveProfile->generate(terrain); - mActiveProfile->generateForCompositeMap(terrain); - if ( store->land[1][1]->landData->usingColours ) { Ogre::TexturePtr vertex = getVertexColours(store, @@ -173,10 +170,10 @@ namespace MWRender //this is a hack to get around the fact that Ogre seems to //corrupt the global colour map leading to rendering errors - MaterialPtr mat = terrain->_getMaterial(); - mat->getTechnique(0)->getPass(0)->getTextureUnitState(1)->setTextureName( vertex->getName() ); - mat = terrain->_getCompositeMapMaterial(); + MaterialPtr mat = terrain->getMaterial(); mat->getTechnique(0)->getPass(0)->getTextureUnitState(1)->setTextureName( vertex->getName() ); + //mat = terrain->_getCompositeMapMaterial(); + //mat->getTechnique(0)->getPass(0)->getTextureUnitState(1)->setTextureName( vertex->getName() ); } } } From bcd2371baa318b9393ccec684a34d1b1a5bc3f53 Mon Sep 17 00:00:00 2001 From: Jacob Essex Date: Sun, 4 Mar 2012 11:21:47 +0000 Subject: [PATCH 30/39] Code cleanup: Removed unneeded Ogre namespace --- apps/openmw/mwrender/terrain.cpp | 56 ++++++++++++++++---------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index 9cc0a9f37..3db5dfc8c 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -15,16 +15,16 @@ namespace MWRender //---------------------------------------------------------------------------------------------- - TerrainManager::TerrainManager(Ogre::SceneManager* mgr) + TerrainManager::TerrainManager(SceneManager* mgr) { - mTerrainGlobals = OGRE_NEW Ogre::TerrainGlobalOptions(); + mTerrainGlobals = OGRE_NEW TerrainGlobalOptions(); - Ogre::TerrainMaterialGeneratorPtr matGen; + TerrainMaterialGeneratorPtr matGen; TerrainMaterialGeneratorB* matGenP = new TerrainMaterialGeneratorB(); matGen.bind(matGenP); mTerrainGlobals->setDefaultMaterialGenerator(matGen); - Ogre::TerrainMaterialGenerator::Profile* const activeProfile = + TerrainMaterialGenerator::Profile* const activeProfile = mTerrainGlobals->getDefaultMaterialGenerator() ->getActiveProfile(); TerrainMaterialGeneratorB::SM2Profile* matProfile = @@ -54,16 +54,16 @@ namespace MWRender matProfile->setReceiveDynamicShadowsEnabled(false); matProfile->setGlobalColourMapEnabled(true); - mTerrainGroup = OGRE_NEW Ogre::TerrainGroup(mgr, - Ogre::Terrain::ALIGN_X_Z, + mTerrainGroup = OGRE_NEW TerrainGroup(mgr, + Terrain::ALIGN_X_Z, mLandSize, mWorldSize); - mTerrainGroup->setOrigin(Ogre::Vector3(mWorldSize/2, + mTerrainGroup->setOrigin(Vector3(mWorldSize/2, 0, -mWorldSize/2)); - Ogre::Terrain::ImportData& importSettings = + Terrain::ImportData& importSettings = mTerrainGroup->getDefaultImportSettings(); importSettings.inputBias = 0; @@ -85,14 +85,14 @@ namespace MWRender //---------------------------------------------------------------------------------------------- - void TerrainManager::setDiffuse(const Ogre::ColourValue& diffuse) + void TerrainManager::setDiffuse(const ColourValue& diffuse) { mTerrainGlobals->setCompositeMapDiffuse(diffuse); } //---------------------------------------------------------------------------------------------- - void TerrainManager::setAmbient(const Ogre::ColourValue& ambient) + void TerrainManager::setAmbient(const ColourValue& ambient) { mTerrainGlobals->setCompositeMapAmbient(ambient); } @@ -112,7 +112,7 @@ namespace MWRender { for ( int y = 0; y < 2; y++ ) { - Ogre::Terrain::ImportData terrainData = + Terrain::ImportData terrainData = mTerrainGroup->getDefaultImportSettings(); const int terrainX = cellX * 2 + x; @@ -120,10 +120,10 @@ namespace MWRender //it makes far more sense to reallocate the memory here, //and let Ogre deal with it due to the issues with deleting - //it at the wrong time if using threads (Which Ogre::Terrain does) + //it at the wrong time if using threads (Which Terrain does) terrainData.inputFloat = OGRE_ALLOC_T(float, mLandSize*mLandSize, - Ogre::MEMCATEGORY_GEOMETRY); + MEMCATEGORY_GEOMETRY); //copy the height data row by row for ( int terrainCopyY = 0; terrainCopyY < mLandSize; terrainCopyY++ ) @@ -150,7 +150,7 @@ namespace MWRender mTerrainGroup->loadTerrain(terrainX, terrainY, true); - Ogre::Terrain* terrain = mTerrainGroup->getTerrain(terrainX, terrainY); + Terrain* terrain = mTerrainGroup->getTerrain(terrainX, terrainY); initTerrainBlendMaps(terrain, store, x * numTextures, y * numTextures, numTextures, @@ -158,7 +158,7 @@ namespace MWRender if ( store->land[1][1]->landData->usingColours ) { - Ogre::TexturePtr vertex = getVertexColours(store, + TexturePtr vertex = getVertexColours(store, x*(mLandSize-1), y*(mLandSize-1), mLandSize); @@ -193,7 +193,7 @@ namespace MWRender //---------------------------------------------------------------------------------------------- - void TerrainManager::initTerrainTextures(Ogre::Terrain::ImportData* terrainData, + void TerrainManager::initTerrainTextures(Terrain::ImportData* terrainData, MWWorld::Ptr::CellStore* store, int fromX, int fromY, int size, std::map& indexes) @@ -257,7 +257,7 @@ namespace MWRender } const size_t position = terrainData->layerList.size(); - terrainData->layerList.push_back(Ogre::Terrain::LayerInstance()); + terrainData->layerList.push_back(Terrain::LayerInstance()); terrainData->layerList[position].worldSize = 256; terrainData->layerList[position].textureNames.push_back("textures\\" + texture); @@ -276,7 +276,7 @@ namespace MWRender //---------------------------------------------------------------------------------------------- - void TerrainManager::initTerrainBlendMaps(Ogre::Terrain* terrain, + void TerrainManager::initTerrainBlendMaps(Terrain* terrain, MWWorld::Ptr::CellStore* store, int fromX, int fromY, int size, const std::map& indexes) @@ -361,7 +361,7 @@ namespace MWRender for ( int i = 1; i < terrain->getLayerCount(); i++ ) { - Ogre::TerrainLayerBlendMap* blend = terrain->getLayerBlendMap(i); + TerrainLayerBlendMap* blend = terrain->getLayerBlendMap(i); blend->dirty(); blend->update(); } @@ -418,10 +418,10 @@ namespace MWRender //---------------------------------------------------------------------------------------------- - Ogre::TexturePtr TerrainManager::getVertexColours(MWWorld::Ptr::CellStore* store, + TexturePtr TerrainManager::getVertexColours(MWWorld::Ptr::CellStore* store, int fromX, int fromY, int size) { - Ogre::TextureManager* const texMgr = Ogre::TextureManager::getSingletonPtr(); + TextureManager* const texMgr = TextureManager::getSingletonPtr(); const std::string colourTextureName = "VtexColours_" + boost::lexical_cast(store->cell->getGridX()) + @@ -432,22 +432,22 @@ namespace MWRender "_" + boost::lexical_cast(fromY); - Ogre::TexturePtr tex = texMgr->getByName(colourTextureName); + TexturePtr tex = texMgr->getByName(colourTextureName); if ( !tex.isNull() ) { return tex; } tex = texMgr->createManual(colourTextureName, - Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - Ogre::TEX_TYPE_2D, size, size, 0, Ogre::PF_BYTE_BGR); + ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + TEX_TYPE_2D, size, size, 0, PF_BYTE_BGR); - Ogre::HardwarePixelBufferSharedPtr pixelBuffer = tex->getBuffer(); + HardwarePixelBufferSharedPtr pixelBuffer = tex->getBuffer(); - pixelBuffer->lock(Ogre::HardwareBuffer::HBL_DISCARD); - const Ogre::PixelBox& pixelBox = pixelBuffer->getCurrentLock(); + pixelBuffer->lock(HardwareBuffer::HBL_DISCARD); + const PixelBox& pixelBox = pixelBuffer->getCurrentLock(); - Ogre::uint8* pDest = static_cast(pixelBox.data); + uint8* pDest = static_cast(pixelBox.data); const char* const colours = store->land[1][1]->landData->colours; for ( int y = 0; y < size; y++ ) From eca91d17bb823a6f0fda3b88764b9a8a8c84274a Mon Sep 17 00:00:00 2001 From: Jacob Essex Date: Sun, 4 Mar 2012 11:46:33 +0000 Subject: [PATCH 31/39] Indentation tweaks and code cleanup --- apps/openmw/mwrender/terrain.cpp | 42 +++++++++++++++----------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index d9e6c1cf6..64acd9fe3 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -27,17 +27,14 @@ namespace MWRender TerrainMaterialGenerator::Profile* const activeProfile = mTerrainGlobals->getDefaultMaterialGenerator() ->getActiveProfile(); - mActiveProfile = - static_cast(activeProfile); + mActiveProfile = static_cast(activeProfile); //The pixel error should be as high as possible without it being noticed //as it governs how fast mesh quality decreases. mTerrainGlobals->setMaxPixelError(8); mTerrainGlobals->setLayerBlendMapSize(32); - mTerrainGlobals->setLightMapSize(256); - mTerrainGlobals->setCompositeMapSize(256); - mTerrainGlobals->setDefaultGlobalColourMapSize(256); + mTerrainGlobals->setDefaultGlobalColourMapSize(65); //10 (default) didn't seem to be quite enough mTerrainGlobals->setSkirtSize(128); @@ -51,19 +48,21 @@ namespace MWRender mActiveProfile->setLayerNormalMappingEnabled(false); mActiveProfile->setLayerParallaxMappingEnabled(false); mActiveProfile->setReceiveDynamicShadowsEnabled(false); + + //composite maps lead to a drastic reduction in loading time so are + //disabled mActiveProfile->setCompositeMapEnabled(false); mTerrainGroup = OGRE_NEW TerrainGroup(mgr, - Terrain::ALIGN_X_Z, - mLandSize, - mWorldSize); + Terrain::ALIGN_X_Z, + mLandSize, + mWorldSize); mTerrainGroup->setOrigin(Vector3(mWorldSize/2, - 0, - -mWorldSize/2)); + 0, + -mWorldSize/2)); - Terrain::ImportData& importSettings = - mTerrainGroup->getDefaultImportSettings(); + Terrain::ImportData& importSettings = mTerrainGroup->getDefaultImportSettings(); importSettings.inputBias = 0; importSettings.terrainSize = mLandSize; @@ -103,7 +102,6 @@ namespace MWRender const int cellX = store->cell->getGridX(); const int cellY = store->cell->getGridY(); - //split the cell terrain into four segments const int numTextures = ESM::Land::LAND_TEXTURE_SIZE/2; @@ -155,18 +153,14 @@ namespace MWRender numTextures, indexes); - // disable or enable global colour map (depends on available vertex colours) if ( store->land[1][1]->landData->usingColours ) + { + // disable or enable global colour map (depends on available vertex colours) mActiveProfile->setGlobalColourMapEnabled(true); - else - mActiveProfile->setGlobalColourMapEnabled(false); - - if ( store->land[1][1]->landData->usingColours ) - { TexturePtr vertex = getVertexColours(store, - x*(mLandSize-1), - y*(mLandSize-1), - mLandSize); + x*(mLandSize-1), + y*(mLandSize-1), + mLandSize); //this is a hack to get around the fact that Ogre seems to //corrupt the global colour map leading to rendering errors @@ -175,6 +169,10 @@ namespace MWRender //mat = terrain->_getCompositeMapMaterial(); //mat->getTechnique(0)->getPass(0)->getTextureUnitState(1)->setTextureName( vertex->getName() ); } + else + { + mActiveProfile->setGlobalColourMapEnabled(false); + } } } } From d22678faab028d31283d4ef0ccbd26c14f19d5ef Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 13 Mar 2012 17:14:02 +0100 Subject: [PATCH 32/39] fixed wrong merge --- CMakeLists.txt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index cc1dea25c..18c312229 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -255,6 +255,20 @@ if (APPLE) configure_file(${OpenMW_SOURCE_DIR}/files/mac/openmw.icns "${APP_BUNDLE_DIR}/Contents/Resources/OpenMW.icns" COPYONLY) + # prepare plugins + if (${CMAKE_BUILD_TYPE} MATCHES "Release") + set(OPENMW_RELEASE_BUILD 1) + endif() + if (${CMAKE_BUILD_TYPE} MATCHES "RelWithDebugInfo") + set(OPENMW_RELEASE_BUILD 1) + endif() + + if (${OPENMW_RELEASE_BUILD}) + set(OGRE_PLUGIN_DIR ${OGRE_PLUGIN_DIR_REL}) + else() + set(OGRE_PLUGIN_DIR ${OGRE_PLUGIN_DIR_DBG}) + endif() + foreach(plugin ${USED_OGRE_PLUGINS}) configure_file("${OGRE_PLUGIN_DIR}/${plugin}.dylib" "${APP_BUNDLE_DIR}/Contents/Plugins/${plugin}.dylib" From 99b2b6b6482cc967694b8f62057367d2f47626ad Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 24 Mar 2012 14:46:48 +0100 Subject: [PATCH 33/39] ogre 1.8 fixes --- apps/openmw/mwrender/terrainmaterial.hpp | 3 ++ components/bsa/bsa_archive.cpp | 31 ++++++++++++++++++-- components/bsa/bsa_file.cpp | 4 +-- components/bsa/bsa_file.hpp | 4 +-- libs/openengine/bullet/BulletShapeLoader.cpp | 8 ++--- 5 files changed, 40 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwrender/terrainmaterial.hpp b/apps/openmw/mwrender/terrainmaterial.hpp index 7c0b87ce4..798821d61 100644 --- a/apps/openmw/mwrender/terrainmaterial.hpp +++ b/apps/openmw/mwrender/terrainmaterial.hpp @@ -63,6 +63,9 @@ namespace Ogre public: SM2Profile(TerrainMaterialGenerator* parent, const String& name, const String& desc); ~SM2Profile(); + + bool isVertexCompressionSupported() const {return false;} + MaterialPtr generate(const Terrain* terrain); MaterialPtr generateForCompositeMap(const Terrain* terrain); uint8 getMaxLayers(const Terrain* terrain) const; diff --git a/components/bsa/bsa_archive.cpp b/components/bsa/bsa_archive.cpp index 72d15944d..80d92dd52 100644 --- a/components/bsa/bsa_archive.cpp +++ b/components/bsa/bsa_archive.cpp @@ -256,8 +256,12 @@ public: return DataStreamPtr(new Mangle2OgreStream(strm)); } +bool exists(const String& filename) { + return cexists(filename); +} + // Check if the file exists. - bool exists(const String& filename) { + bool cexists(const String& filename) const { String passed = filename; if(filename.at(filename.length() - 1) == '*' || filename.at(filename.length() - 1) == '?' || filename.at(filename.length() - 1) == '<' || filename.at(filename.length() - 1) == '"' || filename.at(filename.length() - 1) == '>' || filename.at(filename.length() - 1) == ':' @@ -308,6 +312,29 @@ return arc.exists(passed.c_str()); located in BSAs. So instead we channel it through exists() and set up a single-element result list if the file is found. */ + FileInfoListPtr findFileInfo(const String& pattern, bool recursive = true, + bool dirs = false) const + { + FileInfoListPtr ptr = FileInfoListPtr(new FileInfoList()); + + // Check if the file exists (only works for single files - wild + // cards and recursive search isn't implemented.) + if(cexists(pattern)) + { + FileInfo fi; + fi.archive = this; + fi.filename = pattern; + // It apparently doesn't matter that we return bogus + // information + fi.path = ""; + fi.compressedSize = fi.uncompressedSize = 0; + + ptr->push_back(fi); + } + + return ptr; + } + FileInfoListPtr findFileInfo(const String& pattern, bool recursive = true, bool dirs = false) { @@ -315,7 +342,7 @@ return arc.exists(passed.c_str()); // Check if the file exists (only works for single files - wild // cards and recursive search isn't implemented.) - if(exists(pattern)) + if(cexists(pattern)) { FileInfo fi; fi.archive = this; diff --git a/components/bsa/bsa_file.cpp b/components/bsa/bsa_file.cpp index 95358a362..f19606703 100644 --- a/components/bsa/bsa_file.cpp +++ b/components/bsa/bsa_file.cpp @@ -148,9 +148,9 @@ void BSAFile::readHeader() } /// Get the index of a given file name, or -1 if not found -int BSAFile::getIndex(const char *str) +int BSAFile::getIndex(const char *str) const { - Lookup::iterator it; + Lookup::const_iterator it; it = lookup.find(str); if(it == lookup.end()) return -1; diff --git a/components/bsa/bsa_file.hpp b/components/bsa/bsa_file.hpp index f54a64d2a..95fac0f4d 100644 --- a/components/bsa/bsa_file.hpp +++ b/components/bsa/bsa_file.hpp @@ -93,7 +93,7 @@ class BSAFile void readHeader(); /// Get the index of a given file name, or -1 if not found - int getIndex(const char *str); + int getIndex(const char *str) const; public: @@ -119,7 +119,7 @@ class BSAFile */ /// Check if a file exists - bool exists(const char *file) { return getIndex(file) != -1; } + bool exists(const char *file) const { return getIndex(file) != -1; } /** Open a file contained in the archive. Throws an exception if the file doesn't exist. diff --git a/libs/openengine/bullet/BulletShapeLoader.cpp b/libs/openengine/bullet/BulletShapeLoader.cpp index 48b87e1e8..01b6363d9 100644 --- a/libs/openengine/bullet/BulletShapeLoader.cpp +++ b/libs/openengine/bullet/BulletShapeLoader.cpp @@ -62,17 +62,17 @@ size_t BulletShape::calculateSize() const //============================================================================================================= -template<> BulletShapeManager *Ogre::Singleton::ms_Singleton = 0; +template<> BulletShapeManager *Ogre::Singleton::msSingleton = 0; BulletShapeManager *BulletShapeManager::getSingletonPtr() { - return ms_Singleton; + return msSingleton; } BulletShapeManager &BulletShapeManager::getSingleton() { - assert(ms_Singleton); - return(*ms_Singleton); + assert(msSingleton); + return(*msSingleton); } BulletShapeManager::BulletShapeManager() From b76b62e5e4cbf2db27bd443362e10690305d7330 Mon Sep 17 00:00:00 2001 From: Jacob Essex Date: Tue, 27 Mar 2012 09:20:22 +0100 Subject: [PATCH 34/39] Fixed a crash when there were cells with no data, refactored to remove non mutable data from CellStore --- apps/openmw/mwrender/renderingmanager.cpp | 3 +- apps/openmw/mwrender/terrain.cpp | 152 +++++++++++++--------- apps/openmw/mwrender/terrain.hpp | 25 ++-- components/esm/loadland.cpp | 22 ++-- components/esm/loadland.hpp | 3 +- components/esm_store/cell_store.hpp | 36 ----- components/esm_store/reclists.hpp | 34 ++--- 7 files changed, 145 insertions(+), 130 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 386b36842..ddf7fe3b0 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -23,7 +23,8 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const :mRendering(_rend), mObjects(mRendering), mActors(mRendering, environment), mAmbientMode(0), mDebugging(engine) { mRendering.createScene("PlayerCam", 55, 5); - mTerrainManager = new TerrainManager(mRendering.getScene()); + mTerrainManager = new TerrainManager(mRendering.getScene(), + environment); //The fog type must be set before any terrain objects are created as if the //fog type is set to FOG_NONE then the initially created terrain won't have any fog diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index 64acd9fe3..1c7db2d8b 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -1,12 +1,12 @@ #include #include +#include + +#include "../mwworld/world.hpp" #include "terrainmaterial.hpp" #include "terrain.hpp" -#include "components/esm/loadland.hpp" - -#include using namespace Ogre; @@ -15,7 +15,8 @@ namespace MWRender //---------------------------------------------------------------------------------------------- - TerrainManager::TerrainManager(SceneManager* mgr) + TerrainManager::TerrainManager(Ogre::SceneManager* mgr, const MWWorld::Environment& evn) : + mEnvironment(evn) { mTerrainGlobals = OGRE_NEW TerrainGlobalOptions(); @@ -102,6 +103,12 @@ namespace MWRender const int cellX = store->cell->getGridX(); const int cellY = store->cell->getGridY(); + ESM::Land* land = mEnvironment.mWorld->getStore().lands.search(cellX, cellY); + if ( land != NULL ) + { + land->loadData(); + } + //split the cell terrain into four segments const int numTextures = ESM::Land::LAND_TEXTURE_SIZE/2; @@ -122,22 +129,29 @@ namespace MWRender mLandSize*mLandSize, MEMCATEGORY_GEOMETRY); - //copy the height data row by row - for ( int terrainCopyY = 0; terrainCopyY < mLandSize; terrainCopyY++ ) + if ( land != NULL ) { - //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)); + //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], + &land->landData->heights[yOffset + xOffset], + mLandSize*sizeof(float)); + } + } + else + { + memset(terrainData.inputFloat, 0, mLandSize*mLandSize*sizeof(float)); } std::map indexes; - initTerrainTextures(&terrainData, store, + initTerrainTextures(&terrainData, cellX, cellY, x * numTextures, y * numTextures, numTextures, indexes); @@ -148,16 +162,18 @@ namespace MWRender mTerrainGroup->loadTerrain(terrainX, terrainY, true); Terrain* terrain = mTerrainGroup->getTerrain(terrainX, terrainY); - initTerrainBlendMaps(terrain, store, + initTerrainBlendMaps(terrain, + cellX, cellY, x * numTextures, y * numTextures, numTextures, indexes); - if ( store->land[1][1]->landData->usingColours ) + if ( land->landData->usingColours ) { // disable or enable global colour map (depends on available vertex colours) mActiveProfile->setGlobalColourMapEnabled(true); - TexturePtr vertex = getVertexColours(store, + TexturePtr vertex = getVertexColours(land, + cellX, cellY, x*(mLandSize-1), y*(mLandSize-1), mLandSize); @@ -197,11 +213,10 @@ namespace MWRender //---------------------------------------------------------------------------------------------- void TerrainManager::initTerrainTextures(Terrain::ImportData* terrainData, - MWWorld::Ptr::CellStore* store, + int cellX, int cellY, 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"); @@ -219,7 +234,7 @@ namespace MWRender { for ( int x = fromX - 1; x < fromX + size + 1; x++ ) { - ltexIndexes.insert(getLtexIndexAt(store, x, y)); + ltexIndexes.insert(getLtexIndexAt(cellX, cellY, x, y)); } } @@ -244,7 +259,7 @@ namespace MWRender { //NB: All vtex ids are +1 compared to the ltex ids - assert( (int)store->landTextures->ltex.size() >= (int)ltexIndex - 1 && + assert( (int)mEnvironment.mWorld->getStore().landTexts.getSize() >= (int)ltexIndex - 1 && "LAND.VTEX must be within the bounds of the LTEX array"); std::string texture; @@ -254,7 +269,7 @@ namespace MWRender } else { - texture = store->landTextures->ltex[ltexIndex-1].texture; + texture = mEnvironment.mWorld->getStore().landTexts.search(ltexIndex-1)->texture; //TODO this is needed due to MWs messed up texture handling texture = texture.substr(0, texture.rfind(".")) + ".dds"; } @@ -280,11 +295,10 @@ namespace MWRender //---------------------------------------------------------------------------------------------- void TerrainManager::initTerrainBlendMaps(Terrain* terrain, - MWWorld::Ptr::CellStore* store, + int cellX, int cellY, 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"); @@ -313,7 +327,7 @@ namespace MWRender { for ( int texX = fromX - 1; texX < fromX + size + 1; texX++ ) { - const uint16_t ltexIndex = getLtexIndexAt(store, texX, texY); + const uint16_t ltexIndex = getLtexIndexAt(cellX, cellY, texX, texY); //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 @@ -332,8 +346,10 @@ namespace MWRender float* const pBlend = terrain->getLayerBlendMap(layerIndex) ->getBlendPointer(); - for ( int y = -1; y < splatSize + 1; y++ ){ - for ( int x = -1; x < splatSize + 1; x++ ){ + for ( int y = -1; y < splatSize + 1; y++ ) + { + for ( int x = -1; x < splatSize + 1; x++ ) + { //Note: Y is reversed const int splatY = blendMapSize - 1 - relY * splatSize - y; @@ -373,7 +389,7 @@ namespace MWRender //---------------------------------------------------------------------------------------------- - int TerrainManager::getLtexIndexAt(MWWorld::Ptr::CellStore* store, + int TerrainManager::getLtexIndexAt(int cellX, int cellY, int x, int y) { //check texture index falls within the 9 cell bounds @@ -386,12 +402,6 @@ namespace MWRender 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--; @@ -414,22 +424,32 @@ namespace MWRender y -= ESM::Land::LAND_TEXTURE_SIZE; } - return store->land[cellX][cellY] - ->landData - ->textures[y * ESM::Land::LAND_TEXTURE_SIZE + x]; + + ESM::Land* land = mEnvironment.mWorld->getStore().lands.search(cellX, cellY); + if ( land != NULL ) + { + land->loadData(); + return land->landData + ->textures[y * ESM::Land::LAND_TEXTURE_SIZE + x]; + } + else + { + return 0; + } } //---------------------------------------------------------------------------------------------- - TexturePtr TerrainManager::getVertexColours(MWWorld::Ptr::CellStore* store, - int fromX, int fromY, int size) + TexturePtr TerrainManager::getVertexColours(ESM::Land* land, + int cellX, int cellY, + int fromX, int fromY, int size) { TextureManager* const texMgr = TextureManager::getSingletonPtr(); const std::string colourTextureName = "VtexColours_" + - boost::lexical_cast(store->cell->getGridX()) + + boost::lexical_cast(cellX) + "_" + - boost::lexical_cast(store->cell->getGridY()) + + boost::lexical_cast(cellY) + "_" + boost::lexical_cast(fromX) + "_" + @@ -451,26 +471,42 @@ namespace MWRender const PixelBox& pixelBox = pixelBuffer->getCurrentLock(); uint8* pDest = static_cast(pixelBox.data); - - const char* const colours = store->land[1][1]->landData->colours; - for ( int y = 0; y < size; y++ ) + + if ( land != NULL ) { - for ( int x = 0; x < size; x++ ) + const char* const colours = land->landData->colours; + for ( int y = 0; y < size; y++ ) { - const size_t colourOffset = (y+fromY)*3*65 + (x+fromX)*3; + for ( int x = 0; x < size; x++ ) + { + const size_t colourOffset = (y+fromY)*3*65 + (x+fromX)*3; - assert( colourOffset >= 0 && colourOffset < 65*65*3 && - "Colour offset is out of the expected bounds of record" ); + assert( colourOffset < 65*65*3 && + "Colour offset is out of the expected bounds of record" ); - const unsigned char r = colours[colourOffset + 0]; - const unsigned char g = colours[colourOffset + 1]; - const unsigned char b = colours[colourOffset + 2]; + const unsigned char r = colours[colourOffset + 0]; + const unsigned char g = colours[colourOffset + 1]; + const unsigned char b = colours[colourOffset + 2]; - //as is the case elsewhere we need to flip the y - const size_t imageOffset = (size - 1 - y)*size*4 + x*4; - pDest[imageOffset + 0] = b; - pDest[imageOffset + 1] = g; - pDest[imageOffset + 2] = r; + //as is the case elsewhere we need to flip the y + const size_t imageOffset = (size - 1 - y)*size*4 + x*4; + pDest[imageOffset + 0] = b; + pDest[imageOffset + 1] = g; + pDest[imageOffset + 2] = r; + } + } + } + else + { + for ( int y = 0; y < size; y++ ) + { + for ( int x = 0; x < size; x++ ) + { + for ( int k = 0; k < 3; k++ ) + { + *pDest++ = 0; + } + } } } diff --git a/apps/openmw/mwrender/terrain.hpp b/apps/openmw/mwrender/terrain.hpp index 397c0c473..16c93d6f4 100644 --- a/apps/openmw/mwrender/terrain.hpp +++ b/apps/openmw/mwrender/terrain.hpp @@ -23,7 +23,7 @@ namespace MWRender{ */ class TerrainManager{ public: - TerrainManager(Ogre::SceneManager*); + TerrainManager(Ogre::SceneManager* mgr, const MWWorld::Environment& env); virtual ~TerrainManager(); void setDiffuse(const Ogre::ColourValue& diffuse); @@ -35,6 +35,8 @@ namespace MWRender{ Ogre::TerrainGlobalOptions* mTerrainGlobals; Ogre::TerrainGroup* mTerrainGroup; + const MWWorld::Environment& mEnvironment; + Ogre::TerrainMaterialGeneratorB::SM2Profile* mActiveProfile; /** @@ -53,7 +55,8 @@ namespace MWRender{ * layer * * @param terrainData the terrain data to setup the textures for - * @param store the cell store for the given terrain cell + * @param cellX the coord of the cell + * @param cellY the coord of the 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 @@ -61,7 +64,7 @@ namespace MWRender{ * can be used by initTerrainBlendMaps */ void initTerrainTextures(Ogre::Terrain::ImportData* terrainData, - MWWorld::Ptr::CellStore* store, + int cellX, int cellY, int fromX, int fromY, int size, std::map& indexes); @@ -69,14 +72,15 @@ namespace MWRender{ * 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 cellX the coord of the cell + * @param cellY the coord of the 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 cellX, int cellY, int fromX, int fromY, int size, const std::map& indexes); @@ -85,22 +89,25 @@ namespace MWRender{ * 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 cellX the coord of the cell + * @param cellY the coord of the 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); + int getLtexIndexAt(int cellX, int cellY, int x, int y); /** * Due to the fact that Ogre terrain doesn't support vertex colours * we have to generate them manually * - * @param store the cell store for the given terrain cell + * @param cellX the coord of the cell + * @param cellY the coord of the cell * @param fromX the *vertex* index in the current cell to start making texture from * @param fromY the *vertex* index in the current cell to start making the texture from * @param size the size (number of vertexes) to get */ - Ogre::TexturePtr getVertexColours(MWWorld::Ptr::CellStore* store, + Ogre::TexturePtr getVertexColours(ESM::Land* land, + int cellX, int cellY, int fromX, int fromY, int size); }; diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index 4fe6279d3..cd2cf1d91 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -4,6 +4,8 @@ namespace ESM { void Land::load(ESMReader &esm) { + mEsm = &esm; + // Get the grid location esm.getSubNameIs("INTV"); esm.getSubHeaderIs(8); @@ -51,7 +53,7 @@ void Land::load(ESMReader &esm) landData = NULL; } -void Land::loadData(ESMReader &esm) +void Land::loadData() { if (dataLoaded) { @@ -62,17 +64,17 @@ void Land::loadData(ESMReader &esm) if (hasData) { - esm.restoreContext(context); + mEsm->restoreContext(context); //esm.getHNExact(landData->normals, sizeof(VNML), "VNML"); - if (esm.isNextSub("VNML")) + if (mEsm->isNextSub("VNML")) { - esm.skipHSubSize(12675); + mEsm->skipHSubSize(12675); } VHGT rawHeights; - esm.getHNExact(&rawHeights, sizeof(VHGT), "VHGT"); + mEsm->getHNExact(&rawHeights, sizeof(VHGT), "VHGT"); int currentHeightOffset = rawHeights.heightOffset; for (int y = 0; y < LAND_SIZE; y++) { @@ -87,20 +89,20 @@ void Land::loadData(ESMReader &esm) } } - if (esm.isNextSub("WNAM")) + if (mEsm->isNextSub("WNAM")) { - esm.skipHSubSize(81); + mEsm->skipHSubSize(81); } - if (esm.isNextSub("VCLR")) + if (mEsm->isNextSub("VCLR")) { landData->usingColours = true; - esm.getHExact(&landData->colours, 3*LAND_NUM_VERTS); + mEsm->getHExact(&landData->colours, 3*LAND_NUM_VERTS); }else{ landData->usingColours = false; } //TODO fix magic numbers uint16_t vtex[512]; - esm.getHNExact(&vtex, 512, "VTEX"); + mEsm->getHNExact(&vtex, 512, "VTEX"); int readPos = 0; //bit ugly, but it works for ( int y1 = 0; y1 < 4; y1++ ) diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index eeb198e90..5ccd966d9 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -17,6 +17,7 @@ struct Land // File context. This allows the ESM reader to be 'reset' to this // location later when we are ready to load the full data set. + ESMReader* mEsm; ESM_Context context; bool hasData; @@ -70,7 +71,7 @@ struct Land /** * Actually loads data */ - void loadData(ESMReader &esm); + void loadData(); /** * Frees memory allocated for land data diff --git a/components/esm_store/cell_store.hpp b/components/esm_store/cell_store.hpp index b733a7ee8..c0a84c577 100644 --- a/components/esm_store/cell_store.hpp +++ b/components/esm_store/cell_store.hpp @@ -124,9 +124,6 @@ namespace ESMS CellRefList statics; CellRefList weapons; - const Land* land[3][3]; - const LTexList* landTextures; - void load (const ESMStore &store, ESMReader &esm) { if (mState!=State_Loaded) @@ -138,21 +135,6 @@ namespace ESMS loadRefs (store, esm); - if ( ! (cell->data.flags & ESM::Cell::Interior) ) - { - 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; - } - mState = State_Loaded; } } @@ -198,24 +180,6 @@ namespace ESMS private: - Land* loadTerrain(int X, int Y, const ESMStore &store, ESMReader &esm) - { - // load terrain - Land *land = store.lands.search(X, Y); - if (land != NULL) - { - land->loadData(esm); - } - - return land; - } - - void unloadTerrain(int X, int Y, const ESMStore &store) { - 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 bool forEachImp (Functor& functor, List& list) { diff --git a/components/esm_store/reclists.hpp b/components/esm_store/reclists.hpp index cfbd23d75..a991996be 100644 --- a/components/esm_store/reclists.hpp +++ b/components/esm_store/reclists.hpp @@ -201,15 +201,21 @@ namespace ESMS // TODO: For multiple ESM/ESP files we need one list per file. std::vector ltex; - int count; - LTexList() : count(0) + LTexList() { // More than enough to hold Morrowind.esm. ltex.reserve(128); } - int getSize() { return count; } + const LandTexture* search(size_t index) const + { + assert(index < ltex.size()); + return <ex.at(index); + } + + int getSize() { return ltex.size(); } + int getSize() const { return ltex.size(); } virtual void listIdentifier (std::vector& identifier) const {} @@ -236,9 +242,9 @@ namespace ESMS virtual ~LandList() {} // Map containing all landscapes - typedef std::map LandsCol; - typedef std::map Lands; - Lands lands; + typedef std::pair LandCoord; + typedef std::map LandMap; + LandMap lands; int count; LandList() : count(0) {} @@ -249,15 +255,13 @@ namespace ESMS // Find land for the given coordinates. Return null if no data. Land *search(int x, int y) const { - Lands::const_iterator it = lands.find(x); - if(it==lands.end()) - return NULL; - - LandsCol::const_iterator it2 = it->second.find(y); - if(it2 == it->second.end()) + LandMap::const_iterator itr = lands.find(std::make_pair(x, y)); + if ( itr == lands.end() ) + { return NULL; + } - return it2->second; + return itr->second; } void load(ESMReader &esm, const std::string &id) @@ -266,11 +270,11 @@ namespace ESMS // Create the structure and load it. This actually skips the // landscape data and remembers the file position for later. - Land *land = new Land; + Land *land = new Land(); land->load(esm); // Store the structure - lands[land->X][land->Y] = land; + lands[std::make_pair(land->X, land->Y)] = land; } }; From ec21ff21cc6620ba60a947dcc7d7bf1ffcf12e36 Mon Sep 17 00:00:00 2001 From: Jacob Essex Date: Tue, 27 Mar 2012 13:58:12 +0100 Subject: [PATCH 35/39] Removed memory leak, allocated more on the stack --- apps/openmw/mwrender/terrain.cpp | 48 +++++++++++++------------------ apps/openmw/mwrender/terrain.hpp | 5 ++-- components/esm_store/reclists.hpp | 8 +++++- 3 files changed, 30 insertions(+), 31 deletions(-) diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index 1c7db2d8b..ac60e6323 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -16,33 +16,32 @@ namespace MWRender //---------------------------------------------------------------------------------------------- TerrainManager::TerrainManager(Ogre::SceneManager* mgr, const MWWorld::Environment& evn) : - mEnvironment(evn) + mEnvironment(evn), mTerrainGroup(TerrainGroup(mgr, Terrain::ALIGN_X_Z, mLandSize, mWorldSize)) { - mTerrainGlobals = OGRE_NEW TerrainGlobalOptions(); TerrainMaterialGeneratorPtr matGen; TerrainMaterialGeneratorB* matGenP = new TerrainMaterialGeneratorB(); matGen.bind(matGenP); - mTerrainGlobals->setDefaultMaterialGenerator(matGen); + mTerrainGlobals.setDefaultMaterialGenerator(matGen); TerrainMaterialGenerator::Profile* const activeProfile = - mTerrainGlobals->getDefaultMaterialGenerator() + mTerrainGlobals.getDefaultMaterialGenerator() ->getActiveProfile(); mActiveProfile = static_cast(activeProfile); //The pixel error should be as high as possible without it being noticed //as it governs how fast mesh quality decreases. - mTerrainGlobals->setMaxPixelError(8); + mTerrainGlobals.setMaxPixelError(8); - mTerrainGlobals->setLayerBlendMapSize(32); - mTerrainGlobals->setDefaultGlobalColourMapSize(65); + mTerrainGlobals.setLayerBlendMapSize(32); + mTerrainGlobals.setDefaultGlobalColourMapSize(65); //10 (default) didn't seem to be quite enough - mTerrainGlobals->setSkirtSize(128); + mTerrainGlobals.setSkirtSize(128); //due to the sudden flick between composite and non composite textures, //this seemed the distance where it wasn't too noticeable - mTerrainGlobals->setCompositeMapDistance(mWorldSize*2); + mTerrainGlobals.setCompositeMapDistance(mWorldSize*2); mActiveProfile->setLightmapEnabled(false); mActiveProfile->setLayerSpecularMappingEnabled(false); @@ -54,16 +53,11 @@ namespace MWRender //disabled mActiveProfile->setCompositeMapEnabled(false); - mTerrainGroup = OGRE_NEW TerrainGroup(mgr, - Terrain::ALIGN_X_Z, - mLandSize, - mWorldSize); - - mTerrainGroup->setOrigin(Vector3(mWorldSize/2, + mTerrainGroup.setOrigin(Vector3(mWorldSize/2, 0, -mWorldSize/2)); - Terrain::ImportData& importSettings = mTerrainGroup->getDefaultImportSettings(); + Terrain::ImportData& importSettings = mTerrainGroup.getDefaultImportSettings(); importSettings.inputBias = 0; importSettings.terrainSize = mLandSize; @@ -78,22 +72,20 @@ namespace MWRender TerrainManager::~TerrainManager() { - OGRE_DELETE mTerrainGroup; - OGRE_DELETE mTerrainGlobals; } //---------------------------------------------------------------------------------------------- void TerrainManager::setDiffuse(const ColourValue& diffuse) { - mTerrainGlobals->setCompositeMapDiffuse(diffuse); + mTerrainGlobals.setCompositeMapDiffuse(diffuse); } //---------------------------------------------------------------------------------------------- void TerrainManager::setAmbient(const ColourValue& ambient) { - mTerrainGlobals->setCompositeMapAmbient(ambient); + mTerrainGlobals.setCompositeMapAmbient(ambient); } //---------------------------------------------------------------------------------------------- @@ -117,7 +109,7 @@ namespace MWRender for ( int y = 0; y < 2; y++ ) { Terrain::ImportData terrainData = - mTerrainGroup->getDefaultImportSettings(); + mTerrainGroup.getDefaultImportSettings(); const int terrainX = cellX * 2 + x; const int terrainY = cellY * 2 + y; @@ -155,13 +147,13 @@ namespace MWRender x * numTextures, y * numTextures, numTextures, indexes); - if (mTerrainGroup->getTerrain(terrainX, terrainY) == NULL) + if (mTerrainGroup.getTerrain(terrainX, terrainY) == NULL) { - mTerrainGroup->defineTerrain(terrainX, terrainY, &terrainData); + mTerrainGroup.defineTerrain(terrainX, terrainY, &terrainData); - mTerrainGroup->loadTerrain(terrainX, terrainY, true); + mTerrainGroup.loadTerrain(terrainX, terrainY, true); - Terrain* terrain = mTerrainGroup->getTerrain(terrainX, terrainY); + Terrain* terrain = mTerrainGroup.getTerrain(terrainX, terrainY); initTerrainBlendMaps(terrain, cellX, cellY, x * numTextures, y * numTextures, @@ -193,7 +185,7 @@ namespace MWRender } } - mTerrainGroup->freeTemporaryResources(); + mTerrainGroup.freeTemporaryResources(); } //---------------------------------------------------------------------------------------------- @@ -204,8 +196,8 @@ namespace MWRender { for ( int y = 0; y < 2; y++ ) { - mTerrainGroup->unloadTerrain(store->cell->getGridX() * 2 + x, - store->cell->getGridY() * 2 + y); + mTerrainGroup.unloadTerrain(store->cell->getGridX() * 2 + x, + store->cell->getGridY() * 2 + y); } } } diff --git a/apps/openmw/mwrender/terrain.hpp b/apps/openmw/mwrender/terrain.hpp index 16c93d6f4..29a4ba36b 100644 --- a/apps/openmw/mwrender/terrain.hpp +++ b/apps/openmw/mwrender/terrain.hpp @@ -2,6 +2,7 @@ #define _GAME_RENDER_TERRAIN_H #include +#include #include "terrainmaterial.hpp" #include "../mwworld/ptr.hpp" @@ -32,8 +33,8 @@ namespace MWRender{ void cellAdded(MWWorld::Ptr::CellStore* store); void cellRemoved(MWWorld::Ptr::CellStore* store); private: - Ogre::TerrainGlobalOptions* mTerrainGlobals; - Ogre::TerrainGroup* mTerrainGroup; + Ogre::TerrainGlobalOptions mTerrainGlobals; + Ogre::TerrainGroup mTerrainGroup; const MWWorld::Environment& mEnvironment; diff --git a/components/esm_store/reclists.hpp b/components/esm_store/reclists.hpp index a991996be..16d37bec7 100644 --- a/components/esm_store/reclists.hpp +++ b/components/esm_store/reclists.hpp @@ -239,7 +239,13 @@ namespace ESMS */ struct LandList : RecList { - virtual ~LandList() {} + virtual ~LandList() + { + for ( LandMap::iterator itr = lands.begin(); itr != lands.end(); ++itr ) + { + delete itr->second; + } + } // Map containing all landscapes typedef std::pair LandCoord; From d5c49fa03648081f796f8cdbba4fefbf3e81c05e Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 30 Mar 2012 10:12:28 +0200 Subject: [PATCH 36/39] getWaterLevel use CellStore ; attempted fix for morrowind without tribunal --- apps/openmw/mwscript/cellextensions.cpp | 4 ++-- components/esm/loadcell.cpp | 10 ++++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwscript/cellextensions.cpp b/apps/openmw/mwscript/cellextensions.cpp index 155393f9e..d69c42ab3 100644 --- a/apps/openmw/mwscript/cellextensions.cpp +++ b/apps/openmw/mwscript/cellextensions.cpp @@ -142,8 +142,8 @@ namespace MWScript InterpreterContext& context = static_cast (runtime.getContext()); - const ESM::Cell *cell = context.getWorld().getPlayer().getPlayer().getCell()->cell; - runtime.push (cell->water); + MWWorld::Ptr::CellStore *cell = context.getWorld().getPlayer().getPlayer().getCell(); + runtime.push (cell->mWaterLevel); } }; diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index 6bc12c5cb..158cc0867 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -21,8 +21,14 @@ void Cell::load(ESMReader &esm) if (data.flags & Interior) { // Interior cells - - esm.getHNT(water, "WHGT", 4); + if (esm.isNextSub("INTV")) + { + int waterl; + esm.getHT(waterl); + water = (float) waterl; + } + else if (esm.isNextSub("WHGT")) + esm.getHT(water); // Quasi-exterior cells have a region (which determines the // weather), pure interior cells have ambient lighting From d3f31c349838cd26a6bcd0f0304d0715975760c6 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 30 Mar 2012 13:53:35 +0200 Subject: [PATCH 37/39] dialogue window enhancements --- apps/openmw/mwgui/dialogue.cpp | 6 ++---- files/mygui/openmw_dialogue_window_layout.xml | 17 ++++++----------- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index d6c4ce4e5..ac6681e27 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -43,9 +43,6 @@ DialogueWindow::DialogueWindow(WindowManager& parWindowManager,MWWorld::Environm // Centre dialog center(); - //WindowManager *wm = environment.mWindowManager; - setText("NpcName", "Name of character"); - //History view getWidget(history, "History"); history->setOverflowToTheLeft(true); @@ -116,7 +113,8 @@ void DialogueWindow::onSelectTopic(MyGUI::ListBox* _sender, size_t _index) void DialogueWindow::startDialogue(std::string npcName) { - setText("NpcName", npcName); + static_cast(mMainWidget)->setCaption(npcName); + adjustWindowCaption(); } void DialogueWindow::setKeywords(std::list keyWords) diff --git a/files/mygui/openmw_dialogue_window_layout.xml b/files/mygui/openmw_dialogue_window_layout.xml index 11ac41cb3..29a3b511e 100644 --- a/files/mygui/openmw_dialogue_window_layout.xml +++ b/files/mygui/openmw_dialogue_window_layout.xml @@ -1,34 +1,29 @@ - - - - - - + - + - + - - + - + From 4bbedeb3e7b684d1c0023177fb9eb87d5589fd4c Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 30 Mar 2012 14:01:43 +0200 Subject: [PATCH 38/39] made all dialogs semi transparent (like in morrowind) --- apps/openmw/mwgui/window_manager.cpp | 1 - files/mygui/openmw_windows.skin.xml | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index a04e2dcb8..465467469 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -371,7 +371,6 @@ void WindowManager::updateSkillArea() void WindowManager::removeDialog(OEngine::GUI::Layout*dialog) { - std::cout << "dialogue a la poubelle"; assert(dialog); if (!dialog) return; diff --git a/files/mygui/openmw_windows.skin.xml b/files/mygui/openmw_windows.skin.xml index a986dcffc..3ee33124c 100644 --- a/files/mygui/openmw_windows.skin.xml +++ b/files/mygui/openmw_windows.skin.xml @@ -294,7 +294,7 @@ - + From 557e97bab4872cca91e2770e80f5c4491b7bb75e Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 30 Mar 2012 15:52:09 +0200 Subject: [PATCH 39/39] fixed a terrain-related Redemption crash --- apps/openmw/mwrender/terrain.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwrender/terrain.cpp b/apps/openmw/mwrender/terrain.cpp index ac60e6323..887721565 100644 --- a/apps/openmw/mwrender/terrain.cpp +++ b/apps/openmw/mwrender/terrain.cpp @@ -14,7 +14,7 @@ namespace MWRender { //---------------------------------------------------------------------------------------------- - + TerrainManager::TerrainManager(Ogre::SceneManager* mgr, const MWWorld::Environment& evn) : mEnvironment(evn), mTerrainGroup(TerrainGroup(mgr, Terrain::ALIGN_X_Z, mLandSize, mWorldSize)) { @@ -42,7 +42,7 @@ namespace MWRender //due to the sudden flick between composite and non composite textures, //this seemed the distance where it wasn't too noticeable mTerrainGlobals.setCompositeMapDistance(mWorldSize*2); - + mActiveProfile->setLightmapEnabled(false); mActiveProfile->setLayerSpecularMappingEnabled(false); mActiveProfile->setLayerNormalMappingEnabled(false); @@ -73,16 +73,16 @@ namespace MWRender TerrainManager::~TerrainManager() { } - + //---------------------------------------------------------------------------------------------- - + void TerrainManager::setDiffuse(const ColourValue& diffuse) { mTerrainGlobals.setCompositeMapDiffuse(diffuse); } - + //---------------------------------------------------------------------------------------------- - + void TerrainManager::setAmbient(const ColourValue& ambient) { mTerrainGlobals.setCompositeMapAmbient(ambient); @@ -160,7 +160,7 @@ namespace MWRender numTextures, indexes); - if ( land->landData->usingColours ) + if ( land && land->landData->usingColours ) { // disable or enable global colour map (depends on available vertex colours) mActiveProfile->setGlobalColourMapEnabled(true); @@ -253,7 +253,7 @@ namespace MWRender assert( (int)mEnvironment.mWorld->getStore().landTexts.getSize() >= (int)ltexIndex - 1 && "LAND.VTEX must be within the bounds of the LTEX array"); - + std::string texture; if ( ltexIndex == 0 ) { @@ -359,7 +359,7 @@ namespace MWRender } else { - //this provides a transition shading but also + //this provides a transition shading but also //rounds off the corners slightly pBlend[index] = std::min(1.0f, pBlend[index] + 0.5f); } @@ -458,10 +458,10 @@ namespace MWRender TEX_TYPE_2D, size, size, 0, PF_BYTE_BGR); HardwarePixelBufferSharedPtr pixelBuffer = tex->getBuffer(); - + pixelBuffer->lock(HardwareBuffer::HBL_DISCARD); const PixelBox& pixelBox = pixelBuffer->getCurrentLock(); - + uint8* pDest = static_cast(pixelBox.data); if ( land != NULL ) @@ -501,7 +501,7 @@ namespace MWRender } } } - + pixelBuffer->unlock(); return tex;