diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 7094f8799..76d1e9a64 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -26,7 +26,7 @@ opencs_units (model/world opencs_units_noqt (model/world universalid record commands columnbase scriptcontext cell refidcollection - refidadapter refiddata refidadapterimp ref collectionbase refcollection columns infocollection tablemimedata cellcoordinates cellselection resources resourcesmanager scope + refidadapter refiddata refidadapterimp ref collectionbase refcollection columns infocollection tablemimedata cellcoordinates cellselection resources resourcesmanager scope landtexture land ) opencs_hdrs_noqt (model/world @@ -76,7 +76,7 @@ opencs_units (view/widget opencs_units (view/render scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget - previewwidget + previewwidget terrainstorage ) opencs_units_noqt (view/render diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index bb4fa0b43..78349a8db 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -539,6 +539,16 @@ CSMWorld::IdCollection& CSMWorld::Data::getDebugProfiles() return mDebugProfiles; } +const CSMWorld::IdCollection& CSMWorld::Data::getLand() const +{ + return mLand; +} + +const CSMWorld::IdCollection& CSMWorld::Data::getLandTextures() const +{ + return mLandTextures; +} + const CSMWorld::Resources& CSMWorld::Data::getResources (const UniversalId& id) const { return mResourcesManager.get (id.getType()); @@ -573,8 +583,11 @@ void CSMWorld::Data::merge() int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base, bool project) { - delete mReader; + // Don't delete the Reader yet. Some record types store a reference to the Reader to handle on-demand loading + boost::shared_ptr ptr(mReader); + mReaders.push_back(ptr); mReader = 0; + mDialogue = 0; mRefLoadCache.clear(); @@ -598,8 +611,11 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Stage::Messages& messages) if (!mReader->hasMoreRecs()) { - delete mReader; + // Don't delete the Reader yet. Some record types store a reference to the Reader to handle on-demand loading + boost::shared_ptr ptr(mReader); + mReaders.push_back(ptr); mReader = 0; + mDialogue = 0; mRefLoadCache.clear(); return true; @@ -626,6 +642,9 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Stage::Messages& messages) case ESM::REC_ENCH: mEnchantments.load (*mReader, mBase); break; case ESM::REC_BODY: mBodyParts.load (*mReader, mBase); break; + case ESM::REC_LTEX: mLandTextures.load (*mReader, mBase); break; + case ESM::REC_LAND: mLand.load(*mReader, mBase); break; + case ESM::REC_CELL: { mCells.load (*mReader, mBase); @@ -775,7 +794,9 @@ bool CSMWorld::Data::hasId (const std::string& id) const getCells().searchId (id)!=-1 || getEnchantments().searchId (id)!=-1 || getBodyParts().searchId (id)!=-1 || - getReferenceables().searchId (id)!=-1; + getReferenceables().searchId (id)!=-1 || + getLand().searchId (id) != -1 || + getLandTextures().searchId (id) != -1; } int CSMWorld::Data::count (RecordBase::State state) const @@ -795,7 +816,9 @@ int CSMWorld::Data::count (RecordBase::State state) const count (state, mCells) + count (state, mEnchantments) + count (state, mBodyParts) + - count (state, mReferenceables); + count (state, mReferenceables) + + count (state, mLand) + + count (state, mLandTextures); } void CSMWorld::Data::setDescription (const std::string& description) @@ -838,6 +861,8 @@ std::vector CSMWorld::Data::getIds (bool listDeleted) const appendIds (ids, mEnchantments, listDeleted); appendIds (ids, mBodyParts, listDeleted); appendIds (ids, mReferenceables, listDeleted); + appendIds (ids, mLand, listDeleted); + appendIds (ids, mLandTextures, listDeleted); std::sort (ids.begin(), ids.end()); diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index d0a07c677..aa4e640c2 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -33,6 +33,8 @@ #include "idcollection.hpp" #include "universalid.hpp" #include "cell.hpp" +#include "land.hpp" +#include "landtexture.hpp" #include "refidcollection.hpp" #include "refcollection.hpp" #include "infocollection.hpp" @@ -74,6 +76,8 @@ namespace CSMWorld InfoCollection mTopicInfos; InfoCollection mJournalInfos; IdCollection mCells; + IdCollection mLandTextures; + IdCollection mLand; RefIdCollection mReferenceables; RefCollection mRefs; IdCollection mFilters; @@ -88,6 +92,8 @@ namespace CSMWorld bool mProject; std::map > mRefLoadCache; + std::vector > mReaders; + // not implemented Data (const Data&); Data& operator= (const Data&); @@ -195,6 +201,10 @@ namespace CSMWorld IdCollection& getDebugProfiles(); + const IdCollection& getLand() const; + + const IdCollection& getLandTextures() const; + /// Throws an exception, if \a id does not match a resources list. const Resources& getResources (const UniversalId& id) const; @@ -247,4 +257,4 @@ namespace CSMWorld }; } -#endif \ No newline at end of file +#endif diff --git a/apps/opencs/model/world/land.cpp b/apps/opencs/model/world/land.cpp new file mode 100644 index 000000000..119e18761 --- /dev/null +++ b/apps/opencs/model/world/land.cpp @@ -0,0 +1,28 @@ +#include "land.hpp" + +#include + +namespace CSMWorld +{ + + Land::Land() + { + mLand.reset(new ESM::Land()); + } + + void Land::load(ESM::ESMReader &esm) + { + mLand->load(esm); + + std::ostringstream stream; + stream << "#" << mLand->mX << " " << mLand->mY; + + mId = stream.str(); + } + + void Land::blank() + { + /// \todo + } + +} diff --git a/apps/opencs/model/world/land.hpp b/apps/opencs/model/world/land.hpp new file mode 100644 index 000000000..e97a2d7dd --- /dev/null +++ b/apps/opencs/model/world/land.hpp @@ -0,0 +1,29 @@ +#ifndef CSM_WORLD_LAND_H +#define CSM_WORLD_LAND_H + +#include +#include +#include + +namespace CSMWorld +{ + /// \brief Wrapper for Land record. Encodes X and Y cell index in the ID. + /// + /// \todo Add worldspace support to the Land record. + /// \todo Add a proper copy constructor (currently worked around using shared_ptr) + struct Land + { + Land(); + + boost::shared_ptr mLand; + + std::string mId; + + /// Loads the metadata and ID + void load (ESM::ESMReader &esm); + + void blank(); + }; +} + +#endif diff --git a/apps/opencs/model/world/landtexture.cpp b/apps/opencs/model/world/landtexture.cpp new file mode 100644 index 000000000..4725866a5 --- /dev/null +++ b/apps/opencs/model/world/landtexture.cpp @@ -0,0 +1,21 @@ +#include "landtexture.hpp" + +#include + +namespace CSMWorld +{ + + void LandTexture::load(ESM::ESMReader &esm) + { + ESM::LandTexture::load(esm); + + int plugin = esm.getIndex(); + + std::ostringstream stream; + + stream << mIndex << "_" << plugin; + + mId = stream.str(); + } + +} diff --git a/apps/opencs/model/world/landtexture.hpp b/apps/opencs/model/world/landtexture.hpp new file mode 100644 index 000000000..b13903186 --- /dev/null +++ b/apps/opencs/model/world/landtexture.hpp @@ -0,0 +1,22 @@ +#ifndef CSM_WORLD_LANDTEXTURE_H +#define CSM_WORLD_LANDTEXTURE_H + +#include + +#include + +namespace CSMWorld +{ + /// \brief Wrapper for LandTexture record. Encodes mIndex and the plugin index (obtained from ESMReader) + /// in the ID. + /// + /// \attention The mId field of the ESM::LandTexture struct is not used. + struct LandTexture : public ESM::LandTexture + { + std::string mId; + + void load (ESM::ESMReader &esm); + }; +} + +#endif diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index 1e25f42e2..4fd66b14a 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -10,6 +10,9 @@ #include "../../model/world/columns.hpp" #include "../../model/world/data.hpp" +#include "elements.hpp" +#include "terrainstorage.hpp" + bool CSVRender::Cell::removeObject (const std::string& id) { std::map::iterator iter = @@ -67,6 +70,18 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, Ogre::SceneManager *sceneManager, int rows = references.rowCount(); addObjects (0, rows-1); + + const CSMWorld::IdCollection& land = mData.getLand(); + int landIndex = land.searchId(mId); + if (landIndex != -1) + { + mTerrain.reset(new Terrain::TerrainGrid(sceneManager, new TerrainStorage(mData), Element_Terrain, true, + Terrain::Align_XY)); + + const ESM::Land* esmLand = land.getRecord(mId).get().mLand.get(); + mTerrain->loadCell(esmLand->mX, + esmLand->mY); + } } CSVRender::Cell::~Cell() @@ -198,4 +213,4 @@ bool CSVRender::Cell::referenceAdded (const QModelIndex& parent, int start, int return false; return addObjects (start, end); -} \ No newline at end of file +} diff --git a/apps/opencs/view/render/cell.hpp b/apps/opencs/view/render/cell.hpp index 70adebe45..e63e09520 100644 --- a/apps/opencs/view/render/cell.hpp +++ b/apps/opencs/view/render/cell.hpp @@ -6,6 +6,8 @@ #include +#include + #include "object.hpp" class QModelIndex; @@ -29,6 +31,7 @@ namespace CSVRender std::string mId; Ogre::SceneNode *mCellNode; std::map mObjects; + std::auto_ptr mTerrain; /// Ignored if cell does not have an object with the given ID. /// diff --git a/apps/opencs/view/render/terrainstorage.cpp b/apps/opencs/view/render/terrainstorage.cpp new file mode 100644 index 000000000..a14eea5dd --- /dev/null +++ b/apps/opencs/view/render/terrainstorage.cpp @@ -0,0 +1,43 @@ +#include "terrainstorage.hpp" + +namespace CSVRender +{ + + TerrainStorage::TerrainStorage(const CSMWorld::Data &data) + : mData(data) + { + } + + ESM::Land* TerrainStorage::getLand(int cellX, int cellY) + { + std::ostringstream stream; + stream << "#" << cellX << " " << cellY; + + // The cell isn't guaranteed to have Land. This is because the terrain implementation + // has to wrap the vertices of the last row and column to the next cell, which may be a nonexisting cell + int index = mData.getLand().searchId(stream.str()); + if (index == -1) + return NULL; + + ESM::Land* land = mData.getLand().getRecord(index).get().mLand.get(); + int mask = ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX; + if (!land->isDataLoaded(mask)) + land->loadData(mask); + return land; + } + + const ESM::LandTexture* TerrainStorage::getLandTexture(int index, short plugin) + { + std::ostringstream stream; + stream << index << "_" << plugin; + + return &mData.getLandTextures().getRecord(stream.str()).get(); + } + + void TerrainStorage::getBounds(float &minX, float &maxX, float &minY, float &maxY) + { + // not needed at the moment - this returns the bounds of the whole world, but we only edit individual cells + throw std::runtime_error("getBounds not implemented"); + } + +} diff --git a/apps/opencs/view/render/terrainstorage.hpp b/apps/opencs/view/render/terrainstorage.hpp new file mode 100644 index 000000000..97782ad17 --- /dev/null +++ b/apps/opencs/view/render/terrainstorage.hpp @@ -0,0 +1,29 @@ +#ifndef OPENCS_RENDER_TERRAINSTORAGE_H +#define OPENCS_RENDER_TERRAINSTORAGE_H + +#include + +#include "../../model/world/data.hpp" + +namespace CSVRender +{ + + /** + * @brief A bridge between the terrain component and OpenCS's terrain data storage. + */ + class TerrainStorage : public ESMTerrain::Storage + { + public: + TerrainStorage(const CSMWorld::Data& data); + private: + const CSMWorld::Data& mData; + + virtual ESM::Land* 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); + }; + +} + +#endif diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index 028341ced..019bf4161 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -98,6 +98,8 @@ struct Land void load(ESMReader &esm); void save(ESMWriter &esm) const; + void blank() {} + /** * Actually loads data */ diff --git a/components/esm/loadltex.cpp b/components/esm/loadltex.cpp index bd28c8488..c3e2d50ff 100644 --- a/components/esm/loadltex.cpp +++ b/components/esm/loadltex.cpp @@ -19,4 +19,10 @@ void LandTexture::save(ESMWriter &esm) const esm.writeHNCString("DATA", mTexture); } +void LandTexture::blank() +{ + mTexture.clear(); + mIndex = -1; +} + } diff --git a/components/esm/loadltex.hpp b/components/esm/loadltex.hpp index 5e84428b2..8b45f8211 100644 --- a/components/esm/loadltex.hpp +++ b/components/esm/loadltex.hpp @@ -32,6 +32,9 @@ struct LandTexture std::string mId, mTexture; int mIndex; + void blank(); + ///< Set record to default state (does not touch the ID). + void load(ESMReader &esm); void save(ESMWriter &esm) const; };