forked from mirror/openmw-tes3mp
LTEX importing
This commit is contained in:
parent
5c3e90da88
commit
97d0fd756a
8 changed files with 204 additions and 5 deletions
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include <cmath>
|
||||
#include <sstream>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
|
@ -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<uint16_t*>(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<uint16_t*>(newTextureByteArray.data());
|
||||
|
||||
// Find all indices used
|
||||
std::unordered_set<int> 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<std::string> oldTextures;
|
||||
for (int index : texIndices)
|
||||
{
|
||||
oldTextures.push_back(LandTexture::createUniqueRecordId(oldPlugin, index));
|
||||
}
|
||||
|
||||
// Import the textures, replace old values
|
||||
LandTextureIdTable::ImportResults results = dynamic_cast<LandTextureIdTable&>(mLtexs).importTextures(oldTextures);
|
||||
mCreatedTextures = std::move(results.createdRecords);
|
||||
for (const auto& it : results.recordMapping)
|
||||
{
|
||||
int plugin = 0, newIndex = 0, oldIndex = 0;
|
||||
LandTexture::parseUniqueRecordId(it.first, plugin, oldIndex);
|
||||
LandTexture::parseUniqueRecordId(it.second, plugin, newIndex);
|
||||
|
||||
if (newIndex != oldIndex)
|
||||
{
|
||||
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)
|
||||
|
|
|
@ -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<RecordBase> mOld;
|
||||
std::vector<std::string> mCreatedTextures;
|
||||
|
||||
bool mChanged;
|
||||
};
|
||||
|
||||
class ModifyCommand : public QUndoCommand
|
||||
{
|
||||
QAbstractItemModel *mModel;
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
#include "idtable.hpp"
|
||||
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <components/esm/cellid.hpp>
|
||||
|
||||
#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<std::string>& 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<const Record<LandTexture>&>(idCollection()->getRecord(oldRow)).get();
|
||||
const LandTexture& newLtex = dynamic_cast<const Record<LandTexture>&>(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<uint16_t>::max();
|
||||
size_t Prime = (1 << 19) - 1; // A mersenne prime
|
||||
size_t K = 2154;
|
||||
|
||||
index = ((K * index) % Prime) % MaxIndex;
|
||||
} while (true);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
|
|
@ -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<std::string,std::string>;
|
||||
|
||||
/// The newly added records
|
||||
std::vector<std::string> createdRecords;
|
||||
/// The 1st string is the original id, the 2nd is the mapped id
|
||||
std::vector<StringPair> recordMapping;
|
||||
};
|
||||
|
||||
LandTextureIdTable(CollectionBase* idCollection, unsigned int features=0);
|
||||
|
||||
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<std::string>& ids);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#include "land.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#include "landtexture.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include <limits>
|
||||
|
||||
#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<CSMWorld::UniversalId>& ids)
|
||||
{
|
||||
// Combine multiple touch commands into one "macro" command
|
||||
std::unique_ptr<QUndoCommand> macro(new QUndoCommand());
|
||||
macro->setText("Touch records");
|
||||
|
||||
CSMWorld::IdTable& lands = dynamic_cast<CSMWorld::IdTable&>(*getData().getTableModel(CSMWorld::UniversalId::Type_Lands));
|
||||
CSMWorld::IdTable& ltexs = dynamic_cast<CSMWorld::IdTable&>(*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();
|
||||
|
|
|
@ -23,6 +23,8 @@ namespace CSVWorld
|
|||
|
||||
void cloneMode(const std::string& originId, const CSMWorld::UniversalId::Type type) override;
|
||||
|
||||
void touch(const std::vector<CSMWorld::UniversalId>& ids) override;
|
||||
|
||||
void focus() override;
|
||||
|
||||
void reset() override;
|
||||
|
|
Loading…
Reference in a new issue