From 5d14a2afcce241fe9b1677aa48e2b1b4a07a274f Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Thu, 24 Aug 2017 17:12:15 -0400 Subject: [PATCH 01/42] Initial LTEX/LAND tables --- apps/opencs/model/prefs/state.cpp | 2 + apps/opencs/model/world/columnimp.hpp | 64 ++++++++++++++++++++++--- apps/opencs/model/world/columns.cpp | 4 +- apps/opencs/model/world/columns.hpp | 3 ++ apps/opencs/model/world/data.cpp | 14 ++++++ apps/opencs/model/world/universalid.cpp | 8 +++- apps/opencs/model/world/universalid.hpp | 4 ++ apps/opencs/view/doc/view.cpp | 20 ++++++++ apps/opencs/view/doc/view.hpp | 4 ++ apps/opencs/view/world/subviews.cpp | 2 + 10 files changed, 116 insertions(+), 9 deletions(-) diff --git a/apps/opencs/model/prefs/state.cpp b/apps/opencs/model/prefs/state.cpp index 5c0b2e282..0101a432b 100644 --- a/apps/opencs/model/prefs/state.cpp +++ b/apps/opencs/model/prefs/state.cpp @@ -240,6 +240,8 @@ void CSMPrefs::State::declare() declareShortcut ("document-world-cells", "Open Cell List", QKeySequence()); declareShortcut ("document-world-referencables", "Open Object List", QKeySequence()); declareShortcut ("document-world-references", "Open Instance List", QKeySequence()); + declareShortcut ("document-world-lands", "Open Lands List", QKeySequence()); + declareShortcut ("document-world-landtextures", "Open Land Textures List", QKeySequence()); declareShortcut ("document-world-pathgrid", "Open Pathgrid List", QKeySequence()); declareShortcut ("document-world-regionmap", "Open Region Map", QKeySequence()); declareShortcut ("document-mechanics-globals", "Open Global List", QKeySequence()); diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index 154bdab82..a9a48ef0d 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -15,6 +15,9 @@ #include "columns.hpp" #include "info.hpp" +#include "land.hpp" +#include "landtexture.hpp" + namespace CSMWorld { /// \note Shares ID with VarValueColumn. A table can not have both. @@ -1499,9 +1502,9 @@ namespace CSMWorld template struct TopicColumn : public Column { - TopicColumn (bool journal) + TopicColumn (bool journal) : Column (journal ? Columns::ColumnId_Journal : Columns::ColumnId_Topic, - journal ? ColumnBase::Display_Journal : ColumnBase::Display_Topic) + journal ? ColumnBase::Display_Journal : ColumnBase::Display_Topic) {} virtual QVariant get (const Record& record) const @@ -1755,7 +1758,7 @@ namespace CSMWorld return true; } }; - + template struct GenderNpcColumn : public Column { @@ -2198,8 +2201,8 @@ namespace CSMWorld struct EffectTextureColumn : public Column { EffectTextureColumn (Columns::ColumnId columnId) - : Column (columnId, - columnId == Columns::ColumnId_Particle ? ColumnBase::Display_Texture + : Column (columnId, + columnId == Columns::ColumnId_Particle ? ColumnBase::Display_Texture : ColumnBase::Display_Icon) { assert (this->mColumnId==Columns::ColumnId_Icon || @@ -2417,7 +2420,56 @@ namespace CSMWorld return true; } }; - + + template + struct TextureIndexColumn : public Column + { + TextureIndexColumn() + : Column (Columns::ColumnId_TextureIndex, ColumnBase::Display_Integer) + {} + + QVariant get (const Record& record) const + { + return record.get().mIndex; + } + + virtual bool isEditable() const + { + return false; + } + }; + + // TODO remove + template + struct PluginIndexColumn : public Column + { + PluginIndexColumn() + : Column (Columns::ColumnId_PluginIndex, ColumnBase::Display_Integer) + {} + + QVariant get (const Record& record) const + { + return -1; + } + + virtual bool isEditable() const + { + return false; + } + }; + + template<> + inline QVariant PluginIndexColumn::get (const Record& record) const + { + return record.get().mPlugin; + } + + template<> + inline QVariant PluginIndexColumn::get (const Record& record) const + { + return record.get().mPluginIndex; + } + struct BodyPartRaceColumn : public RaceColumn { const MeshTypeColumn *mMeshType; diff --git a/apps/opencs/model/world/columns.cpp b/apps/opencs/model/world/columns.cpp index aeee0d208..cec11211c 100644 --- a/apps/opencs/model/world/columns.cpp +++ b/apps/opencs/model/world/columns.cpp @@ -124,7 +124,7 @@ namespace CSMWorld { ColumnId_Portable, "Portable" }, { ColumnId_NegativeLight, "Negative Light" }, { ColumnId_EmitterType, "Emitter Type" }, - + { ColumnId_Fire, "Fire" }, { ColumnId_OffByDefault, "Off by default" }, { ColumnId_IsKey, "Is Key" }, @@ -330,6 +330,8 @@ namespace CSMWorld { ColumnId_WeatherChance, "Percent Chance" }, { ColumnId_Text, "Text" }, + { ColumnId_PluginIndex, "Plugin Index" }, + { ColumnId_TextureIndex, "Texture Index" }, { ColumnId_UseValue1, "Use value 1" }, { ColumnId_UseValue2, "Use value 2" }, diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index b23d86367..1ff871c84 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -329,6 +329,9 @@ namespace CSMWorld ColumnId_Text = 297, + ColumnId_PluginIndex = 298, + ColumnId_TextureIndex = 299, + // Allocated to a separate value range, so we don't get a collision should we ever need // to extend the number of use values. ColumnId_UseValue1 = 0x10000, diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 007190e4a..456d2bccc 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -412,6 +412,18 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat Columns::ColumnId_NegativeLight, ESM::MagicEffect::NegativeLight)); mMagicEffects.addColumn (new DescriptionColumn); + mLand.addColumn (new StringIdColumn); + mLand.addColumn (new RecordStateColumn); + mLand.addColumn (new FixedRecordTypeColumn(UniversalId::Type_Land)); + mLand.addColumn (new PluginIndexColumn); + + mLandTextures.addColumn (new StringIdColumn); + mLandTextures.addColumn (new RecordStateColumn); + mLandTextures.addColumn (new FixedRecordTypeColumn(UniversalId::Type_LandTexture)); + mLandTextures.addColumn (new PluginIndexColumn); + mLandTextures.addColumn (new TextureIndexColumn); + mLandTextures.addColumn (new TextureColumn); + mPathgrids.addColumn (new StringIdColumn); mPathgrids.addColumn (new RecordStateColumn); mPathgrids.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Pathgrid)); @@ -531,6 +543,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat addModel (new IdTable (&mBodyParts), UniversalId::Type_BodyPart); addModel (new IdTable (&mSoundGens), UniversalId::Type_SoundGen); addModel (new IdTable (&mMagicEffects), UniversalId::Type_MagicEffect); + addModel (new IdTable (&mLand), UniversalId::Type_Land); + addModel (new IdTable (&mLandTextures), UniversalId::Type_LandTexture); addModel (new IdTree (&mPathgrids, &mPathgrids), UniversalId::Type_Pathgrid); addModel (new IdTable (&mStartScripts), UniversalId::Type_StartScript); addModel (new IdTree (&mReferenceables, &mReferenceables, IdTable::Feature_Preview), diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp index 77db2be10..38386f6da 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -53,6 +53,8 @@ namespace { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_RunLog, "Run Log", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_SoundGens, "Sound Generators", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MagicEffects, "Magic Effects", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Lands, "Lands", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_LandTextures, "LandTextures", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Pathgrids, "Pathgrids", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_StartScripts, "Start Scripts", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_MetaDatas, "Meta Data Table", 0 }, @@ -118,6 +120,8 @@ namespace { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_DebugProfile, "Debug Profile", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_SoundGen, "Sound Generator", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MagicEffect, "Magic Effect", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Land, "Land", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_LandTexture, "LandTexture", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Pathgrid, "Pathgrid", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_StartScript, "Start Script", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_MetaData, "Meta Data", 0 }, @@ -364,8 +368,8 @@ std::vector CSMWorld::UniversalId::listTypes (int c for (int i=0; sIndexArg[i].mName; ++i) if (sIndexArg[i].mClass & classes) list.push_back (sIndexArg[i].mType); - - return list; + + return list; } CSMWorld::UniversalId::Type CSMWorld::UniversalId::getParentType (Type type) diff --git a/apps/opencs/model/world/universalid.hpp b/apps/opencs/model/world/universalid.hpp index e9104fc22..accd1b78d 100644 --- a/apps/opencs/model/world/universalid.hpp +++ b/apps/opencs/model/world/universalid.hpp @@ -126,6 +126,10 @@ namespace CSMWorld Type_SoundGen, Type_MagicEffects, Type_MagicEffect, + Type_Lands, + Type_Land, + Type_LandTextures, + Type_LandTexture, Type_Pathgrids, Type_Pathgrid, Type_StartScripts, diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index dfbeea031..10de46e06 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -172,6 +172,16 @@ void CSVDoc::View::setupWorldMenu() setupShortcut("document-world-references", references); world->addAction (references); + QAction *lands = new QAction (tr ("Lands"), this); + connect (lands, SIGNAL (triggered()), this, SLOT (addLandsSubView())); + setupShortcut("document-world-lands", lands); + world->addAction (lands); + + QAction *landTextures = new QAction (tr ("Land Textures"), this); + connect (landTextures, SIGNAL (triggered()), this, SLOT (addLandTexturesSubView())); + setupShortcut("document-world-landtextures", landTextures); + world->addAction (landTextures); + QAction *grid = new QAction (tr ("Pathgrid"), this); connect (grid, SIGNAL (triggered()), this, SLOT (addPathgridSubView())); setupShortcut("document-world-pathgrid", grid); @@ -876,6 +886,16 @@ void CSVDoc::View::addRunLogSubView() addSubView (CSMWorld::UniversalId::Type_RunLog); } +void CSVDoc::View::addLandsSubView() +{ + addSubView (CSMWorld::UniversalId::Type_Lands); +} + +void CSVDoc::View::addLandTexturesSubView() +{ + addSubView (CSMWorld::UniversalId::Type_LandTextures); +} + void CSVDoc::View::addPathgridSubView() { addSubView (CSMWorld::UniversalId::Type_Pathgrids); diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index 834407be0..46aa87891 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -224,6 +224,10 @@ namespace CSVDoc void addRunLogSubView(); + void addLandsSubView(); + + void addLandTexturesSubView(); + void addPathgridSubView(); void addStartScriptsSubView(); diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp index 7c27bdf7a..b570397b7 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -43,6 +43,8 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) CSMWorld::UniversalId::Type_Spells, CSMWorld::UniversalId::Type_Enchantments, CSMWorld::UniversalId::Type_SoundGens, + CSMWorld::UniversalId::Type_Lands, + CSMWorld::UniversalId::Type_LandTextures, CSMWorld::UniversalId::Type_None // end marker }; From 9e41f1340ae3b7bd9ee3a598b11d7fb479bc36e5 Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Fri, 25 Aug 2017 19:51:30 -0400 Subject: [PATCH 02/42] Replace nonconst getId with setId, add template specialization and specialized derived classes for LandTexture --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/model/world/collection.hpp | 31 +++-- apps/opencs/model/world/columnimp.hpp | 43 ++++++- apps/opencs/model/world/columns.cpp | 1 + apps/opencs/model/world/columns.hpp | 5 +- apps/opencs/model/world/data.cpp | 5 +- apps/opencs/model/world/idtable.cpp | 31 +++++ apps/opencs/model/world/idtable.hpp | 15 +++ apps/opencs/model/world/landtexture.cpp | 17 +++ apps/opencs/model/world/landtexture.hpp | 5 + apps/opencs/view/world/landtexturecreator.cpp | 106 ++++++++++++++++++ apps/opencs/view/world/landtexturecreator.hpp | 49 ++++++++ apps/opencs/view/world/subviews.cpp | 15 ++- components/esm/loadltex.cpp | 2 +- components/esm/loadltex.hpp | 3 +- 15 files changed, 308 insertions(+), 22 deletions(-) create mode 100644 apps/opencs/view/world/landtexturecreator.cpp create mode 100644 apps/opencs/view/world/landtexturecreator.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 0a146dc06..4207258db 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -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 + bodypartcreator landtexturecreator ) opencs_units_noqt (view/world diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 16f5ce51f..9d867ff93 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -13,8 +14,8 @@ #include #include "columnbase.hpp" - #include "collectionbase.hpp" +#include "landtexture.hpp" namespace CSMWorld { @@ -22,15 +23,14 @@ namespace CSMWorld template struct IdAccessor { - std::string& getId (ESXRecordT& record); - + void setId(ESXRecordT& record, const std::string& id) const; const std::string getId (const ESXRecordT& record) const; }; template - std::string& IdAccessor::getId (ESXRecordT& record) + void IdAccessor::setId(ESXRecordT& record, const std::string& id) const { - return record.mId; + record.mId = id; } template @@ -39,6 +39,23 @@ namespace CSMWorld return record.mId; } + template<> + inline void IdAccessor::setId (LandTexture& record, const std::string& id) const + { + int plugin = 0; + int index = 0; + + LandTexture::parseUniqueRecordId(id, plugin, index); + record.mPluginIndex = plugin; + record.mIndex = index; + } + + template<> + inline const std::string IdAccessor::getId (const LandTexture& record) const + { + return LandTexture::createUniqueRecordId(record.mPluginIndex, record.mIndex); + } + /// \brief Single-type record collection template > class Collection : public CollectionBase @@ -213,7 +230,7 @@ namespace CSMWorld Record copy; copy.mModified = getRecord(origin).get(); copy.mState = RecordBase::State_ModifiedOnly; - copy.get().mId = destination; + IdAccessorT().setId(copy.get(), destination); insertRecord(copy, getAppendIndex(destination, type)); } @@ -366,7 +383,7 @@ namespace CSMWorld UniversalId::Type type) { ESXRecordT record; - IdAccessorT().getId (record) = id; + IdAccessorT().setId(record, id); record.blank(); Record record2; diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index a9a48ef0d..316fa4366 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -63,6 +63,13 @@ namespace CSMWorld } }; + template<> + inline QVariant StringIdColumn::get(const Record& record) const + { + const LandTexture& ltex = record.get(); + return QString::fromUtf8(std::string('L' + std::to_string(ltex.mPluginIndex) + '#' + std::to_string(ltex.mIndex)).c_str()); + } + template struct RecordStateColumn : public Column { @@ -2421,6 +2428,31 @@ namespace CSMWorld } }; + template + struct TextureHandleColumn : public Column + { + TextureHandleColumn() + : Column (Columns::ColumnId_TextureHandle, ColumnBase::Display_String) + {} + + QVariant get(const Record& record) const override + { + return QString::fromUtf8(record.get().mId.c_str()); + } + + void set(Record& record, const QVariant& data) override + { + ESXRecordT copy = record.get(); + copy.mId = data.toString().toUtf8().constData(); + record.setModified(copy); + } + + bool isEditable() const override + { + return true; + } + }; + template struct TextureIndexColumn : public Column { @@ -2428,31 +2460,30 @@ namespace CSMWorld : Column (Columns::ColumnId_TextureIndex, ColumnBase::Display_Integer) {} - QVariant get (const Record& record) const + QVariant get(const Record& record) const override { return record.get().mIndex; } - virtual bool isEditable() const + bool isEditable() const override { return false; } }; - // TODO remove template struct PluginIndexColumn : public Column { PluginIndexColumn() - : Column (Columns::ColumnId_PluginIndex, ColumnBase::Display_Integer) + : Column (Columns::ColumnId_PluginIndex, ColumnBase::Display_Integer,0) {} - QVariant get (const Record& record) const + QVariant get(const Record& record) const override { return -1; } - virtual bool isEditable() const + virtual bool isEditable() const override { return false; } diff --git a/apps/opencs/model/world/columns.cpp b/apps/opencs/model/world/columns.cpp index cec11211c..49935d40d 100644 --- a/apps/opencs/model/world/columns.cpp +++ b/apps/opencs/model/world/columns.cpp @@ -330,6 +330,7 @@ namespace CSMWorld { ColumnId_WeatherChance, "Percent Chance" }, { ColumnId_Text, "Text" }, + { ColumnId_TextureHandle, "Texture Handle" }, { ColumnId_PluginIndex, "Plugin Index" }, { ColumnId_TextureIndex, "Texture Index" }, diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index 1ff871c84..c6e2a2a41 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -329,8 +329,9 @@ namespace CSMWorld ColumnId_Text = 297, - ColumnId_PluginIndex = 298, - ColumnId_TextureIndex = 299, + ColumnId_TextureHandle = 298, + ColumnId_PluginIndex = 299, + ColumnId_TextureIndex = 300, // Allocated to a separate value range, so we don't get a collision should we ever need // to extend the number of use values. diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 456d2bccc..b0ab73de1 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -417,9 +417,10 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat mLand.addColumn (new FixedRecordTypeColumn(UniversalId::Type_Land)); mLand.addColumn (new PluginIndexColumn); - mLandTextures.addColumn (new StringIdColumn); + mLandTextures.addColumn (new StringIdColumn(true)); mLandTextures.addColumn (new RecordStateColumn); mLandTextures.addColumn (new FixedRecordTypeColumn(UniversalId::Type_LandTexture)); + mLandTextures.addColumn (new TextureHandleColumn); mLandTextures.addColumn (new PluginIndexColumn); mLandTextures.addColumn (new TextureIndexColumn); mLandTextures.addColumn (new TextureColumn); @@ -544,7 +545,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat addModel (new IdTable (&mSoundGens), UniversalId::Type_SoundGen); addModel (new IdTable (&mMagicEffects), UniversalId::Type_MagicEffect); addModel (new IdTable (&mLand), UniversalId::Type_Land); - addModel (new IdTable (&mLandTextures), UniversalId::Type_LandTexture); + addModel (new LandTextureIdTable (&mLandTextures), UniversalId::Type_LandTexture); addModel (new IdTree (&mPathgrids, &mPathgrids), UniversalId::Type_Pathgrid); addModel (new IdTable (&mStartScripts), UniversalId::Type_StartScript); addModel (new IdTree (&mReferenceables, &mReferenceables, IdTable::Feature_Preview), diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index 7975e05ea..078484aab 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -281,3 +281,34 @@ CSMWorld::CollectionBase *CSMWorld::IdTable::idCollection() const { return mIdCollection; } + +CSMWorld::LandTextureIdTable::LandTextureIdTable(CollectionBase* idCollection, unsigned int features) + : IdTable(idCollection, features) +{ +} + +QVariant CSMWorld::LandTextureIdTable::data(const QModelIndex& index, int role) const +{ + if (role==Qt::EditRole && !idCollection()->getRecord(index.row()).isModified()) + return QVariant(); + + return IdTable::data(index, role); +} + +bool CSMWorld::LandTextureIdTable::setData(const QModelIndex& index, const QVariant& value, int role) +{ + if (!idCollection()->getRecord(index.row()).isModified()) + return false; + else + return IdTable::setData(index, value, role); +} + +Qt::ItemFlags CSMWorld::LandTextureIdTable::flags(const QModelIndex& index) const +{ + Qt::ItemFlags flags = IdTable::flags(index); + + if (!idCollection()->getRecord(index.row()).isModified()) + flags &= ~Qt::ItemIsEditable; + + return flags; +} diff --git a/apps/opencs/model/world/idtable.hpp b/apps/opencs/model/world/idtable.hpp index 9faf64d71..ecca269d3 100644 --- a/apps/opencs/model/world/idtable.hpp +++ b/apps/opencs/model/world/idtable.hpp @@ -93,6 +93,21 @@ namespace CSMWorld virtual CollectionBase *idCollection() const; }; + + /// An IdTable customized to handle the more unique needs of LandTextureId's which behave + /// differently from other records. + class LandTextureIdTable : public IdTable + { + public: + + LandTextureIdTable(CollectionBase* idCollection, unsigned int features=0); + + QVariant data(const QModelIndex& index, int role=Qt::DisplayRole) const override; + + bool setData(const QModelIndex& index, const QVariant& value, int role) override; + + Qt::ItemFlags flags (const QModelIndex & index) const override; + }; } #endif diff --git a/apps/opencs/model/world/landtexture.cpp b/apps/opencs/model/world/landtexture.cpp index 266377d0f..4b82f8d73 100644 --- a/apps/opencs/model/world/landtexture.cpp +++ b/apps/opencs/model/world/landtexture.cpp @@ -1,5 +1,7 @@ #include "landtexture.hpp" +#include + #include namespace CSMWorld @@ -11,4 +13,19 @@ namespace CSMWorld mPluginIndex = esm.getIndex(); } + std::string LandTexture::createUniqueRecordId(int plugin, int index) + { + return 'L' + std::to_string(plugin) + '#' + std::to_string(index); + } + + 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 = std::stoi(id.substr(1,middle-1)); + index = std::stoi(id.substr(middle+1)); + } } diff --git a/apps/opencs/model/world/landtexture.hpp b/apps/opencs/model/world/landtexture.hpp index 91459eee2..a7376438c 100644 --- a/apps/opencs/model/world/landtexture.hpp +++ b/apps/opencs/model/world/landtexture.hpp @@ -13,6 +13,11 @@ namespace CSMWorld 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); }; } diff --git a/apps/opencs/view/world/landtexturecreator.cpp b/apps/opencs/view/world/landtexturecreator.cpp new file mode 100644 index 000000000..f79c08427 --- /dev/null +++ b/apps/opencs/view/world/landtexturecreator.cpp @@ -0,0 +1,106 @@ +#include "landtexturecreator.hpp" + +#include +#include + +#include +#include +#include + +#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::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); + + QIntValidator* indexValidator = new QIntValidator(0, MaxIndex, this); + + mIndexEdit = new QLineEdit(this); + mIndexEdit->setValidator(indexValidator); + insertBeforeButtons(mIndexEdit, true); + + connect(mNameEdit, SIGNAL(textChanged(const QString&)), this, SLOT(nameChanged(const QString&))); + connect(mIndexEdit, SIGNAL(textChanged(const QString&)), this, SLOT(indexChanged(const QString&))); + } + + void LandTextureCreator::cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type) + { + GenericCreator::cloneMode(originId, type); + + CSMWorld::IdTable& table = dynamic_cast(*getData().getTableModel(getCollectionId())); + + int column = table.findColumnIndex(CSMWorld::Columns::ColumnId_TextureHandle); + mNameEdit->setText((table.data(table.getModelIndex(originId, column)).toString())); + + column = table.findColumnIndex(CSMWorld::Columns::ColumnId_TextureIndex); + mIndexEdit->setText((table.data(table.getModelIndex(originId, column)).toString())); + } + + void LandTextureCreator::focus() + { + mIndexEdit->setFocus(); + } + + void LandTextureCreator::reset() + { + GenericCreator::reset(); + mNameEdit->setText(""); + mIndexEdit->setText(""); + } + + std::string LandTextureCreator::getErrors() const + { + std::string id = getId(); + + // TODO empty index edit? + if (getData().getLandTextures().searchId(getId()) >= 0) + { + return "Index is already in use"; + } + + return ""; + } + + void LandTextureCreator::configureCreateCommand(CSMWorld::CreateCommand& command) const + { + GenericCreator::configureCreateCommand(command); + + CSMWorld::IdTable& table = dynamic_cast(*getData().getTableModel(getCollectionId())); + int column = table.findColumnIndex(CSMWorld::Columns::ColumnId_TextureHandle); + command.addValue(column, mName.c_str()); + } + + std::string LandTextureCreator::getId() const + { + return CSMWorld::LandTexture::createUniqueRecordId(0, mIndex); + } + + void LandTextureCreator::nameChanged(const QString& value) + { + mName = value.toUtf8().constData(); + update(); + } + + void LandTextureCreator::indexChanged(const QString& value) + { + mIndex = value.toInt(); + update(); + } +} diff --git a/apps/opencs/view/world/landtexturecreator.hpp b/apps/opencs/view/world/landtexturecreator.hpp new file mode 100644 index 000000000..46388bf03 --- /dev/null +++ b/apps/opencs/view/world/landtexturecreator.hpp @@ -0,0 +1,49 @@ +#ifndef CSV_WORLD_LANDTEXTURECREATOR_H +#define CSV_WORLD_LANDTEXTURECREATOR_H + +#include + +#include "genericcreator.hpp" + +class QLineEdit; + +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(const QString& val); + + private: + + QLineEdit* mNameEdit; + QLineEdit* mIndexEdit; + + std::string mName; + int mIndex; + }; +} + +#endif diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp index b570397b7..a33802823 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -18,6 +18,7 @@ #include "pathgridcreator.hpp" #include "previewsubview.hpp" #include "bodypartcreator.hpp" +#include "landtexturecreator.hpp" void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) { @@ -43,8 +44,6 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) CSMWorld::UniversalId::Type_Spells, CSMWorld::UniversalId::Type_Enchantments, CSMWorld::UniversalId::Type_SoundGens, - CSMWorld::UniversalId::Type_Lands, - CSMWorld::UniversalId::Type_LandTextures, CSMWorld::UniversalId::Type_None // end marker }; @@ -83,6 +82,12 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) manager.add (CSMWorld::UniversalId::Type_Pathgrids, new CSVDoc::SubViewFactoryWithCreator); + manager.add (CSMWorld::UniversalId::Type_Lands, + new CSVDoc::SubViewFactoryWithCreator >); + + manager.add (CSMWorld::UniversalId::Type_LandTextures, + new CSVDoc::SubViewFactoryWithCreator >); + manager.add (CSMWorld::UniversalId::Type_Globals, new CSVDoc::SubViewFactoryWithCreator >); @@ -183,6 +188,12 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) manager.add (CSMWorld::UniversalId::Type_Pathgrid, new CSVDoc::SubViewFactoryWithCreator (false)); + manager.add (CSMWorld::UniversalId::Type_Land, + new CSVDoc::SubViewFactoryWithCreator >(false)); + + manager.add (CSMWorld::UniversalId::Type_LandTexture, + new CSVDoc::SubViewFactoryWithCreator >(false)); + manager.add (CSMWorld::UniversalId::Type_DebugProfile, new CSVDoc::SubViewFactoryWithCreator > (false)); diff --git a/components/esm/loadltex.cpp b/components/esm/loadltex.cpp index 613b706e3..3e150f9c0 100644 --- a/components/esm/loadltex.cpp +++ b/components/esm/loadltex.cpp @@ -59,7 +59,7 @@ namespace ESM void LandTexture::blank() { + mId.clear(); mTexture.clear(); - mIndex = -1; } } diff --git a/components/esm/loadltex.hpp b/components/esm/loadltex.hpp index 2cb5abf0c..1604e9281 100644 --- a/components/esm/loadltex.hpp +++ b/components/esm/loadltex.hpp @@ -31,14 +31,15 @@ struct LandTexture /// Return a string descriptor for this record type. Currently used for debugging / error logs only. static std::string getRecordType() { return "LandTexture"; } + // mId is merely a user friendly name for the texture in the editor. std::string mId, mTexture; int mIndex; void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; + /// Sets the record to the default state. Does not touch the index. Does touch mID. void blank(); - ///< Set record to default state (does not touch the ID). }; } #endif From 1d480015b46f78f747642d2d7b3b381b161119c9 Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Thu, 31 Aug 2017 22:01:38 -0400 Subject: [PATCH 03/42] Add ability to touch records, LAND records in particular --- apps/opencs/model/world/collection.hpp | 23 ++++++++++++ apps/opencs/model/world/collectionbase.hpp | 2 ++ apps/opencs/model/world/columnimp.hpp | 2 +- apps/opencs/model/world/commands.cpp | 25 +++++++++++++ apps/opencs/model/world/commands.hpp | 19 ++++++++++ apps/opencs/model/world/data.cpp | 2 +- apps/opencs/model/world/idtable.cpp | 20 +++++++++++ apps/opencs/model/world/idtable.hpp | 5 +++ apps/opencs/model/world/idtablebase.hpp | 6 ++-- apps/opencs/model/world/refidcollection.cpp | 6 ++++ apps/opencs/model/world/refidcollection.hpp | 2 ++ apps/opencs/view/world/table.cpp | 40 +++++++++++++++++++-- apps/opencs/view/world/table.hpp | 3 ++ 13 files changed, 149 insertions(+), 6 deletions(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 9d867ff93..f69d06d78 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -125,6 +125,10 @@ namespace CSMWorld const std::string& destination, const UniversalId::Type type); + virtual bool touchRecord(const std::string& id); + ///< Change the state of a record from base to modified, if it is not already. + /// \return True if the record was changed. + virtual int searchId (const std::string& id) const; ////< Search record with \a id. /// \return index of record (if found) or -1 (not found) @@ -235,6 +239,25 @@ namespace CSMWorld insertRecord(copy, getAppendIndex(destination, type)); } + template + bool Collection::touchRecord(const std::string& id) + { + int index = getIndex(id); + Record& record = mRecords.at(index); + if (record.isDeleted()) + { + throw std::runtime_error("attempt to touch deleted record"); + } + + if (!record.isModified() && !record.isDeleted() && !record.isErased()) + { + record.setModified(record.get()); + return true; + } + else + return false; + } + template Collection::Collection() {} diff --git a/apps/opencs/model/world/collectionbase.hpp b/apps/opencs/model/world/collectionbase.hpp index ef826e31c..bac790c5d 100644 --- a/apps/opencs/model/world/collectionbase.hpp +++ b/apps/opencs/model/world/collectionbase.hpp @@ -78,6 +78,8 @@ namespace CSMWorld const std::string& destination, const UniversalId::Type type) = 0; + virtual bool touchRecord(const std::string& id) = 0; + virtual const RecordBase& getRecord (const std::string& id) const = 0; virtual const RecordBase& getRecord (int index) const = 0; diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index 316fa4366..92e899a45 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -67,7 +67,7 @@ namespace CSMWorld inline QVariant StringIdColumn::get(const Record& record) const { const LandTexture& ltex = record.get(); - return QString::fromUtf8(std::string('L' + std::to_string(ltex.mPluginIndex) + '#' + std::to_string(ltex.mIndex)).c_str()); + return QString(LandTexture::createUniqueRecordId(ltex.mPluginIndex, ltex.mIndex).c_str()); } template diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 5f9422376..c6a56f7b8 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -15,6 +15,31 @@ #include "nestedtablewrapper.hpp" #include "pathgrid.hpp" +CSMWorld::TouchCommand::TouchCommand(IdTable& table, const std::string& id, QUndoCommand* parent) + : QUndoCommand(parent) + , mTable(table) + , mId(id) + , mOld(nullptr) + , mChanged(false) +{ + setText(("Touch " + mId).c_str()); + mOld.reset(mTable.getRecord(mId).clone()); +} + +void CSMWorld::TouchCommand::redo() +{ + mChanged = mTable.touchRecord(mId); +} + +void CSMWorld::TouchCommand::undo() +{ + if (mChanged) + { + mTable.setRecord(mId, *mOld); + mChanged = false; + } +} + CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index, const QVariant& new_, QUndoCommand* parent) : QUndoCommand (parent), mModel (&model), mIndex (index), mNew (new_), mHasRecordState(false), mOldRecordState(CSMWorld::RecordBase::State_BaseOnly) diff --git a/apps/opencs/model/world/commands.hpp b/apps/opencs/model/world/commands.hpp index b54a1d5ac..4c9be7e8a 100644 --- a/apps/opencs/model/world/commands.hpp +++ b/apps/opencs/model/world/commands.hpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -24,6 +25,24 @@ namespace CSMWorld struct RecordBase; struct NestedTableWrapperBase; + class TouchCommand : public QUndoCommand + { + public: + + TouchCommand(IdTable& model, const std::string& id, QUndoCommand* parent=nullptr); + + virtual void redo(); + virtual void undo(); + + private: + + IdTable& mTable; + std::string mId; + std::unique_ptr mOld; + + bool mChanged; + }; + class ModifyCommand : public QUndoCommand { QAbstractItemModel *mModel; diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index b0ab73de1..27a914aeb 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -544,7 +544,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat addModel (new IdTable (&mBodyParts), UniversalId::Type_BodyPart); addModel (new IdTable (&mSoundGens), UniversalId::Type_SoundGen); addModel (new IdTable (&mMagicEffects), UniversalId::Type_MagicEffect); - addModel (new IdTable (&mLand), UniversalId::Type_Land); + addModel (new IdTable (&mLand, IdTable::Feature_AllowTouch), UniversalId::Type_Land); addModel (new LandTextureIdTable (&mLandTextures), UniversalId::Type_LandTexture); addModel (new IdTree (&mPathgrids, &mPathgrids), UniversalId::Type_Pathgrid); addModel (new IdTable (&mStartScripts), UniversalId::Type_StartScript); diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index 078484aab..94eab3fc3 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -179,6 +179,26 @@ void CSMWorld::IdTable::cloneRecord(const std::string& origin, endInsertRows(); } +bool CSMWorld::IdTable::touchRecord(const std::string& id) +{ + bool changed = mIdCollection->touchRecord(id); + + int row = mIdCollection->getIndex(id); + int column = mIdCollection->searchColumnIndex(Columns::ColumnId_RecordType); + if (changed && column != -1) + { + QModelIndex modelIndex = index(row, column); + emit dataChanged(modelIndex, modelIndex); + } + + return changed; +} + +std::string CSMWorld::IdTable::getId(int row) const +{ + return mIdCollection->getId(row); +} + ///This method can return only indexes to the top level table cells QModelIndex CSMWorld::IdTable::getModelIndex (const std::string& id, int column) const { diff --git a/apps/opencs/model/world/idtable.hpp b/apps/opencs/model/world/idtable.hpp index ecca269d3..5e5f9da4a 100644 --- a/apps/opencs/model/world/idtable.hpp +++ b/apps/opencs/model/world/idtable.hpp @@ -61,6 +61,11 @@ namespace CSMWorld const std::string& destination, UniversalId::Type type = UniversalId::Type_None); + bool touchRecord(const std::string& id); + ///< Will change the record state to modified, if it is not already. + + std::string getId(int row) const; + virtual QModelIndex getModelIndex (const std::string& id, int column) const; void setRecord (const std::string& id, const RecordBase& record, diff --git a/apps/opencs/model/world/idtablebase.hpp b/apps/opencs/model/world/idtablebase.hpp index 0d77d48ef..8b82f984b 100644 --- a/apps/opencs/model/world/idtablebase.hpp +++ b/apps/opencs/model/world/idtablebase.hpp @@ -32,7 +32,9 @@ namespace CSMWorld Feature_Preview = 8, /// Table can not be modified through ordinary means. - Feature_Constant = 16 + Feature_Constant = 16, + + Feature_AllowTouch = 32 }; private: @@ -61,7 +63,7 @@ namespace CSMWorld virtual bool isDeleted (const std::string& id) const = 0; virtual int getColumnId (int column) const = 0; - + unsigned int getFeatures() const; }; } diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index 31dae256e..44a6ce07d 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -813,6 +813,12 @@ void CSMWorld::RefIdCollection::cloneRecord(const std::string& origin, mData.insertRecord(*newRecord, type, destination); } +bool CSMWorld::RefIdCollection::touchRecord(const std::string& id) +{ + throw std::runtime_error("RefIdCollection::touchRecord is unimplemented"); + return false; +} + void CSMWorld::RefIdCollection::appendRecord (const RecordBase& record, UniversalId::Type type) { diff --git a/apps/opencs/model/world/refidcollection.hpp b/apps/opencs/model/world/refidcollection.hpp index de5a709c6..48558d1c2 100644 --- a/apps/opencs/model/world/refidcollection.hpp +++ b/apps/opencs/model/world/refidcollection.hpp @@ -80,6 +80,8 @@ namespace CSMWorld const std::string& destination, const UniversalId::Type type); + virtual bool touchRecord(const std::string& id); + virtual void appendBlankRecord (const std::string& id, UniversalId::Type type); ///< \param type Will be ignored, unless the collection supports multiple record types diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 30dba4241..580d27a24 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -60,6 +60,9 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) menu.addAction(mCloneAction); } + if (mTouchAction) + menu.addAction (mTouchAction); + if (mCreateAction) menu.addAction (mCreateAction); @@ -226,8 +229,8 @@ void CSVWorld::Table::mouseDoubleClickEvent (QMouseEvent *event) CSVWorld::Table::Table (const CSMWorld::UniversalId& id, bool createAndDelete, bool sorting, CSMDoc::Document& document) -: DragRecordTable(document), mCreateAction (0), - mCloneAction(0), mRecordStatusDisplay (0), mJumpToAddedRecord(false), mUnselectAfterJump(false) + : DragRecordTable(document), mCreateAction (nullptr), mCloneAction(nullptr), mTouchAction(nullptr), + mRecordStatusDisplay (0), mJumpToAddedRecord(false), mUnselectAfterJump(false) { mModel = &dynamic_cast (*mDocument.getData().getTableModel (id)); @@ -302,6 +305,15 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, cloneShortcut->associateAction(mCloneAction); } + if (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_AllowTouch) + { + mTouchAction = new QAction(tr("Touch Record"), this); + connect(mTouchAction, SIGNAL(triggered()), this, SLOT(touchRecord())); + addAction(mTouchAction); + CSMPrefs::Shortcut* touchShortcut = new CSMPrefs::Shortcut("table-touch", this); + touchShortcut->associateAction(mTouchAction); + } + mRevertAction = new QAction (tr ("Revert Record"), this); connect (mRevertAction, SIGNAL (triggered()), mDispatcher, SLOT (executeRevert())); addAction (mRevertAction); @@ -442,6 +454,30 @@ void CSVWorld::Table::cloneRecord() } } +void CSVWorld::Table::touchRecord() +{ + if (!mEditLock && mModel->getFeatures() & CSMWorld::IdTableBase::Feature_AllowTouch) + { + if (CSMWorld::IdTable* table = dynamic_cast(mModel)) + { + QUndoCommand* touchRecords = new QUndoCommand(); + touchRecords->setText("Touch records"); + + QModelIndexList selectedRows = selectionModel()->selectedRows(); + for (auto it = selectedRows.begin(); it != selectedRows.end(); ++it) + { + QModelIndex index = mProxyModel->mapToSource(mProxyModel->index(it->row(),0)); + std::string id = table->getId(index.row()); + + // command is a child of touchRecords + QUndoCommand* command = new CSMWorld::TouchCommand(*table, id, touchRecords); + } + + mDocument.getUndoStack().push(touchRecords); + } + } +} + void CSVWorld::Table::moveUpRecord() { if (mEditLock || (mModel->getFeatures() & CSMWorld::IdTableBase::Feature_Constant)) diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index 26f6e7899..d6fa17ef9 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -56,6 +56,7 @@ namespace CSVWorld QAction *mEditAction; QAction *mCreateAction; QAction *mCloneAction; + QAction *mTouchAction; QAction *mRevertAction; QAction *mDeleteAction; QAction *mMoveUpAction; @@ -129,6 +130,8 @@ namespace CSVWorld void cloneRecord(); + void touchRecord(); + void moveUpRecord(); void moveDownRecord(); From 30ba1d4c2599822730c5e6a204d383517c3206ed Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Sun, 3 Sep 2017 16:41:54 -0400 Subject: [PATCH 04/42] Move touch command to creator, to allow customization and not break abstraction --- apps/opencs/view/world/creator.hpp | 3 +++ apps/opencs/view/world/genericcreator.cpp | 17 +++++++++++++++++ apps/opencs/view/world/genericcreator.hpp | 2 ++ apps/opencs/view/world/table.cpp | 22 +++++++--------------- apps/opencs/view/world/table.hpp | 2 ++ apps/opencs/view/world/tablebottombox.cpp | 11 ++++++++--- apps/opencs/view/world/tablebottombox.hpp | 1 + apps/opencs/view/world/tablesubview.cpp | 3 +++ 8 files changed, 43 insertions(+), 18 deletions(-) diff --git a/apps/opencs/view/world/creator.hpp b/apps/opencs/view/world/creator.hpp index 320bbf6ae..0020fd3c8 100644 --- a/apps/opencs/view/world/creator.hpp +++ b/apps/opencs/view/world/creator.hpp @@ -31,6 +31,9 @@ namespace CSVWorld virtual void cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type) = 0; + /// Touches a record, if the creator supports it. + virtual void touch(const std::vector& ids) = 0; + virtual void setEditLock (bool locked) = 0; virtual void toggleWidgets(bool active = true) = 0; diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index bf4c4967f..bde523a09 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -254,6 +254,23 @@ void CSVWorld::GenericCreator::cloneMode(const std::string& originId, mClonedType = type; } +void CSVWorld::GenericCreator::touch(const std::vector& ids) +{ + // Combine multiple touch commands into one "macro" command + std::unique_ptr macro(new QUndoCommand()); + macro->setText("Touch records"); + + CSMWorld::IdTable& table = dynamic_cast(*mData.getTableModel(mListId)); + for (const CSMWorld::UniversalId& uid : ids) + { + // This is not leaked, touchCmd is a child of macro and managed by Qt + CSMWorld::TouchCommand* touchCmd = new CSMWorld::TouchCommand(table, uid.getId(), macro.get()); + } + + // Execute + mUndoStack.push(macro.release()); +} + void CSVWorld::GenericCreator::toggleWidgets(bool active) { } diff --git a/apps/opencs/view/world/genericcreator.hpp b/apps/opencs/view/world/genericcreator.hpp index 60d487bc1..d708c1047 100644 --- a/apps/opencs/view/world/genericcreator.hpp +++ b/apps/opencs/view/world/genericcreator.hpp @@ -103,6 +103,8 @@ namespace CSVWorld virtual void cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type); + virtual void touch(const std::vector& ids); + virtual std::string getErrors() const; ///< Return formatted error descriptions for the current state of the creator. if an empty /// string is returned, there is no error. diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 580d27a24..3cccaaa22 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -458,23 +458,15 @@ void CSVWorld::Table::touchRecord() { if (!mEditLock && mModel->getFeatures() & CSMWorld::IdTableBase::Feature_AllowTouch) { - if (CSMWorld::IdTable* table = dynamic_cast(mModel)) - { - QUndoCommand* touchRecords = new QUndoCommand(); - touchRecords->setText("Touch records"); - - QModelIndexList selectedRows = selectionModel()->selectedRows(); - for (auto it = selectedRows.begin(); it != selectedRows.end(); ++it) - { - QModelIndex index = mProxyModel->mapToSource(mProxyModel->index(it->row(),0)); - std::string id = table->getId(index.row()); - - // command is a child of touchRecords - QUndoCommand* command = new CSMWorld::TouchCommand(*table, id, touchRecords); - } + std::vector touchIds; - mDocument.getUndoStack().push(touchRecords); + QModelIndexList selectedRows = selectionModel()->selectedRows(); + for (auto it = selectedRows.begin(); it != selectedRows.end(); ++it) + { + touchIds.push_back(getUniversalId(it->row())); } + + emit touchRequest(touchIds); } } diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index d6fa17ef9..02f9023e7 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -116,6 +116,8 @@ namespace CSVWorld void cloneRequest(const CSMWorld::UniversalId&); + void touchRequest(const std::vector& ids); + void closeRequest(); void extendedDeleteConfigRequest(const std::vector &selectedIds); diff --git a/apps/opencs/view/world/tablebottombox.cpp b/apps/opencs/view/world/tablebottombox.cpp index 5a25bbc53..cfde5c694 100644 --- a/apps/opencs/view/world/tablebottombox.cpp +++ b/apps/opencs/view/world/tablebottombox.cpp @@ -72,9 +72,9 @@ void CSVWorld::TableBottomBox::extendedConfigRequest(CSVWorld::ExtendedCommandCo mExtendedConfigurator->setFocus(); } -CSVWorld::TableBottomBox::TableBottomBox (const CreatorFactoryBase& creatorFactory, - CSMDoc::Document& document, - const CSMWorld::UniversalId& id, +CSVWorld::TableBottomBox::TableBottomBox (const CreatorFactoryBase& creatorFactory, + CSMDoc::Document& document, + const CSMWorld::UniversalId& id, QWidget *parent) : QWidget (parent), mShowStatusBar (false), mEditMode(EditMode_None), mHasPosition(false), mRow(0), mColumn(0) { @@ -249,6 +249,11 @@ void CSVWorld::TableBottomBox::cloneRequest(const std::string& id, mCreator->focus(); } +void CSVWorld::TableBottomBox::touchRequest(const std::vector& ids) +{ + mCreator->touch(ids); +} + void CSVWorld::TableBottomBox::extendedDeleteConfigRequest(const std::vector &selectedIds) { extendedConfigRequest(ExtendedCommandConfigurator::Mode_Delete, selectedIds); diff --git a/apps/opencs/view/world/tablebottombox.hpp b/apps/opencs/view/world/tablebottombox.hpp index 781cccc9e..5402c466e 100644 --- a/apps/opencs/view/world/tablebottombox.hpp +++ b/apps/opencs/view/world/tablebottombox.hpp @@ -102,6 +102,7 @@ namespace CSVWorld void createRequest(); void cloneRequest(const std::string& id, const CSMWorld::UniversalId::Type type); + void touchRequest(const std::vector&); void extendedDeleteConfigRequest(const std::vector &selectedIds); void extendedRevertConfigRequest(const std::vector &selectedIds); diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 6664a3771..12e29995d 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -69,6 +69,9 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D connect (this, SIGNAL(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type)), mBottom, SLOT(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type))); + connect (mTable, SIGNAL(touchRequest(const std::vector&)), + mBottom, SLOT(touchRequest(const std::vector&))); + connect (mTable, SIGNAL(extendedDeleteConfigRequest(const std::vector &)), mBottom, SLOT(extendedDeleteConfigRequest(const std::vector &))); connect (mTable, SIGNAL(extendedRevertConfigRequest(const std::vector &)), From 7cc95a11a47721cd9c3f70d6acf9b0d595f576ba Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Sun, 3 Sep 2017 20:00:19 -0400 Subject: [PATCH 05/42] Add more land table columns --- apps/opencs/model/world/columnimp.cpp | 234 ++++++++++++++++++++++++-- apps/opencs/model/world/columnimp.hpp | 45 +++++ apps/opencs/model/world/columns.hpp | 5 + apps/opencs/model/world/data.cpp | 5 + components/esm/loadland.hpp | 4 +- 5 files changed, 274 insertions(+), 19 deletions(-) diff --git a/apps/opencs/model/world/columnimp.cpp b/apps/opencs/model/world/columnimp.cpp index dc3d39edb..65d2fecd2 100644 --- a/apps/opencs/model/world/columnimp.cpp +++ b/apps/opencs/model/world/columnimp.cpp @@ -1,28 +1,226 @@ #include "columnimp.hpp" -CSMWorld::BodyPartRaceColumn::BodyPartRaceColumn(const MeshTypeColumn *meshType) - : mMeshType(meshType) -{} - -QVariant CSMWorld::BodyPartRaceColumn::get(const Record &record) const +namespace CSMWorld { - if (mMeshType != NULL && mMeshType->get(record) == ESM::BodyPart::MT_Skin) + /* LandMapLodColumn */ + LandMapLodColumn::LandMapLodColumn() + : Column(Columns::ColumnId_LandMapLodIndex, ColumnBase::Display_String, 0) { - return QString::fromUtf8(record.get().mRace.c_str()); } - return QVariant(QVariant::UserType); -} -void CSMWorld::BodyPartRaceColumn::set(Record &record, const QVariant &data) -{ - ESM::BodyPart record2 = record.get(); + QVariant LandMapLodColumn::get(const Record& record) const + { + // Note: original data is signed + const char* rawData = reinterpret_cast(&record.get().mWnam[0]); + return QByteArray(rawData, Land::LAND_GLOBAL_MAP_LOD_SIZE); + } + + void LandMapLodColumn::set(Record& record, const QVariant& data) + { + Land copy = record.get(); + QByteArray array = data.toByteArray(); + const signed char* rawData = reinterpret_cast(array.data()); - record2.mRace = data.toString().toUtf8().constData(); + assert (array.count() == Land::LAND_GLOBAL_MAP_LOD_SIZE); - record.setModified(record2); -} + for (int i = 0; i < array.count(); ++i) + { + copy.mWnam[i] = rawData[i]; + } -bool CSMWorld::BodyPartRaceColumn::isEditable() const -{ - return true; + record.setModified(copy); + } + + bool LandMapLodColumn::isEditable() const + { + return true; + } + + /* LandNormalsColumn */ + LandNormalsColumn::LandNormalsColumn() + : Column(Columns::ColumnId_LandNormalsIndex, ColumnBase::Display_String, 0) + { + } + + QVariant LandNormalsColumn::get(const Record& record) const + { + const Land::LandData* landData = record.get().getLandData(); + assert(landData); + + // Note: original data is signed + const char* rawData = reinterpret_cast(&landData->mNormals[0]); + return QByteArray(rawData, Land::LAND_NUM_VERTS * 3); + } + + void LandNormalsColumn::set(Record& record, const QVariant& data) + { + Land copy = record.get(); + Land::LandData* landData = copy.getLandData(); + assert (landData); + + QByteArray array = data.toByteArray(); + const signed char* rawData = reinterpret_cast(array.data()); + + assert (array.count() == Land::LAND_NUM_VERTS * 3); + + for (int i = 0; i < array.count(); ++i) + { + landData->mNormals[i] = rawData[i]; + } + + record.setModified(copy); + } + + bool LandNormalsColumn::isEditable() const + { + return true; + } + + /* LandHeightsColumn */ + LandHeightsColumn::LandHeightsColumn() + : Column(Columns::ColumnId_LandHeightsIndex, ColumnBase::Display_String, 0) + { + } + + QVariant LandHeightsColumn::get(const Record& record) const + { + const Land::LandData* landData = record.get().getLandData(); + assert(landData); + + // Note: original data is float + const char* rawData = reinterpret_cast(&landData->mHeights[0]); + return QByteArray(rawData, Land::LAND_NUM_VERTS * sizeof(float)); + } + + void LandHeightsColumn::set(Record& record, const QVariant& data) + { + Land copy = record.get(); + Land::LandData* landData = copy.getLandData(); + assert (landData); + + QByteArray array = data.toByteArray(); + const float* rawData = reinterpret_cast(array.data()); + + assert (array.count() == Land::LAND_NUM_VERTS * sizeof(float)); + + for (int i = 0; i < array.count(); ++i) + { + landData->mHeights[i] = rawData[i]; + } + + record.setModified(copy); + } + + bool LandHeightsColumn::isEditable() const + { + return true; + } + + /* LandColoursColumn */ + LandColoursColumn::LandColoursColumn() + : Column(Columns::ColumnId_LandColoursIndex, ColumnBase::Display_String, 0) + { + } + + QVariant LandColoursColumn::get(const Record& record) const + { + const Land::LandData* landData = record.get().getLandData(); + assert(landData); + + // Note: original data is unsigned char + const char* rawData = reinterpret_cast(&landData->mColours[0]); + return QByteArray(rawData, Land::LAND_NUM_VERTS * 3); + } + + void LandColoursColumn::set(Record& record, const QVariant& data) + { + Land copy = record.get(); + Land::LandData* landData = copy.getLandData(); + assert (landData); + + QByteArray array = data.toByteArray(); + const unsigned char* rawData = reinterpret_cast(array.data()); + + assert (array.count() == Land::LAND_NUM_VERTS * 3); + + for (int i = 0; i < array.count(); ++i) + { + landData->mColours[i] = rawData[i]; + } + + record.setModified(copy); + } + + bool LandColoursColumn::isEditable() const + { + return true; + } + + /* LandTexturesColumn */ + LandTexturesColumn::LandTexturesColumn() + : Column(Columns::ColumnId_LandTexturesIndex, ColumnBase::Display_String, 0) + { + } + + QVariant LandTexturesColumn::get(const Record& record) const + { + const Land::LandData* landData = record.get().getLandData(); + assert(landData); + + // Note: original data is uint16_t + const char* rawData = reinterpret_cast(&landData->mTextures[0]); + return QByteArray(rawData, Land::LAND_NUM_TEXTURES * sizeof(uint16_t)); + } + + void LandTexturesColumn::set(Record& record, const QVariant& data) + { + Land copy = record.get(); + Land::LandData* landData = copy.getLandData(); + assert (landData); + + QByteArray array = data.toByteArray(); + const uint16_t* rawData = reinterpret_cast(array.data()); + + assert (array.count() == Land::LAND_NUM_TEXTURES * sizeof(uint16_t)); + + for (int i = 0; i < array.count(); ++i) + { + landData->mTextures[i] = rawData[i]; + } + + record.setModified(copy); + } + + bool LandTexturesColumn::isEditable() const + { + return true; + } + + /* BodyPartRaceColumn */ + BodyPartRaceColumn::BodyPartRaceColumn(const MeshTypeColumn *meshType) + : mMeshType(meshType) + {} + + QVariant BodyPartRaceColumn::get(const Record &record) const + { + if (mMeshType != NULL && mMeshType->get(record) == ESM::BodyPart::MT_Skin) + { + return QString::fromUtf8(record.get().mRace.c_str()); + } + return QVariant(QVariant::UserType); + } + + void BodyPartRaceColumn::set(Record &record, const QVariant &data) + { + ESM::BodyPart record2 = record.get(); + + record2.mRace = data.toString().toUtf8().constData(); + + record.setModified(record2); + } + + bool BodyPartRaceColumn::isEditable() const + { + return true; + } } diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index 92e899a45..f58a35bc4 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -2501,6 +2501,51 @@ namespace CSMWorld return record.get().mPluginIndex; } + struct LandMapLodColumn : public Column + { + LandMapLodColumn(); + + QVariant get(const Record& record) const override; + void set(Record& record, const QVariant& data) override; + bool isEditable() const override; + }; + + struct LandNormalsColumn : public Column + { + LandNormalsColumn(); + + QVariant get(const Record& record) const override; + void set(Record& record, const QVariant& data) override; + bool isEditable() const override; + }; + + struct LandHeightsColumn : public Column + { + LandHeightsColumn(); + + QVariant get(const Record& record) const override; + void set(Record& record, const QVariant& data) override; + bool isEditable() const override; + }; + + struct LandColoursColumn : public Column + { + LandColoursColumn(); + + QVariant get(const Record& record) const override; + void set(Record& record, const QVariant& data) override; + bool isEditable() const override; + }; + + struct LandTexturesColumn : public Column + { + LandTexturesColumn(); + + QVariant get(const Record& record) const override; + void set(Record& record, const QVariant& data) override; + bool isEditable() const override; + }; + struct BodyPartRaceColumn : public RaceColumn { const MeshTypeColumn *mMeshType; diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index c6e2a2a41..432597105 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -332,6 +332,11 @@ namespace CSMWorld ColumnId_TextureHandle = 298, ColumnId_PluginIndex = 299, ColumnId_TextureIndex = 300, + ColumnId_LandMapLodIndex = 301, + ColumnId_LandNormalsIndex = 302, + ColumnId_LandHeightsIndex = 303, + ColumnId_LandColoursIndex = 304, + ColumnId_LandTexturesIndex = 305, // Allocated to a separate value range, so we don't get a collision should we ever need // to extend the number of use values. diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 27a914aeb..3284d9b05 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -416,6 +416,11 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat mLand.addColumn (new RecordStateColumn); mLand.addColumn (new FixedRecordTypeColumn(UniversalId::Type_Land)); mLand.addColumn (new PluginIndexColumn); + mLand.addColumn (new LandMapLodColumn); + mLand.addColumn (new LandNormalsColumn); + mLand.addColumn (new LandHeightsColumn); + mLand.addColumn (new LandColoursColumn); + mLand.addColumn (new LandTexturesColumn); mLandTextures.addColumn (new StringIdColumn(true)); mLandTextures.addColumn (new RecordStateColumn); diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index 9f33c37da..a7b97a465 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -64,6 +64,8 @@ struct Land //total number of textures per land static const int LAND_NUM_TEXTURES = LAND_TEXTURE_SIZE * LAND_TEXTURE_SIZE; + static const int LAND_GLOBAL_MAP_LOD_SIZE = 81; + #pragma pack(push,1) struct VHGT { @@ -109,7 +111,7 @@ struct Land }; // low-LOD heightmap (used for rendering the global map) - signed char mWnam[81]; + signed char mWnam[LAND_GLOBAL_MAP_LOD_SIZE]; void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; From 90c485104a8e13e9f1e876b40b7b1b539bf73c9f Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Mon, 4 Sep 2017 01:06:58 -0400 Subject: [PATCH 06/42] Land creator --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/model/world/collection.hpp | 86 ++++++++++++++++++++++---- apps/opencs/model/world/land.cpp | 20 +++++- apps/opencs/model/world/land.hpp | 3 + apps/opencs/view/world/landcreator.cpp | 79 +++++++++++++++++++++++ apps/opencs/view/world/landcreator.hpp | 42 +++++++++++++ apps/opencs/view/world/subviews.cpp | 5 +- 7 files changed, 220 insertions(+), 17 deletions(-) create mode 100644 apps/opencs/view/world/landcreator.cpp create mode 100644 apps/opencs/view/world/landcreator.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 4207258db..36ff8cf67 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -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 + bodypartcreator landtexturecreator landcreator ) opencs_units_noqt (view/world diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index f69d06d78..fccbebd43 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -15,6 +15,7 @@ #include "columnbase.hpp" #include "collectionbase.hpp" +#include "land.hpp" #include "landtexture.hpp" namespace CSMWorld @@ -39,6 +40,18 @@ namespace CSMWorld return record.mId; } + template<> + inline void IdAccessor::setId (Land& record, const std::string& id) const + { + int x=0, y=0; + + Land::parseUniqueRecordId(id, x, y); + record.mX = x; + record.mY = y; + // TODO check for uses of mId and remove them + record.mId = id; + } + template<> inline void IdAccessor::setId (LandTexture& record, const std::string& id) const { @@ -50,6 +63,12 @@ namespace CSMWorld record.mIndex = index; } + template<> + inline const std::string IdAccessor::getId (const Land& record) const + { + return Land::createUniqueRecordId(record.mX, record.mY); + } + template<> inline const std::string IdAccessor::getId (const LandTexture& record) const { @@ -86,6 +105,13 @@ namespace CSMWorld /// /// \return Success? + int cloneRecordImp (const std::string& origin, const std::string& dest, + UniversalId::Type type); + ///< Returns the index of the clone. + + int touchRecordImp (const std::string& id); + ///< Returns the index of the record on success, -1 on failure. + public: Collection(); @@ -227,20 +253,22 @@ namespace CSMWorld } template - void Collection::cloneRecord(const std::string& origin, - const std::string& destination, - const UniversalId::Type type) + int Collection::cloneRecordImp(const std::string& origin, + const std::string& destination, UniversalId::Type type) { - Record copy; - copy.mModified = getRecord(origin).get(); - copy.mState = RecordBase::State_ModifiedOnly; - IdAccessorT().setId(copy.get(), destination); + Record copy; + copy.mModified = getRecord(origin).get(); + copy.mState = RecordBase::State_ModifiedOnly; + IdAccessorT().setId(copy.get(), destination); - insertRecord(copy, getAppendIndex(destination, type)); + int index = getAppendIndex(destination, type); + insertRecord(copy, getAppendIndex(destination, type)); + + return index; } template - bool Collection::touchRecord(const std::string& id) + int Collection::touchRecordImp(const std::string& id) { int index = getIndex(id); Record& record = mRecords.at(index); @@ -249,13 +277,47 @@ namespace CSMWorld throw std::runtime_error("attempt to touch deleted record"); } - if (!record.isModified() && !record.isDeleted() && !record.isErased()) + if (!record.isModified()) { record.setModified(record.get()); + return index; + } + + return -1; + } + + template + void Collection::cloneRecord(const std::string& origin, + const std::string& destination, const UniversalId::Type type) + { + cloneRecordImp(origin, destination, type); + } + + template<> + inline void Collection >::cloneRecord(const std::string& origin, + const std::string& destination, const UniversalId::Type type) + { + int index = cloneRecordImp(origin, destination, type); + mRecords.at(index).get().mPlugin = 0; + } + + template + bool Collection::touchRecord(const std::string& id) + { + return touchRecordImp(id) != -1; + } + + template<> + inline bool Collection >::touchRecord(const std::string& id) + { + int index = touchRecordImp(id); + if (index >= 0) + { + mRecords.at(index).get().mPlugin = 0; return true; } - else - return false; + + return false; } template diff --git a/apps/opencs/model/world/land.cpp b/apps/opencs/model/world/land.cpp index 80f86c746..22fc96599 100644 --- a/apps/opencs/model/world/land.cpp +++ b/apps/opencs/model/world/land.cpp @@ -8,8 +8,24 @@ namespace CSMWorld { ESM::Land::load(esm, isDeleted); + mId = createUniqueRecordId(mX, mY); + } + + std::string Land::createUniqueRecordId(int x, int y) + { std::ostringstream stream; - stream << "#" << mX << " " << mY; - mId = stream.str(); + stream << "#" << x << " " << y; + return stream.str(); + } + + void Land::parseUniqueRecordId(const std::string& id, int& x, int& y) + { + size_t mid = id.find(' '); + + if (mid == std::string::npos || id[0] != '#') + throw std::runtime_error("Invalid Land ID"); + + x = std::stoi(id.substr(1, mid - 1)); + y = std::stoi(id.substr(mid + 1)); } } diff --git a/apps/opencs/model/world/land.hpp b/apps/opencs/model/world/land.hpp index e5f25c1d3..cc7e914af 100644 --- a/apps/opencs/model/world/land.hpp +++ b/apps/opencs/model/world/land.hpp @@ -16,6 +16,9 @@ namespace CSMWorld /// Loads the metadata and ID void load (ESM::ESMReader &esm, bool &isDeleted); + + static std::string createUniqueRecordId(int x, int y); + static void parseUniqueRecordId(const std::string& id, int& x, int& y); }; } diff --git a/apps/opencs/view/world/landcreator.cpp b/apps/opencs/view/world/landcreator.cpp new file mode 100644 index 000000000..34599b2c4 --- /dev/null +++ b/apps/opencs/view/world/landcreator.cpp @@ -0,0 +1,79 @@ +#include "landcreator.hpp" + +#include + +#include "../../model/world/land.hpp" + +namespace CSVWorld +{ + LandCreator::LandCreator(CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id) + : GenericCreator(data, undoStack, id) + , mXLabel(nullptr) + , mYLabel(nullptr) + , mX(nullptr) + , mY(nullptr) + { + const int MaxInt = std::numeric_limits::max(); + const int MinInt = std::numeric_limits::min(); + + setManualEditing(false); + + mXLabel = new QLabel("X: "); + mX = new QSpinBox(); + mX->setMinimum(MinInt); + mX->setMaximum(MaxInt); + insertBeforeButtons(mXLabel, false); + insertBeforeButtons(mX, true); + + mYLabel = new QLabel("Y: "); + mY = new QSpinBox(); + mY->setMinimum(MinInt); + mY->setMaximum(MaxInt); + insertBeforeButtons(mYLabel, false); + insertBeforeButtons(mY, true); + + connect (mX, SIGNAL(valueChanged(int)), this, SLOT(coordChanged(int))); + connect (mY, SIGNAL(valueChanged(int)), this, SLOT(coordChanged(int))); + } + + void LandCreator::cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type) + { + GenericCreator::cloneMode(originId, type); + + int x = 0, y = 0; + CSMWorld::Land::parseUniqueRecordId(originId, x, y); + + mX->setValue(x); + mY->setValue(y); + } + + void LandCreator::focus() + { + mX->setFocus(); + } + + void LandCreator::reset() + { + GenericCreator::reset(); + mX->setValue(0); + mY->setValue(0); + } + + std::string LandCreator::getErrors() const + { + if (getData().getLand().searchId(getId()) >= 0) + return "A land with that name already exists."; + + return ""; + } + + std::string LandCreator::getId() const + { + return CSMWorld::Land::createUniqueRecordId(mX->value(), mY->value()); + } + + void LandCreator::coordChanged(int value) + { + update(); + } +} diff --git a/apps/opencs/view/world/landcreator.hpp b/apps/opencs/view/world/landcreator.hpp new file mode 100644 index 000000000..aea202dcd --- /dev/null +++ b/apps/opencs/view/world/landcreator.hpp @@ -0,0 +1,42 @@ +#ifndef CSV_WORLD_LANDCREATOR_H +#define CSV_WORLD_LANDCREATOR_H + +#include +#include + +#include "genericcreator.hpp" + +namespace CSVWorld +{ + class LandCreator : public GenericCreator + { + Q_OBJECT + + QLabel* mXLabel; + QLabel* mYLabel; + QSpinBox* mX; + QSpinBox* mY; + + public: + + LandCreator(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; + + private slots: + + void coordChanged(int value); + + protected: + + std::string getId() const override; + }; +} + +#endif diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp index a33802823..3e72f9a9e 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -18,6 +18,7 @@ #include "pathgridcreator.hpp" #include "previewsubview.hpp" #include "bodypartcreator.hpp" +#include "landcreator.hpp" #include "landtexturecreator.hpp" void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) @@ -83,7 +84,7 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) new CSVDoc::SubViewFactoryWithCreator); manager.add (CSMWorld::UniversalId::Type_Lands, - new CSVDoc::SubViewFactoryWithCreator >); + new CSVDoc::SubViewFactoryWithCreator >); manager.add (CSMWorld::UniversalId::Type_LandTextures, new CSVDoc::SubViewFactoryWithCreator >); @@ -189,7 +190,7 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) new CSVDoc::SubViewFactoryWithCreator (false)); manager.add (CSMWorld::UniversalId::Type_Land, - new CSVDoc::SubViewFactoryWithCreator >(false)); + new CSVDoc::SubViewFactoryWithCreator >(false)); manager.add (CSMWorld::UniversalId::Type_LandTexture, new CSVDoc::SubViewFactoryWithCreator >(false)); From d3014cf39424972a3b86bc61bfe92cf54551d7b2 Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Mon, 4 Sep 2017 15:13:45 -0400 Subject: [PATCH 07/42] Temporary fix for adding new lands, cloned lands will still reference old data though --- apps/opencs/view/render/terrainstorage.cpp | 21 ++++----- components/esm/loadland.cpp | 52 +++++++++++++++++++++- components/esm/loadland.hpp | 6 ++- 3 files changed, 64 insertions(+), 15 deletions(-) diff --git a/apps/opencs/view/render/terrainstorage.cpp b/apps/opencs/view/render/terrainstorage.cpp index c63d41be3..51c9dd009 100644 --- a/apps/opencs/view/render/terrainstorage.cpp +++ b/apps/opencs/view/render/terrainstorage.cpp @@ -1,5 +1,8 @@ #include "terrainstorage.hpp" +#include "../../model/world/land.hpp" +#include "../../model/world/landtexture.hpp" + namespace CSVRender { @@ -11,12 +14,9 @@ namespace CSVRender osg::ref_ptr 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()); + int index = mData.getLand().searchId(CSMWorld::Land::createUniqueRecordId(cellX, cellY)); if (index == -1) return NULL; @@ -26,16 +26,11 @@ namespace CSVRender const ESM::LandTexture* TerrainStorage::getLandTexture(int index, short plugin) { - int numRecords = mData.getLandTextures().getSize(); - - for (int i=0; imIndex == index && ltex->mPluginIndex == plugin) - return ltex; - } + int row = mData.getLandTextures().searchId(CSMWorld::LandTexture::createUniqueRecordId(plugin, index)); + if (row == -1) + return nullptr; - return NULL; + return &mData.getLandTextures().getRecord(row).get(); } void TerrainStorage::getBounds(float &minX, float &maxX, float &minY, float &maxY) diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index 72c3eb98d..7d48dd037 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -15,6 +15,7 @@ namespace ESM , mX(0) , mY(0) , mPlugin(0) + , mNoFile(false) , mDataTypes(0) , mLandData(NULL) { @@ -175,6 +176,47 @@ namespace ESM } + void Land::blank() + { + if (mLandData) + { + delete mLandData; + } + + mPlugin = 0; + + for (int i = 0; i < LAND_GLOBAL_MAP_LOD_SIZE; ++i) + mWnam[0] = 0; + + mLandData = new LandData; + mLandData->mHeightOffset = 0; + for (int i = 0; i < LAND_NUM_VERTS; ++i) + mLandData->mHeights[i] = 0; + mLandData->mMinHeight = 0; + mLandData->mMaxHeight = 0; + for (int i = 0; i < LAND_NUM_VERTS; ++i) + { + mLandData->mNormals[i*3+0] = 0; + mLandData->mNormals[i*3+1] = -1; + mLandData->mNormals[i*3+2] = 0; + } + for (int i = 0; i < LAND_NUM_TEXTURES; ++i) + mLandData->mTextures[i] = 0; + for (int i = 0; i < LAND_NUM_VERTS; ++i) + { + mLandData->mColours[i*3+0] = -1; + mLandData->mColours[i*3+1] = -1; + mLandData->mColours[i*3+2] = -1; + } + mLandData->mUnk1 = 0; + mLandData->mUnk2 = 0; + mLandData->mDataLoaded = Land::DATA_VNML | Land::DATA_VHGT | Land::DATA_WNAM | + Land::DATA_VCLR | Land::DATA_VTEX; + mDataTypes = mLandData->mDataLoaded; + + mNoFile = true; + } + void Land::loadData(int flags, LandData* target) const { // Create storage if nothing is loaded @@ -193,6 +235,13 @@ namespace ESM return; } + // Copy data to target if no file + if (mNoFile) + { + *target = *mLandData; + return; + } + ESM::ESMReader reader; reader.restoreContext(mContext); @@ -271,7 +320,7 @@ namespace ESM Land::Land (const Land& land) : mFlags (land.mFlags), mX (land.mX), mY (land.mY), mPlugin (land.mPlugin), - mContext (land.mContext), mDataTypes (land.mDataTypes), + mContext (land.mContext), mNoFile(land.mNoFile), mDataTypes (land.mDataTypes), mLandData (land.mLandData ? new LandData (*land.mLandData) : 0) {} @@ -288,6 +337,7 @@ namespace ESM std::swap (mY, land.mY); std::swap (mPlugin, land.mPlugin); std::swap (mContext, land.mContext); + std::swap (mNoFile, land.mNoFile); std::swap (mDataTypes, land.mDataTypes); std::swap (mLandData, land.mLandData); } diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index a7b97a465..223b7975a 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -33,6 +33,10 @@ struct Land // location later when we are ready to load the full data set. ESM_Context mContext; + // In the editor, a new Land is not associated with a file, thus mContext should not be accessed + // when land data is being requested. Instead simply copy over the data. + bool mNoFile; + int mDataTypes; enum @@ -116,7 +120,7 @@ struct Land void load(ESMReader &esm, bool &isDeleted); void save(ESMWriter &esm, bool isDeleted = false) const; - void blank() {} + void blank(); /** * Actually loads data into target From 5c3e90da881948ff1f2fde5dd595fe7e2a0882e9 Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Mon, 4 Sep 2017 15:14:42 -0400 Subject: [PATCH 08/42] Fix includes --- apps/opencs/model/world/land.cpp | 1 + apps/opencs/model/world/landtexture.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/apps/opencs/model/world/land.cpp b/apps/opencs/model/world/land.cpp index 22fc96599..2b12b55fe 100644 --- a/apps/opencs/model/world/land.cpp +++ b/apps/opencs/model/world/land.cpp @@ -1,6 +1,7 @@ #include "land.hpp" #include +#include namespace CSMWorld { diff --git a/apps/opencs/model/world/landtexture.cpp b/apps/opencs/model/world/landtexture.cpp index 4b82f8d73..63ebbc11f 100644 --- a/apps/opencs/model/world/landtexture.cpp +++ b/apps/opencs/model/world/landtexture.cpp @@ -1,6 +1,7 @@ #include "landtexture.hpp" #include +#include #include From 97d0fd756a2dff3ac73bd308a1ad7f88c3e4e13d Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Mon, 4 Sep 2017 19:31:09 -0400 Subject: [PATCH 09/42] LTEX importing --- apps/opencs/model/world/commands.cpp | 85 +++++++++++++++++++++++++ apps/opencs/model/world/commands.hpp | 25 +++++++- apps/opencs/model/world/idtable.cpp | 57 +++++++++++++++++ apps/opencs/model/world/idtable.hpp | 16 ++++- apps/opencs/model/world/land.cpp | 2 +- apps/opencs/model/world/landtexture.cpp | 2 +- apps/opencs/view/world/landcreator.cpp | 20 ++++++ apps/opencs/view/world/landcreator.hpp | 2 + 8 files changed, 204 insertions(+), 5 deletions(-) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index c6a56f7b8..c98f52777 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -2,6 +2,7 @@ #include #include +#include #include @@ -40,6 +41,90 @@ void CSMWorld::TouchCommand::undo() } } +CSMWorld::TouchLandCommand::TouchLandCommand(IdTable& landTable, IdTable& ltexTable, const std::string& id, QUndoCommand* parent) + : QUndoCommand(parent) + , mLands(landTable) + , mLtexs(ltexTable) + , mId(id) + , mOld(nullptr) + , mChanged(false) +{ + setText(("Touch " + mId).c_str()); + mOld.reset(mLands.getRecord(mId).clone()); +} + +void CSMWorld::TouchLandCommand::redo() +{ + int pluginColumn = mLands.findColumnIndex(Columns::ColumnId_PluginIndex); + int oldPlugin = mLands.data(mLands.getModelIndex(mId, pluginColumn)).toInt(); + + mChanged = mLands.touchRecord(mId); + if (mChanged) + { + // Original data + int textureColumn = mLands.findColumnIndex(Columns::ColumnId_LandTexturesIndex); + QByteArray textureByteArray = mLands.data(mLands.getModelIndex(mId, textureColumn)).toByteArray(); + const uint16_t* textureData = reinterpret_cast(textureByteArray.data()); + + // Need to make a copy so the old values can be looked up + QByteArray newTextureByteArray(textureByteArray.data(), textureByteArray.size()); + uint16_t* newTextureData = reinterpret_cast(newTextureByteArray.data()); + + // Find all indices used + std::unordered_set texIndices; + for (int i = 0; i < Land::LAND_NUM_TEXTURES; ++i) + { + // All indices are offset by 1 for a default texture + if (textureData[i] > 0) + texIndices.insert(textureData[i] - 1); + } + + std::vector oldTextures; + for (int index : texIndices) + { + oldTextures.push_back(LandTexture::createUniqueRecordId(oldPlugin, index)); + } + + // Import the textures, replace old values + LandTextureIdTable::ImportResults results = dynamic_cast(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) + { + for (int i = 0; i < Land::LAND_NUM_TEXTURES; ++i) + { + // All indices are offset by 1 for a default texture + if (textureData[i] == oldIndex) + newTextureData[i] = newIndex + 1; + } + } + } + + mLands.setData(mLands.getModelIndex(mId, textureColumn), newTextureByteArray); + } +} + +void CSMWorld::TouchLandCommand::undo() +{ + if (mChanged) + { + mLands.setRecord(mId, *mOld); + mChanged = false; + + for (const std::string& id : mCreatedTextures) + { + int row = mLtexs.getModelIndex(id, 0).row(); + mLtexs.removeRows(row, 1); + } + mCreatedTextures.clear(); + } +} + CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index, const QVariant& new_, QUndoCommand* parent) : QUndoCommand (parent), mModel (&model), mIndex (index), mNew (new_), mHasRecordState(false), mOldRecordState(CSMWorld::RecordBase::State_BaseOnly) diff --git a/apps/opencs/model/world/commands.hpp b/apps/opencs/model/world/commands.hpp index 4c9be7e8a..5f4259cb8 100644 --- a/apps/opencs/model/world/commands.hpp +++ b/apps/opencs/model/world/commands.hpp @@ -31,8 +31,8 @@ namespace CSMWorld TouchCommand(IdTable& model, const std::string& id, QUndoCommand* parent=nullptr); - virtual void redo(); - virtual void undo(); + void redo() override; + void undo() override; private: @@ -43,6 +43,27 @@ namespace CSMWorld bool mChanged; }; + class TouchLandCommand : public QUndoCommand + { + public: + + TouchLandCommand(IdTable& landTable, IdTable& ltexTable, const std::string& id, + QUndoCommand* parent = nullptr); + + void redo() override; + void undo() override; + + private: + + IdTable& mLands; + IdTable& mLtexs; + std::string mId; + std::unique_ptr mOld; + std::vector mCreatedTextures; + + bool mChanged; + }; + class ModifyCommand : public QUndoCommand { QAbstractItemModel *mModel; diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index 94eab3fc3..5543fc13f 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -1,11 +1,13 @@ #include "idtable.hpp" +#include #include #include #include "collectionbase.hpp" #include "columnbase.hpp" +#include "landtexture.hpp" CSMWorld::IdTable::IdTable (CollectionBase *idCollection, unsigned int features) : IdTableBase (features), mIdCollection (idCollection) @@ -332,3 +334,58 @@ Qt::ItemFlags CSMWorld::LandTextureIdTable::flags(const QModelIndex& index) cons return flags; } + +CSMWorld::LandTextureIdTable::ImportResults CSMWorld::LandTextureIdTable::importTextures(const std::vector& ids) +{ + ImportResults results; + + for (const std::string& id : ids) + { + int plugin, index; + + LandTexture::parseUniqueRecordId(id, plugin, index); + int oldRow = idCollection()->searchId(id); + + // If it does not exist or it is in the current plugin, it can be skipped. + if (oldRow <= 0 || plugin == 0) + { + results.recordMapping.push_back(std::make_pair(id, id)); + continue; + } + + // Try a direct mapping to the current plugin first. Otherwise iterate until one is found. + // Iteration is deterministic to avoid duplicates. + do { + std::string newId = LandTexture::createUniqueRecordId(0, index); + int newRow = idCollection()->searchId(newId); + + if (newRow < 0) + { + // Id not taken, clone it + cloneRecord(id, newId, UniversalId::Type_LandTexture); + results.createdRecords.push_back(newId); + results.recordMapping.push_back(std::make_pair(id, newId)); + break; + } + + // Id is taken, check if same handle and texture. Note that mId is the handle. + const LandTexture& oldLtex = dynamic_cast&>(idCollection()->getRecord(oldRow)).get(); + const LandTexture& newLtex = dynamic_cast&>(idCollection()->getRecord(newRow)).get(); + if (oldLtex.mId == newLtex.mId && oldLtex.mTexture == newLtex.mTexture) + { + // It's a match + results.recordMapping.push_back(std::make_pair(id, newId)); + break; + } + + // Determine next index. Spread out the indices to reduce conflicts. + size_t MaxIndex = std::numeric_limits::max(); + size_t Prime = (1 << 19) - 1; // A mersenne prime + size_t K = 2154; + + index = ((K * index) % Prime) % MaxIndex; + } while (true); + } + + return results; +} diff --git a/apps/opencs/model/world/idtable.hpp b/apps/opencs/model/world/idtable.hpp index 5e5f9da4a..ccc5ac404 100644 --- a/apps/opencs/model/world/idtable.hpp +++ b/apps/opencs/model/world/idtable.hpp @@ -100,11 +100,22 @@ namespace CSMWorld }; /// An IdTable customized to handle the more unique needs of LandTextureId's which behave - /// differently from other records. + /// 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; + + /// The newly added records + std::vector createdRecords; + /// The 1st string is the original id, the 2nd is the mapped id + std::vector recordMapping; + }; + LandTextureIdTable(CollectionBase* idCollection, unsigned int features=0); QVariant data(const QModelIndex& index, int role=Qt::DisplayRole) const override; @@ -112,6 +123,9 @@ namespace CSMWorld bool setData(const QModelIndex& index, const QVariant& value, int role) override; Qt::ItemFlags flags (const QModelIndex & index) const override; + + /// Finds and maps/recreates the specified ids. + ImportResults importTextures(const std::vector& ids); }; } diff --git a/apps/opencs/model/world/land.cpp b/apps/opencs/model/world/land.cpp index 2b12b55fe..74833d7ef 100644 --- a/apps/opencs/model/world/land.cpp +++ b/apps/opencs/model/world/land.cpp @@ -1,7 +1,7 @@ #include "land.hpp" #include -#include +#include namespace CSMWorld { diff --git a/apps/opencs/model/world/landtexture.cpp b/apps/opencs/model/world/landtexture.cpp index 63ebbc11f..32bab1e6d 100644 --- a/apps/opencs/model/world/landtexture.cpp +++ b/apps/opencs/model/world/landtexture.cpp @@ -1,7 +1,7 @@ #include "landtexture.hpp" #include -#include +#include #include diff --git a/apps/opencs/view/world/landcreator.cpp b/apps/opencs/view/world/landcreator.cpp index 34599b2c4..08b8b3620 100644 --- a/apps/opencs/view/world/landcreator.cpp +++ b/apps/opencs/view/world/landcreator.cpp @@ -2,6 +2,8 @@ #include +#include "../../model/world/commands.hpp" +#include "../../model/world/idtable.hpp" #include "../../model/world/land.hpp" namespace CSVWorld @@ -47,6 +49,24 @@ namespace CSVWorld mY->setValue(y); } + void LandCreator::touch(const std::vector& ids) + { + // Combine multiple touch commands into one "macro" command + std::unique_ptr macro(new QUndoCommand()); + macro->setText("Touch records"); + + CSMWorld::IdTable& lands = dynamic_cast(*getData().getTableModel(CSMWorld::UniversalId::Type_Lands)); + CSMWorld::IdTable& ltexs = dynamic_cast(*getData().getTableModel(CSMWorld::UniversalId::Type_LandTextures)); + for (const CSMWorld::UniversalId& uid : ids) + { + // This is not leaked, touchCmd is a child of macro and managed by Qt + CSMWorld::TouchLandCommand* touchCmd = new CSMWorld::TouchLandCommand(lands, ltexs, uid.getId(), macro.get()); + } + + // Execute + getUndoStack().push(macro.release()); + } + void LandCreator::focus() { mX->setFocus(); diff --git a/apps/opencs/view/world/landcreator.hpp b/apps/opencs/view/world/landcreator.hpp index aea202dcd..ef8cf60d5 100644 --- a/apps/opencs/view/world/landcreator.hpp +++ b/apps/opencs/view/world/landcreator.hpp @@ -23,6 +23,8 @@ namespace CSVWorld void cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type) override; + void touch(const std::vector& ids) override; + void focus() override; void reset() override; From 99e90ef808ec95d3c5e22e3e72d99ef10e58b0d9 Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Tue, 5 Sep 2017 19:29:07 -0400 Subject: [PATCH 10/42] Cleanup. Also modify ltex index generation. --- apps/opencs/model/prefs/state.cpp | 1 + apps/opencs/model/world/collection.hpp | 2 -- apps/opencs/model/world/columnimp.hpp | 9 ++++++- apps/opencs/model/world/columns.cpp | 5 ++++ apps/opencs/model/world/idtable.cpp | 8 +++--- apps/opencs/model/world/land.cpp | 2 -- apps/opencs/model/world/land.hpp | 2 -- apps/opencs/model/world/landtexture.cpp | 6 +++-- apps/opencs/view/world/landcreator.cpp | 3 +++ apps/opencs/view/world/landcreator.hpp | 6 ++--- apps/opencs/view/world/landtexturecreator.cpp | 27 ++++++++----------- apps/opencs/view/world/landtexturecreator.hpp | 6 ++--- 12 files changed, 42 insertions(+), 35 deletions(-) diff --git a/apps/opencs/model/prefs/state.cpp b/apps/opencs/model/prefs/state.cpp index 0101a432b..1f84c5a87 100644 --- a/apps/opencs/model/prefs/state.cpp +++ b/apps/opencs/model/prefs/state.cpp @@ -278,6 +278,7 @@ void CSMPrefs::State::declare() declareShortcut ("table-edit", "Edit Record", QKeySequence()); declareShortcut ("table-add", "Add Row/Record", QKeySequence(Qt::ShiftModifier | Qt::Key_A)); declareShortcut ("table-clone", "Clone Record", QKeySequence(Qt::ShiftModifier | Qt::Key_D)); + declareShortcut ("touch-record", "Touch Record", QKeySequence()); declareShortcut ("table-revert", "Revert Record", QKeySequence()); declareShortcut ("table-remove", "Remove Row/Record", QKeySequence(Qt::Key_Delete)); declareShortcut ("table-moveup", "Move Record Up", QKeySequence()); diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index fccbebd43..80117d0c6 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -48,8 +48,6 @@ namespace CSMWorld Land::parseUniqueRecordId(id, x, y); record.mX = x; record.mY = y; - // TODO check for uses of mId and remove them - record.mId = id; } template<> diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index f58a35bc4..8e5908bd3 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -63,6 +63,13 @@ namespace CSMWorld } }; + template<> + inline QVariant StringIdColumn::get(const Record& record) const + { + const Land& land = record.get(); + return QString(Land::createUniqueRecordId(land.mX, land.mY).c_str()); + } + template<> inline QVariant StringIdColumn::get(const Record& record) const { @@ -2483,7 +2490,7 @@ namespace CSMWorld return -1; } - virtual bool isEditable() const override + bool isEditable() const override { return false; } diff --git a/apps/opencs/model/world/columns.cpp b/apps/opencs/model/world/columns.cpp index 49935d40d..63ccb6017 100644 --- a/apps/opencs/model/world/columns.cpp +++ b/apps/opencs/model/world/columns.cpp @@ -333,6 +333,11 @@ namespace CSMWorld { ColumnId_TextureHandle, "Texture Handle" }, { ColumnId_PluginIndex, "Plugin Index" }, { ColumnId_TextureIndex, "Texture Index" }, + { ColumnId_LandMapLodIndex, "Land map height LOD" }, + { ColumnId_LandNormalsIndex, "Land normals" }, + { ColumnId_LandHeightsIndex, "Land heights" }, + { ColumnId_LandColoursIndex, "Land colors" }, + { ColumnId_LandTexturesIndex, "Land textures" }, { ColumnId_UseValue1, "Use value 1" }, { ColumnId_UseValue2, "Use value 2" }, diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index 5543fc13f..127ffde2c 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -1,5 +1,6 @@ #include "idtable.hpp" +#include #include #include @@ -379,11 +380,10 @@ CSMWorld::LandTextureIdTable::ImportResults CSMWorld::LandTextureIdTable::import } // Determine next index. Spread out the indices to reduce conflicts. - size_t MaxIndex = std::numeric_limits::max(); - size_t Prime = (1 << 19) - 1; // A mersenne prime - size_t K = 2154; + size_t MaxIndex = std::numeric_limits::max() - 1; + size_t Prime = (1 << 13) - 1; // A mersenne prime - index = ((K * index) % Prime) % MaxIndex; + index = (index + Prime) % MaxIndex; } while (true); } diff --git a/apps/opencs/model/world/land.cpp b/apps/opencs/model/world/land.cpp index 74833d7ef..bfa927444 100644 --- a/apps/opencs/model/world/land.cpp +++ b/apps/opencs/model/world/land.cpp @@ -8,8 +8,6 @@ namespace CSMWorld void Land::load(ESM::ESMReader &esm, bool &isDeleted) { ESM::Land::load(esm, isDeleted); - - mId = createUniqueRecordId(mX, mY); } std::string Land::createUniqueRecordId(int x, int y) diff --git a/apps/opencs/model/world/land.hpp b/apps/opencs/model/world/land.hpp index cc7e914af..e604f1311 100644 --- a/apps/opencs/model/world/land.hpp +++ b/apps/opencs/model/world/land.hpp @@ -12,8 +12,6 @@ namespace CSMWorld /// \todo Add worldspace support to the Land record. struct Land : public ESM::Land { - std::string mId; - /// Loads the metadata and ID void load (ESM::ESMReader &esm, bool &isDeleted); diff --git a/apps/opencs/model/world/landtexture.cpp b/apps/opencs/model/world/landtexture.cpp index 32bab1e6d..43deb64a4 100644 --- a/apps/opencs/model/world/landtexture.cpp +++ b/apps/opencs/model/world/landtexture.cpp @@ -1,6 +1,6 @@ #include "landtexture.hpp" -#include +#include #include #include @@ -16,7 +16,9 @@ namespace CSMWorld std::string LandTexture::createUniqueRecordId(int plugin, int index) { - return 'L' + std::to_string(plugin) + '#' + std::to_string(index); + std::stringstream ss; + ss << 'L' << plugin << '#' << index; + return ss.str(); } void LandTexture::parseUniqueRecordId(const std::string& id, int& plugin, int& index) diff --git a/apps/opencs/view/world/landcreator.cpp b/apps/opencs/view/world/landcreator.cpp index 08b8b3620..a227a6a87 100644 --- a/apps/opencs/view/world/landcreator.cpp +++ b/apps/opencs/view/world/landcreator.cpp @@ -2,6 +2,9 @@ #include +#include +#include + #include "../../model/world/commands.hpp" #include "../../model/world/idtable.hpp" #include "../../model/world/land.hpp" diff --git a/apps/opencs/view/world/landcreator.hpp b/apps/opencs/view/world/landcreator.hpp index ef8cf60d5..9674a2b58 100644 --- a/apps/opencs/view/world/landcreator.hpp +++ b/apps/opencs/view/world/landcreator.hpp @@ -1,11 +1,11 @@ #ifndef CSV_WORLD_LANDCREATOR_H #define CSV_WORLD_LANDCREATOR_H -#include -#include - #include "genericcreator.hpp" +class QLabel; +class QSpinBox; + namespace CSVWorld { class LandCreator : public GenericCreator diff --git a/apps/opencs/view/world/landtexturecreator.cpp b/apps/opencs/view/world/landtexturecreator.cpp index f79c08427..fdabfb281 100644 --- a/apps/opencs/view/world/landtexturecreator.cpp +++ b/apps/opencs/view/world/landtexturecreator.cpp @@ -3,9 +3,9 @@ #include #include -#include #include #include +#include #include "../../model/world/commands.hpp" #include "../../model/world/idtable.hpp" @@ -30,14 +30,13 @@ namespace CSVWorld QLabel* indexLabel = new QLabel("Index"); insertBeforeButtons(indexLabel, false); - QIntValidator* indexValidator = new QIntValidator(0, MaxIndex, this); - - mIndexEdit = new QLineEdit(this); - mIndexEdit->setValidator(indexValidator); - insertBeforeButtons(mIndexEdit, true); + mIndexBox = new QSpinBox(this); + mIndexBox->setMinimum(0); + mIndexBox->setMaximum(MaxIndex); + insertBeforeButtons(mIndexBox, true); connect(mNameEdit, SIGNAL(textChanged(const QString&)), this, SLOT(nameChanged(const QString&))); - connect(mIndexEdit, SIGNAL(textChanged(const QString&)), this, SLOT(indexChanged(const QString&))); + connect(mIndexBox, SIGNAL(valueChanged(int)), this, SLOT(indexChanged(int))); } void LandTextureCreator::cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type) @@ -50,26 +49,23 @@ namespace CSVWorld mNameEdit->setText((table.data(table.getModelIndex(originId, column)).toString())); column = table.findColumnIndex(CSMWorld::Columns::ColumnId_TextureIndex); - mIndexEdit->setText((table.data(table.getModelIndex(originId, column)).toString())); + mIndexBox->setValue((table.data(table.getModelIndex(originId, column)).toInt())); } void LandTextureCreator::focus() { - mIndexEdit->setFocus(); + mIndexBox->setFocus(); } void LandTextureCreator::reset() { GenericCreator::reset(); mNameEdit->setText(""); - mIndexEdit->setText(""); + mIndexBox->setValue(0); } std::string LandTextureCreator::getErrors() const { - std::string id = getId(); - - // TODO empty index edit? if (getData().getLandTextures().searchId(getId()) >= 0) { return "Index is already in use"; @@ -89,7 +85,7 @@ namespace CSVWorld std::string LandTextureCreator::getId() const { - return CSMWorld::LandTexture::createUniqueRecordId(0, mIndex); + return CSMWorld::LandTexture::createUniqueRecordId(0, mIndexBox->value()); } void LandTextureCreator::nameChanged(const QString& value) @@ -98,9 +94,8 @@ namespace CSVWorld update(); } - void LandTextureCreator::indexChanged(const QString& value) + void LandTextureCreator::indexChanged(int value) { - mIndex = value.toInt(); update(); } } diff --git a/apps/opencs/view/world/landtexturecreator.hpp b/apps/opencs/view/world/landtexturecreator.hpp index 46388bf03..b11c47758 100644 --- a/apps/opencs/view/world/landtexturecreator.hpp +++ b/apps/opencs/view/world/landtexturecreator.hpp @@ -6,6 +6,7 @@ #include "genericcreator.hpp" class QLineEdit; +class QSpinBox; namespace CSVWorld { @@ -34,15 +35,14 @@ namespace CSVWorld private slots: void nameChanged(const QString& val); - void indexChanged(const QString& val); + void indexChanged(int val); private: QLineEdit* mNameEdit; - QLineEdit* mIndexEdit; + QSpinBox* mIndexBox; std::string mName; - int mIndex; }; } From ab607f302834c1409e1f0d72caa0dc68be83361c Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Fri, 8 Sep 2017 00:51:46 -0400 Subject: [PATCH 11/42] Tweaks to land loading, land cloning, terrain signals, placeholder land update handling --- apps/opencs/model/world/collection.hpp | 2 + apps/opencs/model/world/commands.cpp | 170 ++++++++++++------ apps/opencs/model/world/commands.hpp | 54 +++++- apps/opencs/view/render/cell.cpp | 72 ++++++-- apps/opencs/view/render/cell.hpp | 14 ++ .../view/render/pagedworldspacewidget.cpp | 81 +++++++++ .../view/render/pagedworldspacewidget.hpp | 8 + apps/opencs/view/world/genericcreator.cpp | 5 + apps/opencs/view/world/genericcreator.hpp | 2 + apps/opencs/view/world/landcreator.cpp | 19 ++ apps/opencs/view/world/landcreator.hpp | 11 +- components/esm/loadland.cpp | 9 +- components/esm/loadland.hpp | 6 +- 13 files changed, 363 insertions(+), 90 deletions(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 80117d0c6..6e74a1726 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -297,6 +297,7 @@ namespace CSMWorld { int index = cloneRecordImp(origin, destination, type); mRecords.at(index).get().mPlugin = 0; + mRecords.at(index).get().mContext.filename.clear(); } template @@ -312,6 +313,7 @@ namespace CSMWorld if (index >= 0) { mRecords.at(index).get().mPlugin = 0; + mRecords.at(index).get().mContext.filename.clear(); return true; } diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index c98f52777..5bfcd6846 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -41,87 +41,145 @@ void CSMWorld::TouchCommand::undo() } } -CSMWorld::TouchLandCommand::TouchLandCommand(IdTable& landTable, IdTable& ltexTable, const std::string& id, QUndoCommand* parent) +CSMWorld::ImportLandTexturesCommand::ImportLandTexturesCommand(IdTable& landTable, + IdTable& ltexTable, QUndoCommand* parent) : QUndoCommand(parent) , mLands(landTable) , mLtexs(ltexTable) - , mId(id) - , mOld(nullptr) - , mChanged(false) + , mOldState(0) { - setText(("Touch " + mId).c_str()); - mOld.reset(mLands.getRecord(mId).clone()); + setText("Import land textures"); } -void CSMWorld::TouchLandCommand::redo() +void CSMWorld::ImportLandTexturesCommand::redo() { int pluginColumn = mLands.findColumnIndex(Columns::ColumnId_PluginIndex); - int oldPlugin = mLands.data(mLands.getModelIndex(mId, pluginColumn)).toInt(); + int oldPlugin = mLands.data(mLands.getModelIndex(getOriginId(), pluginColumn)).toInt(); - mChanged = mLands.touchRecord(mId); - if (mChanged) + // Original data + int textureColumn = mLands.findColumnIndex(Columns::ColumnId_LandTexturesIndex); + mOld = mLands.data(mLands.getModelIndex(getOriginId(), textureColumn)).toByteArray(); + const uint16_t* textureData = reinterpret_cast(mOld.data()); + + // Need to make a copy so the old values can be looked up + QByteArray newTextureByteArray(mOld.data(), mOld.size()); + uint16_t* newTextureData = reinterpret_cast(newTextureByteArray.data()); + + // Perform touch/copy/etc... + onRedo(); + + // Find all indices used + std::unordered_set texIndices; + for (int i = 0; i < Land::LAND_NUM_TEXTURES; ++i) { - // Original data - int textureColumn = mLands.findColumnIndex(Columns::ColumnId_LandTexturesIndex); - QByteArray textureByteArray = mLands.data(mLands.getModelIndex(mId, textureColumn)).toByteArray(); - const uint16_t* textureData = reinterpret_cast(textureByteArray.data()); - - // Need to make a copy so the old values can be looked up - QByteArray newTextureByteArray(textureByteArray.data(), textureByteArray.size()); - uint16_t* newTextureData = reinterpret_cast(newTextureByteArray.data()); - - // Find all indices used - std::unordered_set texIndices; - for (int i = 0; i < Land::LAND_NUM_TEXTURES; ++i) - { - // All indices are offset by 1 for a default texture - if (textureData[i] > 0) - texIndices.insert(textureData[i] - 1); - } + // All indices are offset by 1 for a default texture + if (textureData[i] > 0) + texIndices.insert(textureData[i] - 1); + } - std::vector oldTextures; - for (int index : texIndices) - { - oldTextures.push_back(LandTexture::createUniqueRecordId(oldPlugin, index)); - } + std::vector oldTextures; + for (int index : texIndices) + { + oldTextures.push_back(LandTexture::createUniqueRecordId(oldPlugin, index)); + } - // Import the textures, replace old values - LandTextureIdTable::ImportResults results = dynamic_cast(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); + // Import the textures, replace old values + LandTextureIdTable::ImportResults results = dynamic_cast(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 (newIndex != oldIndex) + { + for (int i = 0; i < Land::LAND_NUM_TEXTURES; ++i) { - for (int i = 0; i < Land::LAND_NUM_TEXTURES; ++i) - { - // All indices are offset by 1 for a default texture - if (textureData[i] == oldIndex) - newTextureData[i] = newIndex + 1; - } + // All indices are offset by 1 for a default texture + if (textureData[i] == oldIndex + 1) + newTextureData[i] = newIndex + 1; } } + } - mLands.setData(mLands.getModelIndex(mId, textureColumn), newTextureByteArray); + // Apply modification + int stateColumn = mLands.findColumnIndex(Columns::ColumnId_Modification); + mOldState = mLands.data(mLands.getModelIndex(getDestinationId(), stateColumn)).toInt(); + + mLands.setData(mLands.getModelIndex(getDestinationId(), textureColumn), newTextureByteArray); +} + +void CSMWorld::ImportLandTexturesCommand::undo() +{ + // Restore to previous + int textureColumn = mLands.findColumnIndex(Columns::ColumnId_LandTexturesIndex); + mLands.setData(mLands.getModelIndex(getDestinationId(), textureColumn), mOld); + + int stateColumn = mLands.findColumnIndex(Columns::ColumnId_Modification); + mLands.setData(mLands.getModelIndex(getDestinationId(), stateColumn), mOldState); + + // Undo copy/touch/etc... + onUndo(); + + for (const std::string& id : mCreatedTextures) + { + int row = mLtexs.getModelIndex(id, 0).row(); + mLtexs.removeRows(row, 1); } + mCreatedTextures.clear(); } -void CSMWorld::TouchLandCommand::undo() +CSMWorld::CopyLandTexturesCommand::CopyLandTexturesCommand(IdTable& landTable, IdTable& ltexTable, + const std::string& origin, const std::string& dest, QUndoCommand* parent) + : ImportLandTexturesCommand(landTable, ltexTable, parent) + , mOriginId(origin) + , mDestId(dest) +{ +} + +const std::string& CSMWorld::CopyLandTexturesCommand::getOriginId() const +{ + return mOriginId; +} + +const std::string& CSMWorld::CopyLandTexturesCommand::getDestinationId() const +{ + return mDestId; +} + +CSMWorld::TouchLandCommand::TouchLandCommand(IdTable& landTable, IdTable& ltexTable, + const std::string& id, QUndoCommand* parent) + : ImportLandTexturesCommand(landTable, ltexTable, parent) + , mId(id) + , mOld(nullptr) + , mChanged(false) +{ + setText(("Touch " + mId).c_str()); + mOld.reset(mLands.getRecord(mId).clone()); +} + +const std::string& CSMWorld::TouchLandCommand::getOriginId() const +{ + return mId; +} + +const std::string& CSMWorld::TouchLandCommand::getDestinationId() const +{ + return mId; +} + +void CSMWorld::TouchLandCommand::onRedo() +{ + mChanged = mLands.touchRecord(mId); +} + +void CSMWorld::TouchLandCommand::onUndo() { if (mChanged) { mLands.setRecord(mId, *mOld); mChanged = false; - - for (const std::string& id : mCreatedTextures) - { - int row = mLtexs.getModelIndex(id, 0).row(); - mLtexs.removeRows(row, 1); - } - mCreatedTextures.clear(); } } diff --git a/apps/opencs/model/world/commands.hpp b/apps/opencs/model/world/commands.hpp index 5f4259cb8..aab0c5410 100644 --- a/apps/opencs/model/world/commands.hpp +++ b/apps/opencs/model/world/commands.hpp @@ -43,23 +43,67 @@ namespace CSMWorld bool mChanged; }; - class TouchLandCommand : public QUndoCommand + class ImportLandTexturesCommand : public QUndoCommand { public: - TouchLandCommand(IdTable& landTable, IdTable& ltexTable, const std::string& id, - QUndoCommand* parent = nullptr); + ImportLandTexturesCommand(IdTable& landTable, IdTable& ltexTable, + QUndoCommand* parent); void redo() override; void undo() override; - private: + protected: + + virtual const std::string& getOriginId() const = 0; + virtual const std::string& getDestinationId() const = 0; + + virtual void onRedo() = 0; + virtual void onUndo() = 0; IdTable& mLands; IdTable& mLtexs; + QByteArray mOld; + int mOldState; + std::vector mCreatedTextures; + }; + + class CopyLandTexturesCommand : public ImportLandTexturesCommand + { + public: + + CopyLandTexturesCommand(IdTable& landTable, IdTable& ltexTable, const std::string& origin, + const std::string& dest, QUndoCommand* parent = nullptr); + + private: + + const std::string& getOriginId() const override; + const std::string& getDestinationId() const override; + + void onRedo() override {} + void onUndo() override {} + + std::string mOriginId; + std::string mDestId; + }; + + class TouchLandCommand : public ImportLandTexturesCommand + { + public: + + TouchLandCommand(IdTable& landTable, IdTable& ltexTable, + const std::string& id, QUndoCommand* parent = nullptr); + + private: + + const std::string& getOriginId() const override; + const std::string& getDestinationId() const override; + + void onRedo() override; + void onUndo() override; + std::string mId; std::unique_ptr mOld; - std::vector mCreatedTextures; bool mChanged; }; diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index 765c5b316..51279cac4 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -75,6 +75,31 @@ bool CSVRender::Cell::addObjects (int start, int end) return modified; } +void CSVRender::Cell::createLand() +{ + if (mDeleted) + { + mTerrain.reset(); + return; + } + + const CSMWorld::IdCollection& land = mData.getLand(); + int landIndex = land.searchId(mId); + if (landIndex != -1) + { + const ESM::Land& esmLand = land.getRecord(mId).get(); + + if (esmLand.getLandData (ESM::Land::DATA_VHGT)) + { + mTerrain.reset(new Terrain::TerrainGrid(mCellNode, mCellNode, mData.getResourceSystem().get(), new TerrainStorage(mData), Mask_Terrain)); + mTerrain->loadCell(esmLand.mX, esmLand.mY); + + mCellBorder.reset(new CellBorder(mCellNode, mCoordinates)); + mCellBorder->buildShape(esmLand); + } + } +} + CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::string& id, bool deleted) : mData (data), mId (Misc::StringUtils::lowerCase (id)), mDeleted (deleted), mSubMode (0), @@ -99,22 +124,7 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::st addObjects (0, rows-1); - const CSMWorld::IdCollection& land = mData.getLand(); - int landIndex = land.searchId(mId); - if (landIndex != -1) - { - const ESM::Land& esmLand = land.getRecord(mId).get(); - - if (esmLand.getLandData (ESM::Land::DATA_VHGT)) - { - mTerrain.reset(new Terrain::TerrainGrid(mCellNode, mCellNode, data.getResourceSystem().get(), new TerrainStorage(mData), Mask_Terrain)); - mTerrain->loadCell(esmLand.mX, - esmLand.mY); - - mCellBorder.reset(new CellBorder(mCellNode, mCoordinates)); - mCellBorder->buildShape(esmLand); - } - } + createLand(); mPathgrid.reset(new Pathgrid(mData, mCellNode, mId, mCoordinates)); mCellWater.reset(new CellWater(mData, mCellNode, mId, mCoordinates)); @@ -285,6 +295,36 @@ void CSVRender::Cell::pathgridRemoved() mPathgrid->removeGeometry(); } +void CSVRender::Cell::landDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) +{ + createLand(); +} + +void CSVRender::Cell::landAboutToBeRemoved (const QModelIndex& parent, int start, int end) +{ + createLand(); +} + +void CSVRender::Cell::landAdded (const QModelIndex& parent, int start, int end) +{ + createLand(); +} + +void CSVRender::Cell::landTextureChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) +{ + createLand(); +} + +void CSVRender::Cell::landTextureAboutToBeRemoved (const QModelIndex& parent, int start, int end) +{ + createLand(); +} + +void CSVRender::Cell::landTextureAdded (const QModelIndex& parent, int start, int end) +{ + createLand(); +} + void CSVRender::Cell::reloadAssets() { for (std::map::const_iterator iter (mObjects.begin()); diff --git a/apps/opencs/view/render/cell.hpp b/apps/opencs/view/render/cell.hpp index f53f61973..f6db475d7 100644 --- a/apps/opencs/view/render/cell.hpp +++ b/apps/opencs/view/render/cell.hpp @@ -72,6 +72,8 @@ namespace CSVRender /// \return Have any objects been added? bool addObjects (int start, int end); + void createLand(); + public: enum Selection @@ -118,6 +120,18 @@ namespace CSVRender void pathgridRemoved(); + void landDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); + + void landAboutToBeRemoved (const QModelIndex& parent, int start, int end); + + void landAdded (const QModelIndex& parent, int start, int end); + + void landTextureChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); + + void landTextureAboutToBeRemoved (const QModelIndex& parent, int start, int end); + + void landTextureAdded (const QModelIndex& parent, int start, int end); + void reloadAssets(); void setSelection (int elementMask, Selection mode); diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index b1077139c..b224ac1c4 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -351,6 +351,69 @@ void CSVRender::PagedWorldspaceWidget::pathgridAdded(const QModelIndex& parent, } } +void CSVRender::PagedWorldspaceWidget::landDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) +{ + for (int r = topLeft.row(); r <= bottomRight.row(); ++r) + { + std::string id = mDocument.getData().getLand().getId(r); + + auto cellIt = mCells.find(CSMWorld::CellCoordinates::fromId(id).first); + if (cellIt != mCells.end()) + { + cellIt->second->landDataChanged(topLeft, bottomRight); + flagAsModified(); + } + } +} + +void CSVRender::PagedWorldspaceWidget::landAboutToBeRemoved (const QModelIndex& parent, int start, int end) +{ + for (int r = start; r <= end; ++r) + { + std::string id = mDocument.getData().getLand().getId(r); + + auto cellIt = mCells.find(CSMWorld::CellCoordinates::fromId(id).first); + if (cellIt != mCells.end()) + { + cellIt->second->landAboutToBeRemoved(parent, start, end); + flagAsModified(); + } + } +} + +void CSVRender::PagedWorldspaceWidget::landAdded (const QModelIndex& parent, int start, int end) +{ + for (int r = start; r <= end; ++r) + { + std::string id = mDocument.getData().getLand().getId(r); + + auto cellIt = mCells.find(CSMWorld::CellCoordinates::fromId(id).first); + if (cellIt != mCells.end()) + { + cellIt->second->landAdded(parent, start, end); + flagAsModified(); + } + } +} + +void CSVRender::PagedWorldspaceWidget::landTextureDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) +{ + for (auto cellIt : mCells) + cellIt.second->landTextureChanged(topLeft, bottomRight); +} + +void CSVRender::PagedWorldspaceWidget::landTextureAboutToBeRemoved (const QModelIndex& parent, int start, int end) +{ + for (auto cellIt : mCells) + cellIt.second->landTextureAboutToBeRemoved(parent, start, end); +} + +void CSVRender::PagedWorldspaceWidget::landTextureAdded (const QModelIndex& parent, int start, int end) +{ + for (auto cellIt : mCells) + cellIt.second->landTextureAdded(parent, start, end); +} + std::string CSVRender::PagedWorldspaceWidget::getStartupInstruction() { @@ -475,6 +538,24 @@ CSVRender::PagedWorldspaceWidget::PagedWorldspaceWidget (QWidget* parent, CSMDoc connect (&document.getData(), SIGNAL (assetTablesChanged ()), this, SLOT (assetTablesChanged ())); + QAbstractItemModel *lands = document.getData().getTableModel (CSMWorld::UniversalId::Type_Lands); + + connect (lands, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), + this, SLOT (landDataChanged (const QModelIndex&, const QModelIndex&))); + connect (lands, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), + this, SLOT (landAboutToBeRemoved (const QModelIndex&, int, int))); + connect (lands, SIGNAL (rowsInserted (const QModelIndex&, int, int)), + this, SLOT (landAdded (const QModelIndex&, int, int))); + + QAbstractItemModel *ltexs = document.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures); + + connect (ltexs, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), + this, SLOT (landTextureDataChanged (const QModelIndex&, const QModelIndex&))); + connect (ltexs, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), + this, SLOT (landTextureAboutToBeRemoved (const QModelIndex&, int, int))); + connect (ltexs, SIGNAL (rowsInserted (const QModelIndex&, int, int)), + this, SLOT (landTextureAdded (const QModelIndex&, int, int))); + // Shortcuts CSMPrefs::Shortcut* loadCameraCellShortcut = new CSMPrefs::Shortcut("scene-load-cam-cell", this); connect(loadCameraCellShortcut, SIGNAL(activated()), this, SLOT(loadCameraCell())); diff --git a/apps/opencs/view/render/pagedworldspacewidget.hpp b/apps/opencs/view/render/pagedworldspacewidget.hpp index 8c41df51e..6672c2268 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.hpp +++ b/apps/opencs/view/render/pagedworldspacewidget.hpp @@ -155,6 +155,14 @@ namespace CSVRender virtual void cellAdded (const QModelIndex& index, int start, int end); + virtual void landDataChanged (const QModelIndex& topLeft, const QModelIndex& botomRight); + virtual void landAboutToBeRemoved (const QModelIndex& parent, int start, int end); + virtual void landAdded (const QModelIndex& parent, int start, int end); + + virtual void landTextureDataChanged (const QModelIndex& topLeft, const QModelIndex& botomRight); + virtual void landTextureAboutToBeRemoved (const QModelIndex& parent, int start, int end); + virtual void landTextureAdded (const QModelIndex& parent, int start, int end); + void assetTablesChanged (); void loadCameraCell(); diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index bde523a09..bd3da230d 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -51,6 +51,11 @@ std::string CSVWorld::GenericCreator::getId() const return mId->text().toUtf8().constData(); } +std::string CSVWorld::GenericCreator::getClonedId() const +{ + return mClonedId; +} + std::string CSVWorld::GenericCreator::getIdValidatorResult() const { std::string errors; diff --git a/apps/opencs/view/world/genericcreator.hpp b/apps/opencs/view/world/genericcreator.hpp index d708c1047..3baacfc06 100644 --- a/apps/opencs/view/world/genericcreator.hpp +++ b/apps/opencs/view/world/genericcreator.hpp @@ -64,6 +64,8 @@ namespace CSVWorld virtual std::string getId() const; + std::string getClonedId() const; + virtual std::string getIdValidatorResult() const; /// Allow subclasses to add additional data to \a command. diff --git a/apps/opencs/view/world/landcreator.cpp b/apps/opencs/view/world/landcreator.cpp index a227a6a87..dd7bfe68d 100644 --- a/apps/opencs/view/world/landcreator.cpp +++ b/apps/opencs/view/world/landcreator.cpp @@ -95,6 +95,25 @@ namespace CSVWorld return CSMWorld::Land::createUniqueRecordId(mX->value(), mY->value()); } + void LandCreator::pushCommand(std::unique_ptr command, const std::string& id) + { + if (mCloneMode) + { + CSMWorld::IdTable& lands = dynamic_cast(*getData().getTableModel(CSMWorld::UniversalId::Type_Lands)); + CSMWorld::IdTable& ltexs = dynamic_cast(*getData().getTableModel(CSMWorld::UniversalId::Type_LandTextures)); + + getUndoStack().beginMacro(("Clone " + id).c_str()); + getUndoStack().push(command.release()); + + CSMWorld::CopyLandTexturesCommand* ltexCopy = new CSMWorld::CopyLandTexturesCommand(lands, ltexs, getClonedId(), getId()); + getUndoStack().push(ltexCopy); + + getUndoStack().endMacro(); + } + else + getUndoStack().push (command.release()); + } + void LandCreator::coordChanged(int value) { update(); diff --git a/apps/opencs/view/world/landcreator.hpp b/apps/opencs/view/world/landcreator.hpp index 9674a2b58..e0c5577e4 100644 --- a/apps/opencs/view/world/landcreator.hpp +++ b/apps/opencs/view/world/landcreator.hpp @@ -31,13 +31,16 @@ namespace CSVWorld std::string getErrors() const override; - private slots: - - void coordChanged(int value); - protected: std::string getId() const override; + + void pushCommand(std::unique_ptr command, + const std::string& id) override; + + private slots: + + void coordChanged(int value); }; } diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index 7d48dd037..c81675556 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -15,7 +15,6 @@ namespace ESM , mX(0) , mY(0) , mPlugin(0) - , mNoFile(false) , mDataTypes(0) , mLandData(NULL) { @@ -214,7 +213,8 @@ namespace ESM Land::DATA_VCLR | Land::DATA_VTEX; mDataTypes = mLandData->mDataLoaded; - mNoFile = true; + // No file associated with the land now + mContext.filename.clear(); } void Land::loadData(int flags, LandData* target) const @@ -236,7 +236,7 @@ namespace ESM } // Copy data to target if no file - if (mNoFile) + if (mContext.filename.empty()) { *target = *mLandData; return; @@ -320,7 +320,7 @@ namespace ESM Land::Land (const Land& land) : mFlags (land.mFlags), mX (land.mX), mY (land.mY), mPlugin (land.mPlugin), - mContext (land.mContext), mNoFile(land.mNoFile), mDataTypes (land.mDataTypes), + mContext (land.mContext), mDataTypes (land.mDataTypes), mLandData (land.mLandData ? new LandData (*land.mLandData) : 0) {} @@ -337,7 +337,6 @@ namespace ESM std::swap (mY, land.mY); std::swap (mPlugin, land.mPlugin); std::swap (mContext, land.mContext); - std::swap (mNoFile, land.mNoFile); std::swap (mDataTypes, land.mDataTypes); std::swap (mLandData, land.mLandData); } diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index 223b7975a..d7b736f99 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -31,12 +31,10 @@ struct Land // File context. This allows the ESM reader to be 'reset' to this // location later when we are ready to load the full data set. + // In the editor, there may not be a file associated with the Land, + // in which case the filename will be empty. ESM_Context mContext; - // In the editor, a new Land is not associated with a file, thus mContext should not be accessed - // when land data is being requested. Instead simply copy over the data. - bool mNoFile; - int mDataTypes; enum From 2eacc2f093e921c983047308eaf0a400b5294889 Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Fri, 8 Sep 2017 14:37:03 -0400 Subject: [PATCH 12/42] Changes to land creation, add ability to specifically clear terrain cache --- apps/opencs/view/render/cell.cpp | 36 ++++++++++++++++--- apps/opencs/view/render/cell.hpp | 1 + .../view/render/pagedworldspacewidget.cpp | 3 ++ components/terrain/world.cpp | 6 ++++ components/terrain/world.hpp | 4 +++ 5 files changed, 46 insertions(+), 4 deletions(-) diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index 51279cac4..18614a85f 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -77,27 +77,55 @@ bool CSVRender::Cell::addObjects (int start, int end) void CSVRender::Cell::createLand() { + // Cell is deleted if (mDeleted) { - mTerrain.reset(); + unloadLand(); return; } + // Setup land if available const CSMWorld::IdCollection& land = mData.getLand(); int landIndex = land.searchId(mId); - if (landIndex != -1) + if (landIndex != -1 && !land.getRecord(mId).isDeleted()) { const ESM::Land& esmLand = land.getRecord(mId).get(); if (esmLand.getLandData (ESM::Land::DATA_VHGT)) { - mTerrain.reset(new Terrain::TerrainGrid(mCellNode, mCellNode, mData.getResourceSystem().get(), new TerrainStorage(mData), Mask_Terrain)); + if (mTerrain) + { + mTerrain->unloadCell(mCoordinates.getX(), mCoordinates.getY()); + mTerrain->clearAssociatedCaches(); + } + else + { + mTerrain.reset(new Terrain::TerrainGrid(mCellNode, mCellNode, + mData.getResourceSystem().get(), new TerrainStorage(mData), Mask_Terrain)); + } + mTerrain->loadCell(esmLand.mX, esmLand.mY); - mCellBorder.reset(new CellBorder(mCellNode, mCoordinates)); + if (!mCellBorder) + mCellBorder.reset(new CellBorder(mCellNode, mCoordinates)); + mCellBorder->buildShape(esmLand); + + return; } } + + // No land data + unloadLand(); +} + +void CSVRender::Cell::unloadLand() +{ + if (mTerrain) + mTerrain->unloadCell(mCoordinates.getX(), mCoordinates.getY()); + + if (mCellBorder) + mCellBorder.reset(); } CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::string& id, diff --git a/apps/opencs/view/render/cell.hpp b/apps/opencs/view/render/cell.hpp index f6db475d7..6418ed249 100644 --- a/apps/opencs/view/render/cell.hpp +++ b/apps/opencs/view/render/cell.hpp @@ -73,6 +73,7 @@ namespace CSVRender bool addObjects (int start, int end); void createLand(); + void unloadLand(); public: diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index b224ac1c4..4a745195b 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -400,18 +400,21 @@ void CSVRender::PagedWorldspaceWidget::landTextureDataChanged (const QModelIndex { for (auto cellIt : mCells) cellIt.second->landTextureChanged(topLeft, bottomRight); + flagAsModified(); } void CSVRender::PagedWorldspaceWidget::landTextureAboutToBeRemoved (const QModelIndex& parent, int start, int end) { for (auto cellIt : mCells) cellIt.second->landTextureAboutToBeRemoved(parent, start, end); + flagAsModified(); } void CSVRender::PagedWorldspaceWidget::landTextureAdded (const QModelIndex& parent, int start, int end) { for (auto cellIt : mCells) cellIt.second->landTextureAdded(parent, start, end); + flagAsModified(); } diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index b7cc0ae01..22c65b62c 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -75,4 +75,10 @@ void World::updateTextureFiltering() mTextureManager->updateTextureFiltering(); } +void World::clearAssociatedCaches() +{ + mTextureManager->clearCache(); + mChunkManager->clearCache(); +} + } diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index d0576fbd3..e1c3828fc 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -63,6 +63,10 @@ namespace Terrain float getHeightAt (const osg::Vec3f& worldPos); + /// Clears the cached land and landtexture data. + /// @note Thread safe. + virtual void clearAssociatedCaches(); + /// Load a terrain cell at maximum LOD and store it in the View for later use. /// @note Thread safe. virtual void cacheCell(View* view, int x, int y) {} From 72cb405de220d5cfe706c5c1c0e57358c32ab2dc Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Fri, 8 Sep 2017 21:03:52 -0400 Subject: [PATCH 13/42] Some bug fixes, changes to land load code. --- apps/opencs/model/world/columnimp.cpp | 6 ++++-- apps/opencs/model/world/data.cpp | 14 +------------- apps/opencs/model/world/idcollection.hpp | 17 +++++++++++++++++ components/esm/loadland.cpp | 4 ++-- 4 files changed, 24 insertions(+), 17 deletions(-) diff --git a/apps/opencs/model/world/columnimp.cpp b/apps/opencs/model/world/columnimp.cpp index 65d2fecd2..e6b406864 100644 --- a/apps/opencs/model/world/columnimp.cpp +++ b/apps/opencs/model/world/columnimp.cpp @@ -103,7 +103,8 @@ namespace CSMWorld assert (array.count() == Land::LAND_NUM_VERTS * sizeof(float)); - for (int i = 0; i < array.count(); ++i) + int count = array.count() / sizeof(float); + for (int i = 0; i < count; ++i) { landData->mHeights[i] = rawData[i]; } @@ -183,7 +184,8 @@ namespace CSMWorld assert (array.count() == Land::LAND_NUM_TEXTURES * sizeof(uint16_t)); - for (int i = 0; i < array.count(); ++i) + int count = array.count() / sizeof(uint16_t); + for (int i = 0; i < count; ++i) { landData->mTextures[i] = rawData[i]; } diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 3284d9b05..0ffe5fa21 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -1013,19 +1013,7 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages) case ESM::REC_LTEX: mLandTextures.load (*mReader, mBase); break; - case ESM::REC_LAND: - { - int index = mLand.load(*mReader, mBase); - - // Load all land data for now. A future optimisation may only load non-base data - // if a suitable mechanism for avoiding race conditions can be established. - if (index!=-1/* && !mBase*/) - mLand.getRecord (index).get().loadData ( - ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR | - ESM::Land::DATA_VTEX); - - break; - } + case ESM::REC_LAND: mLand.load(*mReader, mBase); break; case ESM::REC_CELL: { diff --git a/apps/opencs/model/world/idcollection.hpp b/apps/opencs/model/world/idcollection.hpp index ea6eefb88..7849aab92 100644 --- a/apps/opencs/model/world/idcollection.hpp +++ b/apps/opencs/model/world/idcollection.hpp @@ -4,6 +4,7 @@ #include #include "collection.hpp" +#include "land.hpp" namespace CSMWorld { @@ -39,6 +40,22 @@ namespace CSMWorld record.load (reader, isDeleted); } + template<> + inline void IdCollection >::loadRecord (Land& record, + ESM::ESMReader& reader, bool& isDeleted) + { + record.load (reader, isDeleted); + + // Load all land data for now. A future optimisation may only load non-base data + // if a suitable mechanism for avoiding race conditions can be established. + int flags = ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | + ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX; + record.loadData (flags); + + // Prevent data from being reloaded. + record.mContext.filename.clear(); + } + template int IdCollection::load (ESM::ESMReader& reader, bool base) { diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index c81675556..6f118c1ab 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -196,8 +196,8 @@ namespace ESM for (int i = 0; i < LAND_NUM_VERTS; ++i) { mLandData->mNormals[i*3+0] = 0; - mLandData->mNormals[i*3+1] = -1; - mLandData->mNormals[i*3+2] = 0; + mLandData->mNormals[i*3+1] = 0; + mLandData->mNormals[i*3+2] = 127; } for (int i = 0; i < LAND_NUM_TEXTURES; ++i) mLandData->mTextures[i] = 0; From d030b595f8460e92eea09052805fbfcd2fdaeef9 Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Sat, 9 Sep 2017 11:48:13 -0400 Subject: [PATCH 14/42] Fix potential segfault, cleanup, get rid of warnings. --- apps/opencs/model/world/collection.hpp | 2 -- apps/opencs/view/render/cell.cpp | 16 ++++++++-------- apps/opencs/view/render/cell.hpp | 2 +- apps/opencs/view/world/genericcreator.cpp | 9 ++++----- apps/opencs/view/world/landcreator.cpp | 9 ++++----- components/esm/loadland.cpp | 6 +++++- 6 files changed, 22 insertions(+), 22 deletions(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 6e74a1726..80117d0c6 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -297,7 +297,6 @@ namespace CSMWorld { int index = cloneRecordImp(origin, destination, type); mRecords.at(index).get().mPlugin = 0; - mRecords.at(index).get().mContext.filename.clear(); } template @@ -313,7 +312,6 @@ namespace CSMWorld if (index >= 0) { mRecords.at(index).get().mPlugin = 0; - mRecords.at(index).get().mContext.filename.clear(); return true; } diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index 18614a85f..1c1d496bb 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -75,7 +75,7 @@ bool CSVRender::Cell::addObjects (int start, int end) return modified; } -void CSVRender::Cell::createLand() +void CSVRender::Cell::updateLand() { // Cell is deleted if (mDeleted) @@ -152,7 +152,7 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::st addObjects (0, rows-1); - createLand(); + updateLand(); mPathgrid.reset(new Pathgrid(mData, mCellNode, mId, mCoordinates)); mCellWater.reset(new CellWater(mData, mCellNode, mId, mCoordinates)); @@ -325,32 +325,32 @@ void CSVRender::Cell::pathgridRemoved() void CSVRender::Cell::landDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { - createLand(); + updateLand(); } void CSVRender::Cell::landAboutToBeRemoved (const QModelIndex& parent, int start, int end) { - createLand(); + updateLand(); } void CSVRender::Cell::landAdded (const QModelIndex& parent, int start, int end) { - createLand(); + updateLand(); } void CSVRender::Cell::landTextureChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { - createLand(); + updateLand(); } void CSVRender::Cell::landTextureAboutToBeRemoved (const QModelIndex& parent, int start, int end) { - createLand(); + updateLand(); } void CSVRender::Cell::landTextureAdded (const QModelIndex& parent, int start, int end) { - createLand(); + updateLand(); } void CSVRender::Cell::reloadAssets() diff --git a/apps/opencs/view/render/cell.hpp b/apps/opencs/view/render/cell.hpp index 6418ed249..101aebd58 100644 --- a/apps/opencs/view/render/cell.hpp +++ b/apps/opencs/view/render/cell.hpp @@ -72,7 +72,7 @@ namespace CSVRender /// \return Have any objects been added? bool addObjects (int start, int end); - void createLand(); + void updateLand(); void unloadLand(); public: diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index bd3da230d..5e2118e9b 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -262,18 +262,17 @@ void CSVWorld::GenericCreator::cloneMode(const std::string& originId, void CSVWorld::GenericCreator::touch(const std::vector& ids) { // Combine multiple touch commands into one "macro" command - std::unique_ptr macro(new QUndoCommand()); - macro->setText("Touch records"); + mUndoStack.beginMacro("Touch Records"); CSMWorld::IdTable& table = dynamic_cast(*mData.getTableModel(mListId)); for (const CSMWorld::UniversalId& uid : ids) { - // This is not leaked, touchCmd is a child of macro and managed by Qt - CSMWorld::TouchCommand* touchCmd = new CSMWorld::TouchCommand(table, uid.getId(), macro.get()); + CSMWorld::TouchCommand* touchCmd = new CSMWorld::TouchCommand(table, uid.getId()); + mUndoStack.push(touchCmd); } // Execute - mUndoStack.push(macro.release()); + mUndoStack.endMacro(); } void CSVWorld::GenericCreator::toggleWidgets(bool active) diff --git a/apps/opencs/view/world/landcreator.cpp b/apps/opencs/view/world/landcreator.cpp index dd7bfe68d..2ebfe1869 100644 --- a/apps/opencs/view/world/landcreator.cpp +++ b/apps/opencs/view/world/landcreator.cpp @@ -55,19 +55,18 @@ namespace CSVWorld void LandCreator::touch(const std::vector& ids) { // Combine multiple touch commands into one "macro" command - std::unique_ptr macro(new QUndoCommand()); - macro->setText("Touch records"); + getUndoStack().beginMacro("Touch records"); CSMWorld::IdTable& lands = dynamic_cast(*getData().getTableModel(CSMWorld::UniversalId::Type_Lands)); CSMWorld::IdTable& ltexs = dynamic_cast(*getData().getTableModel(CSMWorld::UniversalId::Type_LandTextures)); for (const CSMWorld::UniversalId& uid : ids) { - // This is not leaked, touchCmd is a child of macro and managed by Qt - CSMWorld::TouchLandCommand* touchCmd = new CSMWorld::TouchLandCommand(lands, ltexs, uid.getId(), macro.get()); + CSMWorld::TouchLandCommand* touchCmd = new CSMWorld::TouchLandCommand(lands, ltexs, uid.getId()); + getUndoStack().push(touchCmd); } // Execute - getUndoStack().push(macro.release()); + getUndoStack().endMacro(); } void LandCreator::focus() diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index 6f118c1ab..f597defd7 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -238,7 +238,11 @@ namespace ESM // Copy data to target if no file if (mContext.filename.empty()) { - *target = *mLandData; + if (mLandData) + *target = *mLandData; + else + target = new LandData; + return; } From 25d4a0370f7022c8c83b80d99741ad9d9345e5b1 Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Sat, 9 Sep 2017 15:37:52 -0400 Subject: [PATCH 15/42] Changes to land data access in tables, also update Land once per frame in scene view. --- apps/opencs/model/world/columnimp.cpp | 131 ++++++++++++++++---------- apps/opencs/model/world/idtable.cpp | 3 +- apps/opencs/view/render/cell.cpp | 51 ++++++++-- apps/opencs/view/render/cell.hpp | 3 + components/esm/loadland.cpp | 9 ++ components/esm/loadland.hpp | 3 + 6 files changed, 143 insertions(+), 57 deletions(-) diff --git a/apps/opencs/model/world/columnimp.cpp b/apps/opencs/model/world/columnimp.cpp index e6b406864..1ee7f8a03 100644 --- a/apps/opencs/model/world/columnimp.cpp +++ b/apps/opencs/model/world/columnimp.cpp @@ -10,19 +10,31 @@ namespace CSMWorld QVariant LandMapLodColumn::get(const Record& record) const { - // Note: original data is signed - const char* rawData = reinterpret_cast(&record.get().mWnam[0]); - return QByteArray(rawData, Land::LAND_GLOBAL_MAP_LOD_SIZE); + const int Size = Land::LAND_GLOBAL_MAP_LOD_SIZE; + const Land& land = record.get(); + + if (land.isDataLoaded(Land::DATA_WNAM)) + { + // Note: original data is signed + const char* rawData = reinterpret_cast(&land.mWnam[0]); + return QByteArray(rawData, Size); + } + else + { + // Return a blank array + return QByteArray(Size, 0); + } } void LandMapLodColumn::set(Record& record, const QVariant& data) { - Land copy = record.get(); QByteArray array = data.toByteArray(); const signed char* rawData = reinterpret_cast(array.data()); - assert (array.count() == Land::LAND_GLOBAL_MAP_LOD_SIZE); + Land copy = record.get(); + copy.setDataLoaded(Land::DATA_WNAM); + for (int i = 0; i < array.count(); ++i) { copy.mWnam[i] = rawData[i]; @@ -44,28 +56,34 @@ namespace CSMWorld QVariant LandNormalsColumn::get(const Record& record) const { - const Land::LandData* landData = record.get().getLandData(); - assert(landData); + const int Size = Land::LAND_NUM_VERTS * 3; + const Land& land = record.get(); - // Note: original data is signed - const char* rawData = reinterpret_cast(&landData->mNormals[0]); - return QByteArray(rawData, Land::LAND_NUM_VERTS * 3); + if (land.isDataLoaded(Land::DATA_VNML)) + { + // Note: original data is signed + const char* rawData = reinterpret_cast(&land.getLandData()->mNormals[0]); + return QByteArray(rawData, Size); + } + else + { + // Return a blank array + return QByteArray(Size, 0); + } } void LandNormalsColumn::set(Record& record, const QVariant& data) { - Land copy = record.get(); - Land::LandData* landData = copy.getLandData(); - assert (landData); - QByteArray array = data.toByteArray(); const signed char* rawData = reinterpret_cast(array.data()); - assert (array.count() == Land::LAND_NUM_VERTS * 3); + Land copy = record.get(); + copy.setDataLoaded(Land::DATA_VNML); + for (int i = 0; i < array.count(); ++i) { - landData->mNormals[i] = rawData[i]; + copy.getLandData()->mNormals[i] = rawData[i]; } record.setModified(copy); @@ -84,29 +102,34 @@ namespace CSMWorld QVariant LandHeightsColumn::get(const Record& record) const { - const Land::LandData* landData = record.get().getLandData(); - assert(landData); + const int Size = Land::LAND_NUM_VERTS * sizeof(float); + const Land& land = record.get(); - // Note: original data is float - const char* rawData = reinterpret_cast(&landData->mHeights[0]); - return QByteArray(rawData, Land::LAND_NUM_VERTS * sizeof(float)); + if (land.isDataLoaded(Land::DATA_VHGT)) + { + // Note: original data is float + const char* rawData = reinterpret_cast(&land.getLandData()->mHeights[0]); + return QByteArray(rawData, Size); + } + else + { + return QByteArray(Size, 0); + } } void LandHeightsColumn::set(Record& record, const QVariant& data) { - Land copy = record.get(); - Land::LandData* landData = copy.getLandData(); - assert (landData); - QByteArray array = data.toByteArray(); const float* rawData = reinterpret_cast(array.data()); - assert (array.count() == Land::LAND_NUM_VERTS * sizeof(float)); + Land copy = record.get(); + copy.setDataLoaded(Land::DATA_VHGT); + int count = array.count() / sizeof(float); for (int i = 0; i < count; ++i) { - landData->mHeights[i] = rawData[i]; + copy.getLandData()->mHeights[i] = rawData[i]; } record.setModified(copy); @@ -125,28 +148,33 @@ namespace CSMWorld QVariant LandColoursColumn::get(const Record& record) const { - const Land::LandData* landData = record.get().getLandData(); - assert(landData); + const int Size = Land::LAND_NUM_VERTS * 3; + const Land& land = record.get(); - // Note: original data is unsigned char - const char* rawData = reinterpret_cast(&landData->mColours[0]); - return QByteArray(rawData, Land::LAND_NUM_VERTS * 3); + if (land.isDataLoaded(Land::DATA_VCLR)) + { + // Note: original data is unsigned char + const char* rawData = reinterpret_cast(&land.getLandData()->mColours[0]); + return QByteArray(rawData, Size); + } + else + { + return QByteArray(Size, 0); + } } void LandColoursColumn::set(Record& record, const QVariant& data) { - Land copy = record.get(); - Land::LandData* landData = copy.getLandData(); - assert (landData); - QByteArray array = data.toByteArray(); const unsigned char* rawData = reinterpret_cast(array.data()); - assert (array.count() == Land::LAND_NUM_VERTS * 3); + Land copy = record.get(); + copy.setDataLoaded(Land::DATA_VCLR); + for (int i = 0; i < array.count(); ++i) { - landData->mColours[i] = rawData[i]; + copy.getLandData()->mColours[i] = rawData[i]; } record.setModified(copy); @@ -165,29 +193,34 @@ namespace CSMWorld QVariant LandTexturesColumn::get(const Record& record) const { - const Land::LandData* landData = record.get().getLandData(); - assert(landData); + const int Size = Land::LAND_NUM_TEXTURES * sizeof(uint16_t); + const Land& land = record.get(); - // Note: original data is uint16_t - const char* rawData = reinterpret_cast(&landData->mTextures[0]); - return QByteArray(rawData, Land::LAND_NUM_TEXTURES * sizeof(uint16_t)); + if (land.isDataLoaded(Land::DATA_VTEX)) + { + // Note: original data is uint16_t + const char* rawData = reinterpret_cast(&land.getLandData()->mTextures[0]); + return QByteArray(rawData, Size); + } + else + { + return QByteArray(Size, 0); + } } void LandTexturesColumn::set(Record& record, const QVariant& data) { - Land copy = record.get(); - Land::LandData* landData = copy.getLandData(); - assert (landData); - QByteArray array = data.toByteArray(); const uint16_t* rawData = reinterpret_cast(array.data()); - assert (array.count() == Land::LAND_NUM_TEXTURES * sizeof(uint16_t)); + Land copy = record.get(); + copy.setDataLoaded(Land::DATA_VTEX); + int count = array.count() / sizeof(uint16_t); for (int i = 0; i < count; ++i) { - landData->mTextures[i] = rawData[i]; + copy.getLandData()->mTextures[i] = rawData[i]; } record.setModified(copy); diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index 127ffde2c..b41eea8f8 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -356,6 +356,7 @@ CSMWorld::LandTextureIdTable::ImportResults CSMWorld::LandTextureIdTable::import // Try a direct mapping to the current plugin first. Otherwise iterate until one is found. // Iteration is deterministic to avoid duplicates. + int startIndex = index; do { std::string newId = LandTexture::createUniqueRecordId(0, index); int newRow = idCollection()->searchId(newId); @@ -384,7 +385,7 @@ CSMWorld::LandTextureIdTable::ImportResults CSMWorld::LandTextureIdTable::import size_t Prime = (1 << 13) - 1; // A mersenne prime index = (index + Prime) % MaxIndex; - } while (true); + } while (index != startIndex); } return results; diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index 1c1d496bb..552a54ac2 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -26,6 +26,33 @@ #include "terrainstorage.hpp" #include "object.hpp" +namespace CSVRender +{ + class CellNodeContainer : public osg::Referenced + { + public: + + CellNodeContainer(Cell* cell) : mCell(cell) {} + + Cell* getCell(){ return mCell; } + + private: + + Cell* mCell; + }; + + class CellNodeCallback : public osg::NodeCallback + { + public: + + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + CellNodeContainer* container = static_cast(node->getUserData()); + container->getCell()->updateLand(); + } + }; +} + bool CSVRender::Cell::removeObject (const std::string& id) { std::map::iterator iter = @@ -77,6 +104,11 @@ bool CSVRender::Cell::addObjects (int start, int end) void CSVRender::Cell::updateLand() { + if (!mUpdateLand || mLandDeleted) + return; + + mUpdateLand = false; + // Cell is deleted if (mDeleted) { @@ -116,6 +148,7 @@ void CSVRender::Cell::updateLand() } // No land data + mLandDeleted = true; unloadLand(); } @@ -131,7 +164,7 @@ void CSVRender::Cell::unloadLand() CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::string& id, bool deleted) : mData (data), mId (Misc::StringUtils::lowerCase (id)), mDeleted (deleted), mSubMode (0), - mSubModeElementMask (0) + mSubModeElementMask (0), mUpdateLand(true), mLandDeleted(false) { std::pair result = CSMWorld::CellCoordinates::fromId (id); @@ -139,6 +172,8 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::st mCoordinates = result.first; mCellNode = new osg::Group; + mCellNode->setUserData(new CellNodeContainer(this)); + mCellNode->setUpdateCallback(new CellNodeCallback); rootNode->addChild(mCellNode); setCellMarker(); @@ -325,32 +360,34 @@ void CSVRender::Cell::pathgridRemoved() void CSVRender::Cell::landDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { - updateLand(); + mUpdateLand = true; } void CSVRender::Cell::landAboutToBeRemoved (const QModelIndex& parent, int start, int end) { - updateLand(); + mLandDeleted = true; + unloadLand(); } void CSVRender::Cell::landAdded (const QModelIndex& parent, int start, int end) { - updateLand(); + mUpdateLand = true; + mLandDeleted = false; } void CSVRender::Cell::landTextureChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { - updateLand(); + mUpdateLand = true; } void CSVRender::Cell::landTextureAboutToBeRemoved (const QModelIndex& parent, int start, int end) { - updateLand(); + mUpdateLand = true; } void CSVRender::Cell::landTextureAdded (const QModelIndex& parent, int start, int end) { - updateLand(); + mUpdateLand = true; } void CSVRender::Cell::reloadAssets() diff --git a/apps/opencs/view/render/cell.hpp b/apps/opencs/view/render/cell.hpp index 101aebd58..444608688 100644 --- a/apps/opencs/view/render/cell.hpp +++ b/apps/opencs/view/render/cell.hpp @@ -57,6 +57,7 @@ namespace CSVRender bool mDeleted; int mSubMode; unsigned int mSubModeElementMask; + bool mUpdateLand, mLandDeleted; /// Ignored if cell does not have an object with the given ID. /// @@ -160,6 +161,8 @@ namespace CSVRender /// Erase all overrides and restore the visual representation of the cell to its /// true state. void reset (unsigned int elementMask); + + friend class CellNodeCallback; }; } diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index f597defd7..1d2cf7575 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -322,6 +322,15 @@ namespace ESM return mLandData && (mLandData->mDataLoaded & flags) == (flags & mDataTypes); } + void Land::setDataLoaded(int flags) + { + if (!mLandData) + mLandData = new LandData; + + mDataTypes |= flags; + mLandData->mDataLoaded |= flags; + } + Land::Land (const Land& land) : mFlags (land.mFlags), mX (land.mX), mY (land.mY), mPlugin (land.mPlugin), mContext (land.mContext), mDataTypes (land.mDataTypes), diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index d7b736f99..7be954b3e 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -135,6 +135,9 @@ struct Land /// @note We only check data types that *can* be loaded (present in mDataTypes) bool isDataLoaded(int flags) const; + /// Sets the flags and creates a LandData if needed + void setDataLoaded(int flags); + Land (const Land& land); Land& operator= (Land land); From 09e645a0e094950891ec19a7a4dc210a0fd11ae5 Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Sat, 9 Sep 2017 23:18:09 -0400 Subject: [PATCH 16/42] Fix careless mistake. --- components/esm/loadland.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index 1d2cf7575..083f27cef 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -238,10 +238,9 @@ namespace ESM // Copy data to target if no file if (mContext.filename.empty()) { - if (mLandData) + // Make sure there is data, and that it doesn't point to the same object. + if (mLandData && mLandData != target) *target = *mLandData; - else - target = new LandData; return; } From 2abf7f1752bfd06530425c5d33d6ae73653d90be Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Fri, 15 Sep 2017 12:19:12 -0400 Subject: [PATCH 17/42] Remove unnecessary cache dump --- components/terrain/world.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index 22c65b62c..335bb496b 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -77,7 +77,6 @@ void World::updateTextureFiltering() void World::clearAssociatedCaches() { - mTextureManager->clearCache(); mChunkManager->clearCache(); } From 50d9d9f78f4f66b2721986bff2151f61282af216 Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Sun, 17 Sep 2017 20:29:51 -0400 Subject: [PATCH 18/42] Get rid of some templates, exceptions instead of assert, and other small changes. --- apps/opencs/model/world/columnimp.cpp | 93 ++++++++++++++++++- apps/opencs/model/world/columnimp.hpp | 80 ++++------------ apps/opencs/model/world/columns.cpp | 2 +- apps/opencs/model/world/columns.hpp | 2 +- apps/opencs/model/world/commands.cpp | 2 +- apps/opencs/model/world/data.cpp | 8 +- apps/opencs/view/world/landtexturecreator.cpp | 4 +- components/esm/loadland.cpp | 9 +- 8 files changed, 120 insertions(+), 80 deletions(-) diff --git a/apps/opencs/model/world/columnimp.cpp b/apps/opencs/model/world/columnimp.cpp index 1ee7f8a03..6a19df0d5 100644 --- a/apps/opencs/model/world/columnimp.cpp +++ b/apps/opencs/model/world/columnimp.cpp @@ -1,7 +1,80 @@ #include "columnimp.hpp" +#include + namespace CSMWorld { + /* LandTextureNicknameColumn */ + LandTextureNicknameColumn::LandTextureNicknameColumn() + : Column(Columns::ColumnId_TextureNickname, ColumnBase::Display_String) + { + } + + QVariant LandTextureNicknameColumn::get(const Record& record) const + { + return QString::fromUtf8(record.get().mId.c_str()); + } + + void LandTextureNicknameColumn::set(Record& record, const QVariant& data) + { + LandTexture copy = record.get(); + copy.mId = data.toString().toUtf8().constData(); + record.setModified(copy); + } + + bool LandTextureNicknameColumn::isEditable() const + { + return true; + } + + /* LandTextureIndexColumn */ + LandTextureIndexColumn::LandTextureIndexColumn() + : Column(Columns::ColumnId_TextureIndex, ColumnBase::Display_Integer) + { + } + + QVariant LandTextureIndexColumn::get(const Record& record) const + { + return record.get().mIndex; + } + + bool LandTextureIndexColumn::isEditable() const + { + return false; + } + + /* LandPluginIndexColumn */ + LandPluginIndexColumn::LandPluginIndexColumn() + : Column(Columns::ColumnId_PluginIndex, ColumnBase::Display_Integer, 0) + { + } + + QVariant LandPluginIndexColumn::get(const Record& record) const + { + return record.get().mPlugin; + } + + bool LandPluginIndexColumn::isEditable() const + { + return false; + } + + /* LandTexturePluginIndexColumn */ + LandTexturePluginIndexColumn::LandTexturePluginIndexColumn() + : Column(Columns::ColumnId_PluginIndex, ColumnBase::Display_Integer, 0) + { + } + + QVariant LandTexturePluginIndexColumn::get(const Record& record) const + { + return record.get().mPluginIndex; + } + + bool LandTexturePluginIndexColumn::isEditable() const + { + return false; + } + /* LandMapLodColumn */ LandMapLodColumn::LandMapLodColumn() : Column(Columns::ColumnId_LandMapLodIndex, ColumnBase::Display_String, 0) @@ -30,7 +103,9 @@ namespace CSMWorld { QByteArray array = data.toByteArray(); const signed char* rawData = reinterpret_cast(array.data()); - assert (array.count() == Land::LAND_GLOBAL_MAP_LOD_SIZE); + + if (array.count() != Land::LAND_GLOBAL_MAP_LOD_SIZE) + throw std::runtime_error("invalid land map LOD data"); Land copy = record.get(); copy.setDataLoaded(Land::DATA_WNAM); @@ -76,7 +151,9 @@ namespace CSMWorld { QByteArray array = data.toByteArray(); const signed char* rawData = reinterpret_cast(array.data()); - assert (array.count() == Land::LAND_NUM_VERTS * 3); + + if (array.count() != Land::LAND_NUM_VERTS * 3) + throw std::runtime_error("invalid land normals data"); Land copy = record.get(); copy.setDataLoaded(Land::DATA_VNML); @@ -121,7 +198,9 @@ namespace CSMWorld { QByteArray array = data.toByteArray(); const float* rawData = reinterpret_cast(array.data()); - assert (array.count() == Land::LAND_NUM_VERTS * sizeof(float)); + + if (array.count() != Land::LAND_NUM_VERTS * sizeof(float)) + throw std::runtime_error("invalid land heights data"); Land copy = record.get(); copy.setDataLoaded(Land::DATA_VHGT); @@ -167,7 +246,9 @@ namespace CSMWorld { QByteArray array = data.toByteArray(); const unsigned char* rawData = reinterpret_cast(array.data()); - assert (array.count() == Land::LAND_NUM_VERTS * 3); + + if (array.count() != Land::LAND_NUM_VERTS * 3) + throw std::runtime_error("invalid land colours data"); Land copy = record.get(); copy.setDataLoaded(Land::DATA_VCLR); @@ -212,7 +293,9 @@ namespace CSMWorld { QByteArray array = data.toByteArray(); const uint16_t* rawData = reinterpret_cast(array.data()); - assert (array.count() == Land::LAND_NUM_TEXTURES * sizeof(uint16_t)); + + if (array.count() != Land::LAND_NUM_TEXTURES * sizeof(uint16_t)) + throw std::runtime_error("invalid land textures data"); Land copy = record.get(); copy.setDataLoaded(Land::DATA_VTEX); diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index 8e5908bd3..025b064c6 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -67,14 +67,14 @@ namespace CSMWorld inline QVariant StringIdColumn::get(const Record& record) const { const Land& land = record.get(); - return QString(Land::createUniqueRecordId(land.mX, land.mY).c_str()); + return QString::fromUtf8(Land::createUniqueRecordId(land.mX, land.mY).c_str()); } template<> inline QVariant StringIdColumn::get(const Record& record) const { const LandTexture& ltex = record.get(); - return QString(LandTexture::createUniqueRecordId(ltex.mPluginIndex, ltex.mIndex).c_str()); + return QString::fromUtf8(LandTexture::createUniqueRecordId(ltex.mPluginIndex, ltex.mIndex).c_str()); } template @@ -2435,78 +2435,38 @@ namespace CSMWorld } }; - template - struct TextureHandleColumn : public Column + struct LandTextureNicknameColumn : public Column { - TextureHandleColumn() - : Column (Columns::ColumnId_TextureHandle, ColumnBase::Display_String) - {} - - QVariant get(const Record& record) const override - { - return QString::fromUtf8(record.get().mId.c_str()); - } + LandTextureNicknameColumn(); - void set(Record& record, const QVariant& data) override - { - ESXRecordT copy = record.get(); - copy.mId = data.toString().toUtf8().constData(); - record.setModified(copy); - } - - bool isEditable() const override - { - return true; - } + QVariant get(const Record& record) const override; + void set(Record& record, const QVariant& data) override; + bool isEditable() const override; }; - template - struct TextureIndexColumn : public Column + struct LandTextureIndexColumn : public Column { - TextureIndexColumn() - : Column (Columns::ColumnId_TextureIndex, ColumnBase::Display_Integer) - {} + LandTextureIndexColumn(); - QVariant get(const Record& record) const override - { - return record.get().mIndex; - } - - bool isEditable() const override - { - return false; - } + QVariant get(const Record& record) const override; + bool isEditable() const override; }; - template - struct PluginIndexColumn : public Column + struct LandPluginIndexColumn : public Column { - PluginIndexColumn() - : Column (Columns::ColumnId_PluginIndex, ColumnBase::Display_Integer,0) - {} - - QVariant get(const Record& record) const override - { - return -1; - } + LandPluginIndexColumn(); - bool isEditable() const override - { - return false; - } + QVariant get(const Record& record) const override; + bool isEditable() const override; }; - template<> - inline QVariant PluginIndexColumn::get (const Record& record) const + struct LandTexturePluginIndexColumn : public Column { - return record.get().mPlugin; - } + LandTexturePluginIndexColumn(); - template<> - inline QVariant PluginIndexColumn::get (const Record& record) const - { - return record.get().mPluginIndex; - } + QVariant get(const Record& record) const override; + bool isEditable() const override; + }; struct LandMapLodColumn : public Column { diff --git a/apps/opencs/model/world/columns.cpp b/apps/opencs/model/world/columns.cpp index 63ccb6017..ec010ba36 100644 --- a/apps/opencs/model/world/columns.cpp +++ b/apps/opencs/model/world/columns.cpp @@ -330,7 +330,7 @@ namespace CSMWorld { ColumnId_WeatherChance, "Percent Chance" }, { ColumnId_Text, "Text" }, - { ColumnId_TextureHandle, "Texture Handle" }, + { ColumnId_TextureNickname, "Texture Nickname" }, { ColumnId_PluginIndex, "Plugin Index" }, { ColumnId_TextureIndex, "Texture Index" }, { ColumnId_LandMapLodIndex, "Land map height LOD" }, diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index 432597105..15018795c 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -329,7 +329,7 @@ namespace CSMWorld ColumnId_Text = 297, - ColumnId_TextureHandle = 298, + ColumnId_TextureNickname = 298, ColumnId_PluginIndex = 299, ColumnId_TextureIndex = 300, ColumnId_LandMapLodIndex = 301, diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 5bfcd6846..b654eb00d 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -48,7 +48,7 @@ CSMWorld::ImportLandTexturesCommand::ImportLandTexturesCommand(IdTable& landTabl , mLtexs(ltexTable) , mOldState(0) { - setText("Import land textures"); + setText("Copy land textures to current plugin"); } void CSMWorld::ImportLandTexturesCommand::redo() diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 0ffe5fa21..2216d5ca6 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -415,7 +415,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat mLand.addColumn (new StringIdColumn); mLand.addColumn (new RecordStateColumn); mLand.addColumn (new FixedRecordTypeColumn(UniversalId::Type_Land)); - mLand.addColumn (new PluginIndexColumn); + mLand.addColumn (new LandPluginIndexColumn); mLand.addColumn (new LandMapLodColumn); mLand.addColumn (new LandNormalsColumn); mLand.addColumn (new LandHeightsColumn); @@ -425,9 +425,9 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat mLandTextures.addColumn (new StringIdColumn(true)); mLandTextures.addColumn (new RecordStateColumn); mLandTextures.addColumn (new FixedRecordTypeColumn(UniversalId::Type_LandTexture)); - mLandTextures.addColumn (new TextureHandleColumn); - mLandTextures.addColumn (new PluginIndexColumn); - mLandTextures.addColumn (new TextureIndexColumn); + mLandTextures.addColumn (new LandTextureNicknameColumn); + mLandTextures.addColumn (new LandTexturePluginIndexColumn); + mLandTextures.addColumn (new LandTextureIndexColumn); mLandTextures.addColumn (new TextureColumn); mPathgrids.addColumn (new StringIdColumn); diff --git a/apps/opencs/view/world/landtexturecreator.cpp b/apps/opencs/view/world/landtexturecreator.cpp index fdabfb281..43d911e50 100644 --- a/apps/opencs/view/world/landtexturecreator.cpp +++ b/apps/opencs/view/world/landtexturecreator.cpp @@ -45,7 +45,7 @@ namespace CSVWorld CSMWorld::IdTable& table = dynamic_cast(*getData().getTableModel(getCollectionId())); - int column = table.findColumnIndex(CSMWorld::Columns::ColumnId_TextureHandle); + int column = table.findColumnIndex(CSMWorld::Columns::ColumnId_TextureNickname); mNameEdit->setText((table.data(table.getModelIndex(originId, column)).toString())); column = table.findColumnIndex(CSMWorld::Columns::ColumnId_TextureIndex); @@ -79,7 +79,7 @@ namespace CSVWorld GenericCreator::configureCreateCommand(command); CSMWorld::IdTable& table = dynamic_cast(*getData().getTableModel(getCollectionId())); - int column = table.findColumnIndex(CSMWorld::Columns::ColumnId_TextureHandle); + int column = table.findColumnIndex(CSMWorld::Columns::ColumnId_TextureNickname); command.addValue(column, mName.c_str()); } diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index 083f27cef..f3f72e88a 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -177,17 +177,14 @@ namespace ESM void Land::blank() { - if (mLandData) - { - delete mLandData; - } - mPlugin = 0; for (int i = 0; i < LAND_GLOBAL_MAP_LOD_SIZE; ++i) mWnam[0] = 0; - mLandData = new LandData; + if (!mLandData) + mLandData = new LandData; + mLandData->mHeightOffset = 0; for (int i = 0; i < LAND_NUM_VERTS; ++i) mLandData->mHeights[i] = 0; From 4921e7f5c1e2be8c4820bf8228ce83851ceccb16 Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Fri, 22 Sep 2017 21:29:40 -0400 Subject: [PATCH 19/42] Get rid of reinterpret cast. --- apps/opencs/model/world/columnimp.cpp | 129 +++++++++++++------------- apps/opencs/model/world/columnimp.hpp | 12 +++ apps/opencs/model/world/commands.cpp | 24 ++--- apps/opencs/model/world/commands.hpp | 6 +- 4 files changed, 93 insertions(+), 78 deletions(-) diff --git a/apps/opencs/model/world/columnimp.cpp b/apps/opencs/model/world/columnimp.cpp index 6a19df0d5..18da81b53 100644 --- a/apps/opencs/model/world/columnimp.cpp +++ b/apps/opencs/model/world/columnimp.cpp @@ -1,6 +1,7 @@ #include "columnimp.hpp" #include +#include namespace CSMWorld { @@ -86,33 +87,32 @@ namespace CSMWorld const int Size = Land::LAND_GLOBAL_MAP_LOD_SIZE; const Land& land = record.get(); + DataType values(Size, 0); + if (land.isDataLoaded(Land::DATA_WNAM)) { - // Note: original data is signed - const char* rawData = reinterpret_cast(&land.mWnam[0]); - return QByteArray(rawData, Size); - } - else - { - // Return a blank array - return QByteArray(Size, 0); + for (int i = 0; i < Size; ++i) + values[i] = land.mWnam[i]; } + + QVariant variant; + variant.setValue(values); + return variant; } void LandMapLodColumn::set(Record& record, const QVariant& data) { - QByteArray array = data.toByteArray(); - const signed char* rawData = reinterpret_cast(array.data()); + DataType values = data.value(); - if (array.count() != Land::LAND_GLOBAL_MAP_LOD_SIZE) + if (values.size() != Land::LAND_GLOBAL_MAP_LOD_SIZE) throw std::runtime_error("invalid land map LOD data"); Land copy = record.get(); copy.setDataLoaded(Land::DATA_WNAM); - for (int i = 0; i < array.count(); ++i) + for (int i = 0; i < values.size(); ++i) { - copy.mWnam[i] = rawData[i]; + copy.mWnam[i] = values[i]; } record.setModified(copy); @@ -134,33 +134,32 @@ namespace CSMWorld const int Size = Land::LAND_NUM_VERTS * 3; const Land& land = record.get(); + DataType values(Size, 0); + if (land.isDataLoaded(Land::DATA_VNML)) { - // Note: original data is signed - const char* rawData = reinterpret_cast(&land.getLandData()->mNormals[0]); - return QByteArray(rawData, Size); - } - else - { - // Return a blank array - return QByteArray(Size, 0); + for (int i = 0; i < Size; ++i) + values[i] = land.getLandData()->mNormals[i]; } + + QVariant variant; + variant.setValue(values); + return variant; } void LandNormalsColumn::set(Record& record, const QVariant& data) { - QByteArray array = data.toByteArray(); - const signed char* rawData = reinterpret_cast(array.data()); + DataType values = data.value(); - if (array.count() != Land::LAND_NUM_VERTS * 3) + if (values.size() != Land::LAND_NUM_VERTS * 3) throw std::runtime_error("invalid land normals data"); Land copy = record.get(); copy.setDataLoaded(Land::DATA_VNML); - for (int i = 0; i < array.count(); ++i) + for (int i = 0; i < values.size(); ++i) { - copy.getLandData()->mNormals[i] = rawData[i]; + copy.getLandData()->mNormals[i] = values[i]; } record.setModified(copy); @@ -179,36 +178,35 @@ namespace CSMWorld QVariant LandHeightsColumn::get(const Record& record) const { - const int Size = Land::LAND_NUM_VERTS * sizeof(float); + const int Size = Land::LAND_NUM_VERTS; const Land& land = record.get(); + DataType values(Size, 0); + if (land.isDataLoaded(Land::DATA_VHGT)) { - // Note: original data is float - const char* rawData = reinterpret_cast(&land.getLandData()->mHeights[0]); - return QByteArray(rawData, Size); - } - else - { - return QByteArray(Size, 0); + for (int i = 0; i < Size; ++i) + values[i] = land.getLandData()->mHeights[i]; } + + QVariant variant; + variant.setValue(values); + return variant; } void LandHeightsColumn::set(Record& record, const QVariant& data) { - QByteArray array = data.toByteArray(); - const float* rawData = reinterpret_cast(array.data()); + DataType values = data.value(); - if (array.count() != Land::LAND_NUM_VERTS * sizeof(float)) + if (values.size() != Land::LAND_NUM_VERTS) throw std::runtime_error("invalid land heights data"); Land copy = record.get(); copy.setDataLoaded(Land::DATA_VHGT); - int count = array.count() / sizeof(float); - for (int i = 0; i < count; ++i) + for (int i = 0; i < values.size(); ++i) { - copy.getLandData()->mHeights[i] = rawData[i]; + copy.getLandData()->mHeights[i] = values[i]; } record.setModified(copy); @@ -230,32 +228,32 @@ namespace CSMWorld const int Size = Land::LAND_NUM_VERTS * 3; const Land& land = record.get(); + DataType values(Size, 0); + if (land.isDataLoaded(Land::DATA_VCLR)) { - // Note: original data is unsigned char - const char* rawData = reinterpret_cast(&land.getLandData()->mColours[0]); - return QByteArray(rawData, Size); - } - else - { - return QByteArray(Size, 0); + for (int i = 0; i < Size; ++i) + values[i] = land.getLandData()->mColours[i]; } + + QVariant variant; + variant.setValue(values); + return variant; } void LandColoursColumn::set(Record& record, const QVariant& data) { - QByteArray array = data.toByteArray(); - const unsigned char* rawData = reinterpret_cast(array.data()); + DataType values = data.value(); - if (array.count() != Land::LAND_NUM_VERTS * 3) + if (values.size() != Land::LAND_NUM_VERTS * 3) throw std::runtime_error("invalid land colours data"); Land copy = record.get(); copy.setDataLoaded(Land::DATA_VCLR); - for (int i = 0; i < array.count(); ++i) + for (int i = 0; i < values.size(); ++i) { - copy.getLandData()->mColours[i] = rawData[i]; + copy.getLandData()->mColours[i] = values[i]; } record.setModified(copy); @@ -274,36 +272,35 @@ namespace CSMWorld QVariant LandTexturesColumn::get(const Record& record) const { - const int Size = Land::LAND_NUM_TEXTURES * sizeof(uint16_t); + const int Size = Land::LAND_NUM_TEXTURES; const Land& land = record.get(); + DataType values(Size, 0); + if (land.isDataLoaded(Land::DATA_VTEX)) { - // Note: original data is uint16_t - const char* rawData = reinterpret_cast(&land.getLandData()->mTextures[0]); - return QByteArray(rawData, Size); - } - else - { - return QByteArray(Size, 0); + for (int i = 0; i < Size; ++i) + values[i] = land.getLandData()->mTextures[i]; } + + QVariant variant; + variant.setValue(values); + return variant; } void LandTexturesColumn::set(Record& record, const QVariant& data) { - QByteArray array = data.toByteArray(); - const uint16_t* rawData = reinterpret_cast(array.data()); + DataType values = data.value(); - if (array.count() != Land::LAND_NUM_TEXTURES * sizeof(uint16_t)) + if (values.size() != Land::LAND_NUM_TEXTURES) throw std::runtime_error("invalid land textures data"); Land copy = record.get(); copy.setDataLoaded(Land::DATA_VTEX); - int count = array.count() / sizeof(uint16_t); - for (int i = 0; i < count; ++i) + for (int i = 0; i < values.size(); ++i) { - copy.getLandData()->mTextures[i] = rawData[i]; + copy.getLandData()->mTextures[i] = values[i]; } record.setModified(copy); diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index 025b064c6..d653ddece 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -2,10 +2,12 @@ #define CSM_WOLRD_COLUMNIMP_H #include +#include #include #include #include +#include #include #include @@ -2470,6 +2472,8 @@ namespace CSMWorld struct LandMapLodColumn : public Column { + using DataType = QVector; + LandMapLodColumn(); QVariant get(const Record& record) const override; @@ -2479,6 +2483,8 @@ namespace CSMWorld struct LandNormalsColumn : public Column { + using DataType = QVector; + LandNormalsColumn(); QVariant get(const Record& record) const override; @@ -2488,6 +2494,8 @@ namespace CSMWorld struct LandHeightsColumn : public Column { + using DataType = QVector; + LandHeightsColumn(); QVariant get(const Record& record) const override; @@ -2497,6 +2505,8 @@ namespace CSMWorld struct LandColoursColumn : public Column { + using DataType = QVector; + LandColoursColumn(); QVariant get(const Record& record) const override; @@ -2506,6 +2516,8 @@ namespace CSMWorld struct LandTexturesColumn : public Column { + using DataType = QVector; + LandTexturesColumn(); QVariant get(const Record& record) const override; diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index b654eb00d..79900c6c4 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -58,23 +58,21 @@ void CSMWorld::ImportLandTexturesCommand::redo() // Original data int textureColumn = mLands.findColumnIndex(Columns::ColumnId_LandTexturesIndex); - mOld = mLands.data(mLands.getModelIndex(getOriginId(), textureColumn)).toByteArray(); - const uint16_t* textureData = reinterpret_cast(mOld.data()); + mOld = mLands.data(mLands.getModelIndex(getOriginId(), textureColumn)).value(); // Need to make a copy so the old values can be looked up - QByteArray newTextureByteArray(mOld.data(), mOld.size()); - uint16_t* newTextureData = reinterpret_cast(newTextureByteArray.data()); + DataType copy(mOld); // Perform touch/copy/etc... onRedo(); // Find all indices used std::unordered_set texIndices; - for (int i = 0; i < Land::LAND_NUM_TEXTURES; ++i) + for (int i = 0; i < mOld.size(); ++i) { // All indices are offset by 1 for a default texture - if (textureData[i] > 0) - texIndices.insert(textureData[i] - 1); + if (mOld[i] > 0) + texIndices.insert(mOld[i] - 1); } std::vector oldTextures; @@ -97,8 +95,8 @@ void CSMWorld::ImportLandTexturesCommand::redo() for (int i = 0; i < Land::LAND_NUM_TEXTURES; ++i) { // All indices are offset by 1 for a default texture - if (textureData[i] == oldIndex + 1) - newTextureData[i] = newIndex + 1; + if (mOld[i] == oldIndex + 1) + copy[i] = newIndex + 1; } } } @@ -107,14 +105,18 @@ void CSMWorld::ImportLandTexturesCommand::redo() int stateColumn = mLands.findColumnIndex(Columns::ColumnId_Modification); mOldState = mLands.data(mLands.getModelIndex(getDestinationId(), stateColumn)).toInt(); - mLands.setData(mLands.getModelIndex(getDestinationId(), textureColumn), newTextureByteArray); + QVariant variant; + variant.setValue(copy); + mLands.setData(mLands.getModelIndex(getDestinationId(), textureColumn), variant); } void CSMWorld::ImportLandTexturesCommand::undo() { // Restore to previous int textureColumn = mLands.findColumnIndex(Columns::ColumnId_LandTexturesIndex); - mLands.setData(mLands.getModelIndex(getDestinationId(), textureColumn), mOld); + QVariant variant; + variant.setValue(mOld); + mLands.setData(mLands.getModelIndex(getDestinationId(), textureColumn), variant); int stateColumn = mLands.findColumnIndex(Columns::ColumnId_Modification); mLands.setData(mLands.getModelIndex(getDestinationId(), stateColumn), mOldState); diff --git a/apps/opencs/model/world/commands.hpp b/apps/opencs/model/world/commands.hpp index aab0c5410..0f9d3591c 100644 --- a/apps/opencs/model/world/commands.hpp +++ b/apps/opencs/model/world/commands.hpp @@ -3,12 +3,14 @@ #include "record.hpp" +#include #include #include #include #include #include +#include #include #include @@ -55,6 +57,8 @@ namespace CSMWorld protected: + using DataType = QVector; + virtual const std::string& getOriginId() const = 0; virtual const std::string& getDestinationId() const = 0; @@ -63,7 +67,7 @@ namespace CSMWorld IdTable& mLands; IdTable& mLtexs; - QByteArray mOld; + DataType mOld; int mOldState; std::vector mCreatedTextures; }; From 054e6a780e2c8fa4bbf1863751020ab036e5ad96 Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Fri, 22 Sep 2017 22:43:45 -0400 Subject: [PATCH 20/42] Use map for texture lookup. --- apps/opencs/model/world/idtable.cpp | 39 ++++++++++++++++------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index b41eea8f8..2c066ff6c 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -340,6 +341,15 @@ CSMWorld::LandTextureIdTable::ImportResults CSMWorld::LandTextureIdTable::import { ImportResults results; + // Map existing textures to ids + std::map reverseLookupMap; + for (int i = 0; i < idCollection()->getSize(); ++i) + { + auto& record = static_cast&>(idCollection()->getRecord(i)); + if (record.isModified()) + reverseLookupMap.emplace(record.get().mTexture, idCollection()->getId(i)); + } + for (const std::string& id : ids) { int plugin, index; @@ -354,8 +364,16 @@ CSMWorld::LandTextureIdTable::ImportResults CSMWorld::LandTextureIdTable::import continue; } - // Try a direct mapping to the current plugin first. Otherwise iterate until one is found. - // Iteration is deterministic to avoid duplicates. + // Look for a pre-existing record + auto& record = static_cast&>(idCollection()->getRecord(oldRow)); + auto searchIt = reverseLookupMap.find(record.get().mTexture); + if (searchIt != reverseLookupMap.end()) + { + results.recordMapping.push_back(std::make_pair(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(0, index); @@ -370,21 +388,8 @@ CSMWorld::LandTextureIdTable::ImportResults CSMWorld::LandTextureIdTable::import break; } - // Id is taken, check if same handle and texture. Note that mId is the handle. - const LandTexture& oldLtex = dynamic_cast&>(idCollection()->getRecord(oldRow)).get(); - const LandTexture& newLtex = dynamic_cast&>(idCollection()->getRecord(newRow)).get(); - if (oldLtex.mId == newLtex.mId && oldLtex.mTexture == newLtex.mTexture) - { - // It's a match - results.recordMapping.push_back(std::make_pair(id, newId)); - break; - } - - // Determine next index. Spread out the indices to reduce conflicts. - size_t MaxIndex = std::numeric_limits::max() - 1; - size_t Prime = (1 << 13) - 1; // A mersenne prime - - index = (index + Prime) % MaxIndex; + const size_t MaxIndex = std::numeric_limits::max() - 1; + index = (index + 1) % MaxIndex; } while (index != startIndex); } From 3981f79d38af642f423b4adb5666b98a596a24f9 Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Fri, 22 Sep 2017 22:59:50 -0400 Subject: [PATCH 21/42] Change flag for base land textures --- apps/opencs/model/world/idtable.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index 2c066ff6c..54d65ac3f 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -332,7 +332,7 @@ Qt::ItemFlags CSMWorld::LandTextureIdTable::flags(const QModelIndex& index) cons Qt::ItemFlags flags = IdTable::flags(index); if (!idCollection()->getRecord(index.row()).isModified()) - flags &= ~Qt::ItemIsEditable; + flags &= ~Qt::ItemIsEnabled; return flags; } From 1fe1ec63c43af800d3988081b802e690ad8d0a34 Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Fri, 22 Sep 2017 23:33:50 -0400 Subject: [PATCH 22/42] Fix Qt4 build --- apps/opencs/model/world/columnimp.hpp | 7 +++++++ apps/opencs/model/world/commands.hpp | 5 ++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index d653ddece..e36e386c9 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -2537,4 +2537,11 @@ namespace CSMWorld }; } +// This is required to access the type as a QVariant. +Q_DECLARE_METATYPE(CSMWorld::LandMapLodColumn::DataType) +//Q_DECLARE_METATYPE(CSMWorld::LandNormalsColumn::DataType) // Same as LandMapLodColumn::DataType +Q_DECLARE_METATYPE(CSMWorld::LandHeightsColumn::DataType) +Q_DECLARE_METATYPE(CSMWorld::LandColoursColumn::DataType) +Q_DECLARE_METATYPE(CSMWorld::LandTexturesColumn::DataType) + #endif diff --git a/apps/opencs/model/world/commands.hpp b/apps/opencs/model/world/commands.hpp index 0f9d3591c..be86dd508 100644 --- a/apps/opencs/model/world/commands.hpp +++ b/apps/opencs/model/world/commands.hpp @@ -3,17 +3,16 @@ #include "record.hpp" -#include #include #include #include #include #include -#include #include #include +#include "columnimp.hpp" #include "universalid.hpp" #include "nestedtablewrapper.hpp" @@ -57,7 +56,7 @@ namespace CSMWorld protected: - using DataType = QVector; + using DataType = LandTexturesColumn::DataType; virtual const std::string& getOriginId() const = 0; virtual const std::string& getDestinationId() const = 0; From dfcbee3ab162e5c7ff8809bb753dafc083848548 Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Sat, 30 Sep 2017 00:04:52 -0400 Subject: [PATCH 23/42] Ignore case when comparing textures, also add new textures to lookup map. --- apps/opencs/model/world/idtable.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index 54d65ac3f..bde5af3d9 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -1,5 +1,7 @@ #include "idtable.hpp" +#include +#include #include #include #include @@ -346,8 +348,10 @@ CSMWorld::LandTextureIdTable::ImportResults CSMWorld::LandTextureIdTable::import for (int i = 0; i < idCollection()->getSize(); ++i) { auto& record = static_cast&>(idCollection()->getRecord(i)); + std::string texture = record.get().mTexture; + std::transform(texture.begin(), texture.end(), texture.begin(), tolower); if (record.isModified()) - reverseLookupMap.emplace(record.get().mTexture, idCollection()->getId(i)); + reverseLookupMap.emplace(texture, idCollection()->getId(i)); } for (const std::string& id : ids) @@ -366,7 +370,9 @@ CSMWorld::LandTextureIdTable::ImportResults CSMWorld::LandTextureIdTable::import // Look for a pre-existing record auto& record = static_cast&>(idCollection()->getRecord(oldRow)); - auto searchIt = reverseLookupMap.find(record.get().mTexture); + std::string texture = record.get().mTexture; + std::transform(texture.begin(), texture.end(), texture.begin(), tolower); + auto searchIt = reverseLookupMap.find(texture); if (searchIt != reverseLookupMap.end()) { results.recordMapping.push_back(std::make_pair(id, searchIt->second)); @@ -385,6 +391,7 @@ CSMWorld::LandTextureIdTable::ImportResults CSMWorld::LandTextureIdTable::import cloneRecord(id, newId, UniversalId::Type_LandTexture); results.createdRecords.push_back(newId); results.recordMapping.push_back(std::make_pair(id, newId)); + reverseLookupMap.emplace(texture, newId); break; } From 137ea872d34683c8e2631b7b96b4fe9d716a2ec3 Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Mon, 2 Oct 2017 15:56:22 -0400 Subject: [PATCH 24/42] Hide base land textures. --- apps/opencs/CMakeLists.txt | 2 +- .../world/landtexturetableproxymodel.cpp | 21 ++++++++++++++++++ .../world/landtexturetableproxymodel.hpp | 22 +++++++++++++++++++ apps/opencs/view/world/table.cpp | 6 +++++ 4 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 apps/opencs/model/world/landtexturetableproxymodel.cpp create mode 100644 apps/opencs/model/world/landtexturetableproxymodel.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 36ff8cf67..f10525e08 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -18,7 +18,7 @@ opencs_hdrs_noqt (model/doc opencs_units (model/world - idtable idtableproxymodel regionmap data commanddispatcher idtablebase resourcetable nestedtableproxymodel idtree infotableproxymodel + idtable idtableproxymodel regionmap data commanddispatcher idtablebase resourcetable nestedtableproxymodel idtree infotableproxymodel landtexturetableproxymodel ) diff --git a/apps/opencs/model/world/landtexturetableproxymodel.cpp b/apps/opencs/model/world/landtexturetableproxymodel.cpp new file mode 100644 index 000000000..cf33fab9e --- /dev/null +++ b/apps/opencs/model/world/landtexturetableproxymodel.cpp @@ -0,0 +1,21 @@ +#include "landtexturetableproxymodel.hpp" + +#include "idtable.hpp" + +namespace CSMWorld +{ + LandTextureTableProxyModel::LandTextureTableProxyModel(QObject* parent) + : IdTableProxyModel(parent) + { + } + + bool LandTextureTableProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent) const + { + int columnIndex = mSourceModel->findColumnIndex(Columns::ColumnId_Modification); + QModelIndex index = mSourceModel->index(sourceRow, columnIndex); + if (mSourceModel->data(index).toInt() != RecordBase::State_ModifiedOnly) + return false; + + return IdTableProxyModel::filterAcceptsRow(sourceRow, sourceParent); + } +} diff --git a/apps/opencs/model/world/landtexturetableproxymodel.hpp b/apps/opencs/model/world/landtexturetableproxymodel.hpp new file mode 100644 index 000000000..bbedecb53 --- /dev/null +++ b/apps/opencs/model/world/landtexturetableproxymodel.hpp @@ -0,0 +1,22 @@ +#ifndef CSM_WORLD_LANDTEXTURETABLEPROXYMODEL_H +#define CSM_WORLD_LANDTEXTURETABLEPROXYMODEL_H + +#include "idtableproxymodel.hpp" + +namespace CSMWorld +{ + /// \brief Removes base records from filtered results. + class LandTextureTableProxyModel : public IdTableProxyModel + { + Q_OBJECT + public: + + LandTextureTableProxyModel(QObject* parent = nullptr); + + protected: + + bool filterAcceptsRow (int sourceRow, const QModelIndex& sourceParent) const override; + }; +} + +#endif diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 3cccaaa22..34ecd57d0 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -16,6 +16,7 @@ #include "../../model/world/idtableproxymodel.hpp" #include "../../model/world/idtablebase.hpp" #include "../../model/world/idtable.hpp" +#include "../../model/world/landtexturetableproxymodel.hpp" #include "../../model/world/record.hpp" #include "../../model/world/columns.hpp" #include "../../model/world/commanddispatcher.hpp" @@ -236,10 +237,15 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, bool isInfoTable = id.getType() == CSMWorld::UniversalId::Type_TopicInfos || id.getType() == CSMWorld::UniversalId::Type_JournalInfos; + bool isLtexTable = (id.getType() == CSMWorld::UniversalId::Type_LandTextures); if (isInfoTable) { mProxyModel = new CSMWorld::InfoTableProxyModel(id.getType(), this); } + else if (isLtexTable) + { + mProxyModel = new CSMWorld::LandTextureTableProxyModel (this); + } else { mProxyModel = new CSMWorld::IdTableProxyModel (this); From 2f5449a68c6ff2b0c3f7345e0d9746dd59eba490 Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Mon, 2 Oct 2017 16:13:40 -0400 Subject: [PATCH 25/42] Remove now unnecessary overrides. --- apps/opencs/model/world/idtable.cpp | 26 -------------------------- apps/opencs/model/world/idtable.hpp | 6 ------ 2 files changed, 32 deletions(-) diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index bde5af3d9..fcfc8577e 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -313,32 +313,6 @@ CSMWorld::LandTextureIdTable::LandTextureIdTable(CollectionBase* idCollection, u { } -QVariant CSMWorld::LandTextureIdTable::data(const QModelIndex& index, int role) const -{ - if (role==Qt::EditRole && !idCollection()->getRecord(index.row()).isModified()) - return QVariant(); - - return IdTable::data(index, role); -} - -bool CSMWorld::LandTextureIdTable::setData(const QModelIndex& index, const QVariant& value, int role) -{ - if (!idCollection()->getRecord(index.row()).isModified()) - return false; - else - return IdTable::setData(index, value, role); -} - -Qt::ItemFlags CSMWorld::LandTextureIdTable::flags(const QModelIndex& index) const -{ - Qt::ItemFlags flags = IdTable::flags(index); - - if (!idCollection()->getRecord(index.row()).isModified()) - flags &= ~Qt::ItemIsEnabled; - - return flags; -} - CSMWorld::LandTextureIdTable::ImportResults CSMWorld::LandTextureIdTable::importTextures(const std::vector& ids) { ImportResults results; diff --git a/apps/opencs/model/world/idtable.hpp b/apps/opencs/model/world/idtable.hpp index ccc5ac404..4136061e4 100644 --- a/apps/opencs/model/world/idtable.hpp +++ b/apps/opencs/model/world/idtable.hpp @@ -118,12 +118,6 @@ namespace CSMWorld LandTextureIdTable(CollectionBase* idCollection, unsigned int features=0); - QVariant data(const QModelIndex& index, int role=Qt::DisplayRole) const override; - - bool setData(const QModelIndex& index, const QVariant& value, int role) override; - - Qt::ItemFlags flags (const QModelIndex & index) const override; - /// Finds and maps/recreates the specified ids. ImportResults importTextures(const std::vector& ids); }; From d7a5622485a03ef6324470fcb84976882f444af7 Mon Sep 17 00:00:00 2001 From: scrawl <720642+scrawl@users.noreply.github.com> Date: Tue, 3 Oct 2017 09:16:46 +0000 Subject: [PATCH 26/42] Fix book window buttons overlap --- files/mygui/openmw_book.layout | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/files/mygui/openmw_book.layout b/files/mygui/openmw_book.layout index a7bea5eb5..c83c4982b 100644 --- a/files/mygui/openmw_book.layout +++ b/files/mygui/openmw_book.layout @@ -14,19 +14,15 @@ - - - - - - + + + + - - - - - - + + + + From c598f1313c4233af2aec6dcb92e4924408120961 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Tue, 3 Oct 2017 17:34:45 +0200 Subject: [PATCH 27/42] [macOS, CI] Update dependencies The following changes are included: - https://github.com/OpenMW/openmw-deps-mac/pull/37 - https://github.com/OpenMW/openmw-deps-mac/pull/38 --- CI/before_install.osx.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CI/before_install.osx.sh b/CI/before_install.osx.sh index 25e1c9517..86e8fb81b 100755 --- a/CI/before_install.osx.sh +++ b/CI/before_install.osx.sh @@ -6,5 +6,5 @@ brew outdated cmake || brew upgrade cmake brew outdated pkgconfig || brew upgrade pkgconfig brew install $macos_qt_formula -curl https://downloads.openmw.org/osx/dependencies/openmw-deps-5e144e2.zip -o ~/openmw-deps.zip +curl https://downloads.openmw.org/osx/dependencies/openmw-deps-c40905f.zip -o ~/openmw-deps.zip unzip ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null From 433028e8d7395db109920e0b74309388ddec8984 Mon Sep 17 00:00:00 2001 From: scrawl <720642+scrawl@users.noreply.github.com> Date: Tue, 3 Oct 2017 22:07:56 +0000 Subject: [PATCH 28/42] Fix GUI regressions --- apps/openmw/mwgui/dialogue.cpp | 1 + apps/openmw/mwgui/windowmanagerimp.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/dialogue.cpp b/apps/openmw/mwgui/dialogue.cpp index 8dd027c4a..e13e626ae 100644 --- a/apps/openmw/mwgui/dialogue.cpp +++ b/apps/openmw/mwgui/dialogue.cpp @@ -390,6 +390,7 @@ namespace MWGui { // No greetings found. The dialogue window should not be shown. // If this is a companion, we must show the companion window directly (used by BM_bear_be_unique). + MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Dialogue); if (isCompanion()) MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Companion, mPtr); return; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index e96743b91..fdf4da8a7 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -606,7 +606,7 @@ namespace MWGui mMap->setVisible(false); mStatsWindow->setVisible(false); mSpellWindow->setVisible(false); - mInventoryWindow->setVisible(getMode() == GM_Container || getMode() == GM_Barter); + mInventoryWindow->setVisible(getMode() == GM_Container || getMode() == GM_Barter || getMode() == GM_Companion); } GuiMode mode = mGuiModes.back(); From 1cb7ed5db102117b595b8d026d49a51a6244d068 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Tue, 3 Oct 2017 09:59:31 +0400 Subject: [PATCH 29/42] Use owned tooltips for items in containers correctly --- apps/openmw/mwgui/container.cpp | 20 ++++++--- apps/openmw/mwgui/containeritemmodel.cpp | 16 +++++++ apps/openmw/mwgui/containeritemmodel.hpp | 1 + apps/openmw/mwgui/itemmodel.cpp | 10 +++++ apps/openmw/mwgui/itemmodel.hpp | 6 +++ apps/openmw/mwgui/pickpocketitemmodel.cpp | 5 +++ apps/openmw/mwgui/pickpocketitemmodel.hpp | 2 + apps/openmw/mwgui/sortfilteritemmodel.cpp | 5 +++ apps/openmw/mwgui/sortfilteritemmodel.hpp | 1 + apps/openmw/mwgui/tooltips.cpp | 44 +++++++++---------- apps/openmw/mwgui/tooltips.hpp | 4 +- apps/openmw/mwgui/tradeitemmodel.cpp | 5 +++ apps/openmw/mwgui/tradeitemmodel.hpp | 2 + .../mwmechanics/mechanicsmanagerimp.cpp | 3 ++ 14 files changed, 92 insertions(+), 32 deletions(-) diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index a70d833c7..74ed4fff9 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -22,6 +22,7 @@ #include "itemview.hpp" #include "itemwidget.hpp" #include "inventoryitemmodel.hpp" +#include "containeritemmodel.hpp" #include "sortfilteritemmodel.hpp" #include "pickpocketitemmodel.hpp" #include "draganddrop.hpp" @@ -136,15 +137,22 @@ namespace MWGui bool loot = mPtr.getClass().isActor() && mPtr.getClass().getCreatureStats(mPtr).isDead(); - if (mPtr.getClass().isNpc() && !loot) + if (mPtr.getClass().hasInventoryStore(mPtr)) { - // we are stealing stuff - MWWorld::Ptr player = MWMechanics::getPlayer(); - mModel = new PickpocketItemModel(player, new InventoryItemModel(container), - !mPtr.getClass().getCreatureStats(mPtr).getKnockedDown()); + if (mPtr.getClass().isNpc() && !loot) + { + // we are stealing stuff + MWWorld::Ptr player = MWMechanics::getPlayer(); + mModel = new PickpocketItemModel(player, new InventoryItemModel(container), + !mPtr.getClass().getCreatureStats(mPtr).getKnockedDown()); + } + else + mModel = new InventoryItemModel(container); } else - mModel = new InventoryItemModel(container); + { + mModel = new ContainerItemModel(container); + } mDisposeCorpseButton->setVisible(loot); diff --git a/apps/openmw/mwgui/containeritemmodel.cpp b/apps/openmw/mwgui/containeritemmodel.cpp index d62cd16b0..479638672 100644 --- a/apps/openmw/mwgui/containeritemmodel.cpp +++ b/apps/openmw/mwgui/containeritemmodel.cpp @@ -6,8 +6,11 @@ #include "../mwworld/class.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/environment.hpp" +#include "../mwmechanics/actorutil.hpp" + namespace { @@ -47,6 +50,19 @@ ContainerItemModel::ContainerItemModel (const MWWorld::Ptr& source) mItemSources.push_back(source); } +bool ContainerItemModel::allowedToUseItems() const +{ + if (mItemSources.size() == 0) + return true; + + MWWorld::Ptr ptr = MWMechanics::getPlayer(); + MWWorld::Ptr victim; + + // Check if the player is allowed to use items from opened container + MWBase::MechanicsManager* mm = MWBase::Environment::get().getMechanicsManager(); + return mm->isAllowedToUse(ptr, mItemSources[0], victim); +} + ItemStack ContainerItemModel::getItem (ModelIndex index) { if (index < 0) diff --git a/apps/openmw/mwgui/containeritemmodel.hpp b/apps/openmw/mwgui/containeritemmodel.hpp index 7ced6ae34..c6ecafd46 100644 --- a/apps/openmw/mwgui/containeritemmodel.hpp +++ b/apps/openmw/mwgui/containeritemmodel.hpp @@ -17,6 +17,7 @@ namespace MWGui ContainerItemModel (const MWWorld::Ptr& source); + virtual bool allowedToUseItems() const; virtual ItemStack getItem (ModelIndex index); virtual ModelIndex getIndex (ItemStack item); virtual size_t getItemCount(); diff --git a/apps/openmw/mwgui/itemmodel.cpp b/apps/openmw/mwgui/itemmodel.cpp index 390bb0586..ffcf9075e 100644 --- a/apps/openmw/mwgui/itemmodel.cpp +++ b/apps/openmw/mwgui/itemmodel.cpp @@ -119,6 +119,11 @@ namespace MWGui return ret; } + bool ItemModel::allowedToUseItems() const + { + return true; + } + bool ItemModel::allowedToInsertItems() const { return true; @@ -135,6 +140,11 @@ namespace MWGui delete mSourceModel; } + bool ProxyItemModel::allowedToUseItems() const + { + return mSourceModel->allowedToUseItems(); + } + MWWorld::Ptr ProxyItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner) { return mSourceModel->copyItem (item, count, setNewOwner); diff --git a/apps/openmw/mwgui/itemmodel.hpp b/apps/openmw/mwgui/itemmodel.hpp index 20955b206..bc6be8023 100644 --- a/apps/openmw/mwgui/itemmodel.hpp +++ b/apps/openmw/mwgui/itemmodel.hpp @@ -70,6 +70,9 @@ namespace MWGui virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false) = 0; virtual void removeItem (const ItemStack& item, size_t count) = 0; + /// Is the player allowed to use items from this item model? (default true) + virtual bool allowedToUseItems() const; + /// Is the player allowed to insert items into this model? (default true) virtual bool allowedToInsertItems() const; @@ -85,6 +88,9 @@ namespace MWGui public: ProxyItemModel(); virtual ~ProxyItemModel(); + + bool allowedToUseItems() const; + virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false); virtual void removeItem (const ItemStack& item, size_t count); virtual ModelIndex getIndex (ItemStack item); diff --git a/apps/openmw/mwgui/pickpocketitemmodel.cpp b/apps/openmw/mwgui/pickpocketitemmodel.cpp index 238fb5913..ec4b378a0 100644 --- a/apps/openmw/mwgui/pickpocketitemmodel.cpp +++ b/apps/openmw/mwgui/pickpocketitemmodel.cpp @@ -26,6 +26,11 @@ namespace MWGui } } + bool PickpocketItemModel::allowedToUseItems() const + { + return false; + } + ItemStack PickpocketItemModel::getItem (ModelIndex index) { if (index < 0) diff --git a/apps/openmw/mwgui/pickpocketitemmodel.hpp b/apps/openmw/mwgui/pickpocketitemmodel.hpp index 61f0569b5..1b08ec485 100644 --- a/apps/openmw/mwgui/pickpocketitemmodel.hpp +++ b/apps/openmw/mwgui/pickpocketitemmodel.hpp @@ -11,6 +11,8 @@ namespace MWGui { public: PickpocketItemModel (const MWWorld::Ptr& thief, ItemModel* sourceModel, bool hideItems=true); + + virtual bool allowedToUseItems() const; virtual ItemStack getItem (ModelIndex index); virtual size_t getItemCount(); virtual void update(); diff --git a/apps/openmw/mwgui/sortfilteritemmodel.cpp b/apps/openmw/mwgui/sortfilteritemmodel.cpp index 45aa261df..e294ebe07 100644 --- a/apps/openmw/mwgui/sortfilteritemmodel.cpp +++ b/apps/openmw/mwgui/sortfilteritemmodel.cpp @@ -159,6 +159,11 @@ namespace MWGui mSourceModel = sourceModel; } + bool SortFilterItemModel::allowedToUseItems() const + { + return mSourceModel->allowedToUseItems(); + } + void SortFilterItemModel::addDragItem (const MWWorld::Ptr& dragItem, size_t count) { mDragItems.push_back(std::make_pair(dragItem, count)); diff --git a/apps/openmw/mwgui/sortfilteritemmodel.hpp b/apps/openmw/mwgui/sortfilteritemmodel.hpp index 4731cbb8a..6ddb019b0 100644 --- a/apps/openmw/mwgui/sortfilteritemmodel.hpp +++ b/apps/openmw/mwgui/sortfilteritemmodel.hpp @@ -15,6 +15,7 @@ namespace MWGui bool filterAccepts (const ItemStack& item); + bool allowedToUseItems() const; virtual ItemStack getItem (ModelIndex index); virtual size_t getItemCount(); diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index a9930db4f..af6bf4726 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -120,7 +120,7 @@ namespace MWGui if (info.caption.empty()) info.caption=mFocusObject.getCellRef().getRefId(); info.icon=""; - tooltipSize = createToolTip(info, true); + tooltipSize = createToolTip(info, checkOwned()); } else tooltipSize = getToolTipViaPtr(mFocusObject.getRefData().getCount(), true); @@ -186,22 +186,24 @@ namespace MWGui ToolTipInfo info; info.text = data.caption; info.notes = data.notes; - tooltipSize = createToolTip(info, false); + tooltipSize = createToolTip(info); } else if (type == "ItemPtr") { mFocusObject = *focus->getUserData(); - tooltipSize = getToolTipViaPtr(mFocusObject.getRefData().getCount(), false); + bool isAllowedToUse = checkOwned(); + tooltipSize = getToolTipViaPtr(mFocusObject.getRefData().getCount(), false, !isAllowedToUse); } else if (type == "ItemModelIndex") { std::pair pair = *focus->getUserData >(); mFocusObject = pair.second->getItem(pair.first).mBase; - tooltipSize = getToolTipViaPtr(pair.second->getItem(pair.first).mCount, false); + bool isAllowedToUse = pair.second->allowedToUseItems(); + tooltipSize = getToolTipViaPtr(pair.second->getItem(pair.first).mCount, false, !isAllowedToUse); } else if (type == "ToolTipInfo") { - tooltipSize = createToolTip(*focus->getUserData(), false); + tooltipSize = createToolTip(*focus->getUserData()); } else if (type == "AvatarItemSelection") { @@ -244,7 +246,7 @@ namespace MWGui info.text = "#{sSchool}: " + sSchoolNames[school]; } info.effects = effects; - tooltipSize = createToolTip(info, false); + tooltipSize = createToolTip(info); } else if (type == "Layout") { @@ -298,7 +300,7 @@ namespace MWGui { if (!mFocusObject.isEmpty()) { - MyGUI::IntSize tooltipSize = getToolTipViaPtr(mFocusObject.getRefData().getCount()); + MyGUI::IntSize tooltipSize = getToolTipViaPtr(mFocusObject.getRefData().getCount(), true, checkOwned()); setCoord(viewSize.width/2 - tooltipSize.width/2, std::max(0, int(mFocusToolTipY*viewSize.height - tooltipSize.height)), @@ -332,7 +334,7 @@ namespace MWGui update(mFrameDuration); } - MyGUI::IntSize ToolTips::getToolTipViaPtr (int count, bool image) + MyGUI::IntSize ToolTips::getToolTipViaPtr (int count, bool image, bool isOwned) { // this the maximum width of the tooltip before it starts word-wrapping setCoord(0, 0, 300, 300); @@ -351,7 +353,7 @@ namespace MWGui ToolTipInfo info = object.getToolTipInfo(mFocusObject, count); if (!image) info.icon = ""; - tooltipSize = createToolTip(info, true); + tooltipSize = createToolTip(info, isOwned); } return tooltipSize; @@ -359,27 +361,21 @@ namespace MWGui bool ToolTips::checkOwned() { - if(!mFocusObject.isEmpty()) - { - MWWorld::Ptr ptr = MWMechanics::getPlayer(); - MWWorld::Ptr victim; - - MWBase::MechanicsManager* mm = MWBase::Environment::get().getMechanicsManager(); - bool allowed = mm->isAllowedToUse(ptr, mFocusObject, victim); - - return !allowed; - } - else - { + if(mFocusObject.isEmpty()) return false; - } + + MWWorld::Ptr ptr = MWMechanics::getPlayer(); + MWWorld::Ptr victim; + + MWBase::MechanicsManager* mm = MWBase::Environment::get().getMechanicsManager(); + return !mm->isAllowedToUse(ptr, mFocusObject, victim); } - MyGUI::IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info, bool isFocusObject) + MyGUI::IntSize ToolTips::createToolTip(const MWGui::ToolTipInfo& info, bool isOwned) { mDynamicToolTipBox->setVisible(true); - if((mShowOwned == 1 || mShowOwned == 3) && isFocusObject && checkOwned()) + if((mShowOwned == 1 || mShowOwned == 3) && isOwned) mDynamicToolTipBox->changeWidgetSkin(MWBase::Environment::get().getWindowManager()->isGuiMode() ? "HUD_Box_NoTransp_Owned" : "HUD_Box_Owned"); else mDynamicToolTipBox->changeWidgetSkin(MWBase::Environment::get().getWindowManager()->isGuiMode() ? "HUD_Box_NoTransp" : "HUD_Box"); diff --git a/apps/openmw/mwgui/tooltips.hpp b/apps/openmw/mwgui/tooltips.hpp index 2793e6544..1ef473b5a 100644 --- a/apps/openmw/mwgui/tooltips.hpp +++ b/apps/openmw/mwgui/tooltips.hpp @@ -98,10 +98,10 @@ namespace MWGui MWWorld::Ptr mFocusObject; - MyGUI::IntSize getToolTipViaPtr (int count, bool image=true); + MyGUI::IntSize getToolTipViaPtr (int count, bool image = true, bool isOwned = false); ///< @return requested tooltip size - MyGUI::IntSize createToolTip(const ToolTipInfo& info, bool isFocusObject); + MyGUI::IntSize createToolTip(const ToolTipInfo& info, bool isOwned = false); ///< @return requested tooltip size /// @param isFocusObject Is the object this tooltips originates from mFocusObject? diff --git a/apps/openmw/mwgui/tradeitemmodel.cpp b/apps/openmw/mwgui/tradeitemmodel.cpp index ae3feb61c..a26294a0e 100644 --- a/apps/openmw/mwgui/tradeitemmodel.cpp +++ b/apps/openmw/mwgui/tradeitemmodel.cpp @@ -15,6 +15,11 @@ namespace MWGui mSourceModel = sourceModel; } + bool TradeItemModel::allowedToUseItems() const + { + return true; + } + ItemStack TradeItemModel::getItem (ModelIndex index) { if (index < 0) diff --git a/apps/openmw/mwgui/tradeitemmodel.hpp b/apps/openmw/mwgui/tradeitemmodel.hpp index 1bfee9b2a..cdb949c49 100644 --- a/apps/openmw/mwgui/tradeitemmodel.hpp +++ b/apps/openmw/mwgui/tradeitemmodel.hpp @@ -15,6 +15,8 @@ namespace MWGui public: TradeItemModel (ItemModel* sourceModel, const MWWorld::Ptr& merchant); + bool allowedToUseItems() const; + virtual ItemStack getItem (ModelIndex index); virtual size_t getItemCount(); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index da98e7f5f..2c7b6a500 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -842,6 +842,9 @@ namespace MWMechanics bool MechanicsManager::isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, MWWorld::Ptr& victim) { + if (target.isEmpty()) + return true; + const MWWorld::CellRef& cellref = target.getCellRef(); // there is no harm to use unlocked doors if (target.getClass().isDoor() && cellref.getLockLevel() <= 0 && ptr.getCellRef().getTrap().empty()) From 7c68ed04b2bb458d02fd6b57dd1f1b9a3187ab48 Mon Sep 17 00:00:00 2001 From: scrawl <720642+scrawl@users.noreply.github.com> Date: Wed, 4 Oct 2017 17:03:23 +0200 Subject: [PATCH 30/42] Don't require modal windows to be removed in the same order they were added --- apps/openmw/mwgui/windowmanagerimp.cpp | 24 ++++++++++++++---------- apps/openmw/mwgui/windowmanagerimp.hpp | 2 +- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index fdf4da8a7..a9ca9bd19 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -865,7 +865,7 @@ namespace MWGui window->onFrame(frameDuration); } if (!mCurrentModals.empty()) - mCurrentModals.top()->onFrame(frameDuration); + mCurrentModals.back()->onFrame(frameDuration); mKeyboardNavigation->onFrame(); @@ -1727,9 +1727,9 @@ namespace MWGui { if (!mCurrentModals.empty()) { - if (!mCurrentModals.top()->exit()) + if (!mCurrentModals.back()->exit()) return; - mCurrentModals.top()->setVisible(false); + mCurrentModals.back()->setVisible(false); } } @@ -1738,7 +1738,7 @@ namespace MWGui if (mCurrentModals.empty()) mKeyboardNavigation->saveFocus(getMode()); - mCurrentModals.push(input); + mCurrentModals.push_back(input); mKeyboardNavigation->restoreFocus(-1); mKeyboardNavigation->setModalWindow(input->mMainWidget); @@ -1747,17 +1747,21 @@ namespace MWGui void WindowManager::removeCurrentModal(WindowModal* input) { - // Only remove the top if it matches the current pointer. A lot of things hide their visibility before showing it, - //so just popping the top would cause massive issues. if(!mCurrentModals.empty()) { - if(input == mCurrentModals.top()) + if(input == mCurrentModals.back()) { - mCurrentModals.pop(); + mCurrentModals.pop_back(); mKeyboardNavigation->saveFocus(-1); } else - std::cout << " warning: modal widget " << input << " " << typeid(input).name() << " not found " << std::endl; + { + auto found = std::find(mCurrentModals.begin(), mCurrentModals.end(), input); + if (found != mCurrentModals.end()) + mCurrentModals.erase(found); + else + std::cerr << " warning: can't find modal window " << input << std::endl; + } } if (mCurrentModals.empty()) { @@ -1765,7 +1769,7 @@ namespace MWGui mKeyboardNavigation->restoreFocus(getMode()); } else - mKeyboardNavigation->setModalWindow(mCurrentModals.top()->mMainWidget); + mKeyboardNavigation->setModalWindow(mCurrentModals.back()->mMainWidget); } void WindowManager::onVideoKeyPressed(MyGUI::Widget *_sender, MyGUI::KeyCode _key, MyGUI::Char _char) diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index 612470a7e..bdd2aa094 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -401,7 +401,7 @@ namespace MWGui MWWorld::Ptr mSelectedEnchantItem; MWWorld::Ptr mSelectedWeapon; - std::stack mCurrentModals; + std::vector mCurrentModals; // Markers placed manually by the player. Must be shared between both map views (the HUD map and the map window). CustomMarkerCollection mCustomMarkers; From 49a0922f1b4b26c5dd421ab326fb6529b55d79eb Mon Sep 17 00:00:00 2001 From: scrawl <720642+scrawl@users.noreply.github.com> Date: Wed, 4 Oct 2017 17:08:09 +0200 Subject: [PATCH 31/42] Remove 'visibility mask' gui feature which did not end up being useful --- apps/openmw/mwgui/windowmanagerimp.cpp | 29 +++++++++++--------------- apps/openmw/mwgui/windowmanagerimp.hpp | 1 - 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index a9ca9bd19..9a7a7437a 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -613,18 +613,18 @@ namespace MWGui mInventoryWindow->setTrading(mode == GM_Barter); - // For the inventory mode, compute the effective set of windows to show. - // This is controlled both by what windows the - // user has opened/closed (the 'shown' variable) and by what - // windows we are allowed to show (the 'allowed' var.) - int eff = mShown & mAllowed & ~mForceHidden; - mGuiModeStates[GM_Inventory].mVisibilityMask.resize(4); - mGuiModeStates[GM_Inventory].mVisibilityMask[0] = eff & GW_Map; - mGuiModeStates[GM_Inventory].mVisibilityMask[1] = eff & GW_Inventory; - mGuiModeStates[GM_Inventory].mVisibilityMask[2] = eff & GW_Magic; - mGuiModeStates[GM_Inventory].mVisibilityMask[3] = eff & GW_Stats; if (getMode() == GM_Inventory) - mGuiModeStates[GM_Inventory].update(true); + { + // For the inventory mode, compute the effective set of windows to show. + // This is controlled both by what windows the + // user has opened/closed (the 'shown' variable) and by what + // windows we are allowed to show (the 'allowed' var.) + int eff = mShown & mAllowed & ~mForceHidden; + mMap->setVisible(eff & GW_Map); + mInventoryWindow->setVisible(eff & GW_Inventory); + mSpellWindow->setVisible(eff & GW_Magic); + mStatsWindow->setVisible(eff & GW_Stats); + } switch (mode) { @@ -2063,12 +2063,7 @@ namespace MWGui void WindowManager::GuiModeState::update(bool visible) { for (unsigned int i=0; isetVisible(visible && visibilityMask); - } + mWindows[i]->setVisible(visible); } } diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index bdd2aa094..c8bc8d21e 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -477,7 +477,6 @@ namespace MWGui void update(bool visible); std::vector mWindows; - std::vector mVisibilityMask; // optional, may be used to temporarily exclude windows from this mode. std::string mCloseSound; std::string mOpenSound; From e4f0f7157a796ccf647b6e841b06604ce8f0bf69 Mon Sep 17 00:00:00 2001 From: scrawl <720642+scrawl@users.noreply.github.com> Date: Wed, 4 Oct 2017 17:08:52 +0200 Subject: [PATCH 32/42] Fix pinned windows not being updated --- apps/openmw/mwgui/windowmanagerimp.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 9a7a7437a..eae706ec3 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -864,6 +864,14 @@ namespace MWGui for (WindowBase* window : state.mWindows) window->onFrame(frameDuration); } + else + { + // update pinned windows if visible + for (WindowBase* window : mGuiModeStates[GM_Inventory].mWindows) + if (window->isVisible()) + window->onFrame(frameDuration); + } + if (!mCurrentModals.empty()) mCurrentModals.back()->onFrame(frameDuration); From a4737d84178e0f7ae58ed9364d648d1e9c4c687f Mon Sep 17 00:00:00 2001 From: scrawl <720642+scrawl@users.noreply.github.com> Date: Wed, 4 Oct 2017 17:26:23 +0200 Subject: [PATCH 33/42] Use MyGUI frame events for ScreenFader --- apps/openmw/mwgui/screenfader.cpp | 10 +++++++++- apps/openmw/mwgui/screenfader.hpp | 3 ++- apps/openmw/mwgui/windowmanagerimp.cpp | 6 ------ 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwgui/screenfader.cpp b/apps/openmw/mwgui/screenfader.cpp index d1118848b..9a9a1ae01 100644 --- a/apps/openmw/mwgui/screenfader.cpp +++ b/apps/openmw/mwgui/screenfader.cpp @@ -2,6 +2,7 @@ #include #include +#include namespace MWGui { @@ -80,6 +81,8 @@ namespace MWGui , mFactor(1.f) , mRepeat(false) { + MyGUI::Gui::getInstance().eventFrameStart += MyGUI::newDelegate(this, &ScreenFader::onFrameStart); + mMainWidget->setSize(MyGUI::RenderManager::getInstance().getViewSize()); MyGUI::ImageBox* imageBox = mMainWidget->castType(false); @@ -94,7 +97,12 @@ namespace MWGui } } - void ScreenFader::update(float dt) + ScreenFader::~ScreenFader() + { + MyGUI::Gui::getInstance().eventFrameStart -= MyGUI::newDelegate(this, &ScreenFader::onFrameStart); + } + + void ScreenFader::onFrameStart(float dt) { if (!mQueue.empty()) { diff --git a/apps/openmw/mwgui/screenfader.hpp b/apps/openmw/mwgui/screenfader.hpp index 88dd0c57b..79bea30e5 100644 --- a/apps/openmw/mwgui/screenfader.hpp +++ b/apps/openmw/mwgui/screenfader.hpp @@ -36,8 +36,9 @@ namespace MWGui { public: ScreenFader(const std::string & texturePath, const std::string& layout = "openmw_screen_fader.layout", const MyGUI::FloatCoord& texCoordOverride = MyGUI::FloatCoord(0,0,1,1)); + ~ScreenFader(); - void update(float dt); + void onFrameStart(float dt); void fadeIn(const float time, float delay=0); void fadeOut(const float time, float delay=0); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index eae706ec3..504bef964 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -894,12 +894,6 @@ namespace MWGui mHud->onFrame(frameDuration); - if (mWerewolfFader) - mWerewolfFader->update(frameDuration); - mBlindnessFader->update(frameDuration); - mHitFader->update(frameDuration); - mScreenFader->update(frameDuration); - mDebugWindow->onFrame(frameDuration); if (mCharGen) From 12510efab7bd2e29f23e5054356718a8847bf763 Mon Sep 17 00:00:00 2001 From: scrawl <720642+scrawl@users.noreply.github.com> Date: Wed, 4 Oct 2017 17:39:19 +0200 Subject: [PATCH 34/42] Fade screen out during loading screen (when travelling) --- apps/openmw/mwgui/loadingscreen.cpp | 12 ++++++++++-- apps/openmw/mwgui/loadingscreen.hpp | 1 + apps/openmw/mwgui/travelwindow.cpp | 2 ++ files/mygui/openmw_layers.xml | 1 + 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index f22d825dd..4753c39f2 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -50,6 +50,8 @@ namespace MWGui mBackgroundImage = MyGUI::Gui::getInstance().createWidgetReal("ImageBox", 0,0,1,1, MyGUI::Align::Stretch, "Menu"); + mSceneImage = MyGUI::Gui::getInstance().createWidgetReal("ImageBox", 0,0,1,1, + MyGUI::Align::Stretch, "Scene"); findSplashScreens(); } @@ -110,6 +112,7 @@ namespace MWGui { WindowBase::setVisible(visible); mBackgroundImage->setVisible(visible); + mSceneImage->setVisible(visible); } double LoadingScreen::getTargetFrameRate() const @@ -214,8 +217,11 @@ namespace MWGui // TODO: add option (filename pattern?) to use image aspect ratio instead of 4:3 // we can't do this by default, because the Morrowind splash screens are 1024x1024, but should be displayed as 4:3 bool stretch = Settings::Manager::getBool("stretch menu background", "GUI"); + mBackgroundImage->setVisible(true); mBackgroundImage->setBackgroundImage(randomSplash, true, stretch); } + mSceneImage->setBackgroundImage(""); + mSceneImage->setVisible(false); } void LoadingScreen::setProgressRange (size_t range) @@ -292,9 +298,11 @@ namespace MWGui mViewer->getCamera()->setInitialDrawCallback(new CopyFramebufferToTextureCallback(mTexture)); mBackgroundImage->setBackgroundImage(""); + mBackgroundImage->setVisible(false); - mBackgroundImage->setRenderItemTexture(mGuiTexture.get()); - mBackgroundImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f)); + mSceneImage->setRenderItemTexture(mGuiTexture.get()); + mSceneImage->getSubWidgetMain()->_setUVSet(MyGUI::FloatRect(0.f, 0.f, 1.f, 1.f)); + mSceneImage->setVisible(true); } void LoadingScreen::draw() diff --git a/apps/openmw/mwgui/loadingscreen.hpp b/apps/openmw/mwgui/loadingscreen.hpp index 2f8831fdc..8536972a3 100644 --- a/apps/openmw/mwgui/loadingscreen.hpp +++ b/apps/openmw/mwgui/loadingscreen.hpp @@ -72,6 +72,7 @@ namespace MWGui MyGUI::TextBox* mLoadingText; MyGUI::ScrollBar* mProgressBar; BackgroundImage* mBackgroundImage; + BackgroundImage* mSceneImage; std::vector mSplashScreens; diff --git a/apps/openmw/mwgui/travelwindow.cpp b/apps/openmw/mwgui/travelwindow.cpp index 7c7cdb63e..7586bb66a 100644 --- a/apps/openmw/mwgui/travelwindow.cpp +++ b/apps/openmw/mwgui/travelwindow.cpp @@ -178,6 +178,8 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Travel); MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode(); + MWBase::Environment::get().getWindowManager()->fadeScreenOut(1); + // Teleports any followers, too. MWWorld::ActionTeleport action(interior ? cellname : "", pos, true); action.execute(player); diff --git a/files/mygui/openmw_layers.xml b/files/mygui/openmw_layers.xml index cd8a9f760..5e4a74978 100644 --- a/files/mygui/openmw_layers.xml +++ b/files/mygui/openmw_layers.xml @@ -1,6 +1,7 @@ + From d94235e3a727bcec1d800cd4184bd70da142d306 Mon Sep 17 00:00:00 2001 From: scrawl <720642+scrawl@users.noreply.github.com> Date: Wed, 4 Oct 2017 17:47:42 +0200 Subject: [PATCH 35/42] Update the saves list/preview image when character selection changes to make it more convenient to flip through characters with the keyboard --- apps/openmw/mwgui/savegamedialog.cpp | 13 ++++++++++--- apps/openmw/mwgui/savegamedialog.hpp | 1 + 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 423dbb036..6a290bef1 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -50,7 +50,8 @@ namespace MWGui mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onOkButtonClicked); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onCancelButtonClicked); mDeleteButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onDeleteButtonClicked); - mCharacterSelection->eventComboAccept += MyGUI::newDelegate(this, &SaveGameDialog::onCharacterSelected); + mCharacterSelection->eventComboChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onCharacterSelected); + mCharacterSelection->eventComboAccept += MyGUI::newDelegate(this, &SaveGameDialog::onCharacterAccept); mSaveList->eventListChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onSlotSelected); mSaveList->eventListMouseItemActivate += MyGUI::newDelegate(this, &SaveGameDialog::onSlotMouseClick); mSaveList->eventListSelectAccept += MyGUI::newDelegate(this, &SaveGameDialog::onSlotActivated); @@ -132,6 +133,8 @@ namespace MWGui mSaveNameEdit->setCaption (""); if (mSaving) MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mSaveNameEdit); + else + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mSaveList); center(); @@ -322,6 +325,12 @@ namespace MWGui fillSaveList(); } + void SaveGameDialog::onCharacterAccept(MyGUI::ComboBox* sender, size_t pos) + { + // Give key focus to save list so we can confirm the selection with Enter + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mSaveList); + } + void SaveGameDialog::fillSaveList() { mSaveList->removeAllItems(); @@ -336,8 +345,6 @@ namespace MWGui { mSaveList->setIndexSelected(0); onSlotSelected(mSaveList, 0); - // Give key focus to save list so we can confirm the selection with Enter - MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mSaveList); } else onSlotSelected(mSaveList, MyGUI::ITEM_NONE); diff --git a/apps/openmw/mwgui/savegamedialog.hpp b/apps/openmw/mwgui/savegamedialog.hpp index a51124705..0a87b6600 100644 --- a/apps/openmw/mwgui/savegamedialog.hpp +++ b/apps/openmw/mwgui/savegamedialog.hpp @@ -29,6 +29,7 @@ namespace MWGui void onOkButtonClicked (MyGUI::Widget* sender); void onDeleteButtonClicked (MyGUI::Widget* sender); void onCharacterSelected (MyGUI::ComboBox* sender, size_t pos); + void onCharacterAccept(MyGUI::ComboBox* sender, size_t pos); // Slot selected (mouse click or arrow keys) void onSlotSelected (MyGUI::ListBox* sender, size_t pos); // Slot activated (double click or enter key) From cd437f094d9fbeb77850dbaa0d84fb25ca749151 Mon Sep 17 00:00:00 2001 From: scrawl <720642+scrawl@users.noreply.github.com> Date: Thu, 5 Oct 2017 18:35:09 +0200 Subject: [PATCH 36/42] Fix exiting savegamedialog with 'esc' when no game is running --- apps/openmw/mwgui/mainmenu.cpp | 5 +++++ apps/openmw/mwgui/mainmenu.hpp | 2 ++ apps/openmw/mwgui/windowmanagerimp.cpp | 4 ++-- apps/openmw/mwinput/inputmanagerimp.cpp | 4 +--- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index 00e4e30a5..5017b8893 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -210,6 +210,11 @@ namespace MWGui } } + bool MainMenu::exit() + { + return MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_Running; + } + void MainMenu::updateMenu() { setCoord(0,0, mWidth, mHeight); diff --git a/apps/openmw/mwgui/mainmenu.hpp b/apps/openmw/mwgui/mainmenu.hpp index 1beb9ee16..82553d22e 100644 --- a/apps/openmw/mwgui/mainmenu.hpp +++ b/apps/openmw/mwgui/mainmenu.hpp @@ -38,6 +38,8 @@ namespace MWGui void onFrame(float dt); + bool exit(); + private: const VFS::Manager* mVFS; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 504bef964..a81c3a614 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -750,8 +750,8 @@ namespace MWGui if (!window->exit()) { // unable to exit window, but give access to main menu - if (!MyGUI::InputManager::getInstance().isModalAny()) - pushGuiMode (MWGui::GM_MainMenu); + if (!MyGUI::InputManager::getInstance().isModalAny() && getMode() != GM_MainMenu) + pushGuiMode (GM_MainMenu); return; } } diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index e6acbbd52..06ebeb0b8 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -241,9 +241,7 @@ namespace MWInput switch (action) { case A_GameMenu: - if(!(MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_Running - && MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_MainMenu)) - toggleMainMenu (); + toggleMainMenu (); break; case A_Screenshot: screenshot(); From 8c0790580a80431d9f31d7beed07f8e61beae121 Mon Sep 17 00:00:00 2001 From: scrawl <720642+scrawl@users.noreply.github.com> Date: Thu, 5 Oct 2017 18:39:28 +0200 Subject: [PATCH 37/42] Add screen fading for exterior cell transitions --- apps/openmw/mwworld/scene.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index c83165239..0a27cf257 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -580,10 +580,16 @@ namespace MWWorld MWBase::Environment::get().getWorld()->positionToIndex (position.pos[0], position.pos[1], x, y); + if (changeEvent) + MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.5); + changeCellGrid(x, y, changeEvent); CellStore* current = MWBase::Environment::get().getWorld()->getExterior(x, y); changePlayerCell(current, position, adjustPlayerPos); + + if (changeEvent) + MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.5); } CellStore* Scene::getCurrentCell () From 842a42ee664b30dee6e9bd74c4a70de1bfca19ea Mon Sep 17 00:00:00 2001 From: scrawl <720642+scrawl@users.noreply.github.com> Date: Thu, 5 Oct 2017 18:39:58 +0200 Subject: [PATCH 38/42] Fix non-top level Windows accepting key focus --- apps/openmw/mwgui/keyboardnavigation.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwgui/keyboardnavigation.cpp b/apps/openmw/mwgui/keyboardnavigation.cpp index 2b06de295..d27185204 100644 --- a/apps/openmw/mwgui/keyboardnavigation.cpp +++ b/apps/openmw/mwgui/keyboardnavigation.cpp @@ -12,6 +12,11 @@ namespace MWGui { +bool shouldAcceptKeyFocus(MyGUI::Widget* w) +{ + return w && !w->castType(false) && w->getInheritedEnabled() && w->getInheritedVisible() && w->getVisible() && w->getEnabled(); +} + /// Recursively get all child widgets that accept keyboard input void getKeyFocusWidgets(MyGUI::Widget* parent, std::vector& results) { @@ -24,18 +29,13 @@ void getKeyFocusWidgets(MyGUI::Widget* parent, std::vector& resu MyGUI::Widget* w = enumerator.current(); if (!w->getVisible() || !w->getEnabled()) continue; - if (w->getNeedKeyFocus()) + if (w->getNeedKeyFocus() && shouldAcceptKeyFocus(w)) results.push_back(w); else getKeyFocusWidgets(w, results); } } -bool shouldAcceptKeyFocus(MyGUI::Widget* w) -{ - return w && !w->castType(false) && w->getInheritedEnabled() && w->getInheritedVisible() && w->getVisible() && w->getEnabled(); -} - KeyboardNavigation::KeyboardNavigation() : mCurrentFocus(nullptr) , mModalWindow(nullptr) From e2afd3690c0f2495a1efd12576e5ed91c3dab932 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 6 Oct 2017 10:54:25 +0400 Subject: [PATCH 39/42] Remove item by id from InventoryStore --- apps/openmw/mwmechanics/actors.cpp | 6 +----- apps/openmw/mwworld/inventorystore.cpp | 19 +++++++++++++++++++ apps/openmw/mwworld/inventorystore.hpp | 3 +++ 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 0715eb177..a144911c5 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -70,11 +70,7 @@ void adjustBoundItem (const std::string& item, bool bound, const MWWorld::Ptr& a } } else - { - MWWorld::Ptr itemPtr = actor.getClass().getInventoryStore(actor).search(item); - if (!itemPtr.isEmpty()) - actor.getClass().getInventoryStore(actor).remove(itemPtr, 1, actor, true); - } + actor.getClass().getInventoryStore(actor).remove(item, 1, actor, true); } class CheckActorCommanded : public MWMechanics::EffectSourceVisitor diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index b599b3583..77141f269 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -654,11 +654,30 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::getSelectedEnchantItem( return mSelectedEnchantItem; } +int MWWorld::InventoryStore::remove(const std::string& itemId, int count, const Ptr& actor) +{ + return remove(itemId, count, actor, false); +} + int MWWorld::InventoryStore::remove(const Ptr& item, int count, const Ptr& actor) { return remove(item, count, actor, false); } +int MWWorld::InventoryStore::remove(const std::string& itemId, int count, const Ptr& actor, bool equipReplacement) +{ + int toRemove = count; + + for (ContainerStoreIterator iter(begin()); iter != end() && toRemove > 0; ++iter) + if (Misc::StringUtils::ciEqual(iter->getCellRef().getRefId(), itemId)) + toRemove -= remove(*iter, toRemove, actor, equipReplacement); + + flagAsModified(); + + // number of removed items + return count - toRemove; +} + int MWWorld::InventoryStore::remove(const Ptr& item, int count, const Ptr& actor, bool equipReplacement) { int retCount = ContainerStore::remove(item, count, actor); diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index 851abf408..49991c164 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -177,6 +177,9 @@ namespace MWWorld virtual bool stacks (const ConstPtr& ptr1, const ConstPtr& ptr2) const; ///< @return true if the two specified objects can stack with each other + virtual int remove(const std::string& itemId, int count, const Ptr& actor); + virtual int remove(const std::string& itemId, int count, const Ptr& actor, bool equipReplacement); + virtual int remove(const Ptr& item, int count, const Ptr& actor); virtual int remove(const Ptr& item, int count, const Ptr& actor, bool equipReplacement); ///< Remove \a count item(s) designated by \a item from this inventory. From ad27e0f945ed2bafca11b4fdeff5cefd01e52863 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Fri, 6 Oct 2017 11:38:27 +0400 Subject: [PATCH 40/42] Fix owned tooltip in the spellwindow --- apps/openmw/mwgui/tooltips.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index af6bf4726..b2991a034 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -191,8 +191,7 @@ namespace MWGui else if (type == "ItemPtr") { mFocusObject = *focus->getUserData(); - bool isAllowedToUse = checkOwned(); - tooltipSize = getToolTipViaPtr(mFocusObject.getRefData().getCount(), false, !isAllowedToUse); + tooltipSize = getToolTipViaPtr(mFocusObject.getRefData().getCount(), false, checkOwned()); } else if (type == "ItemModelIndex") { From 280578154222556506c3c293c63e7ea5b7a0b4b7 Mon Sep 17 00:00:00 2001 From: scrawl <720642+scrawl@users.noreply.github.com> Date: Fri, 6 Oct 2017 16:18:57 +0000 Subject: [PATCH 41/42] Fix a crash when exit() already hides the window (Fixes #4148) --- apps/openmw/mwgui/windowmanagerimp.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index a81c3a614..ccdd6916c 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1729,9 +1729,10 @@ namespace MWGui { if (!mCurrentModals.empty()) { - if (!mCurrentModals.back()->exit()) + WindowModal* window = mCurrentModals.back(); + if (!window->exit()) return; - mCurrentModals.back()->setVisible(false); + window->setVisible(false); } } From b14404e9cccbb3c8a91d7f5e6476b15d30be1850 Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Fri, 6 Oct 2017 20:05:06 -0400 Subject: [PATCH 42/42] Fix region colors --- apps/opencs/model/world/columnimp.hpp | 17 ++++------------- apps/opencs/view/world/colordelegate.cpp | 15 +++++++++------ 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index e36e386c9..f1025acb9 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -692,32 +692,23 @@ namespace CSMWorld } }; - /// \todo QColor is a GUI class and should not be in model. Need to think of an alternative - /// solution. template struct MapColourColumn : public Column { - /// \todo Replace Display_Integer with something that displays the colour value more directly. MapColourColumn() : Column (Columns::ColumnId_MapColour, ColumnBase::Display_Colour) {} virtual QVariant get (const Record& record) const { - int colour = record.get().mMapColor; - - return QColor (colour & 0xff, (colour>>8) & 0xff, (colour>>16) & 0xff); + return record.get().mMapColor; } virtual void set (Record& record, const QVariant& data) { - ESXRecordT record2 = record.get(); - - QColor colour = data.value(); - - record2.mMapColor = (colour.blue() << 16) | (colour.green() << 8) | colour.red(); - - record.setModified (record2); + ESXRecordT copy = record.get(); + copy.mMapColor = data.toInt(); + record.setModified (copy); } virtual bool isEditable() const diff --git a/apps/opencs/view/world/colordelegate.cpp b/apps/opencs/view/world/colordelegate.cpp index 1a89fc675..15a07b42c 100644 --- a/apps/opencs/view/world/colordelegate.cpp +++ b/apps/opencs/view/world/colordelegate.cpp @@ -5,29 +5,32 @@ #include "../widget/coloreditor.hpp" -CSVWorld::ColorDelegate::ColorDelegate(CSMWorld::CommandDispatcher *dispatcher, - CSMDoc::Document& document, +CSVWorld::ColorDelegate::ColorDelegate(CSMWorld::CommandDispatcher *dispatcher, + CSMDoc::Document& document, QObject *parent) : CommandDelegate(dispatcher, document, parent) {} -void CSVWorld::ColorDelegate::paint(QPainter *painter, +void CSVWorld::ColorDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { + int colorInt = index.data().toInt(); + QColor color(colorInt & 0xff, (colorInt >> 8) & 0xff, (colorInt >> 16) & 0xff); + QRect coloredRect(option.rect.x() + qRound(option.rect.width() / 4.0), option.rect.y() + qRound(option.rect.height() / 4.0), option.rect.width() / 2, option.rect.height() / 2); painter->save(); - painter->fillRect(coloredRect, index.data().value()); + painter->fillRect(coloredRect, color); painter->setPen(Qt::black); painter->drawRect(coloredRect); painter->restore(); } -CSVWorld::CommandDelegate *CSVWorld::ColorDelegateFactory::makeDelegate(CSMWorld::CommandDispatcher *dispatcher, - CSMDoc::Document &document, +CSVWorld::CommandDelegate *CSVWorld::ColorDelegateFactory::makeDelegate(CSMWorld::CommandDispatcher *dispatcher, + CSMDoc::Document &document, QObject *parent) const { return new ColorDelegate(dispatcher, document, parent);