From 9a3a64f0c4972095ae08c9909d883b41cb672e83 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 6 Mar 2017 19:04:17 +0100 Subject: [PATCH] Add resource manager for ESM::Land to allow data to be unloaded when no longer required --- apps/opencs/view/render/terrainstorage.cpp | 4 +- apps/opencs/view/render/terrainstorage.hpp | 2 +- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwphysics/physicssystem.cpp | 9 +- apps/openmw/mwphysics/physicssystem.hpp | 3 +- apps/openmw/mwrender/landmanager.cpp | 48 +++++++++++ apps/openmw/mwrender/landmanager.hpp | 33 ++++++++ apps/openmw/mwrender/renderingmanager.cpp | 16 +++- apps/openmw/mwrender/renderingmanager.hpp | 5 ++ apps/openmw/mwrender/terrainstorage.cpp | 35 ++++---- apps/openmw/mwrender/terrainstorage.hpp | 22 ++++- apps/openmw/mwworld/cellpreloader.cpp | 12 ++- apps/openmw/mwworld/cellpreloader.hpp | 8 +- apps/openmw/mwworld/scene.cpp | 26 +++--- components/esmterrain/storage.cpp | 96 ++++++++++++++++------ components/esmterrain/storage.hpp | 35 +++++--- components/resource/stats.cpp | 2 +- 17 files changed, 266 insertions(+), 92 deletions(-) create mode 100644 apps/openmw/mwrender/landmanager.cpp create mode 100644 apps/openmw/mwrender/landmanager.hpp diff --git a/apps/opencs/view/render/terrainstorage.cpp b/apps/opencs/view/render/terrainstorage.cpp index 2be4efd73..9894ce17c 100644 --- a/apps/opencs/view/render/terrainstorage.cpp +++ b/apps/opencs/view/render/terrainstorage.cpp @@ -9,7 +9,7 @@ namespace CSVRender { } - const ESM::Land* TerrainStorage::getLand(int cellX, int cellY) + osg::ref_ptr TerrainStorage::getLand(int cellX, int cellY) { std::ostringstream stream; stream << "#" << cellX << " " << cellY; @@ -23,7 +23,7 @@ namespace CSVRender const ESM::Land& land = mData.getLand().getRecord(index).get(); int mask = ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX; land.loadData (mask); - return &land; + return new ESMTerrain::LandObject(&land, 0); } const ESM::LandTexture* TerrainStorage::getLandTexture(int index, short plugin) diff --git a/apps/opencs/view/render/terrainstorage.hpp b/apps/opencs/view/render/terrainstorage.hpp index 16b0f3ec7..949311248 100644 --- a/apps/opencs/view/render/terrainstorage.hpp +++ b/apps/opencs/view/render/terrainstorage.hpp @@ -18,7 +18,7 @@ namespace CSVRender private: const CSMWorld::Data& mData; - virtual const ESM::Land* getLand (int cellX, int cellY); + virtual osg::ref_ptr getLand (int cellX, int cellY); virtual const ESM::LandTexture* getLandTexture(int index, short plugin); virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY); diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index baa523654..a06678488 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -23,7 +23,7 @@ add_openmw_dir (mwrender actors objects renderingmanager animation rotatecontroller sky npcanimation vismask creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage ripplesimulation - renderbin actoranimation + renderbin actoranimation landmanager ) add_openmw_dir (mwinput diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 9844667ae..3193e71a4 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -516,7 +516,7 @@ namespace MWPhysics class HeightField { public: - HeightField(const float* heights, int x, int y, float triSize, float sqrtVerts) + HeightField(const float* heights, int x, int y, float triSize, float sqrtVerts, const osg::Object* holdObject) { // find the minimum and maximum heights (needed for bullet) float minh = heights[0]; @@ -544,6 +544,8 @@ namespace MWPhysics mCollisionObject = new btCollisionObject; mCollisionObject->setCollisionShape(mShape); mCollisionObject->setWorldTransform(transform); + + mHoldObject = holdObject; } ~HeightField() { @@ -558,6 +560,7 @@ namespace MWPhysics private: btHeightfieldTerrainShape* mShape; btCollisionObject* mCollisionObject; + osg::ref_ptr mHoldObject; void operator=(const HeightField&); HeightField(const HeightField&); @@ -1140,9 +1143,9 @@ namespace MWPhysics return MovementSolver::traceDown(ptr, position, found->second, mCollisionWorld, maxHeight); } - void PhysicsSystem::addHeightField (const float* heights, int x, int y, float triSize, float sqrtVerts) + void PhysicsSystem::addHeightField (const float* heights, int x, int y, float triSize, float sqrtVerts, const osg::Object* holdObject) { - HeightField *heightfield = new HeightField(heights, x, y, triSize, sqrtVerts); + HeightField *heightfield = new HeightField(heights, x, y, triSize, sqrtVerts, holdObject); mHeightFields[std::make_pair(x,y)] = heightfield; mCollisionWorld->addCollisionObject(heightfield->getCollisionObject(), CollisionType_HeightMap, diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index 9730cda74..ae585281b 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -15,6 +15,7 @@ namespace osg { class Group; + class Object; } namespace MWRender @@ -80,7 +81,7 @@ namespace MWPhysics void updatePosition (const MWWorld::Ptr& ptr); - void addHeightField (const float* heights, int x, int y, float triSize, float sqrtVerts); + void addHeightField (const float* heights, int x, int y, float triSize, float sqrtVerts, const osg::Object* holdObject); void removeHeightField (int x, int y); diff --git a/apps/openmw/mwrender/landmanager.cpp b/apps/openmw/mwrender/landmanager.cpp new file mode 100644 index 000000000..082a136d5 --- /dev/null +++ b/apps/openmw/mwrender/landmanager.cpp @@ -0,0 +1,48 @@ +#include "landmanager.hpp" + +#include + +#include + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" +#include "../mwworld/esmstore.hpp" + +namespace MWRender +{ + +LandManager::LandManager(int loadFlags) + : ResourceManager(NULL) + , mLoadFlags(loadFlags) +{ +} + +osg::ref_ptr LandManager::getLand(int x, int y) +{ + std::ostringstream id; + id << x << " " << y; + std::string idstr = id.str(); + + osg::ref_ptr obj = mCache->getRefFromObjectCache(idstr); + if (obj) + return static_cast(obj.get()); + else + { + const ESM::Land* land = MWBase::Environment::get().getWorld()->getStore().get().search(x,y); + if (!land) + return NULL; + osg::ref_ptr landObj (new ESMTerrain::LandObject(land, mLoadFlags)); + mCache->addEntryToObjectCache(idstr, landObj.get()); + return landObj; + } +} + +void LandManager::reportStats(unsigned int frameNumber, osg::Stats *stats) +{ + stats->setAttribute(frameNumber, "Land", mCache->getCacheSize()); +} + + +} diff --git a/apps/openmw/mwrender/landmanager.hpp b/apps/openmw/mwrender/landmanager.hpp new file mode 100644 index 000000000..4fd05f064 --- /dev/null +++ b/apps/openmw/mwrender/landmanager.hpp @@ -0,0 +1,33 @@ +#ifndef OPENMW_COMPONENTS_ESMTERRAIN_LANDMANAGER_H +#define OPENMW_COMPONENTS_ESMTERRAIN_LANDMANAGER_H + +#include + +#include +#include + +namespace ESM +{ + struct Land; +} + +namespace MWRender +{ + + class LandManager : public Resource::ResourceManager + { + public: + LandManager(int loadFlags); + + /// @note Will return NULL if not found. + osg::ref_ptr getLand(int x, int y); + + virtual void reportStats(unsigned int frameNumber, osg::Stats* stats); + + private: + int mLoadFlags; + }; + +} + +#endif diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 69aa30702..886673caf 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -212,11 +212,13 @@ namespace MWRender mWater.reset(new Water(mRootNode, sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), fallback, resourcePath)); + + + mTerrainStorage = new TerrainStorage(mResourceSystem, Settings::Manager::getString("normal map pattern", "Shaders"), Settings::Manager::getString("normal height map pattern", "Shaders"), + Settings::Manager::getBool("auto use terrain normal maps", "Shaders"), + Settings::Manager::getString("terrain specular map pattern", "Shaders"), Settings::Manager::getBool("auto use terrain specular maps", "Shaders")); mTerrain.reset(new Terrain::TerrainGrid(sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), - new TerrainStorage(mResourceSystem->getVFS(), Settings::Manager::getString("normal map pattern", "Shaders"), Settings::Manager::getString("normal height map pattern", "Shaders"), - Settings::Manager::getBool("auto use terrain normal maps", "Shaders"), - Settings::Manager::getString("terrain specular map pattern", "Shaders"), Settings::Manager::getBool("auto use terrain specular maps", "Shaders")), - Mask_Terrain, &mResourceSystem->getSceneManager()->getShaderManager(), mUnrefQueue.get())); + mTerrainStorage, Mask_Terrain, &mResourceSystem->getSceneManager()->getShaderManager(), mUnrefQueue.get())); mCamera.reset(new Camera(mViewer->getCamera())); @@ -1055,4 +1057,10 @@ namespace MWRender SceneUtil::writeScene(node, filename, format); } + LandManager *RenderingManager::getLandManager() const + { + return mTerrainStorage->getLandManager(); + } + + } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index b1bb9fe0d..a1c962ff0 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -59,6 +59,8 @@ namespace MWRender class Pathgrid; class Camera; class Water; + class TerrainStorage; + class LandManager; class RenderingManager : public MWRender::RenderingInterface { @@ -190,6 +192,8 @@ namespace MWRender void exportSceneGraph(const MWWorld::Ptr& ptr, const std::string& filename, const std::string& format); + LandManager* getLandManager() const; + private: void updateProjectionMatrix(); void updateTextureFiltering(); @@ -212,6 +216,7 @@ namespace MWRender std::auto_ptr mObjects; std::auto_ptr mWater; std::auto_ptr mTerrain; + TerrainStorage* mTerrainStorage; std::auto_ptr mSky; std::auto_ptr mEffectManager; osg::ref_ptr mPlayerAnimation; diff --git a/apps/openmw/mwrender/terrainstorage.cpp b/apps/openmw/mwrender/terrainstorage.cpp index 311eb1621..9d229c96c 100644 --- a/apps/openmw/mwrender/terrainstorage.cpp +++ b/apps/openmw/mwrender/terrainstorage.cpp @@ -6,12 +6,22 @@ #include "../mwbase/environment.hpp" #include "../mwworld/esmstore.hpp" +#include "landmanager.hpp" + namespace MWRender { - TerrainStorage::TerrainStorage(const VFS::Manager* vfs, const std::string& normalMapPattern, const std::string& normalHeightMapPattern, bool autoUseNormalMaps, const std::string& specularMapPattern, bool autoUseSpecularMaps) - : ESMTerrain::Storage(vfs, normalMapPattern, normalHeightMapPattern, autoUseNormalMaps, specularMapPattern, autoUseSpecularMaps) + TerrainStorage::TerrainStorage(Resource::ResourceSystem* resourceSystem, const std::string& normalMapPattern, const std::string& normalHeightMapPattern, bool autoUseNormalMaps, const std::string& specularMapPattern, bool autoUseSpecularMaps) + : ESMTerrain::Storage(resourceSystem->getVFS(), normalMapPattern, normalHeightMapPattern, autoUseNormalMaps, specularMapPattern, autoUseSpecularMaps) + , mLandManager(new LandManager(ESM::Land::DATA_VCLR|ESM::Land::DATA_VHGT|ESM::Land::DATA_VNML|ESM::Land::DATA_VTEX)) + , mResourceSystem(resourceSystem) + { + mResourceSystem->addResourceManager(mLandManager.get()); + } + + TerrainStorage::~TerrainStorage() { + mResourceSystem->removeResourceManager(mLandManager.get()); } void TerrainStorage::getBounds(float& minX, float& maxX, float& minY, float& maxY) @@ -39,22 +49,14 @@ namespace MWRender maxY += 1; } - const ESM::Land* TerrainStorage::getLand(int cellX, int cellY) + LandManager *TerrainStorage::getLandManager() const { - const MWWorld::ESMStore &esmStore = - MWBase::Environment::get().getWorld()->getStore(); - - const ESM::Land* land = esmStore.get().search(cellX, cellY); - if (!land) - return NULL; - - const int flags = ESM::Land::DATA_VCLR|ESM::Land::DATA_VHGT|ESM::Land::DATA_VNML|ESM::Land::DATA_VTEX; - if (!land->isDataLoaded(flags)) - land->loadData(flags); - - // TODO: unload land data when it's no longer needed + return mLandManager.get(); + } - return land; + osg::ref_ptr TerrainStorage::getLand(int cellX, int cellY) + { + return mLandManager->getLand(cellX, cellY); } const ESM::LandTexture* TerrainStorage::getLandTexture(int index, short plugin) @@ -64,4 +66,5 @@ namespace MWRender return esmStore.get().search(index, plugin); } + } diff --git a/apps/openmw/mwrender/terrainstorage.hpp b/apps/openmw/mwrender/terrainstorage.hpp index 6fa8b98ce..cf4011d45 100644 --- a/apps/openmw/mwrender/terrainstorage.hpp +++ b/apps/openmw/mwrender/terrainstorage.hpp @@ -1,23 +1,37 @@ #ifndef MWRENDER_TERRAINSTORAGE_H #define MWRENDER_TERRAINSTORAGE_H +#include + #include +#include + namespace MWRender { + class LandManager; + /// @brief Connects the ESM Store used in OpenMW with the ESMTerrain storage. class TerrainStorage : public ESMTerrain::Storage { - private: - virtual const ESM::Land* getLand (int cellX, int cellY); - virtual const ESM::LandTexture* getLandTexture(int index, short plugin); public: - TerrainStorage(const VFS::Manager* vfs, const std::string& normalMapPattern = "", const std::string& normalHeightMapPatteern = "", bool autoUseNormalMaps = false, const std::string& specularMapPattern = "", bool autoUseSpecularMaps = false); + TerrainStorage(Resource::ResourceSystem* resourceSystem, const std::string& normalMapPattern = "", const std::string& normalHeightMapPatteern = "", bool autoUseNormalMaps = false, const std::string& specularMapPattern = "", bool autoUseSpecularMaps = false); + ~TerrainStorage(); + + virtual osg::ref_ptr getLand (int cellX, int cellY); + virtual const ESM::LandTexture* getLandTexture(int index, short plugin); /// Get bounds of the whole terrain in cell units virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY); + + LandManager* getLandManager() const; + + private: + std::auto_ptr mLandManager; + + Resource::ResourceSystem* mResourceSystem; }; } diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 60c1f9ca0..db3ec5018 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "../mwbase/environment.hpp" @@ -16,6 +17,7 @@ #include "../mwworld/inventorystore.hpp" #include "../mwworld/esmstore.hpp" +#include "../mwrender/landmanager.hpp" #include "cellstore.hpp" #include "manualref.hpp" @@ -46,7 +48,7 @@ namespace MWWorld { public: /// Constructor to be called from the main thread. - PreloadItem(MWWorld::CellStore* cell, Resource::SceneManager* sceneManager, Resource::BulletShapeManager* bulletShapeManager, Resource::KeyframeManager* keyframeManager, Terrain::World* terrain, bool preloadInstances) + PreloadItem(MWWorld::CellStore* cell, Resource::SceneManager* sceneManager, Resource::BulletShapeManager* bulletShapeManager, Resource::KeyframeManager* keyframeManager, Terrain::World* terrain, MWRender::LandManager* landManager, bool preloadInstances) : mIsExterior(cell->getCell()->isExterior()) , mX(cell->getCell()->getGridX()) , mY(cell->getCell()->getGridY()) @@ -54,6 +56,7 @@ namespace MWWorld , mBulletShapeManager(bulletShapeManager) , mKeyframeManager(keyframeManager) , mTerrain(terrain) + , mLandManager(landManager) , mPreloadInstances(preloadInstances) , mAbort(false) { @@ -90,6 +93,7 @@ namespace MWWorld try { mPreloadedObjects.push_back(mTerrain->cacheCell(mX, mY)); + mPreloadedObjects.push_back(mLandManager->getLand(mX, mY)); } catch(std::exception& e) { @@ -151,6 +155,7 @@ namespace MWWorld Resource::BulletShapeManager* mBulletShapeManager; Resource::KeyframeManager* mKeyframeManager; Terrain::World* mTerrain; + MWRender::LandManager* mLandManager; bool mPreloadInstances; volatile bool mAbort; @@ -183,10 +188,11 @@ namespace MWWorld Terrain::World* mTerrain; }; - CellPreloader::CellPreloader(Resource::ResourceSystem* resourceSystem, Resource::BulletShapeManager* bulletShapeManager, Terrain::World* terrain) + CellPreloader::CellPreloader(Resource::ResourceSystem* resourceSystem, Resource::BulletShapeManager* bulletShapeManager, Terrain::World* terrain, MWRender::LandManager* landManager) : mResourceSystem(resourceSystem) , mBulletShapeManager(bulletShapeManager) , mTerrain(terrain) + , mLandManager(landManager) , mExpiryDelay(0.0) , mMinCacheSize(0) , mMaxCacheSize(0) @@ -251,7 +257,7 @@ namespace MWWorld return; } - osg::ref_ptr item (new PreloadItem(cell, mResourceSystem->getSceneManager(), mBulletShapeManager, mResourceSystem->getKeyframeManager(), mTerrain, mPreloadInstances)); + osg::ref_ptr item (new PreloadItem(cell, mResourceSystem->getSceneManager(), mBulletShapeManager, mResourceSystem->getKeyframeManager(), mTerrain, mLandManager, mPreloadInstances)); mWorkQueue->addWorkItem(item); mPreloadCells[cell] = PreloadEntry(timestamp, item); diff --git a/apps/openmw/mwworld/cellpreloader.hpp b/apps/openmw/mwworld/cellpreloader.hpp index e8e59a3a2..ee73a4ea1 100644 --- a/apps/openmw/mwworld/cellpreloader.hpp +++ b/apps/openmw/mwworld/cellpreloader.hpp @@ -21,6 +21,11 @@ namespace SceneUtil class UnrefQueue; } +namespace MWRender +{ + class LandManager; +} + namespace MWWorld { class CellStore; @@ -28,7 +33,7 @@ namespace MWWorld class CellPreloader { public: - CellPreloader(Resource::ResourceSystem* resourceSystem, Resource::BulletShapeManager* bulletShapeManager, Terrain::World* terrain); + CellPreloader(Resource::ResourceSystem* resourceSystem, Resource::BulletShapeManager* bulletShapeManager, Terrain::World* terrain, MWRender::LandManager* landManager); ~CellPreloader(); /// Ask a background thread to preload rendering meshes and collision shapes for objects in this cell. @@ -64,6 +69,7 @@ namespace MWWorld Resource::ResourceSystem* mResourceSystem; Resource::BulletShapeManager* mBulletShapeManager; Terrain::World* mTerrain; + MWRender::LandManager* mLandManager; osg::ref_ptr mWorkQueue; osg::ref_ptr mUnrefQueue; double mExpiryDelay; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index dadada7b2..13546eb3f 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -16,6 +16,7 @@ #include "../mwbase/windowmanager.hpp" #include "../mwrender/renderingmanager.hpp" +#include "../mwrender/landmanager.hpp" #include "../mwphysics/physicssystem.hpp" @@ -271,26 +272,19 @@ namespace MWWorld // Load terrain physics first... if (cell->getCell()->isExterior()) { - const ESM::Land* land = - MWBase::Environment::get().getWorld()->getStore().get().search( - cell->getCell()->getGridX(), - cell->getCell()->getGridY() - ); - if (land && land->mDataTypes&ESM::Land::DATA_VHGT) { - // Actually only VHGT is needed here, but we'll need the rest for rendering anyway. - // Load everything now to reduce IO overhead. - const int flags = ESM::Land::DATA_VCLR|ESM::Land::DATA_VHGT|ESM::Land::DATA_VNML|ESM::Land::DATA_VTEX; - - const ESM::Land::LandData *data = land->getLandData (flags); - mPhysics->addHeightField (data->mHeights, cell->getCell()->getGridX(), cell->getCell()->getGridY(), - worldsize / (verts-1), verts); + int cellX = cell->getCell()->getGridX(); + int cellY = cell->getCell()->getGridY(); + osg::ref_ptr land = mRendering.getLandManager()->getLand(cellX, cellY); + const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VHGT) : 0; + if (data) + { + mPhysics->addHeightField (data->mHeights, cellX, cell->getCell()->getGridY(), worldsize / (verts-1), verts, land.get()); } else { static std::vector defaultHeight; defaultHeight.resize(verts*verts, ESM::Land::DEFAULT_HEIGHT); - mPhysics->addHeightField (&defaultHeight[0], cell->getCell()->getGridX(), cell->getCell()->getGridY(), - worldsize / (verts-1), verts); + mPhysics->addHeightField (&defaultHeight[0], cell->getCell()->getGridX(), cell->getCell()->getGridY(), worldsize / (verts-1), verts, land.get()); } } @@ -487,7 +481,7 @@ namespace MWWorld , mPreloadDoors(Settings::Manager::getBool("preload doors", "Cells")) , mPreloadFastTravel(Settings::Manager::getBool("preload fast travel", "Cells")) { - mPreloader.reset(new CellPreloader(rendering.getResourceSystem(), physics->getShapeManager(), rendering.getTerrain())); + mPreloader.reset(new CellPreloader(rendering.getResourceSystem(), physics->getShapeManager(), rendering.getTerrain(), rendering.getLandManager())); mPreloader->setWorkQueue(mRendering.getWorkQueue()); mPreloader->setUnrefQueue(rendering.getUnrefQueue()); diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index d1ccbeab5..e48c8aeb2 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -16,6 +16,39 @@ namespace ESMTerrain { + + LandObject::LandObject() + { + } + + LandObject::LandObject(const ESM::Land *land, int loadFlags) + : mLand(land) + , mLoadFlags(loadFlags) + { + mLand->loadData(mLoadFlags); + } + + LandObject::LandObject(const LandObject ©, const osg::CopyOp ©op) + { + } + + LandObject::~LandObject() + { + if (mLand && mLoadFlags) // only unload if we were responsible for loading to begin with. + mLand->unloadData(); + } + + const ESM::Land::LandData *LandObject::getData(int flags) const + { + return mLand->getLandData(flags); + } + + int LandObject::getPlugin() const + { + return mLand->mPlugin; + } + + const float defaultHeight = ESM::Land::DEFAULT_HEIGHT; Storage::Storage(const VFS::Manager *vfs, const std::string& normalMapPattern, const std::string& normalHeightMapPattern, bool autoUseNormalMaps, const std::string& specularMapPattern, bool autoUseSpecularMaps) @@ -28,14 +61,6 @@ namespace ESMTerrain { } - const ESM::Land::LandData *Storage::getLandData (int cellX, int cellY, int flags) - { - if (const ESM::Land *land = getLand (cellX, cellY)) - return land->getLandData (flags); - - return 0; - } - bool Storage::getMinMaxHeights(float size, const osg::Vec2f ¢er, float &min, float &max) { assert (size <= 1 && "Storage::getMinMaxHeights, chunk size should be <= 1 cell"); @@ -53,7 +78,9 @@ namespace ESMTerrain int endRow = startRow + size * (ESM::Land::LAND_SIZE-1) + 1; int endColumn = startColumn + size * (ESM::Land::LAND_SIZE-1) + 1; - if (const ESM::Land::LandData *data = getLandData (cellX, cellY, ESM::Land::DATA_VHGT)) + osg::ref_ptr land = getLand (cellX, cellY); + const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VHGT) : 0; + if (data) { min = std::numeric_limits::max(); max = -std::numeric_limits::max(); @@ -99,7 +126,9 @@ namespace ESMTerrain row += ESM::Land::LAND_SIZE-1; } - if (const ESM::Land::LandData *data = getLandData (cellX, cellY, ESM::Land::DATA_VNML)) + osg::ref_ptr land = getLand(cellX, cellY); + const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VNML) : 0; + if (data) { normal.x() = data->mNormals[col*ESM::Land::LAND_SIZE*3+row*3]; normal.y() = data->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1]; @@ -134,7 +163,9 @@ namespace ESMTerrain row = 0; } - if (const ESM::Land::LandData *data = getLandData (cellX, cellY, ESM::Land::DATA_VCLR)) + osg::ref_ptr land = getLand(cellX, cellY); + const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VCLR) : 0; + if (data) { color.r() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f; color.g() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f; @@ -146,7 +177,6 @@ namespace ESMTerrain color.g() = 1; color.b() = 1; } - } void Storage::fillVertexBuffers (int lodLevel, float size, const osg::Vec2f& center, @@ -180,9 +210,16 @@ namespace ESMTerrain float vertX_ = 0; // of current cell corner for (int cellX = startCellX; cellX < startCellX + std::ceil(size); ++cellX) { - const ESM::Land::LandData *heightData = getLandData (cellX, cellY, ESM::Land::DATA_VHGT); - const ESM::Land::LandData *normalData = getLandData (cellX, cellY, ESM::Land::DATA_VNML); - const ESM::Land::LandData *colourData = getLandData (cellX, cellY, ESM::Land::DATA_VCLR); + osg::ref_ptr land = getLand(cellX, cellY); + const ESM::Land::LandData *heightData = 0; + const ESM::Land::LandData *normalData = 0; + const ESM::Land::LandData *colourData = 0; + if (land) + { + heightData = land->getData(ESM::Land::DATA_VHGT); + normalData = land->getData(ESM::Land::DATA_VNML); + colourData = land->getData(ESM::Land::DATA_VCLR); + } int rowStart = 0; int colStart = 0; @@ -298,15 +335,16 @@ namespace ESMTerrain assert(x land = getLand(cellX, cellY); + const ESM::Land::LandData *data = land ? land->getData(ESM::Land::DATA_VTEX) : 0; + if (data) { int tex = data->mTextures[y * ESM::Land::LAND_TEXTURE_SIZE + x]; if (tex == 0) return std::make_pair(0,0); // vtex 0 is always the base texture, regardless of plugin - return std::make_pair(tex, getLand (cellX, cellY)->mPlugin); + return std::make_pair(tex, land->getPlugin()); } - else - return std::make_pair(0,0); + return std::make_pair(0,0); } std::string Storage::getTextureName(UniqueTextureId id) @@ -421,8 +459,12 @@ namespace ESMTerrain int cellX = static_cast(std::floor(worldPos.x() / 8192.f)); int cellY = static_cast(std::floor(worldPos.y() / 8192.f)); - const ESM::Land* land = getLand(cellX, cellY); - if (!land || !(land->mDataTypes&ESM::Land::DATA_VHGT)) + osg::ref_ptr land = getLand(cellX, cellY); + if (!land) + return defaultHeight; + + const ESM::Land::LandData* data = land->getData(ESM::Land::DATA_VHGT); + if (!data) return defaultHeight; // Mostly lifted from Ogre::Terrain::getHeightAtTerrainPosition @@ -461,10 +503,10 @@ namespace ESMTerrain */ // Build all 4 positions in normalized cell space, using point-sampled height - osg::Vec3f v0 (startXTS, startYTS, getVertexHeight(land, startX, startY) / 8192.f); - osg::Vec3f v1 (endXTS, startYTS, getVertexHeight(land, endX, startY) / 8192.f); - osg::Vec3f v2 (endXTS, endYTS, getVertexHeight(land, endX, endY) / 8192.f); - osg::Vec3f v3 (startXTS, endYTS, getVertexHeight(land, startX, endY) / 8192.f); + osg::Vec3f v0 (startXTS, startYTS, getVertexHeight(data, startX, startY) / 8192.f); + osg::Vec3f v1 (endXTS, startYTS, getVertexHeight(data, endX, startY) / 8192.f); + osg::Vec3f v2 (endXTS, endYTS, getVertexHeight(data, endX, endY) / 8192.f); + osg::Vec3f v3 (startXTS, endYTS, getVertexHeight(data, startX, endY) / 8192.f); // define this plane in terrain space osg::Plane plane; // FIXME: deal with differing triangle alignment @@ -496,11 +538,11 @@ namespace ESMTerrain } - float Storage::getVertexHeight(const ESM::Land *land, int x, int y) + float Storage::getVertexHeight(const ESM::Land::LandData* data, int x, int y) { assert(x < ESM::Land::LAND_SIZE); assert(y < ESM::Land::LAND_SIZE); - return land->getLandData()->mHeights[y * ESM::Land::LAND_SIZE + x]; + return data->mHeights[y * ESM::Land::LAND_SIZE + x]; } Terrain::LayerInfo Storage::getLayerInfo(const std::string& texture) diff --git a/components/esmterrain/storage.hpp b/components/esmterrain/storage.hpp index 092998b2e..9204d4059 100644 --- a/components/esmterrain/storage.hpp +++ b/components/esmterrain/storage.hpp @@ -16,25 +16,36 @@ namespace VFS namespace ESMTerrain { - /// @brief Feeds data from ESM terrain records (ESM::Land, ESM::LandTexture) - /// into the terrain component, converting it on the fly as needed. - class Storage : public Terrain::Storage + /// @brief Wrapper around ESM::Land with reference counting. The wrapper needs to be held as long as the data is still in use + /// Data will be unloaded when wrapper object is deleted + class LandObject : public osg::Object { private: + const ESM::Land* mLand; + int mLoadFlags; - // Not implemented in this class, because we need different Store implementations for game and editor - virtual const ESM::Land* getLand (int cellX, int cellY)= 0; - virtual const ESM::LandTexture* getLandTexture(int index, short plugin) = 0; + public: + META_Object(ESMTerrain, LandObject) + const ESM::Land::LandData* getData(int flags) const; + int getPlugin() const; + + LandObject(); + LandObject(const ESM::Land* land, int loadFlags); + LandObject(const LandObject& copy, const osg::CopyOp& copyop); + virtual ~LandObject(); + }; + + /// @brief Feeds data from ESM terrain records (ESM::Land, ESM::LandTexture) + /// into the terrain component, converting it on the fly as needed. + class Storage : public Terrain::Storage + { public: Storage(const VFS::Manager* vfs, const std::string& normalMapPattern = "", const std::string& normalHeightMapPattern = "", bool autoUseNormalMaps = false, const std::string& specularMapPattern = "", bool autoUseSpecularMaps = false); - /// Data is loaded first, if necessary. Will return a 0-pointer if there is no data for - /// any of the data types specified via \a flags. Will also return a 0-pointer if there - /// is no land record for the coordinates \a cellX / \a cellY. - const ESM::Land::LandData *getLandData (int cellX, int cellY, int flags); - // Not implemented in this class, because we need different Store implementations for game and editor + virtual osg::ref_ptr getLand (int cellX, int cellY)= 0; + virtual const ESM::LandTexture* getLandTexture(int index, short plugin) = 0; /// Get bounds of the whole terrain in cell units virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY) = 0; @@ -95,7 +106,7 @@ namespace ESMTerrain void fixColour (osg::Vec4f& colour, int cellX, int cellY, int col, int row); void averageNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row); - float getVertexHeight (const ESM::Land* land, int x, int y); + float getVertexHeight (const ESM::Land::LandData* data, int x, int y); // Since plugins can define new texture palettes, we need to know the plugin index too // in order to retrieve the correct texture name. diff --git a/components/resource/stats.cpp b/components/resource/stats.cpp index d696c9ecc..3cc2d3b01 100644 --- a/components/resource/stats.cpp +++ b/components/resource/stats.cpp @@ -259,7 +259,7 @@ void StatsHandler::setUpScene(osgViewer::ViewerBase *viewer) _resourceStatsChildNum = _switch->getNumChildren(); _switch->addChild(group, false); - const char* statNames[] = {"Compiling", "WorkQueue", "WorkThread", "", "Texture", "StateSet", "Node", "Node Instance", "Shape", "Shape Instance", "Image", "Nif", "Keyframe", "Terrain Cell", "Terrain Texture", "", "UnrefQueue"}; + const char* statNames[] = {"Compiling", "WorkQueue", "WorkThread", "", "Texture", "StateSet", "Node", "Node Instance", "Shape", "Shape Instance", "Image", "Nif", "Keyframe", "Terrain Cell", "Terrain Texture", "Land", "", "UnrefQueue"}; int numLines = sizeof(statNames) / sizeof(statNames[0]);