diff --git a/apps/opencs/model/doc/savingstages.cpp b/apps/opencs/model/doc/savingstages.cpp index 546e8471c..16a91c865 100644 --- a/apps/opencs/model/doc/savingstages.cpp +++ b/apps/opencs/model/doc/savingstages.cpp @@ -127,27 +127,22 @@ void CSMDoc::WriteDialogueCollectionStage::perform (int stage, Messages& message if (topic.isModified() || infoModified) { - ESM::Dialogue dialogue = topic.get(); - if (infoModified && state != CSMWorld::RecordBase::State_Modified - && state != CSMWorld::RecordBase::State_ModifiedOnly) + if (infoModified && topic.mState != CSMWorld::RecordBase::State_Modified + && topic.mState != CSMWorld::RecordBase::State_ModifiedOnly) { mState.getWriter().startRecord (topic.mBase.sRecordId); mState.getWriter().writeHNCString ("NAME", topic.mBase.mId); - topic.mBase.save (mState.getWriter()); + topic.mBase.save (mState.getWriter(), topic.mState == CSMWorld::RecordBase::State_Deleted); mState.getWriter().endRecord (topic.mBase.sRecordId); } else { mState.getWriter().startRecord (topic.mModified.sRecordId); mState.getWriter().writeHNCString ("NAME", topic.mModified.mId); - topic.mModified.save (mState.getWriter()); + topic.mModified.save (mState.getWriter(), topic.mState == CSMWorld::RecordBase::State_Deleted); mState.getWriter().endRecord (topic.mModified.sRecordId); } - writer.startRecord (dialogue.sRecordId); - dialogue.save (writer); - writer.endRecord (dialogue.sRecordId); - // write modified selected info records for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second; ++iter) { @@ -398,10 +393,14 @@ void CSMDoc::WriteLandCollectionStage::perform (int stage, Messages& messages) if (land.isModified() || land.mState == CSMWorld::RecordBase::State_Deleted) { - const CSMWorld::Land& record = land.get(); - writer.startRecord (record.mLand->sRecordId); - record.mLand->save (writer, land.mState == CSMWorld::RecordBase::State_Deleted); - writer.endRecord (record.mLand->sRecordId); + CSMWorld::Land record = land.get(); + writer.startRecord (record.sRecordId); + record.save (writer, land.mState == CSMWorld::RecordBase::State_Deleted); + + if (const ESM::Land::LandData *data = record.getLandData (record.mDataTypes)) + data->save (mState.getWriter()); + + writer.endRecord (record.sRecordId); } } diff --git a/apps/opencs/model/world/land.cpp b/apps/opencs/model/world/land.cpp index e134eed38..80f86c746 100644 --- a/apps/opencs/model/world/land.cpp +++ b/apps/opencs/model/world/land.cpp @@ -6,7 +6,7 @@ namespace CSMWorld { void Land::load(ESM::ESMReader &esm, bool &isDeleted) { - mLand->load(esm, isDeleted); + ESM::Land::load(esm, isDeleted); std::ostringstream stream; stream << "#" << mX << " " << mY; diff --git a/apps/opencs/model/world/land.hpp b/apps/opencs/model/world/land.hpp index fd97ac2c5..e5f25c1d3 100644 --- a/apps/opencs/model/world/land.hpp +++ b/apps/opencs/model/world/land.hpp @@ -10,7 +10,6 @@ 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 : public ESM::Land { std::string mId; diff --git a/apps/opencs/model/world/landtexture.cpp b/apps/opencs/model/world/landtexture.cpp index 9e8dcff9f..266377d0f 100644 --- a/apps/opencs/model/world/landtexture.cpp +++ b/apps/opencs/model/world/landtexture.cpp @@ -7,11 +7,8 @@ namespace CSMWorld void LandTexture::load(ESM::ESMReader &esm, bool &isDeleted) { ESM::LandTexture::load(esm, isDeleted); - int plugin = esm.getIndex(); - std::ostringstream stream; - stream << mIndex << "_" << plugin; - mId = stream.str(); + mPluginIndex = esm.getIndex(); } } diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index 87ec2af55..e7be03321 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -55,16 +55,6 @@ namespace ESM } } - void Land::LandData::transposeTextureData(const uint16_t *in, uint16_t *out) - { - 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++ ) - out[(y1*4+y2)*16+(x1*4+x2)] = in[readPos++]; - } - Land::Land() : mFlags(0) , mX(0) @@ -77,6 +67,16 @@ namespace ESM { } + void Land::LandData::transposeTextureData(const uint16_t *in, uint16_t *out) + { + 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++ ) + out[(y1*4+y2)*16+(x1*4+x2)] = in[readPos++]; + } + Land::~Land() { delete mLandData; @@ -256,4 +256,69 @@ namespace ESM return (mDataLoaded & flags) == (flags & mDataTypes); } + Land::Land (const Land& land) + : mFlags (land.mFlags), mX (land.mX), mY (land.mY), mPlugin (land.mPlugin), + mEsm (land.mEsm), mContext (land.mContext), mDataTypes (land.mDataTypes), + mDataLoaded (land.mDataLoaded), + mLandData (land.mLandData ? new LandData (*land.mLandData) : 0) + {} + + Land& Land::operator= (Land land) + { + swap (land); + return *this; + } + + void Land::swap (Land& land) + { + std::swap (mFlags, land.mFlags); + std::swap (mX, land.mX); + std::swap (mY, land.mY); + std::swap (mPlugin, land.mPlugin); + std::swap (mEsm, land.mEsm); + std::swap (mContext, land.mContext); + std::swap (mDataTypes, land.mDataTypes); + std::swap (mDataLoaded, land.mDataLoaded); + std::swap (mLandData, land.mLandData); + } + + const Land::LandData *Land::getLandData (int flags) const + { + if (!(flags & mDataTypes)) + return 0; + + loadData (flags); + return mLandData; + } + + const Land::LandData *Land::getLandData() const + { + return mLandData; + } + + Land::LandData *Land::getLandData() + { + return mLandData; + } + + void Land::add (int flags) + { + if (!mLandData) + mLandData = new LandData; + + mDataTypes |= flags; + mDataLoaded |= flags; + } + + void Land::remove (int flags) + { + mDataTypes &= ~flags; + mDataLoaded &= ~flags; + + if (!mDataLoaded) + { + delete mLandData; + mLandData = 0; + } + } } diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index 83fa85890..26ef890bd 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -1,5 +1,8 @@ #include "storage.hpp" +#include +#include + #include #include #include @@ -32,19 +35,22 @@ namespace ESMTerrain Ogre::Vector2 origin = center - Ogre::Vector2(size/2.f, size/2.f); - assert(origin.x == (int) origin.x); - assert(origin.y == (int) origin.y); + int cellX = static_cast(std::floor(origin.x)); + int cellY = static_cast(std::floor(origin.y)); - int cellX = static_cast(origin.x); - int cellY = static_cast(origin.y); + int startRow = (origin.x - cellX) * ESM::Land::LAND_SIZE; + int startColumn = (origin.y - cellY) * ESM::Land::LAND_SIZE; + + 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)) { min = std::numeric_limits::max(); max = -std::numeric_limits::max(); - for (int row=0; rowmHeights[col*ESM::Land::LAND_SIZE+row]; if (h > max) @@ -141,17 +147,15 @@ namespace ESMTerrain size_t increment = 1 << lodLevel; Ogre::Vector2 origin = center - Ogre::Vector2(size/2.f, size/2.f); - assert(origin.x == (int) origin.x); - assert(origin.y == (int) origin.y); - int startX = static_cast(origin.x); - int startY = static_cast(origin.y); + int startCellX = static_cast(std::floor(origin.x)); + int startCellY = static_cast(std::floor(origin.y)); size_t numVerts = static_cast(size*(ESM::Land::LAND_SIZE - 1) / increment + 1); - colours.resize(numVerts*numVerts*4); positions.resize(numVerts*numVerts*3); normals.resize(numVerts*numVerts*3); + colours.resize(numVerts*numVerts*4); Ogre::Vector3 normal; Ogre::ColourValue color; @@ -160,10 +164,10 @@ namespace ESMTerrain float vertX = 0; float vertY_ = 0; // of current cell corner - for (int cellY = startY; cellY < startY + std::ceil(size); ++cellY) + for (int cellY = startCellY; cellY < startCellY + std::ceil(size); ++cellY) { float vertX_ = 0; // of current cell corner - for (int cellX = startX; cellX < startX + std::ceil(size); ++cellX) + 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); @@ -173,20 +177,33 @@ namespace ESMTerrain int colStart = 0; // Skip the first row / column unless we're at a chunk edge, // since this row / column is already contained in a previous cell + // This is only relevant if we're creating a chunk spanning multiple cells if (colStart == 0 && vertY_ != 0) colStart += increment; if (rowStart == 0 && vertX_ != 0) rowStart += increment; + // Only relevant for chunks smaller than (contained in) one cell + rowStart += (origin.x - startCellX) * ESM::Land::LAND_SIZE; + colStart += (origin.y - startCellY) * ESM::Land::LAND_SIZE; + int rowEnd = rowStart + std::min(1.f, size) * (ESM::Land::LAND_SIZE-1) + 1; + int colEnd = colStart + std::min(1.f, size) * (ESM::Land::LAND_SIZE-1) + 1; + vertY = vertY_; - for (int col=colStart; col(vertX*numVerts * 3 + vertY * 3)] = ((vertX / float(numVerts - 1) - 0.5f) * size * 8192); positions[static_cast(vertX*numVerts * 3 + vertY * 3 + 1)] = ((vertY / float(numVerts - 1) - 0.5f) * size * 8192); + assert(row >= 0 && row < ESM::Land::LAND_SIZE); + assert(col >= 0 && col < ESM::Land::LAND_SIZE); + + assert (vertX < numVerts); + assert (vertY < numVerts); + float height = -2048; if (heightData) height = heightData->mHeights[col*ESM::Land::LAND_SIZE + row]; @@ -290,7 +307,7 @@ namespace ESMTerrain // NB: All vtex ids are +1 compared to the ltex ids const ESM::LandTexture* ltex = getLandTexture(id.first-1, id.second); - //TODO this is needed due to MWs messed up texture handling + // this is needed due to MWs messed up texture handling std::string texture = Misc::ResourceHelpers::correctTexturePath(ltex->mTexture); return texture; @@ -320,8 +337,19 @@ namespace ESMTerrain // and interpolate the rest of the cell by hand? :/ Ogre::Vector2 origin = chunkCenter - Ogre::Vector2(chunkSize/2.f, chunkSize/2.f); - int cellX = static_cast(origin.x); - int cellY = static_cast(origin.y); + int cellX = static_cast(std::floor(origin.x)); + int cellY = static_cast(std::floor(origin.y)); + + int realTextureSize = ESM::Land::LAND_TEXTURE_SIZE+1; // add 1 to wrap around next cell + + int rowStart = (origin.x - cellX) * realTextureSize; + int colStart = (origin.y - cellY) * realTextureSize; + int rowEnd = rowStart + chunkSize * (realTextureSize-1) + 1; + int colEnd = colStart + chunkSize * (realTextureSize-1) + 1; + + assert (rowStart >= 0 && colStart >= 0); + assert (rowEnd <= realTextureSize); + assert (colEnd <= realTextureSize); // Save the used texture indices so we know the total number of textures // and number of required blend maps @@ -332,8 +360,8 @@ namespace ESMTerrain // So we're always adding _land_default.dds as the base layer here, even if it's not referenced in this cell. textureIndices.insert(std::make_pair(0,0)); - for (int y=0; ysecond; int blendIndex = (pack ? static_cast(std::floor((layerIndex - 1) / 4.f)) : layerIndex - 1); int channel = pack ? std::max(0, (layerIndex-1) % 4) : 0;