Rework land texture handling

pull/3236/head
Evil Eye 7 months ago
parent 965bc20bab
commit fbc6629d40

@ -25,7 +25,7 @@ opencs_units (model/world
opencs_units (model/world
universalid record commands columnbase columnimp scriptcontext cell refidcollection
refiddata refidadapterimp ref collectionbase refcollection columns infocollection tablemimedata cellcoordinates cellselection resources resourcesmanager scope
pathgrid landtexture land nestedtablewrapper nestedcollection nestedcoladapterimp nestedinfocollection
pathgrid land nestedtablewrapper nestedcollection nestedcoladapterimp nestedinfocollection
idcompletionmanager metadata defaultgmsts infoselectwrapper commandmacro
)
@ -70,7 +70,7 @@ opencs_units (view/world
cellcreator pathgridcreator referenceablecreator startscriptcreator referencecreator scenesubview
infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable nestedtable
dialoguespinbox recordbuttonbar tableeditidaction scripterrortable extendedcommandconfigurator
bodypartcreator landtexturecreator landcreator tableheadermouseeventhandler
bodypartcreator landcreator tableheadermouseeventhandler
)
opencs_units (view/world

@ -17,7 +17,6 @@
#include <apps/opencs/model/world/idcollection.hpp>
#include <apps/opencs/model/world/info.hpp>
#include <apps/opencs/model/world/land.hpp>
#include <apps/opencs/model/world/landtexture.hpp>
#include <apps/opencs/model/world/metadata.hpp>
#include <apps/opencs/model/world/pathgrid.hpp>
#include <apps/opencs/model/world/record.hpp>
@ -498,11 +497,11 @@ int CSMDoc::WriteLandTextureCollectionStage::setup()
void CSMDoc::WriteLandTextureCollectionStage::perform(int stage, Messages& messages)
{
ESM::ESMWriter& writer = mState.getWriter();
const CSMWorld::Record<CSMWorld::LandTexture>& landTexture = mDocument.getData().getLandTextures().getRecord(stage);
const CSMWorld::Record<ESM::LandTexture>& landTexture = mDocument.getData().getLandTextures().getRecord(stage);
if (landTexture.isModified() || landTexture.mState == CSMWorld::RecordBase::State_Deleted)
{
CSMWorld::LandTexture record = landTexture.get();
ESM::LandTexture record = landTexture.get();
writer.startRecord(record.sRecordId);
record.save(writer, landTexture.mState == CSMWorld::RecordBase::State_Deleted);
writer.endRecord(record.sRecordId);

@ -6,7 +6,6 @@
#include <vector>
#include <apps/opencs/model/world/land.hpp>
#include <apps/opencs/model/world/landtexture.hpp>
#include <apps/opencs/model/world/metadata.hpp>
#include <apps/opencs/model/world/ref.hpp>
#include <apps/opencs/model/world/refcollection.hpp>
@ -137,13 +136,12 @@ int CSMTools::PopulateLandTexturesMergeStage::setup()
void CSMTools::PopulateLandTexturesMergeStage::perform(int stage, CSMDoc::Messages& messages)
{
const CSMWorld::Record<CSMWorld::LandTexture>& record = mState.mSource.getData().getLandTextures().getRecord(stage);
const CSMWorld::Record<ESM::LandTexture>& record = mState.mSource.getData().getLandTextures().getRecord(stage);
if (!record.isDeleted())
{
mState.mTarget->getData().getLandTextures().appendRecord(
std::make_unique<CSMWorld::Record<CSMWorld::LandTexture>>(CSMWorld::Record<CSMWorld::LandTexture>(
CSMWorld::RecordBase::State_ModifiedOnly, nullptr, &record.get())));
mState.mTarget->getData().getLandTextures().appendRecord(std::make_unique<CSMWorld::Record<ESM::LandTexture>>(
CSMWorld::Record<ESM::LandTexture>(CSMWorld::RecordBase::State_ModifiedOnly, nullptr, &record.get())));
}
}

@ -24,7 +24,6 @@
#include "columnimp.hpp"
#include "info.hpp"
#include "land.hpp"
#include "landtexture.hpp"
#include "record.hpp"
#include "ref.hpp"
@ -74,16 +73,6 @@ namespace CSMWorld
return ESM::RefId::stringRefId(Land::createUniqueRecordId(record.mX, record.mY));
}
inline void setRecordId(const ESM::RefId& id, LandTexture& record)
{
int plugin = 0;
int index = 0;
LandTexture::parseUniqueRecordId(id.getRefIdString(), plugin, index);
record.mPluginIndex = plugin;
record.mIndex = index;
}
inline ESM::RefId getRecordId(const ESM::MagicEffect& record)
{
return ESM::RefId::stringRefId(CSMWorld::getStringId(record.mId));
@ -95,11 +84,6 @@ namespace CSMWorld
record.mId = ESM::RefId::index(ESM::REC_MGEF, static_cast<std::uint32_t>(index));
}
inline ESM::RefId getRecordId(const LandTexture& record)
{
return ESM::RefId::stringRefId(LandTexture::createUniqueRecordId(record.mPluginIndex, record.mIndex));
}
inline void setRecordId(const ESM::RefId& id, ESM::Skill& record)
{
if (const auto* skillId = id.getIf<ESM::SkillId>())
@ -339,7 +323,7 @@ namespace CSMWorld
const ESM::RefId& origin, const ESM::RefId& destination, const UniversalId::Type type)
{
const int index = cloneRecordImp(origin, destination, type);
mRecords.at(index)->get().setPlugin(0);
mRecords.at(index)->get().setPlugin(-1);
}
template <typename ESXRecordT>
@ -354,7 +338,7 @@ namespace CSMWorld
const int index = touchRecordImp(id);
if (index >= 0)
{
mRecords.at(index)->get().setPlugin(0);
mRecords.at(index)->get().setPlugin(-1);
return true;
}

@ -3,10 +3,10 @@
#include <apps/opencs/model/world/columnbase.hpp>
#include <apps/opencs/model/world/columns.hpp>
#include <apps/opencs/model/world/land.hpp>
#include <apps/opencs/model/world/landtexture.hpp>
#include <apps/opencs/model/world/record.hpp>
#include <components/esm3/loadland.hpp>
#include <components/esm3/loadltex.hpp>
#include <components/esm3/loadmgef.hpp>
#include <algorithm>
@ -45,36 +45,13 @@ namespace CSMWorld
};
}
/* LandTextureNicknameColumn */
LandTextureNicknameColumn::LandTextureNicknameColumn()
: Column<LandTexture>(Columns::ColumnId_TextureNickname, ColumnBase::Display_String)
{
}
QVariant LandTextureNicknameColumn::get(const Record<LandTexture>& record) const
{
return QString::fromStdString(record.get().mId.toString());
}
void LandTextureNicknameColumn::set(Record<LandTexture>& record, const QVariant& data)
{
LandTexture copy = record.get();
copy.mId = ESM::RefId::stringRefId(data.toString().toUtf8().constData());
record.setModified(copy);
}
bool LandTextureNicknameColumn::isEditable() const
{
return true;
}
/* LandTextureIndexColumn */
LandTextureIndexColumn::LandTextureIndexColumn()
: Column<LandTexture>(Columns::ColumnId_TextureIndex, ColumnBase::Display_Integer)
: Column<ESM::LandTexture>(Columns::ColumnId_TextureIndex, ColumnBase::Display_Integer)
{
}
QVariant LandTextureIndexColumn::get(const Record<LandTexture>& record) const
QVariant LandTextureIndexColumn::get(const Record<ESM::LandTexture>& record) const
{
return record.get().mIndex;
}
@ -100,22 +77,6 @@ namespace CSMWorld
return false;
}
/* LandTexturePluginIndexColumn */
LandTexturePluginIndexColumn::LandTexturePluginIndexColumn()
: Column<LandTexture>(Columns::ColumnId_PluginIndex, ColumnBase::Display_Integer, 0)
{
}
QVariant LandTexturePluginIndexColumn::get(const Record<LandTexture>& record) const
{
return record.get().mPluginIndex;
}
bool LandTexturePluginIndexColumn::isEditable() const
{
return false;
}
/* LandNormalsColumn */
LandNormalsColumn::LandNormalsColumn()
: Column<Land>(Columns::ColumnId_LandNormalsIndex, ColumnBase::Display_String, 0)

@ -14,6 +14,7 @@
#include <components/esm3/loadbody.hpp>
#include <components/esm3/loaddial.hpp>
#include <components/esm3/loadinfo.hpp>
#include <components/esm3/loadltex.hpp>
#include <components/esm3/loadrace.hpp>
#include <components/esm3/loadskil.hpp>
#include <components/esm3/selectiongroup.hpp>
@ -29,7 +30,6 @@
#include "columns.hpp"
#include "info.hpp"
#include "land.hpp"
#include "landtexture.hpp"
#include "record.hpp"
namespace CSMWorld
@ -83,13 +83,6 @@ namespace CSMWorld
return QString::fromUtf8(Land::createUniqueRecordId(land.mX, land.mY).c_str());
}
template <>
inline QVariant StringIdColumn<LandTexture>::get(const Record<LandTexture>& record) const
{
const LandTexture& ltex = record.get();
return QString::fromUtf8(LandTexture::createUniqueRecordId(ltex.mPluginIndex, ltex.mIndex).c_str());
}
template <typename ESXRecordT>
struct RecordStateColumn : public Column<ESXRecordT>
{
@ -2365,20 +2358,11 @@ namespace CSMWorld
bool isEditable() const override { return true; }
};
struct LandTextureNicknameColumn : public Column<LandTexture>
{
LandTextureNicknameColumn();
QVariant get(const Record<LandTexture>& record) const override;
void set(Record<LandTexture>& record, const QVariant& data) override;
bool isEditable() const override;
};
struct LandTextureIndexColumn : public Column<LandTexture>
struct LandTextureIndexColumn : public Column<ESM::LandTexture>
{
LandTextureIndexColumn();
QVariant get(const Record<LandTexture>& record) const override;
QVariant get(const Record<ESM::LandTexture>& record) const override;
bool isEditable() const override;
};
@ -2390,14 +2374,6 @@ namespace CSMWorld
bool isEditable() const override;
};
struct LandTexturePluginIndexColumn : public Column<LandTexture>
{
LandTexturePluginIndexColumn();
QVariant get(const Record<LandTexture>& record) const override;
bool isEditable() const override;
};
struct LandNormalsColumn : public Column<Land>
{
using DataType = QVector<signed char>;

@ -7,7 +7,6 @@
#include <apps/opencs/model/world/columns.hpp>
#include <apps/opencs/model/world/land.hpp>
#include <apps/opencs/model/world/landtexture.hpp>
#include <apps/opencs/model/world/record.hpp>
#include <apps/opencs/model/world/universalid.hpp>
@ -61,11 +60,11 @@ CSMWorld::ImportLandTexturesCommand::ImportLandTexturesCommand(
void CSMWorld::ImportLandTexturesCommand::redo()
{
int pluginColumn = mLands.findColumnIndex(Columns::ColumnId_PluginIndex);
int oldPlugin = mLands.data(mLands.getModelIndex(getOriginId(), pluginColumn)).toInt();
const int pluginColumn = mLands.findColumnIndex(Columns::ColumnId_PluginIndex);
const int oldPlugin = mLands.data(mLands.getModelIndex(getOriginId(), pluginColumn)).toInt();
// Original data
int textureColumn = mLands.findColumnIndex(Columns::ColumnId_LandTexturesIndex);
const int textureColumn = mLands.findColumnIndex(Columns::ColumnId_LandTexturesIndex);
mOld = mLands.data(mLands.getModelIndex(getOriginId(), textureColumn)).value<DataType>();
// Need to make a copy so the old values can be looked up
@ -74,44 +73,37 @@ void CSMWorld::ImportLandTexturesCommand::redo()
// Perform touch/copy/etc...
onRedo();
// Find all indices used
std::unordered_set<int> texIndices;
for (int i = 0; i < mOld.size(); ++i)
std::unordered_map<uint16_t, uint16_t> indexMapping;
for (uint16_t index : mOld)
{
// All indices are offset by 1 for a default texture
if (mOld[i] > 0)
texIndices.insert(mOld[i] - 1);
}
std::vector<std::string> oldTextures;
oldTextures.reserve(texIndices.size());
for (int index : texIndices)
{
oldTextures.push_back(LandTexture::createUniqueRecordId(oldPlugin, index));
}
// Import the textures, replace old values
LandTextureIdTable::ImportResults results = dynamic_cast<LandTextureIdTable&>(mLtexs).importTextures(oldTextures);
mCreatedTextures = std::move(results.createdRecords);
for (const auto& it : results.recordMapping)
{
int plugin = 0, newIndex = 0, oldIndex = 0;
LandTexture::parseUniqueRecordId(it.first, plugin, oldIndex);
LandTexture::parseUniqueRecordId(it.second, plugin, newIndex);
if (newIndex != oldIndex)
if (index == 0)
continue;
if (indexMapping.contains(index))
continue;
const CSMWorld::Record<ESM::LandTexture>* record
= static_cast<LandTextureIdTable&>(mLtexs).searchRecord(index - 1, oldPlugin);
if (!record || record->isDeleted())
{
indexMapping.emplace(index, 0);
continue;
}
if (!record->isModified())
{
for (int i = 0; i < Land::LAND_NUM_TEXTURES; ++i)
{
// All indices are offset by 1 for a default texture
if (mOld[i] == oldIndex + 1)
copy[i] = newIndex + 1;
}
mTouchedTextures.emplace_back(record->clone());
mLtexs.touchRecord(record->get().mId.getRefIdString());
}
indexMapping.emplace(index, record->get().mIndex + 1);
}
for (int i = 0; i < Land::LAND_NUM_TEXTURES; ++i)
{
uint16_t oldIndex = mOld[i];
uint16_t newIndex = indexMapping[oldIndex];
copy[i] = newIndex;
}
// Apply modification
int stateColumn = mLands.findColumnIndex(Columns::ColumnId_Modification);
const int stateColumn = mLands.findColumnIndex(Columns::ColumnId_Modification);
mOldState = mLands.data(mLands.getModelIndex(getDestinationId(), stateColumn)).toInt();
QVariant variant;
@ -133,12 +125,12 @@ void CSMWorld::ImportLandTexturesCommand::undo()
// Undo copy/touch/etc...
onUndo();
for (const std::string& id : mCreatedTextures)
for (auto& ltex : mTouchedTextures)
{
int row = mLtexs.getModelIndex(id, 0).row();
mLtexs.removeRows(row, 1);
ESM::RefId id = static_cast<Record<ESM::LandTexture>*>(ltex.get())->get().mId;
mLtexs.setRecord(id.getRefIdString(), std::move(ltex));
}
mCreatedTextures.clear();
mTouchedTextures.clear();
}
CSMWorld::CopyLandTexturesCommand::CopyLandTexturesCommand(

@ -69,7 +69,7 @@ namespace CSMWorld
IdTable& mLtexs;
DataType mOld;
int mOldState;
std::vector<std::string> mCreatedTextures;
std::vector<std::unique_ptr<RecordBase>> mTouchedTextures;
};
/// \brief This command is used to fix LandTexture records and texture

@ -16,7 +16,6 @@
#include <apps/opencs/model/world/info.hpp>
#include <apps/opencs/model/world/infocollection.hpp>
#include <apps/opencs/model/world/land.hpp>
#include <apps/opencs/model/world/landtexture.hpp>
#include <apps/opencs/model/world/metadata.hpp>
#include <apps/opencs/model/world/nestedidcollection.hpp>
#include <apps/opencs/model/world/nestedinfocollection.hpp>
@ -36,6 +35,7 @@
#include <components/esm3/loadcell.hpp>
#include <components/esm3/loaddoor.hpp>
#include <components/esm3/loadglob.hpp>
#include <components/esm3/loadltex.hpp>
#include <components/esm3/loadstat.hpp>
#include <components/files/collections.hpp>
#include <components/misc/strings/lower.hpp>
@ -530,13 +530,11 @@ CSMWorld::Data::Data(ToUTF8::FromType encoding, const Files::PathContainer& data
mLand.addColumn(new LandColoursColumn);
mLand.addColumn(new LandTexturesColumn);
mLandTextures.addColumn(new StringIdColumn<LandTexture>(true));
mLandTextures.addColumn(new RecordStateColumn<LandTexture>);
mLandTextures.addColumn(new FixedRecordTypeColumn<LandTexture>(UniversalId::Type_LandTexture));
mLandTextures.addColumn(new LandTextureNicknameColumn);
mLandTextures.addColumn(new LandTexturePluginIndexColumn);
mLandTextures.addColumn(new StringIdColumn<ESM::LandTexture>);
mLandTextures.addColumn(new RecordStateColumn<ESM::LandTexture>);
mLandTextures.addColumn(new FixedRecordTypeColumn<ESM::LandTexture>(UniversalId::Type_LandTexture));
mLandTextures.addColumn(new LandTextureIndexColumn);
mLandTextures.addColumn(new TextureColumn<LandTexture>);
mLandTextures.addColumn(new TextureColumn<ESM::LandTexture>);
mPathgrids.addColumn(new StringIdColumn<Pathgrid>);
mPathgrids.addColumn(new RecordStateColumn<Pathgrid>);
@ -939,12 +937,12 @@ CSMWorld::IdCollection<CSMWorld::Land>& CSMWorld::Data::getLand()
return mLand;
}
const CSMWorld::IdCollection<CSMWorld::LandTexture>& CSMWorld::Data::getLandTextures() const
const CSMWorld::IdCollection<ESM::LandTexture>& CSMWorld::Data::getLandTextures() const
{
return mLandTextures;
}
CSMWorld::IdCollection<CSMWorld::LandTexture>& CSMWorld::Data::getLandTextures()
CSMWorld::IdCollection<ESM::LandTexture>& CSMWorld::Data::getLandTextures()
{
return mLandTextures;
}

@ -24,6 +24,7 @@
#include <components/esm3/loadfact.hpp>
#include <components/esm3/loadglob.hpp>
#include <components/esm3/loadgmst.hpp>
#include <components/esm3/loadltex.hpp>
#include <components/esm3/loadmgef.hpp>
#include <components/esm3/loadrace.hpp>
#include <components/esm3/loadregn.hpp>
@ -43,7 +44,6 @@
#include "idcollection.hpp"
#include "infocollection.hpp"
#include "land.hpp"
#include "landtexture.hpp"
#include "metadata.hpp"
#include "nestedidcollection.hpp"
#include "nestedinfocollection.hpp"
@ -114,7 +114,7 @@ namespace CSMWorld
InfoCollection mJournalInfos;
NestedIdCollection<Cell> mCells;
SubCellCollection<Pathgrid> mPathgrids;
IdCollection<LandTexture> mLandTextures;
IdCollection<ESM::LandTexture> mLandTextures;
IdCollection<Land> mLand;
RefIdCollection mReferenceables;
RefCollection mRefs;
@ -259,9 +259,9 @@ namespace CSMWorld
IdCollection<CSMWorld::Land>& getLand();
const IdCollection<CSMWorld::LandTexture>& getLandTextures() const;
const IdCollection<ESM::LandTexture>& getLandTextures() const;
IdCollection<CSMWorld::LandTexture>& getLandTextures();
IdCollection<ESM::LandTexture>& getLandTextures();
const IdCollection<ESM::SoundGenerator>& getSoundGens() const;

@ -8,6 +8,7 @@
#include <apps/opencs/model/world/pathgrid.hpp>
#include <apps/opencs/model/world/record.hpp>
#include <components/esm3/esmreader.hpp>
#include <components/esm3/loadpgrd.hpp>
namespace ESM
@ -18,12 +19,12 @@ namespace ESM
namespace CSMWorld
{
template <>
int IdCollection<Pathgrid>::load(ESM::ESMReader& reader, bool base)
int BaseIdCollection<Pathgrid>::load(ESM::ESMReader& reader, bool base)
{
Pathgrid record;
bool isDeleted = false;
loadRecord(record, reader, isDeleted);
loadRecord(record, reader, isDeleted, base);
const ESM::RefId id = getRecordId(record);
int index = this->searchId(id);
@ -55,4 +56,115 @@ namespace CSMWorld
return load(record, base, index);
}
const Record<ESM::LandTexture>* IdCollection<ESM::LandTexture>::searchRecord(std::uint16_t index, int plugin) const
{
auto found = mIndices.find({ plugin, index });
if (found != mIndices.end())
{
int index = searchId(found->second);
if (index != -1)
return &getRecord(index);
}
return nullptr;
}
const std::string* IdCollection<ESM::LandTexture>::getLandTexture(std::uint16_t index, int plugin) const
{
const Record<ESM::LandTexture>* record = searchRecord(index, plugin);
if (record && !record->isDeleted())
return &record->get().mTexture;
return nullptr;
}
void IdCollection<ESM::LandTexture>::loadRecord(
ESM::LandTexture& record, ESM::ESMReader& reader, bool& isDeleted, bool base)
{
record.load(reader, isDeleted);
int plugin = base ? reader.getIndex() : -1;
mIndices.emplace(std::make_pair(plugin, record.mIndex), record.mId);
}
std::uint16_t IdCollection<ESM::LandTexture>::assignNewIndex(ESM::RefId id)
{
std::uint16_t index = 0;
if (!mIndices.empty())
{
auto end = mIndices.lower_bound({ -1, std::numeric_limits<std::uint16_t>::max() });
if (end != mIndices.begin())
end = std::prev(end);
if (end->first.first == -1)
{
constexpr std::uint16_t maxIndex = std::numeric_limits<std::uint16_t>::max() - 1;
if (end->first.second < maxIndex)
index = end->first.second + 1;
else
{
std::uint16_t prevIndex = 0;
for (auto it = mIndices.lower_bound({ -1, 0 }); it != end; ++it)
{
if (prevIndex != it->first.second)
{
index = prevIndex;
break;
}
++prevIndex;
}
}
}
}
mIndices.emplace(std::make_pair(-1, index), id);
return index;
}
bool IdCollection<ESM::LandTexture>::touchRecord(const ESM::RefId& id)
{
int row = BaseIdCollection<ESM::LandTexture>::touchRecordImp(id);
if (row != -1)
{
const_cast<ESM::LandTexture&>(getRecord(row).get()).mIndex = assignNewIndex(id);
return true;
}
return false;
}
void IdCollection<ESM::LandTexture>::cloneRecord(
const ESM::RefId& origin, const ESM::RefId& destination, const UniversalId::Type type)
{
int row = cloneRecordImp(origin, destination, type);
const_cast<ESM::LandTexture&>(getRecord(row).get()).mIndex = assignNewIndex(destination);
}
void IdCollection<ESM::LandTexture>::appendBlankRecord(const ESM::RefId& id, UniversalId::Type type)
{
ESM::LandTexture record;
record.blank();
record.mId = id;
record.mIndex = assignNewIndex(id);
auto record2 = std::make_unique<Record<ESM::LandTexture>>();
record2->mState = Record<ESM::LandTexture>::State_ModifiedOnly;
record2->mModified = std::move(record);
insertRecord(std::move(record2), getAppendIndex(id, type), type);
}
void IdCollection<ESM::LandTexture>::removeRows(int index, int count)
{
for (int row = index; row < index + count; ++row)
{
const auto& record = getRecord(row);
if (record.isModified())
mIndices.erase({ -1, record.get().mIndex });
}
BaseIdCollection<ESM::LandTexture>::removeRows(index, count);
}
void IdCollection<ESM::LandTexture>::replace(int index, std::unique_ptr<RecordBase> record)
{
const auto& current = getRecord(index);
if (current.isModified() && !record->isModified())
mIndices.erase({ -1, current.get().mIndex });
BaseIdCollection<ESM::LandTexture>::replace(index, std::move(record));
}
}

@ -17,6 +17,7 @@
namespace ESM
{
class ESMReader;
struct LandTexture;
}
namespace CSMWorld
@ -25,9 +26,9 @@ namespace CSMWorld
/// \brief Single type collection of top level records
template <typename ESXRecordT>
class IdCollection : public Collection<ESXRecordT>
class BaseIdCollection : public Collection<ESXRecordT>
{
virtual void loadRecord(ESXRecordT& record, ESM::ESMReader& reader, bool& isDeleted);
virtual void loadRecord(ESXRecordT& record, ESM::ESMReader& reader, bool& isDeleted, bool base);
public:
/// \return Index of loaded record (-1 if no record was loaded)
@ -46,14 +47,46 @@ namespace CSMWorld
/// \return Has the ID been deleted?
};
template <class ESXRecordT>
class IdCollection : public BaseIdCollection<ESXRecordT>
{
};
template <>
class IdCollection<ESM::LandTexture> : public BaseIdCollection<ESM::LandTexture>
{
std::map<std::pair<int, std::uint16_t>, ESM::RefId> mIndices;
void loadRecord(ESM::LandTexture& record, ESM::ESMReader& reader, bool& isDeleted, bool base) override;
std::uint16_t assignNewIndex(ESM::RefId id);
public:
const Record<ESM::LandTexture>* searchRecord(std::uint16_t index, int plugin) const;
const std::string* getLandTexture(std::uint16_t index, int plugin) const;
bool touchRecord(const ESM::RefId& id) override;
void cloneRecord(
const ESM::RefId& origin, const ESM::RefId& destination, const UniversalId::Type type) override;
void appendBlankRecord(const ESM::RefId& id, UniversalId::Type type) override;
void removeRows(int index, int count) override;
void replace(int index, std::unique_ptr<RecordBase> record) override;
};
template <typename ESXRecordT>
void IdCollection<ESXRecordT>::loadRecord(ESXRecordT& record, ESM::ESMReader& reader, bool& isDeleted)
void BaseIdCollection<ESXRecordT>::loadRecord(
ESXRecordT& record, ESM::ESMReader& reader, bool& isDeleted, bool base)
{
record.load(reader, isDeleted);
}
template <>
inline void IdCollection<Land>::loadRecord(Land& record, ESM::ESMReader& reader, bool& isDeleted)
inline void BaseIdCollection<Land>::loadRecord(Land& record, ESM::ESMReader& reader, bool& isDeleted, bool base)
{
record.load(reader, isDeleted);
@ -64,22 +97,17 @@ namespace CSMWorld
// Prevent data from being reloaded.
record.mContext.filename.clear();
if (!base)
record.setPlugin(-1);
}
template <typename ESXRecordT>
int IdCollection<ESXRecordT>::load(ESM::ESMReader& reader, bool base)
int BaseIdCollection<ESXRecordT>::load(ESM::ESMReader& reader, bool base)
{
ESXRecordT record;
bool isDeleted = false;
loadRecord(record, reader, isDeleted);
if constexpr (std::is_same_v<ESXRecordT, LandTexture>)
{
// This doesn't really matter since the value never gets saved, but it makes the index uniqueness check more
// sensible
if (!base)
record.mPluginIndex = -1;
}
loadRecord(record, reader, isDeleted, base);
ESM::RefId id = getRecordId(record);
int index = this->searchId(id);
@ -110,7 +138,7 @@ namespace CSMWorld
}
template <typename ESXRecordT>
int IdCollection<ESXRecordT>::load(const ESXRecordT& record, bool base, int index)
int BaseIdCollection<ESXRecordT>::load(const ESXRecordT& record, bool base, int index)
{
if (index == -2) // index unknown
index = this->searchId(getRecordId(record));
@ -142,7 +170,7 @@ namespace CSMWorld
}
template <typename ESXRecordT>
bool IdCollection<ESXRecordT>::tryDelete(const ESM::RefId& id)
bool BaseIdCollection<ESXRecordT>::tryDelete(const ESM::RefId& id)
{
int index = this->searchId(id);
@ -169,7 +197,7 @@ namespace CSMWorld
}
template <>
int IdCollection<Pathgrid>::load(ESM::ESMReader& reader, bool base);
int BaseIdCollection<Pathgrid>::load(ESM::ESMReader& reader, bool base);
}
#endif

@ -12,6 +12,7 @@
#include <type_traits>
#include <apps/opencs/model/world/columns.hpp>
#include <apps/opencs/model/world/idcollection.hpp>
#include <apps/opencs/model/world/idtablebase.hpp>
#include <apps/opencs/model/world/record.hpp>
#include <apps/opencs/model/world/universalid.hpp>
@ -21,7 +22,6 @@
#include "collectionbase.hpp"
#include "columnbase.hpp"
#include "landtexture.hpp"
CSMWorld::IdTable::IdTable(CollectionBase* idCollection, unsigned int features)
: IdTableBase(features)
@ -361,73 +361,8 @@ CSMWorld::LandTextureIdTable::LandTextureIdTable(CollectionBase* idCollection, u
{
}
CSMWorld::LandTextureIdTable::ImportResults CSMWorld::LandTextureIdTable::importTextures(
const std::vector<std::string>& ids)
const CSMWorld::Record<ESM::LandTexture>* CSMWorld::LandTextureIdTable::searchRecord(
std::uint16_t index, int plugin) const
{
ImportResults results;
// Map existing textures to ids
std::map<std::string, std::string> reverseLookupMap;
for (int i = 0; i < idCollection()->getSize(); ++i)
{
auto& record = static_cast<const Record<LandTexture>&>(idCollection()->getRecord(i));
if (record.isModified())
reverseLookupMap.emplace(
Misc::StringUtils::lowerCase(record.get().mTexture), idCollection()->getId(i).getRefIdString());
}
for (const std::string& id : ids)
{
int plugin, index;
LandTexture::parseUniqueRecordId(id, plugin, index);
const ESM::RefId refId = ESM::RefId::stringRefId(id);
const int oldRow = idCollection()->searchId(refId);
// If it does not exist, it can be skipped.
if (oldRow < 0)
{
results.recordMapping.emplace_back(id, id);
continue;
}
// Look for a pre-existing record
auto& record = static_cast<const Record<LandTexture>&>(idCollection()->getRecord(oldRow));
// If it is in the current plugin, it can be skipped.
if (record.mState == Record<LandTexture>::State_ModifiedOnly)
{
results.recordMapping.emplace_back(id, id);
continue;
}
std::string texture = Misc::StringUtils::lowerCase(record.get().mTexture);
auto searchIt = reverseLookupMap.find(texture);
if (searchIt != reverseLookupMap.end())
{
results.recordMapping.emplace_back(id, searchIt->second);
continue;
}
// Iterate until an unused index or found, or the index has completely wrapped around.
int startIndex = index;
do
{
std::string newId = LandTexture::createUniqueRecordId(-1, index);
const ESM::RefId newRefId = ESM::RefId::stringRefId(newId);
int newRow = idCollection()->searchId(newRefId);
if (newRow < 0)
{
// Id not taken, clone it
cloneRecord(refId, newRefId, UniversalId::Type_LandTexture);
results.createdRecords.push_back(newId);
results.recordMapping.emplace_back(id, newId);
reverseLookupMap.emplace(texture, newId);
break;
}
const size_t MaxIndex = std::numeric_limits<uint16_t>::max() - 1;
index = (index + 1) % MaxIndex;
} while (index != startIndex);
}
return results;
return static_cast<CSMWorld::IdCollection<ESM::LandTexture>*>(idCollection())->searchRecord(index, plugin);
}

@ -16,10 +16,17 @@
class QObject;
namespace ESM
{
struct LandTexture;
}
namespace CSMWorld
{
class CollectionBase;
struct RecordBase;
template <typename ESXRecordT>
struct Record;
class IdTable : public IdTableBase
{
@ -103,26 +110,12 @@ namespace CSMWorld
virtual CollectionBase* idCollection() const;
};
/// An IdTable customized to handle the more unique needs of LandTextureId's which behave
/// differently from other records. The major difference is that base records cannot be
/// modified.
class LandTextureIdTable : public IdTable
{
public:
struct ImportResults
{
using StringPair = std::pair<std::string, std::string>;
/// The newly added records
std::vector<std::string> createdRecords;
/// The 1st string is the original id, the 2nd is the mapped id
std::vector<StringPair> recordMapping;
};
LandTextureIdTable(CollectionBase* idCollection, unsigned int features = 0);
/// Finds and maps/recreates the specified ids.
ImportResults importTextures(const std::vector<std::string>& ids);
const CSMWorld::Record<ESM::LandTexture>* searchRecord(std::uint16_t index, int plugin) const;
};
}

@ -1,35 +0,0 @@
#include "landtexture.hpp"
#include <sstream>
#include <stdexcept>
#include <components/esm3/esmreader.hpp>
#include <components/misc/strings/conversion.hpp>
namespace CSMWorld
{
void LandTexture::load(ESM::ESMReader& esm, bool& isDeleted)
{
ESM::LandTexture::load(esm, isDeleted);
mPluginIndex = esm.getIndex();
}
std::string LandTexture::createUniqueRecordId(int plugin, int index)
{
std::stringstream ss;
ss << 'L' << plugin << '#' << index;
return ss.str();
}
void LandTexture::parseUniqueRecordId(const std::string& id, int& plugin, int& index)
{
size_t middle = id.find('#');
if (middle == std::string::npos || id[0] != 'L')
throw std::runtime_error("Invalid LandTexture ID");
plugin = Misc::StringUtils::toNumeric<int>(id.substr(1, middle - 1), 0);
index = Misc::StringUtils::toNumeric<int>(id.substr(middle + 1), 0);
}
}

@ -1,29 +0,0 @@
#ifndef CSM_WORLD_LANDTEXTURE_H
#define CSM_WORLD_LANDTEXTURE_H
#include <string>
#include <components/esm3/loadltex.hpp>
namespace ESM
{
class ESMReader;
}
namespace CSMWorld
{
/// \brief Wrapper for LandTexture record, providing info which plugin the LandTexture was loaded from.
struct LandTexture : public ESM::LandTexture
{
int mPluginIndex;
void load(ESM::ESMReader& esm, bool& isDeleted);
/// Returns a string identifier that will be unique to any LandTexture.
static std::string createUniqueRecordId(int plugin, int index);
/// Deconstructs a unique string identifier into plugin and index.
static void parseUniqueRecordId(const std::string& id, int& plugin, int& index);
};
}
#endif

@ -18,14 +18,15 @@ namespace CSMWorld
{
const IdCollection<Cell>& mCells;
void loadRecord(ESXRecordT& record, ESM::ESMReader& reader, bool& isDeleted) override;
void loadRecord(ESXRecordT& record, ESM::ESMReader& reader, bool& isDeleted, bool base) override;
public:
SubCellCollection(const IdCollection<Cell>& cells);
};
template <typename ESXRecordT>
void SubCellCollection<ESXRecordT>::loadRecord(ESXRecordT& record, ESM::ESMReader& reader, bool& isDeleted)
void SubCellCollection<ESXRecordT>::loadRecord(
ESXRecordT& record, ESM::ESMReader& reader, bool& isDeleted, bool base)
{
record.load(reader, isDeleted, mCells);
}

@ -7,7 +7,6 @@
#include <apps/opencs/model/world/data.hpp>
#include <apps/opencs/model/world/idcollection.hpp>
#include <apps/opencs/model/world/land.hpp>
#include <apps/opencs/model/world/landtexture.hpp>
#include <apps/opencs/model/world/record.hpp>
#include <algorithm>
@ -44,12 +43,7 @@ namespace CSVRender
const std::string* TerrainStorage::getLandTexture(std::uint16_t index, int plugin)
{
const int row = mData.getLandTextures().searchId(
ESM::RefId::stringRefId(CSMWorld::LandTexture::createUniqueRecordId(plugin, index)));
if (row == -1)
return nullptr;
return &mData.getLandTextures().getRecord(row).get().mTexture;
return mData.getLandTextures().getLandTexture(index, plugin);
}
void TerrainStorage::setAlteredHeight(int inCellX, int inCellY, float height)

@ -38,7 +38,6 @@
#include "../../model/world/data.hpp"
#include "../../model/world/idtable.hpp"
#include "../../model/world/idtree.hpp"
#include "../../model/world/landtexture.hpp"
#include "../../model/world/tablemimedata.hpp"
#include "../../model/world/universalid.hpp"
@ -52,7 +51,6 @@ CSVRender::TerrainTextureMode::TerrainTextureMode(
WorldspaceWidget* worldspaceWidget, osg::Group* parentNode, QWidget* parent)
: EditMode(worldspaceWidget, Misc::ScalableIcon::load(":scenetoolbar/editing-terrain-texture"),
Mask_Terrain | Mask_Reference, "Terrain texture editing", parent)
, mBrushTexture("L0#0")
, mBrushSize(1)
, mBrushShape(CSVWidget::BrushShape_Point)
, mTextureBrushScenetool(nullptr)
@ -137,8 +135,8 @@ void CSVRender::TerrainTextureMode::primaryEditPressed(const WorldspaceHitResult
mCellId = getWorldspaceWidget().getCellId(hit.worldPos);
QUndoStack& undoStack = document.getUndoStack();
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures();
int index = landtexturesCollection.searchId(ESM::RefId::stringRefId(mBrushTexture));
CSMWorld::IdCollection<ESM::LandTexture>& landtexturesCollection = document.getData().getLandTextures();
int index = landtexturesCollection.searchId(mBrushTexture);
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted() && hit.hit && hit.tag == nullptr)
{
@ -186,8 +184,8 @@ bool CSVRender::TerrainTextureMode::primaryEditStartDrag(const QPoint& pos)
mDragMode = InteractionType_PrimaryEdit;
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures();
const int index = landtexturesCollection.searchId(ESM::RefId::stringRefId(mBrushTexture));
CSMWorld::IdCollection<ESM::LandTexture>& landtexturesCollection = document.getData().getLandTextures();
const int index = landtexturesCollection.searchId(mBrushTexture);
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted() && hit.hit && hit.tag == nullptr)
{
@ -242,8 +240,8 @@ void CSVRender::TerrainTextureMode::drag(const QPoint& pos, int diffX, int diffY
std::string cellId = getWorldspaceWidget().getCellId(hit.worldPos);
CSMDoc::Document& document = getWorldspaceWidget().getDocument();
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures();
const int index = landtexturesCollection.searchId(ESM::RefId::stringRefId(mBrushTexture));
CSMWorld::IdCollection<ESM::LandTexture>& landtexturesCollection = document.getData().getLandTextures();
const int index = landtexturesCollection.searchId(mBrushTexture);
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted() && hit.hit && hit.tag == nullptr)
{
@ -273,8 +271,8 @@ void CSVRender::TerrainTextureMode::dragCompleted(const QPoint& pos)
CSMDoc::Document& document = getWorldspaceWidget().getDocument();
QUndoStack& undoStack = document.getUndoStack();
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = document.getData().getLandTextures();
const int index = landtexturesCollection.searchId(ESM::RefId::stringRefId(mBrushTexture));
CSMWorld::IdCollection<ESM::LandTexture>& landtexturesCollection = document.getData().getLandTextures();
const int index = landtexturesCollection.searchId(mBrushTexture);
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted())
{
@ -303,18 +301,7 @@ void CSVRender::TerrainTextureMode::handleDropEvent(QDropEvent* event)
for (const CSMWorld::UniversalId& uid : ids)
{
mBrushTexture = uid.getId();
emit passBrushTexture(mBrushTexture);
}
}
if (mime->holdsType(CSMWorld::UniversalId::Type_Texture))
{
const std::vector<CSMWorld::UniversalId> ids = mime->getData();
for (const CSMWorld::UniversalId& uid : ids)
{
std::string textureFileName = uid.toString();
createTexture(textureFileName);
mBrushTexture = ESM::RefId::stringRefId(uid.getId());
emit passBrushTexture(mBrushTexture);
}
}
@ -359,11 +346,8 @@ void CSVRender::TerrainTextureMode::editTerrainTextureGrid(const WorldspaceHitRe
int textureColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandTexturesIndex);
std::size_t hashlocation = mBrushTexture.find('#');
std::string mBrushTextureInt = mBrushTexture.substr(hashlocation + 1);
// All indices are offset by +1
int brushInt = Misc::StringUtils::toNumeric<int>(mBrushTexture.substr(hashlocation + 1), 0) + 1;
uint32_t brushInt = document.getData().getLandTextures().getRecord(mBrushTexture).get().mIndex + 1;
int r = static_cast<float>(mBrushSize) / 2;
@ -662,50 +646,6 @@ void CSVRender::TerrainTextureMode::pushEditToCommand(CSMWorld::LandTexturesColu
undoStack.push(new CSMWorld::TouchLandCommand(landTable, ltexTable, cellId));
}
void CSVRender::TerrainTextureMode::createTexture(const std::string& textureFileName)
{
CSMDoc::Document& document = getWorldspaceWidget().getDocument();
CSMWorld::IdTable& ltexTable
= dynamic_cast<CSMWorld::IdTable&>(*document.getData().getTableModel(CSMWorld::UniversalId::Type_LandTextures));
QUndoStack& undoStack = document.getUndoStack();
std::string newId;
int counter = 0;
bool freeIndexFound = false;
do
{
const size_t maxCounter = std::numeric_limits<uint16_t>::max() - 1;
try
{
newId = CSMWorld::LandTexture::createUniqueRecordId(-1, counter);
if (!ltexTable.getRecord(newId).isDeleted())
counter = (counter + 1) % maxCounter;
}
catch (const std::exception&)
{
newId = CSMWorld::LandTexture::createUniqueRecordId(-1, counter);
freeIndexFound = true;
}
} while (freeIndexFound == false);
std::size_t idlocation = textureFileName.find("Texture: ");
QString fileName = QString::fromStdString(textureFileName.substr(idlocation + 9));
QVariant textureFileNameVariant;
textureFileNameVariant.setValue(fileName);
undoStack.beginMacro("Add land texture record");
undoStack.push(new CSMWorld::CreateCommand(ltexTable, newId));
QModelIndex index(ltexTable.getModelIndex(newId, ltexTable.findColumnIndex(CSMWorld::Columns::ColumnId_Texture)));
undoStack.push(new CSMWorld::ModifyCommand(ltexTable, index, textureFileNameVariant));
undoStack.endMacro();
mBrushTexture = std::move(newId);
}
bool CSVRender::TerrainTextureMode::allowLandTextureEditing(const std::string& cellId)
{
CSMDoc::Document& document = getWorldspaceWidget().getDocument();
@ -832,7 +772,7 @@ void CSVRender::TerrainTextureMode::setBrushShape(CSVWidget::BrushShape brushSha
}
}
void CSVRender::TerrainTextureMode::setBrushTexture(std::string brushTexture)
void CSVRender::TerrainTextureMode::setBrushTexture(ESM::RefId brushTexture)
{
mBrushTexture = std::move(brushTexture);
mBrushTexture = brushTexture;
}

@ -117,14 +117,11 @@ namespace CSVRender
void pushEditToCommand(CSMWorld::LandTexturesColumn::DataType& newLandGrid, CSMDoc::Document& document,
CSMWorld::IdTable& landTable, std::string cellId);
/// \brief Create new land texture record from texture asset
void createTexture(const std::string& textureFileName);
/// \brief Create new cell and land if needed
bool allowLandTextureEditing(const std::string& textureFileName);
std::string mCellId;
std::string mBrushTexture;
ESM::RefId mBrushTexture;
int mBrushSize;
CSVWidget::BrushShape mBrushShape;
std::unique_ptr<BrushDraw> mBrushDraw;
@ -139,13 +136,13 @@ namespace CSVRender
const int landTextureSize{ ESM::Land::LAND_TEXTURE_SIZE };
signals:
void passBrushTexture(std::string brushTexture);
void passBrushTexture(ESM::RefId brushTexture);
public slots:
void handleDropEvent(QDropEvent* event);
void setBrushSize(int brushSize);
void setBrushShape(CSVWidget::BrushShape brushShape);
void setBrushTexture(std::string brushShape);
void setBrushTexture(ESM::RefId brushShape);
};
}

@ -27,6 +27,7 @@
#include <apps/opencs/model/prefs/category.hpp>
#include <apps/opencs/model/prefs/setting.hpp>
#include <apps/opencs/model/world/columns.hpp>
#include <apps/opencs/model/world/commandmacro.hpp>
#include <apps/opencs/model/world/record.hpp>
#include <apps/opencs/view/widget/brushshapes.hpp>
#include <apps/opencs/view/widget/pushbutton.hpp>
@ -39,7 +40,6 @@
#include "../../model/world/data.hpp"
#include "../../model/world/idcollection.hpp"
#include "../../model/world/idtable.hpp"
#include "../../model/world/landtexture.hpp"
#include "../../model/world/universalid.hpp"
namespace CSVWidget
@ -74,12 +74,12 @@ CSVWidget::TextureBrushWindow::TextureBrushWindow(CSMDoc::Document& document, QW
: QFrame(parent, Qt::Popup)
, mDocument(document)
{
mBrushTextureLabel = "Selected texture: " + mBrushTexture + " ";
mBrushTextureLabel = "Selected texture: " + mBrushTexture.getRefIdString() + " ";
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = mDocument.getData().getLandTextures();
CSMWorld::IdCollection<ESM::LandTexture>& landtexturesCollection = mDocument.getData().getLandTextures();
int landTextureFilename = landtexturesCollection.findColumnIndex(CSMWorld::Columns::ColumnId_Texture);
const int index = landtexturesCollection.searchId(ESM::RefId::stringRefId(mBrushTexture));
const int index = landtexturesCollection.searchId(mBrushTexture);
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted())
{
@ -154,68 +154,38 @@ void CSVWidget::TextureBrushWindow::configureButtonInitialSettings(QPushButton*
button->setCheckable(true);
}
void CSVWidget::TextureBrushWindow::setBrushTexture(std::string brushTexture)
void CSVWidget::TextureBrushWindow::setBrushTexture(ESM::RefId brushTexture)
{
CSMWorld::IdTable& ltexTable = dynamic_cast<CSMWorld::IdTable&>(
*mDocument.getData().getTableModel(CSMWorld::UniversalId::Type_LandTextures));
QUndoStack& undoStack = mDocument.getUndoStack();
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = mDocument.getData().getLandTextures();
CSMWorld::IdCollection<ESM::LandTexture>& landtexturesCollection = mDocument.getData().getLandTextures();
int landTextureFilename = landtexturesCollection.findColumnIndex(CSMWorld::Columns::ColumnId_Texture);
int index = 0;
int pluginInDragged = 0;
CSMWorld::LandTexture::parseUniqueRecordId(brushTexture, pluginInDragged, index);
const ESM::RefId brushTextureRefId = ESM::RefId::stringRefId(brushTexture);
std::string newBrushTextureId = CSMWorld::LandTexture::createUniqueRecordId(-1, index);
ESM::RefId newBrushTextureRefId = ESM::RefId::stringRefId(newBrushTextureId);
int rowInBase = landtexturesCollection.searchId(brushTextureRefId);
int rowInNew = landtexturesCollection.searchId(newBrushTextureRefId);
// Check if texture exists in current plugin, and clone if id found in base, otherwise reindex the texture
// TO-DO: Handle case when texture is not found in neither base or plugin properly (finding new index is not enough)
// TO-DO: Handle conflicting plugins properly
if (rowInNew == -1)
int row = landtexturesCollection.getIndex(brushTexture);
const auto& record = landtexturesCollection.getRecord(row);
if (!record.isDeleted())
{
if (rowInBase == -1)
// Ensure the texture is defined by the current plugin
if (!record.isModified())
{
int counter = 0;
bool freeIndexFound = false;
const int maxCounter = std::numeric_limits<uint16_t>::max() - 1;
do
{
newBrushTextureId = CSMWorld::LandTexture::createUniqueRecordId(-1, counter);
newBrushTextureRefId = ESM::RefId::stringRefId(newBrushTextureId);
if (landtexturesCollection.searchId(brushTextureRefId) != -1
&& landtexturesCollection.getRecord(brushTextureRefId).isDeleted() == 0
&& landtexturesCollection.searchId(newBrushTextureRefId) != -1
&& landtexturesCollection.getRecord(newBrushTextureRefId).isDeleted() == 0)
counter = (counter + 1) % maxCounter;
else
freeIndexFound = true;
} while (freeIndexFound == false || counter < maxCounter);
CSMWorld::CommandMacro macro(undoStack);
macro.push(new CSMWorld::TouchCommand(ltexTable, brushTexture.getRefIdString()));
}
undoStack.beginMacro("Add land texture record");
undoStack.push(new CSMWorld::CloneCommand(
ltexTable, brushTexture, newBrushTextureId, CSMWorld::UniversalId::Type_LandTexture));
undoStack.endMacro();
}
if (index != -1 && !landtexturesCollection.getRecord(rowInNew).isDeleted())
{
mBrushTextureLabel = "Selected texture: " + newBrushTextureId + " ";
mBrushTextureLabel = "Selected texture: " + brushTexture.getRefIdString() + " ";
mSelectedBrush->setText(QString::fromStdString(mBrushTextureLabel)
+ landtexturesCollection.getData(rowInNew, landTextureFilename).value<QString>());
+ landtexturesCollection.getData(row, landTextureFilename).value<QString>());
}
else
{
newBrushTextureId.clear();
brushTexture = {};
mBrushTextureLabel = "No selected texture or invalid texture";
mSelectedBrush->setText(QString::fromStdString(mBrushTextureLabel));
}
mBrushTexture = std::move(newBrushTextureId);
mBrushTexture = brushTexture;
emit passTextureId(mBrushTexture);
emit passBrushShape(mBrushShape); // updates the icon tooltip
@ -250,7 +220,6 @@ CSVWidget::SceneToolTextureBrush::SceneToolTextureBrush(
, mTextureBrushWindow(new TextureBrushWindow(document, this))
{
mBrushHistory.resize(1);
mBrushHistory[0] = "L0#0";
setAcceptDrops(true);
connect(mTextureBrushWindow, &TextureBrushWindow::passBrushShape, this, &SceneToolTextureBrush::setButtonIcon);
@ -309,14 +278,15 @@ void CSVWidget::SceneToolTextureBrush::setButtonIcon(CSVWidget::BrushShape brush
tooltip += "<p>(right click to access of previously used brush settings)";
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = mDocument.getData().getLandTextures();
CSMWorld::IdCollection<ESM::LandTexture>& landtexturesCollection = mDocument.getData().getLandTextures();
int landTextureFilename = landtexturesCollection.findColumnIndex(CSMWorld::Columns::ColumnId_Texture);
const int index = landtexturesCollection.searchId(ESM::RefId::stringRefId(mTextureBrushWindow->mBrushTexture));
const int index = landtexturesCollection.searchId(mTextureBrushWindow->mBrushTexture);
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted())
{
tooltip += "<p>Selected texture: " + QString::fromStdString(mTextureBrushWindow->mBrushTexture) + " ";
tooltip += "<p>Selected texture: " + QString::fromStdString(mTextureBrushWindow->mBrushTexture.getRefIdString())
+ " ";
tooltip += landtexturesCollection.getData(index, landTextureFilename).value<QString>();
}
@ -342,25 +312,25 @@ void CSVWidget::SceneToolTextureBrush::updatePanel()
for (int i = mBrushHistory.size() - 1; i >= 0; --i)
{
CSMWorld::IdCollection<CSMWorld::LandTexture>& landtexturesCollection = mDocument.getData().getLandTextures();
CSMWorld::IdCollection<ESM::LandTexture>& landtexturesCollection = mDocument.getData().getLandTextures();
int landTextureFilename = landtexturesCollection.findColumnIndex(CSMWorld::Columns::ColumnId_Texture);
const int index = landtexturesCollection.searchId(ESM::RefId::stringRefId(mBrushHistory[i]));
const int index = landtexturesCollection.searchId(mBrushHistory[i]);
if (index != -1 && !landtexturesCollection.getRecord(index).isDeleted())
{
mTable->setItem(i, 1,
new QTableWidgetItem(landtexturesCollection.getData(index, landTextureFilename).value<QString>()));
mTable->setItem(i, 0, new QTableWidgetItem(QString::fromStdString(mBrushHistory[i])));
mTable->setItem(i, 0, new QTableWidgetItem(QString::fromStdString(mBrushHistory[i].getRefIdString())));
}
else
{
mTable->setItem(i, 1, new QTableWidgetItem("Invalid/deleted texture"));
mTable->setItem(i, 0, new QTableWidgetItem(QString::fromStdString(mBrushHistory[i])));
mTable->setItem(i, 0, new QTableWidgetItem(QString::fromStdString(mBrushHistory[i].getRefIdString())));
}
}
}
void CSVWidget::SceneToolTextureBrush::updateBrushHistory(const std::string& brushTexture)
void CSVWidget::SceneToolTextureBrush::updateBrushHistory(ESM::RefId brushTexture)
{
mBrushHistory.insert(mBrushHistory.begin(), brushTexture);
if (mBrushHistory.size() > 5)
@ -371,7 +341,7 @@ void CSVWidget::SceneToolTextureBrush::clicked(const QModelIndex& index)
{
if (index.column() == 0 || index.column() == 1)
{
std::string brushTexture = mBrushHistory[index.row()];
ESM::RefId brushTexture = mBrushHistory[index.row()];
std::swap(mBrushHistory[index.row()], mBrushHistory[0]);
mTextureBrushWindow->setBrushTexture(brushTexture);
emit passTextureId(brushTexture);

@ -9,6 +9,8 @@
#include "scenetool.hpp"
#endif
#include <components/esm/refid.hpp>
class QTableWidget;
class QDragEnterEvent;
class QDropEvent;
@ -70,7 +72,7 @@ namespace CSVWidget
private:
CSVWidget::BrushShape mBrushShape = CSVWidget::BrushShape_Point;
int mBrushSize = 1;
std::string mBrushTexture = "L0#0";
ESM::RefId mBrushTexture;
CSMDoc::Document& mDocument;
QLabel* mSelectedBrush;
QGroupBox* mHorizontalGroupBox;
@ -85,14 +87,14 @@ namespace CSVWidget
friend class CSVRender::TerrainTextureMode;
public slots:
void setBrushTexture(std::string brushTexture);
void setBrushTexture(ESM::RefId brushTexture);
void setBrushShape();
void setBrushSize(int brushSize);
signals:
void passBrushSize(int brushSize);
void passBrushShape(CSVWidget::BrushShape brushShape);
void passTextureId(std::string brushTexture);
void passTextureId(ESM::RefId brushTexture);
};
class SceneToolTextureBrush : public SceneTool
@ -103,7 +105,7 @@ namespace CSVWidget
CSMDoc::Document& mDocument;
QFrame* mPanel;
QTableWidget* mTable;
std::vector<std::string> mBrushHistory;
std::vector<ESM::RefId> mBrushHistory;
TextureBrushWindow* mTextureBrushWindow;
private:
@ -122,14 +124,14 @@ namespace CSVWidget
public slots:
void setButtonIcon(CSVWidget::BrushShape brushShape);
void updateBrushHistory(const std::string& mBrushTexture);
void updateBrushHistory(ESM::RefId mBrushTexture);
void clicked(const QModelIndex& index);
void activate() override;
signals:
void passEvent(QDropEvent* event);
void passEvent(QDragEnterEvent* event);
void passTextureId(std::string brushTexture);
void passTextureId(ESM::RefId brushTexture);
};
}

@ -1,107 +0,0 @@
#include "landtexturecreator.hpp"
#include <cstdint>
#include <limits>
#include <stddef.h>
#include <QLabel>
#include <QLineEdit>
#include <QSpinBox>
#include <apps/opencs/model/world/columns.hpp>
#include <apps/opencs/model/world/data.hpp>
#include <apps/opencs/model/world/idcollection.hpp>
#include <apps/opencs/view/world/genericcreator.hpp>
#include "../../model/world/commands.hpp"
#include "../../model/world/idtable.hpp"
#include "../../model/world/landtexture.hpp"
namespace CSVWorld
{
LandTextureCreator::LandTextureCreator(CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id)
: GenericCreator(data, undoStack, id)
{
// One index is reserved for a default texture
const size_t MaxIndex = std::numeric_limits<uint16_t>::max() - 1;
setManualEditing(false);
QLabel* nameLabel = new QLabel("Name");
insertBeforeButtons(nameLabel, false);
mNameEdit = new QLineEdit(this);
insertBeforeButtons(mNameEdit, true);
QLabel* indexLabel = new QLabel("Index");
insertBeforeButtons(indexLabel, false);
mIndexBox = new QSpinBox(this);
mIndexBox->setMinimum(0);
mIndexBox->setMaximum(MaxIndex);
insertBeforeButtons(mIndexBox, true);
connect(mNameEdit, &QLineEdit::textChanged, this, &LandTextureCreator::nameChanged);
connect(mIndexBox, qOverload<int>(&QSpinBox::valueChanged), this, &LandTextureCreator::indexChanged);
}
void LandTextureCreator::cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type)
{
GenericCreator::cloneMode(originId, type);
CSMWorld::IdTable& table = dynamic_cast<CSMWorld::IdTable&>(*getData().getTableModel(getCollectionId()));
int column = table.findColumnIndex(CSMWorld::Columns::ColumnId_TextureNickname);
mNameEdit->setText((table.data(table.getModelIndex(originId, column)).toString()));
column = table.findColumnIndex(CSMWorld::Columns::ColumnId_TextureIndex);
mIndexBox->setValue((table.data(table.getModelIndex(originId, column)).toInt()));
}
void LandTextureCreator::focus()
{
mIndexBox->setFocus();
}
void LandTextureCreator::reset()
{
GenericCreator::reset();
mNameEdit->setText("");
mIndexBox->setValue(0);
}
std::string LandTextureCreator::getErrors() const
{
if (getData().getLandTextures().searchId(ESM::RefId::stringRefId(getId())) >= 0)
{
return "Index is already in use";
}
return "";
}
void LandTextureCreator::configureCreateCommand(CSMWorld::CreateCommand& command) const
{
GenericCreator::configureCreateCommand(command);
CSMWorld::IdTable& table = dynamic_cast<CSMWorld::IdTable&>(*getData().getTableModel(getCollectionId()));
int column = table.findColumnIndex(CSMWorld::Columns::ColumnId_TextureNickname);
command.addValue(column, mName.c_str());
}
std::string LandTextureCreator::getId() const
{
return CSMWorld::LandTexture::createUniqueRecordId(-1, mIndexBox->value());
}
void LandTextureCreator::nameChanged(const QString& value)
{
mName = value.toUtf8().constData();
update();
}
void LandTextureCreator::indexChanged(int value)
{
update();
}
}

@ -1,54 +0,0 @@
#ifndef CSV_WORLD_LANDTEXTURECREATOR_H
#define CSV_WORLD_LANDTEXTURECREATOR_H
#include <string>
#include "genericcreator.hpp"
#include <apps/opencs/model/world/universalid.hpp>
namespace CSMWorld
{
class CreateCommand;
class Data;
}
class QLineEdit;
class QSpinBox;
namespace CSVWorld
{
class LandTextureCreator : public GenericCreator
{
Q_OBJECT
public:
LandTextureCreator(CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id);
void cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type) override;
void focus() override;
void reset() override;
std::string getErrors() const override;
protected:
void configureCreateCommand(CSMWorld::CreateCommand& command) const override;
std::string getId() const override;
private slots:
void nameChanged(const QString& val);
void indexChanged(int val);
private:
QLineEdit* mNameEdit;
QSpinBox* mIndexBox;
std::string mName;
};
}
#endif

@ -10,7 +10,6 @@
#include "globalcreator.hpp"
#include "infocreator.hpp"
#include "landcreator.hpp"
#include "landtexturecreator.hpp"
#include "pathgridcreator.hpp"
#include "previewsubview.hpp"
#include "referenceablecreator.hpp"
@ -92,7 +91,7 @@ void CSVWorld::addSubViewFactories(CSVDoc::SubViewFactoryManager& manager)
new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<LandCreator>>);
manager.add(CSMWorld::UniversalId::Type_LandTextures,
new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<LandTextureCreator>>);
new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<GenericCreator>>);
manager.add(CSMWorld::UniversalId::Type_Globals,
new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<GlobalCreator>>);
@ -195,7 +194,7 @@ void CSVWorld::addSubViewFactories(CSVDoc::SubViewFactoryManager& manager)
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<LandCreator>>(false));
manager.add(CSMWorld::UniversalId::Type_LandTexture,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<LandTextureCreator>>(false));
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<GenericCreator>>(false));
manager.add(CSMWorld::UniversalId::Type_DebugProfile,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView,

Loading…
Cancel
Save