From 4f05f2bddfb65354f6843656ab9d145f89074145 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 8 Jul 2013 13:12:50 +0200 Subject: [PATCH 01/36] basic region map; non-interactive for now and working with dummy data instead of real cell/region records --- apps/opencs/CMakeLists.txt | 4 +- apps/opencs/model/world/data.cpp | 3 + apps/opencs/model/world/regionmap.cpp | 73 +++++++++++++++++++++ apps/opencs/model/world/regionmap.hpp | 38 +++++++++++ apps/opencs/model/world/universalid.cpp | 2 + apps/opencs/model/world/universalid.hpp | 3 +- apps/opencs/view/doc/view.cpp | 9 +++ apps/opencs/view/doc/view.hpp | 2 + apps/opencs/view/world/regionmapsubview.cpp | 29 ++++++++ apps/opencs/view/world/regionmapsubview.hpp | 27 ++++++++ apps/opencs/view/world/subviews.cpp | 3 + 11 files changed, 190 insertions(+), 3 deletions(-) create mode 100644 apps/opencs/model/world/regionmap.cpp create mode 100644 apps/opencs/model/world/regionmap.hpp create mode 100644 apps/opencs/view/world/regionmapsubview.cpp create mode 100644 apps/opencs/view/world/regionmapsubview.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index cec3756a3c..c7006c4d78 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 + idtable idtableproxymodel regionmap ) @@ -57,7 +57,7 @@ opencs_hdrs_noqt (view/doc opencs_units (view/world - table tablesubview scriptsubview util + table tablesubview scriptsubview util regionmapsubview ) opencs_units_noqt (view/world diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 3921754425..4a38604b1a 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -11,6 +11,7 @@ #include "idtable.hpp" #include "columns.hpp" +#include "regionmap.hpp" void CSMWorld::Data::addModel (QAbstractItemModel *model, UniversalId::Type type1, UniversalId::Type type2) @@ -161,6 +162,8 @@ CSMWorld::Data::Data() : mRefs (mCells) addModel (new IdTable (&mReferenceables), UniversalId::Type_Referenceables, UniversalId::Type_Referenceable); addModel (new IdTable (&mRefs), UniversalId::Type_References, UniversalId::Type_Reference); + + addModel (new RegionMap, UniversalId::Type_RegionMap, UniversalId::Type_None); } CSMWorld::Data::~Data() diff --git a/apps/opencs/model/world/regionmap.cpp b/apps/opencs/model/world/regionmap.cpp new file mode 100644 index 0000000000..8fbed9c5a0 --- /dev/null +++ b/apps/opencs/model/world/regionmap.cpp @@ -0,0 +1,73 @@ + +#include "regionmap.hpp" + +#include + +std::pair CSMWorld::RegionMap::getIndex (const QModelIndex& index) const +{ + return std::make_pair (index.column()+mMin.first, index.row()+mMin.second); +} + +CSMWorld::RegionMap::RegionMap() +{ + // setting up some placeholder regions + mMap.insert (std::make_pair (std::make_pair (0, 0), "a")); + mMap.insert (std::make_pair (std::make_pair (1, 1), "b")); + mMap.insert (std::make_pair (std::make_pair (1, 0), "a")); + mMin = std::make_pair (0, 0); + mMax = std::make_pair (2, 2); + mColours.insert (std::make_pair ("a", 0xff0000ff)); + mColours.insert (std::make_pair ("b", 0x00ff00ff)); +} + +int CSMWorld::RegionMap::rowCount (const QModelIndex& parent) const +{ + if (parent.isValid()) + return 0; + + return mMax.second-mMin.second; +} + +int CSMWorld::RegionMap::columnCount (const QModelIndex& parent) const +{ + if (parent.isValid()) + return 0; + + return mMax.first-mMin.first; +} + +QVariant CSMWorld::RegionMap::data (const QModelIndex& index, int role) const +{ + if (role==Qt::SizeHintRole) + return QSize (16, 16); + + if (role==Qt::BackgroundRole) + { + /// \todo GUI class in non-GUI code. Needs to be addressed eventually. + + std::map, std::string>::const_iterator cell = + mMap.find (getIndex (index)); + + if (cell!=mMap.end()) + { + std::map::const_iterator iter = mColours.find (cell->second); + + if (iter!=mColours.end()) + return QBrush ( + QColor (iter->second>>24, (iter->second>>16) & 255, (iter->second>>8) & 255, + iter->second & 255)); + } + + return QBrush (Qt::DiagCrossPattern); + } + + return QVariant(); +} + +Qt::ItemFlags CSMWorld::RegionMap::flags (const QModelIndex& index) const +{ + if (mMap.find (getIndex (index))!=mMap.end()) + return Qt::ItemIsSelectable | Qt::ItemIsEnabled; + + return 0; +} \ No newline at end of file diff --git a/apps/opencs/model/world/regionmap.hpp b/apps/opencs/model/world/regionmap.hpp new file mode 100644 index 0000000000..78792fad15 --- /dev/null +++ b/apps/opencs/model/world/regionmap.hpp @@ -0,0 +1,38 @@ +#ifndef CSM_WOLRD_REGIONMAP_H +#define CSM_WOLRD_REGIONMAP_H + +#include +#include + +#include + +namespace CSMWorld +{ + /// \brief Model for the region map + /// + /// This class does not holds any record data (other than for the purpose of buffering). + class RegionMap : public QAbstractTableModel + { + std::map, std::string> mMap; ///< cell index, region + std::pair mMin; ///< inclusive + std::pair mMax; ///< exclusive + std::map mColours; ///< region ID, colour (RGBA) + + std::pair getIndex (const QModelIndex& index) const; + ///< Translates a Qt model index into a cell index (which can contain negative components) + + public: + + RegionMap(); + + virtual int rowCount (const QModelIndex& parent = QModelIndex()) const; + + virtual int columnCount (const QModelIndex& parent = QModelIndex()) const; + + virtual QVariant data (const QModelIndex& index, int role = Qt::DisplayRole) const; + + virtual Qt::ItemFlags flags (const QModelIndex& index) const; + }; +} + +#endif diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp index b5efe6e220..7e07a2989b 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -33,6 +33,8 @@ namespace "Referenceables" }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_References, "References" }, + { CSMWorld::UniversalId::Class_NonRecord, CSMWorld::UniversalId::Type_RegionMap, + "Region Map" }, { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0 } // end marker }; diff --git a/apps/opencs/model/world/universalid.hpp b/apps/opencs/model/world/universalid.hpp index bd6fdeb000..8ca8765e71 100644 --- a/apps/opencs/model/world/universalid.hpp +++ b/apps/opencs/model/world/universalid.hpp @@ -81,7 +81,8 @@ namespace CSMWorld Type_Static, Type_Weapon, Type_References, - Type_Reference + Type_Reference, + Type_RegionMap }; private: diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index bcc0203504..cdc161bd84 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -139,6 +139,10 @@ void CSVDoc::View::setupWorldMenu() QAction *references = new QAction (tr ("References"), this); connect (references, SIGNAL (triggered()), this, SLOT (addReferencesSubView())); world->addAction (references); + + QAction *regionMap = new QAction (tr ("Region Map"), this); + connect (regionMap, SIGNAL (triggered()), this, SLOT (addRegionMapSubView())); + world->addAction (regionMap); } void CSVDoc::View::setupUi() @@ -363,6 +367,11 @@ void CSVDoc::View::addReferencesSubView() addSubView (CSMWorld::UniversalId::Type_References); } +void CSVDoc::View::addRegionMapSubView() +{ + addSubView (CSMWorld::UniversalId::Type_RegionMap); +} + void CSVDoc::View::abortOperation (int type) { mDocument->abortOperation (type); diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index b3c4ae22ab..ff81f8223d 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -151,6 +151,8 @@ namespace CSVDoc void addReferencesSubView(); + void addRegionMapSubView(); + void showUserSettings(); }; } diff --git a/apps/opencs/view/world/regionmapsubview.cpp b/apps/opencs/view/world/regionmapsubview.cpp new file mode 100644 index 0000000000..b82c1afb54 --- /dev/null +++ b/apps/opencs/view/world/regionmapsubview.cpp @@ -0,0 +1,29 @@ + +#include "regionmapsubview.hpp" + +#include +#include + +CSVWorld::RegionMapSubView::RegionMapSubView (CSMWorld::UniversalId universalId, + CSMDoc::Document& document) +: CSVDoc::SubView (universalId) +{ + mTable = new QTableView (this); + + mTable->verticalHeader()->hide(); + mTable->horizontalHeader()->hide(); + + mTable->setSelectionMode (QAbstractItemView::ExtendedSelection); + + mTable->setModel (document.getData().getTableModel (universalId)); + + mTable->resizeColumnsToContents(); + mTable->resizeRowsToContents(); + + setWidget (mTable); +} + +void CSVWorld::RegionMapSubView::setEditLock (bool locked) +{ + +} \ No newline at end of file diff --git a/apps/opencs/view/world/regionmapsubview.hpp b/apps/opencs/view/world/regionmapsubview.hpp new file mode 100644 index 0000000000..1655107af3 --- /dev/null +++ b/apps/opencs/view/world/regionmapsubview.hpp @@ -0,0 +1,27 @@ +#ifndef CSV_WORLD_REGIONMAPSUBVIEW_H +#define CSV_WORLD_REGIONMAPSUBVIEW_H + +#include "../doc/subview.hpp" + +class QTableView; + +namespace CSMDoc +{ + class Document; +} + +namespace CSVWorld +{ + class RegionMapSubView : public CSVDoc::SubView + { + QTableView *mTable; + + public: + + RegionMapSubView (CSMWorld::UniversalId universalId, CSMDoc::Document& document); + + virtual void setEditLock (bool locked); + }; +} + +#endif diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp index cd98ed4e06..1c06ea2f60 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -6,6 +6,7 @@ #include "tablesubview.hpp" #include "dialoguesubview.hpp" #include "scriptsubview.hpp" +#include "regionmapsubview.hpp" void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) { @@ -38,6 +39,8 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) manager.add (CSMWorld::UniversalId::Type_Script, new CSVDoc::SubViewFactory); + manager.add (CSMWorld::UniversalId::Type_RegionMap, new CSVDoc::SubViewFactory); + // manager.add (CSMWorld::UniversalId::Type_Global, // new CSVDoc::SubViewFactoryWithCreateFlag (true)); } \ No newline at end of file From 6159724b04e72809ca55c350d21251716cb37ab8 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 8 Jul 2013 16:53:08 +0200 Subject: [PATCH 02/36] build region map from records (only initial build; updates not implemented yet) --- apps/opencs/model/world/data.cpp | 15 ++- apps/opencs/model/world/regionmap.cpp | 127 ++++++++++++++++++++++++-- apps/opencs/model/world/regionmap.hpp | 24 ++++- 3 files changed, 154 insertions(+), 12 deletions(-) diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 4a38604b1a..d54b3ac161 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -162,8 +162,6 @@ CSMWorld::Data::Data() : mRefs (mCells) addModel (new IdTable (&mReferenceables), UniversalId::Type_Referenceables, UniversalId::Type_Referenceable); addModel (new IdTable (&mRefs), UniversalId::Type_References, UniversalId::Type_Reference); - - addModel (new RegionMap, UniversalId::Type_RegionMap, UniversalId::Type_None); } CSMWorld::Data::~Data() @@ -307,7 +305,20 @@ QAbstractItemModel *CSMWorld::Data::getTableModel (const UniversalId& id) std::map::iterator iter = mModelIndex.find (id.getType()); if (iter==mModelIndex.end()) + { + // try creating missing (secondary) tables on the fly + // + // Note: We create these tables here so we don't have to deal with them during load/initial + // construction of the ESX data where no update signals are available. + if (id.getType()==UniversalId::Type_RegionMap) + { + RegionMap *table = 0; + addModel (table = new RegionMap (*this), UniversalId::Type_RegionMap, + UniversalId::Type_None); + return table; + } throw std::logic_error ("No table model available for " + id.toString()); + } return iter->second; } diff --git a/apps/opencs/model/world/regionmap.cpp b/apps/opencs/model/world/regionmap.cpp index 8fbed9c5a0..01ec979d29 100644 --- a/apps/opencs/model/world/regionmap.cpp +++ b/apps/opencs/model/world/regionmap.cpp @@ -3,21 +3,95 @@ #include +#include "data.hpp" +#include "universalid.hpp" + std::pair CSMWorld::RegionMap::getIndex (const QModelIndex& index) const { return std::make_pair (index.column()+mMin.first, index.row()+mMin.second); } -CSMWorld::RegionMap::RegionMap() +void CSMWorld::RegionMap::buildRegions (Data& data) { - // setting up some placeholder regions - mMap.insert (std::make_pair (std::make_pair (0, 0), "a")); - mMap.insert (std::make_pair (std::make_pair (1, 1), "b")); - mMap.insert (std::make_pair (std::make_pair (1, 0), "a")); - mMin = std::make_pair (0, 0); - mMax = std::make_pair (2, 2); - mColours.insert (std::make_pair ("a", 0xff0000ff)); - mColours.insert (std::make_pair ("b", 0x00ff00ff)); + const IdCollection& regions = data.getRegions(); + + int size = regions.getSize(); + + for (int i=0; i& region = regions.getRecord (i); + + if (!region.isDeleted()) + mColours.insert (std::make_pair (region.get().mId, region.get().mMapColor)); + } +} + +void CSMWorld::RegionMap::buildMap (Data& data) +{ + const IdCollection& cells = data.getCells(); + + int size = cells.getSize(); + + mMin = mMax = std::make_pair (0, 0); + + for (int i=0; i& cell = cells.getRecord (i); + + if (!cell.isDeleted()) + { + const Cell& cell2 = cell.get(); + + if (cell2.isExterior()) + { + std::pair index (cell2.mData.mX, cell2.mData.mY); + + if (mMap.empty()) + { + mMin = index; + mMax = std::make_pair (mMin.first+1, mMin.second+1); + } + else + { + if (index.first=mMax.first) + mMax.first = index.first + 1; + + if (index.second=mMax.second) + mMax.second = index.second + 1; + } + + mMap.insert (std::make_pair (index, cell2.mRegion)); + } + } + } +} + +CSMWorld::RegionMap::RegionMap (Data& data) +{ + buildRegions (data); + buildMap (data); + + QAbstractItemModel *regions = data.getTableModel (UniversalId (UniversalId::Type_Regions)); + + connect (regions, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), + this, SLOT (regionsAboutToBeRemoved (const QModelIndex&, int, int))); + connect (regions, SIGNAL (rowsInserted (const QModelIndex&, int, int)), + this, SLOT (regionsInserted (const QModelIndex&, int, int))); + connect (regions, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), + this, SLOT (regionsChanged (const QModelIndex&, const QModelIndex&))); + + QAbstractItemModel *cells = data.getTableModel (UniversalId (UniversalId::Type_Cells)); + + connect (cells, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), + this, SLOT (regionsAboutToBeRemoved (const QModelIndex&, int, int))); + connect (cells, SIGNAL (rowsInserted (const QModelIndex&, int, int)), + this, SLOT (regionsInserted (const QModelIndex&, int, int))); + connect (cells, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), + this, SLOT (regionsChanged (const QModelIndex&, const QModelIndex&))); } int CSMWorld::RegionMap::rowCount (const QModelIndex& parent) const @@ -70,4 +144,39 @@ Qt::ItemFlags CSMWorld::RegionMap::flags (const QModelIndex& index) const return Qt::ItemIsSelectable | Qt::ItemIsEnabled; return 0; +} + +void CSMWorld::RegionMap::regionsAboutToBeRemoved (const QModelIndex& parent, int start, int end) +{ + + +} + +void CSMWorld::RegionMap::regionsInserted (const QModelIndex& parent, int start, int end) +{ + + +} + +void CSMWorld::RegionMap::regionsChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) +{ + + +} + +void CSMWorld::RegionMap::cellsAboutToBeRemoved (const QModelIndex& parent, int start, int end) +{ + + +} + +void CSMWorld::RegionMap::cellsInserted (const QModelIndex& parent, int start, int end) +{ + + +} + +void CSMWorld::RegionMap::cellsChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) +{ +; } \ No newline at end of file diff --git a/apps/opencs/model/world/regionmap.hpp b/apps/opencs/model/world/regionmap.hpp index 78792fad15..a61875c78e 100644 --- a/apps/opencs/model/world/regionmap.hpp +++ b/apps/opencs/model/world/regionmap.hpp @@ -8,11 +8,15 @@ namespace CSMWorld { + class Data; + /// \brief Model for the region map /// /// This class does not holds any record data (other than for the purpose of buffering). class RegionMap : public QAbstractTableModel { + Q_OBJECT + std::map, std::string> mMap; ///< cell index, region std::pair mMin; ///< inclusive std::pair mMax; ///< exclusive @@ -21,9 +25,13 @@ namespace CSMWorld std::pair getIndex (const QModelIndex& index) const; ///< Translates a Qt model index into a cell index (which can contain negative components) + void buildRegions (Data& data); + + void buildMap (Data& data); + public: - RegionMap(); + RegionMap (Data& data); virtual int rowCount (const QModelIndex& parent = QModelIndex()) const; @@ -32,6 +40,20 @@ namespace CSMWorld virtual QVariant data (const QModelIndex& index, int role = Qt::DisplayRole) const; virtual Qt::ItemFlags flags (const QModelIndex& index) const; + + private slots: + + void regionsAboutToBeRemoved (const QModelIndex& parent, int start, int end); + + void regionsInserted (const QModelIndex& parent, int start, int end); + + void regionsChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); + + void cellsAboutToBeRemoved (const QModelIndex& parent, int start, int end); + + void cellsInserted (const QModelIndex& parent, int start, int end); + + void cellsChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); }; } From 116320cc0dc508d2759c422f1ba96c0c48a00ccd Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 8 Jul 2013 17:13:58 +0200 Subject: [PATCH 03/36] dealing with invalid regions and cells without regions --- apps/opencs/model/world/regionmap.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/opencs/model/world/regionmap.cpp b/apps/opencs/model/world/regionmap.cpp index 01ec979d29..6dd3148d52 100644 --- a/apps/opencs/model/world/regionmap.cpp +++ b/apps/opencs/model/world/regionmap.cpp @@ -130,6 +130,11 @@ QVariant CSMWorld::RegionMap::data (const QModelIndex& index, int role) const return QBrush ( QColor (iter->second>>24, (iter->second>>16) & 255, (iter->second>>8) & 255, iter->second & 255)); + + if (cell->second.empty()) + return QBrush (Qt::Dense6Pattern); // no region + + return QBrush (Qt::red, Qt::Dense6Pattern); } return QBrush (Qt::DiagCrossPattern); From 121978a69e23ebf4d5d58513970d5a608a1de62e Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 9 Jul 2013 14:20:28 +0200 Subject: [PATCH 04/36] some cleanup; added handling of deleted cells (now displayed instead of ignored) --- apps/opencs/model/world/regionmap.cpp | 68 +++++++++++++++------------ apps/opencs/model/world/regionmap.hpp | 22 +++++++-- 2 files changed, 57 insertions(+), 33 deletions(-) diff --git a/apps/opencs/model/world/regionmap.cpp b/apps/opencs/model/world/regionmap.cpp index 6dd3148d52..b45c45a542 100644 --- a/apps/opencs/model/world/regionmap.cpp +++ b/apps/opencs/model/world/regionmap.cpp @@ -6,9 +6,11 @@ #include "data.hpp" #include "universalid.hpp" +CSMWorld::RegionMap::CellDescription::CellDescription() : mDeleted (false) {} + std::pair CSMWorld::RegionMap::getIndex (const QModelIndex& index) const { - return std::make_pair (index.column()+mMin.first, index.row()+mMin.second); + return CellIndex (index.column()+mMin.first, index.row()+mMin.second); } void CSMWorld::RegionMap::buildRegions (Data& data) @@ -38,34 +40,38 @@ void CSMWorld::RegionMap::buildMap (Data& data) { const Record& cell = cells.getRecord (i); - if (!cell.isDeleted()) + const Cell& cell2 = cell.get(); + + if (cell2.isExterior()) { - const Cell& cell2 = cell.get(); + CellDescription description; - if (cell2.isExterior()) + if (cell.isDeleted()) + description.mDeleted = true; + else + description.mRegion = cell2.mRegion; + + CellIndex index (cell2.mData.mX, cell2.mData.mY); + + if (mMap.empty()) { - std::pair index (cell2.mData.mX, cell2.mData.mY); - - if (mMap.empty()) - { - mMin = index; - mMax = std::make_pair (mMin.first+1, mMin.second+1); - } - else - { - if (index.first=mMax.first) - mMax.first = index.first + 1; - - if (index.second=mMax.second) - mMax.second = index.second + 1; - } - - mMap.insert (std::make_pair (index, cell2.mRegion)); + mMin = index; + mMax = std::make_pair (mMin.first+1, mMin.second+1); } + else + { + if (index.first=mMax.first) + mMax.first = index.first + 1; + + if (index.second=mMax.second) + mMax.second = index.second + 1; + } + + mMap.insert (std::make_pair (index, description)); } } } @@ -119,22 +125,26 @@ QVariant CSMWorld::RegionMap::data (const QModelIndex& index, int role) const { /// \todo GUI class in non-GUI code. Needs to be addressed eventually. - std::map, std::string>::const_iterator cell = + std::map::const_iterator cell = mMap.find (getIndex (index)); if (cell!=mMap.end()) { - std::map::const_iterator iter = mColours.find (cell->second); + if (cell->second.mDeleted) + return QBrush (Qt::red, Qt::DiagCrossPattern); + + std::map::const_iterator iter = + mColours.find (cell->second.mRegion); if (iter!=mColours.end()) return QBrush ( QColor (iter->second>>24, (iter->second>>16) & 255, (iter->second>>8) & 255, iter->second & 255)); - if (cell->second.empty()) + if (cell->second.mRegion.empty()) return QBrush (Qt::Dense6Pattern); // no region - return QBrush (Qt::red, Qt::Dense6Pattern); + return QBrush (Qt::red, Qt::Dense6Pattern); // invalid region } return QBrush (Qt::DiagCrossPattern); diff --git a/apps/opencs/model/world/regionmap.hpp b/apps/opencs/model/world/regionmap.hpp index a61875c78e..c5f70b4908 100644 --- a/apps/opencs/model/world/regionmap.hpp +++ b/apps/opencs/model/world/regionmap.hpp @@ -17,12 +17,26 @@ namespace CSMWorld { Q_OBJECT - std::map, std::string> mMap; ///< cell index, region - std::pair mMin; ///< inclusive - std::pair mMax; ///< exclusive + public: + + typedef std::pair CellIndex; + + private: + + struct CellDescription + { + bool mDeleted; + std::string mRegion; + + CellDescription(); + }; + + std::map mMap; + CellIndex mMin; ///< inclusive + CellIndex mMax; ///< exclusive std::map mColours; ///< region ID, colour (RGBA) - std::pair getIndex (const QModelIndex& index) const; + CellIndex getIndex (const QModelIndex& index) const; ///< Translates a Qt model index into a cell index (which can contain negative components) void buildRegions (Data& data); From d389b70ec4cf56b4d272fd53f5d1768e2db65010 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 9 Jul 2013 14:22:58 +0200 Subject: [PATCH 05/36] added missing case folding --- apps/opencs/model/world/regionmap.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/world/regionmap.cpp b/apps/opencs/model/world/regionmap.cpp index b45c45a542..5b28097f9d 100644 --- a/apps/opencs/model/world/regionmap.cpp +++ b/apps/opencs/model/world/regionmap.cpp @@ -3,6 +3,8 @@ #include +#include + #include "data.hpp" #include "universalid.hpp" @@ -24,7 +26,8 @@ void CSMWorld::RegionMap::buildRegions (Data& data) const Record& region = regions.getRecord (i); if (!region.isDeleted()) - mColours.insert (std::make_pair (region.get().mId, region.get().mMapColor)); + mColours.insert (std::make_pair (Misc::StringUtils::lowerCase (region.get().mId), + region.get().mMapColor)); } } @@ -134,7 +137,7 @@ QVariant CSMWorld::RegionMap::data (const QModelIndex& index, int role) const return QBrush (Qt::red, Qt::DiagCrossPattern); std::map::const_iterator iter = - mColours.find (cell->second.mRegion); + mColours.find (Misc::StringUtils::lowerCase (cell->second.mRegion)); if (iter!=mColours.end()) return QBrush ( From c808bf2b23a8297f6582c3eba82557dfa4a7eb74 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 11 Jul 2013 15:05:28 +0200 Subject: [PATCH 06/36] updating region map on changes to region and cell records --- apps/opencs/model/world/regionmap.cpp | 338 +++++++++++++++++++++++--- apps/opencs/model/world/regionmap.hpp | 35 ++- 2 files changed, 337 insertions(+), 36 deletions(-) diff --git a/apps/opencs/model/world/regionmap.cpp b/apps/opencs/model/world/regionmap.cpp index 5b28097f9d..fd278d2a6c 100644 --- a/apps/opencs/model/world/regionmap.cpp +++ b/apps/opencs/model/world/regionmap.cpp @@ -1,6 +1,8 @@ #include "regionmap.hpp" +#include + #include #include @@ -10,14 +12,20 @@ CSMWorld::RegionMap::CellDescription::CellDescription() : mDeleted (false) {} -std::pair CSMWorld::RegionMap::getIndex (const QModelIndex& index) const +CSMWorld::RegionMap::CellIndex CSMWorld::RegionMap::getIndex (const QModelIndex& index) const { return CellIndex (index.column()+mMin.first, index.row()+mMin.second); } -void CSMWorld::RegionMap::buildRegions (Data& data) +QModelIndex CSMWorld::RegionMap::getIndex (const CellIndex& index) const { - const IdCollection& regions = data.getRegions(); + // I hate you, Qt API naming scheme! + return QAbstractTableModel::index (index.second-mMin.second, index.first-mMin.first); +} + +void CSMWorld::RegionMap::buildRegions() +{ + const IdCollection& regions = mData.getRegions(); int size = regions.getSize(); @@ -31,14 +39,12 @@ void CSMWorld::RegionMap::buildRegions (Data& data) } } -void CSMWorld::RegionMap::buildMap (Data& data) +void CSMWorld::RegionMap::buildMap() { - const IdCollection& cells = data.getCells(); + const IdCollection& cells = mData.getCells(); int size = cells.getSize(); - mMin = mMax = std::make_pair (0, 0); - for (int i=0; i& cell = cells.getRecord (i); @@ -56,33 +62,237 @@ void CSMWorld::RegionMap::buildMap (Data& data) CellIndex index (cell2.mData.mX, cell2.mData.mY); - if (mMap.empty()) - { - mMin = index; - mMax = std::make_pair (mMin.first+1, mMin.second+1); - } - else - { - if (index.first=mMax.first) - mMax.first = index.first + 1; - - if (index.second=mMax.second) - mMax.second = index.second + 1; - } - mMap.insert (std::make_pair (index, description)); } } + + std::pair mapSize = getSize(); + + mMin = mapSize.first; + mMax = mapSize.second; +} + +void CSMWorld::RegionMap::addCell (const CellIndex& index, const CellDescription& description) +{ + std::map::iterator cell = mMap.find (index); + + if (cell!=mMap.end()) + { + cell->second = description; + } + else + { + updateSize(); + + mMap.insert (std::make_pair (index, description)); + } + + QModelIndex index2 = getIndex (index); + + dataChanged (index2, index2); +} + +void CSMWorld::RegionMap::addCells (int start, int end) +{ + const IdCollection& cells = mData.getCells(); + + for (int i=start; i<=end; ++i) + { + const Record& cell = cells.getRecord (i); + + const Cell& cell2 = cell.get(); + + if (cell2.isExterior()) + { + CellIndex index (cell2.mData.mX, cell2.mData.mY); + + CellDescription description; + + if (cell.isDeleted()) + description.mDeleted = true; + else + description.mRegion = cell2.mRegion; + + addCell (index, description); + } + } } -CSMWorld::RegionMap::RegionMap (Data& data) +void CSMWorld::RegionMap::removeCell (const CellIndex& index) { - buildRegions (data); - buildMap (data); + std::map::iterator cell = mMap.find (index); + + if (cell!=mMap.end()) + { + mMap.erase (cell); + + QModelIndex index2 = getIndex (index); + + dataChanged (index2, index2); + + updateSize(); + } +} + +void CSMWorld::RegionMap::addRegion (const std::string& region, unsigned int colour) +{ + mColours[Misc::StringUtils::lowerCase (region)] = colour; +} + +void CSMWorld::RegionMap::removeRegion (const std::string& region) +{ + std::map::iterator iter ( + mColours.find (Misc::StringUtils::lowerCase (region))); + + if (iter!=mColours.end()) + mColours.erase (iter); +} + +void CSMWorld::RegionMap::updateRegions (const std::vector& regions) +{ + std::vector regions2 (regions); + + std::for_each (regions2.begin(), regions2.end(), &Misc::StringUtils::lowerCase); + std::sort (regions2.begin(), regions2.end()); + + for (std::map::const_iterator iter (mMap.begin()); + iter!=mMap.end(); ++iter) + { + if (!iter->second.mRegion.empty() && + std::find (regions2.begin(), regions2.end(), + Misc::StringUtils::lowerCase (iter->second.mRegion))!=regions2.end()) + { + QModelIndex index = getIndex (iter->first); + + dataChanged (index, index); + } + } +} + +void CSMWorld::RegionMap::updateSize() +{ + std::pair size = getSize(); + + { + int diff = size.first.first - mMin.first; + + if (diff<0) + { + beginInsertColumns (QModelIndex(), 0, -diff-1); + mMin.first = size.first.first; + endInsertColumns(); + } + else if (diff>0) + { + beginRemoveColumns (QModelIndex(), 0, diff-1); + mMin.first = size.first.first; + endRemoveColumns(); + } + } + + { + int diff = size.first.second - mMin.second; + + if (diff<0) + { + beginInsertRows (QModelIndex(), 0, -diff-1); + mMin.second = size.first.second; + endInsertRows(); + } + else if (diff>0) + { + beginRemoveRows (QModelIndex(), 0, diff-1); + mMin.second = size.first.second; + endRemoveRows(); + } + } + + { + int diff = size.second.first - mMax.first; + + if (diff>0) + { + int columns = columnCount(); + beginInsertColumns (QModelIndex(), columns, columns+diff-1); + mMax.first = size.second.first; + endInsertColumns(); + } + else if (diff<0) + { + int columns = columnCount(); + beginRemoveColumns (QModelIndex(), columns+diff, columns-1); + mMax.first = size.second.first; + endRemoveColumns(); + } + } + + { + int diff = size.second.second - mMax.second; + + if (diff>0) + { + int rows = rowCount(); + beginInsertRows (QModelIndex(), rows, rows+diff-1); + mMax.second = size.second.second; + endInsertRows(); + } + else if (diff<0) + { + int rows = rowCount(); + beginRemoveRows (QModelIndex(), rows+diff, rows-1); + mMax.second = size.second.second; + endRemoveRows(); + } + } +} + +std::pair CSMWorld::RegionMap::getSize() + const +{ + const IdCollection& cells = mData.getCells(); + + int size = cells.getSize(); + + CellIndex min (0, 0); + CellIndex max (0, 0); + + for (int i=0; i& cell = cells.getRecord (i); + + const Cell& cell2 = cell.get(); + + if (cell2.isExterior()) + { + CellIndex index (cell2.mData.mX, cell2.mData.mY); + + if (min==max) + { + min = index; + max = std::make_pair (min.first+1, min.second+1); + } + else + { + if (index.first=max.first) + max.first = index.first + 1; + + if (index.second=max.second) + max.second = index.second + 1; + } + } + } + + return std::make_pair (min, max); +} + +CSMWorld::RegionMap::RegionMap (Data& data) : mData (data) +{ + buildRegions(); + buildMap(); QAbstractItemModel *regions = data.getTableModel (UniversalId (UniversalId::Type_Regions)); @@ -96,11 +306,11 @@ CSMWorld::RegionMap::RegionMap (Data& data) QAbstractItemModel *cells = data.getTableModel (UniversalId (UniversalId::Type_Cells)); connect (cells, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), - this, SLOT (regionsAboutToBeRemoved (const QModelIndex&, int, int))); + this, SLOT (cellsAboutToBeRemoved (const QModelIndex&, int, int))); connect (cells, SIGNAL (rowsInserted (const QModelIndex&, int, int)), - this, SLOT (regionsInserted (const QModelIndex&, int, int))); + this, SLOT (cellsInserted (const QModelIndex&, int, int))); connect (cells, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), - this, SLOT (regionsChanged (const QModelIndex&, const QModelIndex&))); + this, SLOT (cellsChanged (const QModelIndex&, const QModelIndex&))); } int CSMWorld::RegionMap::rowCount (const QModelIndex& parent) const @@ -166,35 +376,95 @@ Qt::ItemFlags CSMWorld::RegionMap::flags (const QModelIndex& index) const void CSMWorld::RegionMap::regionsAboutToBeRemoved (const QModelIndex& parent, int start, int end) { + std::vector update; + const IdCollection& regions = mData.getRegions(); + for (int i=start; i<=end; ++i) + { + const Record& region = regions.getRecord (i); + + update.push_back (region.get().mId); + + removeRegion (region.get().mId); + } + + updateRegions (update); } void CSMWorld::RegionMap::regionsInserted (const QModelIndex& parent, int start, int end) { + std::vector update; + const IdCollection& regions = mData.getRegions(); + for (int i=start; i<=end; ++i) + { + const Record& region = regions.getRecord (i); + + if (!region.isDeleted()) + { + update.push_back (region.get().mId); + + addRegion (region.get().mId, region.get().mMapColor); + } + } + + updateRegions (update); } void CSMWorld::RegionMap::regionsChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { + // Note: At this point an additional check could be inserted to see if there is any change to the + // columns we are interested in. If not we can exit the function here and avoid all updating. + std::vector update; + const IdCollection& regions = mData.getRegions(); + + for (int i=topLeft.row(); i<=bottomRight.column(); ++i) + { + const Record& region = regions.getRecord (i); + + update.push_back (region.get().mId); + + if (!region.isDeleted()) + addRegion (region.get().mId, region.get().mMapColor); + else + removeRegion (region.get().mId); + } + + updateRegions (update); } void CSMWorld::RegionMap::cellsAboutToBeRemoved (const QModelIndex& parent, int start, int end) { + const IdCollection& cells = mData.getCells(); + for (int i=start; i<=end; ++i) + { + const Record& cell = cells.getRecord (i); + const Cell& cell2 = cell.get(); + + if (cell2.isExterior()) + { + CellIndex index (cell2.mData.mX, cell2.mData.mY); + + removeCell (index); + } + } } void CSMWorld::RegionMap::cellsInserted (const QModelIndex& parent, int start, int end) { - - + addCells (start, end); } void CSMWorld::RegionMap::cellsChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { -; + // Note: At this point an additional check could be inserted to see if there is any change to the + // columns we are interested in. If not we can exit the function here and avoid all updating. + + addCells (topLeft.row(), bottomRight.row()); } \ No newline at end of file diff --git a/apps/opencs/model/world/regionmap.hpp b/apps/opencs/model/world/regionmap.hpp index c5f70b4908..0a20324c4c 100644 --- a/apps/opencs/model/world/regionmap.hpp +++ b/apps/opencs/model/world/regionmap.hpp @@ -3,6 +3,7 @@ #include #include +#include #include @@ -31,6 +32,7 @@ namespace CSMWorld CellDescription(); }; + Data& mData; std::map mMap; CellIndex mMin; ///< inclusive CellIndex mMax; ///< exclusive @@ -39,9 +41,38 @@ namespace CSMWorld CellIndex getIndex (const QModelIndex& index) const; ///< Translates a Qt model index into a cell index (which can contain negative components) - void buildRegions (Data& data); + QModelIndex getIndex (const CellIndex& index) const; - void buildMap (Data& data); + void buildRegions(); + + void buildMap(); + + void addCell (const CellIndex& index, const CellDescription& description); + ///< May be called on a cell that is already in the map (in which case an update is + // performed) + + void addCells (int start, int end); + + void removeCell (const CellIndex& index); + ///< May be called on a cell that is not in the map (in which case the call is ignored) + + void addRegion (const std::string& region, unsigned int colour); + ///< May be called on a region that is already listed (in which case an update is + /// performed) + /// + /// \note This function does not update the region map. + + void removeRegion (const std::string& region); + ///< May be called on a region that is not listed (in which case the call is ignored) + /// + /// \note This function does not update the region map. + + void updateRegions (const std::vector& regions); + ///< Update cells affected by the listed regions + + void updateSize(); + + std::pair getSize() const; public: From 29b7734b52f28e1715f3e625a82bd13806a14e0b Mon Sep 17 00:00:00 2001 From: greye Date: Fri, 12 Jul 2013 08:33:02 +0400 Subject: [PATCH 07/36] add qualifiers for names in templates to make gcc 4.7+ happy --- apps/opencs/model/world/idcollection.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/opencs/model/world/idcollection.hpp b/apps/opencs/model/world/idcollection.hpp index e0097747c7..04e65eea7b 100644 --- a/apps/opencs/model/world/idcollection.hpp +++ b/apps/opencs/model/world/idcollection.hpp @@ -47,7 +47,7 @@ namespace CSMWorld { Record record = Collection::getRecord (index); record.mState = RecordBase::State_Deleted; - setRecord (index, record); + this->setRecord (index, record); } } else @@ -56,7 +56,7 @@ namespace CSMWorld IdAccessorT().getId (record) = id; record.load (reader); - int index = searchId (IdAccessorT().getId (record)); + int index = this->searchId (IdAccessorT().getId (record)); if (index==-1) { @@ -65,7 +65,7 @@ namespace CSMWorld record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; (base ? record2.mBase : record2.mModified) = record; - appendRecord (record2); + this->appendRecord (record2); } else { @@ -77,7 +77,7 @@ namespace CSMWorld else record2.setModified (record); - setRecord (index, record2); + this->setRecord (index, record2); } } } From c26a6f884f38ab75edaf692bf3eb75bce705fff9 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 12 Jul 2013 12:55:14 +0200 Subject: [PATCH 08/36] added region map tooltips --- apps/opencs/model/world/regionmap.cpp | 65 ++++++++++++++++++++++----- apps/opencs/model/world/regionmap.hpp | 6 +++ 2 files changed, 59 insertions(+), 12 deletions(-) diff --git a/apps/opencs/model/world/regionmap.cpp b/apps/opencs/model/world/regionmap.cpp index fd278d2a6c..a071bded86 100644 --- a/apps/opencs/model/world/regionmap.cpp +++ b/apps/opencs/model/world/regionmap.cpp @@ -12,6 +12,19 @@ CSMWorld::RegionMap::CellDescription::CellDescription() : mDeleted (false) {} +CSMWorld::RegionMap::CellDescription::CellDescription (const Record& cell) +{ + const Cell& cell2 = cell.get(); + + if (!cell2.isExterior()) + throw std::logic_error ("Interior cell in region map"); + + mDeleted = cell.isDeleted(); + + mRegion = cell2.mRegion; + mName = cell2.mName; +} + CSMWorld::RegionMap::CellIndex CSMWorld::RegionMap::getIndex (const QModelIndex& index) const { return CellIndex (index.column()+mMin.first, index.row()+mMin.second); @@ -53,12 +66,7 @@ void CSMWorld::RegionMap::buildMap() if (cell2.isExterior()) { - CellDescription description; - - if (cell.isDeleted()) - description.mDeleted = true; - else - description.mRegion = cell2.mRegion; + CellDescription description (cell); CellIndex index (cell2.mData.mX, cell2.mData.mY); @@ -106,12 +114,7 @@ void CSMWorld::RegionMap::addCells (int start, int end) { CellIndex index (cell2.mData.mX, cell2.mData.mY); - CellDescription description; - - if (cell.isDeleted()) - description.mDeleted = true; - else - description.mRegion = cell2.mRegion; + CellDescription description (cell); addCell (index, description); } @@ -363,6 +366,44 @@ QVariant CSMWorld::RegionMap::data (const QModelIndex& index, int role) const return QBrush (Qt::DiagCrossPattern); } + if (role==Qt::ToolTipRole) + { + CellIndex cellIndex = getIndex (index); + + std::ostringstream stream; + + stream << cellIndex.first << ", " << cellIndex.second; + + std::map::const_iterator cell = + mMap.find (cellIndex); + + if (cell!=mMap.end()) + { + if (!cell->second.mName.empty()) + stream << " " << cell->second.mName; + + if (cell->second.mDeleted) + stream << " (deleted)"; + + if (!cell->second.mRegion.empty()) + { + stream << "
"; + + std::map::const_iterator iter = + mColours.find (Misc::StringUtils::lowerCase (cell->second.mRegion)); + + if (iter!=mColours.end()) + stream << cell->second.mRegion; + else + stream << "" << cell->second.mRegion << ""; + } + } + else + stream << " (no cell)"; + + return QString::fromUtf8 (stream.str().c_str()); + } + return QVariant(); } diff --git a/apps/opencs/model/world/regionmap.hpp b/apps/opencs/model/world/regionmap.hpp index 0a20324c4c..7fb89f20ac 100644 --- a/apps/opencs/model/world/regionmap.hpp +++ b/apps/opencs/model/world/regionmap.hpp @@ -7,6 +7,9 @@ #include +#include "record.hpp" +#include "cell.hpp" + namespace CSMWorld { class Data; @@ -28,8 +31,11 @@ namespace CSMWorld { bool mDeleted; std::string mRegion; + std::string mName; CellDescription(); + + CellDescription (const Record& cell); }; Data& mData; From 7883087586a205d79cfaa1097ce3eb8550d7ab95 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 8 Jul 2013 14:05:53 -0700 Subject: [PATCH 09/36] Store a pointer to the character controller in the map --- apps/openmw/mwmechanics/actors.cpp | 32 ++++++++++++++++++------------ apps/openmw/mwmechanics/actors.hpp | 2 +- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index e85aa9b834..082100b278 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -172,16 +172,19 @@ namespace MWMechanics MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr); if(!MWWorld::Class::get(ptr).getCreatureStats(ptr).isDead()) - mActors.insert(std::make_pair(ptr, CharacterController(ptr, anim, CharState_Idle))); + mActors.insert(std::make_pair(ptr, new CharacterController(ptr, anim, CharState_Idle))); else - mActors.insert(std::make_pair(ptr, CharacterController(ptr, anim, CharState_Death1))); + mActors.insert(std::make_pair(ptr, new CharacterController(ptr, anim, CharState_Death1))); } void Actors::removeActor (const MWWorld::Ptr& ptr) { PtrControllerMap::iterator iter = mActors.find(ptr); if(iter != mActors.end()) + { + delete iter->second; mActors.erase(iter); + } } void Actors::updateActor(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr) @@ -189,10 +192,10 @@ namespace MWMechanics PtrControllerMap::iterator iter = mActors.find(old); if(iter != mActors.end()) { - CharacterController ctrl = iter->second; + CharacterController *ctrl = iter->second; mActors.erase(iter); - ctrl.updatePtr(ptr); + ctrl->updatePtr(ptr); mActors.insert(std::make_pair(ptr, ctrl)); } } @@ -203,7 +206,10 @@ namespace MWMechanics while(iter != mActors.end()) { if(iter->first.getCell()==cellStore) + { + delete iter->second; mActors.erase(iter++); + } else ++iter; } @@ -222,8 +228,8 @@ namespace MWMechanics { if(!MWWorld::Class::get(iter->first).getCreatureStats(iter->first).isDead()) { - if(iter->second.getState() >= CharState_Death1) - iter->second.setState(CharState_Idle); + if(iter->second->getState() >= CharState_Death1) + iter->second->setState(CharState_Idle); updateActor(iter->first, totalDuration); if(iter->first.getTypeName() == typeid(ESM::NPC).name()) @@ -250,10 +256,10 @@ namespace MWMechanics continue; } - if(iter->second.getState() >= CharState_Death1) + if(iter->second->getState() >= CharState_Death1) continue; - iter->second.setState(CharState_Death1); + iter->second->setState(CharState_Death1); ++mDeathCount[MWWorld::Class::get(iter->first).getId(iter->first)]; @@ -270,7 +276,7 @@ namespace MWMechanics for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter) { Movement movement; - iter->second.update(duration, movement); + iter->second->update(duration, movement); mMovement.push_back(std::make_pair(iter->first, movement)); } MWBase::Environment::get().getWorld()->doPhysics(mMovement, duration); @@ -297,27 +303,27 @@ namespace MWMechanics { PtrControllerMap::iterator iter = mActors.find(ptr); if(iter != mActors.end()) - iter->second.forceStateUpdate(); + iter->second->forceStateUpdate(); } void Actors::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number) { PtrControllerMap::iterator iter = mActors.find(ptr); if(iter != mActors.end()) - iter->second.playGroup(groupName, mode, number); + iter->second->playGroup(groupName, mode, number); } void Actors::skipAnimation(const MWWorld::Ptr& ptr) { PtrControllerMap::iterator iter = mActors.find(ptr); if(iter != mActors.end()) - iter->second.skipAnim(); + iter->second->skipAnim(); } bool Actors::checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) { PtrControllerMap::iterator iter = mActors.find(ptr); if(iter != mActors.end()) - return iter->second.isAnimPlaying(groupName); + return iter->second->isAnimPlaying(groupName); return false; } } diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 386840e3a0..1369d783c6 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -25,7 +25,7 @@ namespace MWMechanics { class Actors { - typedef std::map PtrControllerMap; + typedef std::map PtrControllerMap; PtrControllerMap mActors; MWWorld::PtrMovementList mMovement; From 908f010c742a5f7b847257dc1dbfcf7f7c4378cc Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 8 Jul 2013 18:59:51 -0700 Subject: [PATCH 10/36] Reset the NPC object root when switching POV When in first person, the skeleton in the .1st.nif file is used. In particular, these have extra finger bones that are used by the first person models. --- apps/openmw/mwrender/animation.cpp | 8 +++- apps/openmw/mwrender/animation.hpp | 11 +++++ apps/openmw/mwrender/npcanimation.cpp | 58 ++++++++++++++++----------- 3 files changed, 52 insertions(+), 25 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 853ffc3752..286da2bc13 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -72,8 +72,9 @@ Animation::~Animation() void Animation::setObjectRoot(Ogre::SceneNode *node, const std::string &model, bool baseonly) { - OgreAssert(!mInsert, "Object already has a root!"); - mInsert = node->createChildSceneNode(); + OgreAssert(mAnimSources.size() != 0, "Setting object root while animation sources are set!"); + if(!mInsert) + mInsert = node->createChildSceneNode(); std::string mdlname = Misc::StringUtils::lowerCase(model); std::string::size_type p = mdlname.rfind('\\'); @@ -89,6 +90,9 @@ void Animation::setObjectRoot(Ogre::SceneNode *node, const std::string &model, b Misc::StringUtils::toLower(mdlname); } + mSkelBase = NULL; + destroyObjectList(mInsert->getCreator(), mObjectRoot); + mObjectRoot = (!baseonly ? NifOgre::Loader::createObjects(mInsert, mdlname) : NifOgre::Loader::createObjectBase(mInsert, mdlname)); if(mObjectRoot.mSkelBase) diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 31be0fb2a7..9fd4a606c6 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -131,7 +131,18 @@ protected: bool handleTextKey(AnimState &state, const std::string &groupname, const NifOgre::TextKeyMap::const_iterator &key); + /* Sets the root model of the object. If 'baseonly' is true, then any meshes or particle + * systems in the model are ignored (useful for NPCs, where only the skeleton is needed for + * the root). + * + * Note that you must make sure all animation sources are cleared before reseting the object + * root. All nodes previously retrieved with getNode will also become invalidated. + */ void setObjectRoot(Ogre::SceneNode *node, const std::string &model, bool baseonly); + + /* Adds the keyframe controllers in the specified model as a new animation source. Note that + * the filename portion of the provided model name will be prepended with 'x', and the .nif + * extension will be replaced with .kf. */ void addAnimSource(const std::string &model); static void destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index b5f2ea031a..cc12d1d20a 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -98,17 +98,22 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWor Misc::StringUtils::toLower(mBodyPrefix); bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0; - std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif"); + std::string smodel = (viewMode != VM_FirstPerson) ? + (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif") : + (!isBeast ? "meshes\\base_anim.1st.nif" : "meshes\\base_animkna.1st.nif") ; setObjectRoot(node, smodel, true); - addAnimSource(smodel); - if(mBodyPrefix.find("argonian") != std::string::npos) - addAnimSource("meshes\\argonian_swimkna.nif"); - else if(!mNpc->isMale() && !isBeast) - addAnimSource("meshes\\base_anim_female.nif"); - if(mNpc->mModel.length() > 0) - addAnimSource("meshes\\"+mNpc->mModel); - if(mViewMode == VM_FirstPerson) + if(mViewMode != VM_FirstPerson) + { + addAnimSource(smodel); + if(mBodyPrefix.find("argonian") != std::string::npos) + addAnimSource("meshes\\argonian_swimkna.nif"); + else if(!mNpc->isMale() && !isBeast) + addAnimSource("meshes\\base_anim_female.nif"); + if(mNpc->mModel.length() > 0) + addAnimSource("meshes\\"+mNpc->mModel); + } + else { /* A bit counter-intuitive, but unlike third-person anims, it seems * beast races get both base_anim.1st.nif and base_animkna.1st.nif. @@ -128,20 +133,28 @@ void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode) assert(viewMode != VM_HeadOnly); mViewMode = viewMode; + clearAnimSources(); + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Race *race = store.get().find(mNpc->mRace); - bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0; - std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif"); - clearAnimSources(); - addAnimSource(smodel); - if(mBodyPrefix.find("argonian") != std::string::npos) - addAnimSource("meshes\\argonian_swimkna.nif"); - else if(!mNpc->isMale() && !isBeast) - addAnimSource("meshes\\base_anim_female.nif"); - if(mNpc->mModel.length() > 0) - addAnimSource("meshes\\"+mNpc->mModel); - if(mViewMode == VM_FirstPerson) + bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0; + std::string smodel = (viewMode != VM_FirstPerson) ? + (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif") : + (!isBeast ? "meshes\\base_anim.1st.nif" : "meshes\\base_animkna.1st.nif") ; + setObjectRoot(mInsert->getParentSceneNode(), smodel, true); + + if(mViewMode != VM_FirstPerson) + { + addAnimSource(smodel); + if(mBodyPrefix.find("argonian") != std::string::npos) + addAnimSource("meshes\\argonian_swimkna.nif"); + else if(!mNpc->isMale() && !isBeast) + addAnimSource("meshes\\base_anim_female.nif"); + if(mNpc->mModel.length() > 0) + addAnimSource("meshes\\"+mNpc->mModel); + } + else { /* A bit counter-intuitive, but unlike third-person anims, it seems * beast races get both base_anim.1st.nif and base_animkna.1st.nif. @@ -207,9 +220,9 @@ void NpcAnimation::updateParts(bool forceupdate) for(size_t i = 0;i < slotlistsize && mViewMode != VM_HeadOnly;i++) { - MWWorld::ContainerStoreIterator iter = inv.getSlot(slotlist[i].mSlot); + MWWorld::ContainerStoreIterator store = inv.getSlot(slotlist[i].mSlot); - this->*slotlist[i].mPart = iter; + this->*slotlist[i].mPart = store; removePartGroup(slotlist[i].mSlot); if(this->*slotlist[i].mPart == inv.end()) @@ -219,7 +232,6 @@ void NpcAnimation::updateParts(bool forceupdate) removeIndividualPart(ESM::PRT_Hair); int prio = 1; - MWWorld::ContainerStoreIterator &store = this->*slotlist[i].mPart; if(store->getTypeName() == typeid(ESM::Clothing).name()) { prio = ((slotlist[i].mBasePriority+1)<<1) + 0; From a5e4faaed277434599607e78daf7958f83767bf7 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 8 Jul 2013 22:37:44 -0700 Subject: [PATCH 11/36] Show arms and body parts in first person This isn't yet fully correct. The arms need to rotate up and down with the camera, and the mesh's bounding box is causing them to blink out at certain angles since they don't fit the animation. --- apps/openmw/mwrender/npcanimation.cpp | 38 ++++++++++++++++----------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index cc12d1d20a..6f53010ae1 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -210,14 +210,6 @@ void NpcAnimation::updateParts(bool forceupdate) if(!forceupdate) return; - /* FIXME: Remove this once we figure out how to show what in first-person */ - if(mViewMode == VM_FirstPerson) - { - for(size_t i = 0;i < slotlistsize;i++) - this->*slotlist[i].mPart = inv.getSlot(slotlist[i].mSlot); - return; - } - for(size_t i = 0;i < slotlistsize && mViewMode != VM_HeadOnly;i++) { MWWorld::ContainerStoreIterator store = inv.getSlot(slotlist[i].mSlot); @@ -316,7 +308,8 @@ void NpcAnimation::updateParts(bool forceupdate) bodypartMap[ESM::PRT_Tail] = ESM::BodyPart::MP_Tail; } - sRaceMapping[thisCombination].resize(ESM::PRT_Count, NULL); + std::vector &parts = sRaceMapping[thisCombination]; + parts.resize(ESM::PRT_Count, NULL); const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::Store &partStore = store.get(); @@ -337,11 +330,27 @@ void NpcAnimation::updateParts(bool forceupdate) && bodypart.mId[bodypart.mId.size()-3] == '1' && bodypart.mId[bodypart.mId.size()-2] == 's' && bodypart.mId[bodypart.mId.size()-1] == 't'; - if (firstPerson != (mViewMode == VM_FirstPerson)) + if(firstPerson != (mViewMode == VM_FirstPerson)) + { + if(mViewMode == VM_FirstPerson && (bodypart.mData.mPart == ESM::BodyPart::MP_Hand || + bodypart.mData.mPart == ESM::BodyPart::MP_Wrist || + bodypart.mData.mPart == ESM::BodyPart::MP_Forearm)) + { + /* Allow 3rd person skins as a fallback for the forearms if 1st person is missing. */ + for(std::map::iterator bIt = bodypartMap.begin();bIt != bodypartMap.end();++bIt) + { + if(bIt->second == bodypart.mData.mPart) + { + if(!parts[bIt->first]) + parts[bIt->first] = &*it; + } + } + } continue; - for (std::map::iterator bIt = bodypartMap.begin(); bIt != bodypartMap.end(); ++bIt ) - if (bIt->second == bodypart.mData.mPart) - sRaceMapping[thisCombination][bIt->first] = &*it; + } + for(std::map::iterator bIt = bodypartMap.begin();bIt != bodypartMap.end();++bIt) + if(bIt->second == bodypart.mData.mPart) + parts[bIt->first] = &*it; } } @@ -484,8 +493,7 @@ void NpcAnimation::addPartGroup(int group, int priority, const std::vector Date: Tue, 9 Jul 2013 13:38:18 -0700 Subject: [PATCH 12/36] Use a multimap for the bodypart map --- apps/openmw/mwrender/npcanimation.cpp | 65 ++++++++++++++------------- 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 6f53010ae1..d39b339df6 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -283,29 +283,30 @@ void NpcAnimation::updateParts(bool forceupdate) std::pair thisCombination = std::make_pair(race, flags); if (sRaceMapping.find(thisCombination) == sRaceMapping.end()) { - static std::map bodypartMap; - if(bodypartMap.size() == 0) + typedef std::multimap BodyPartMapType; + static BodyPartMapType sBodyPartMap; + if(sBodyPartMap.size() == 0) { - bodypartMap[ESM::PRT_Neck] = ESM::BodyPart::MP_Neck; - bodypartMap[ESM::PRT_Cuirass] = ESM::BodyPart::MP_Chest; - bodypartMap[ESM::PRT_Groin] = ESM::BodyPart::MP_Groin; - bodypartMap[ESM::PRT_RHand] = ESM::BodyPart::MP_Hand; - bodypartMap[ESM::PRT_LHand] = ESM::BodyPart::MP_Hand; - bodypartMap[ESM::PRT_RWrist] = ESM::BodyPart::MP_Wrist; - bodypartMap[ESM::PRT_LWrist] = ESM::BodyPart::MP_Wrist; - bodypartMap[ESM::PRT_RForearm] = ESM::BodyPart::MP_Forearm; - bodypartMap[ESM::PRT_LForearm] = ESM::BodyPart::MP_Forearm; - bodypartMap[ESM::PRT_RUpperarm] = ESM::BodyPart::MP_Upperarm; - bodypartMap[ESM::PRT_LUpperarm] = ESM::BodyPart::MP_Upperarm; - bodypartMap[ESM::PRT_RFoot] = ESM::BodyPart::MP_Foot; - bodypartMap[ESM::PRT_LFoot] = ESM::BodyPart::MP_Foot; - bodypartMap[ESM::PRT_RAnkle] = ESM::BodyPart::MP_Ankle; - bodypartMap[ESM::PRT_LAnkle] = ESM::BodyPart::MP_Ankle; - bodypartMap[ESM::PRT_RKnee] = ESM::BodyPart::MP_Knee; - bodypartMap[ESM::PRT_LKnee] = ESM::BodyPart::MP_Knee; - bodypartMap[ESM::PRT_RLeg] = ESM::BodyPart::MP_Upperleg; - bodypartMap[ESM::PRT_LLeg] = ESM::BodyPart::MP_Upperleg; - bodypartMap[ESM::PRT_Tail] = ESM::BodyPart::MP_Tail; + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Neck, ESM::PRT_Neck)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Chest, ESM::PRT_Cuirass)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Groin, ESM::PRT_Groin)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Hand, ESM::PRT_RHand)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Hand, ESM::PRT_LHand)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Wrist, ESM::PRT_RWrist)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Wrist, ESM::PRT_LWrist)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Forearm, ESM::PRT_RForearm)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Forearm, ESM::PRT_LForearm)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Upperarm, ESM::PRT_RUpperarm)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Upperarm, ESM::PRT_LUpperarm)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Foot, ESM::PRT_RFoot)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Foot, ESM::PRT_LFoot)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Ankle, ESM::PRT_RAnkle)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Ankle, ESM::PRT_LAnkle)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Knee, ESM::PRT_RKnee)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Knee, ESM::PRT_LKnee)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Upperleg, ESM::PRT_RLeg)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Upperleg, ESM::PRT_LLeg)); + sBodyPartMap.insert(std::make_pair(ESM::BodyPart::MP_Tail, ESM::PRT_Tail)); } std::vector &parts = sRaceMapping[thisCombination]; @@ -337,20 +338,22 @@ void NpcAnimation::updateParts(bool forceupdate) bodypart.mData.mPart == ESM::BodyPart::MP_Forearm)) { /* Allow 3rd person skins as a fallback for the forearms if 1st person is missing. */ - for(std::map::iterator bIt = bodypartMap.begin();bIt != bodypartMap.end();++bIt) + BodyPartMapType::const_iterator bIt = sBodyPartMap.lower_bound(BodyPartMapType::key_type(bodypart.mData.mPart)); + while(bIt != sBodyPartMap.end() && bIt->first == bodypart.mData.mPart) { - if(bIt->second == bodypart.mData.mPart) - { - if(!parts[bIt->first]) - parts[bIt->first] = &*it; - } + if(!parts[bIt->second]) + parts[bIt->second] = &*it; + bIt++; } } continue; } - for(std::map::iterator bIt = bodypartMap.begin();bIt != bodypartMap.end();++bIt) - if(bIt->second == bodypart.mData.mPart) - parts[bIt->first] = &*it; + BodyPartMapType::const_iterator bIt = sBodyPartMap.lower_bound(BodyPartMapType::key_type(bodypart.mData.mPart)); + while(bIt != sBodyPartMap.end() && bIt->first == bodypart.mData.mPart) + { + parts[bIt->second] = &*it; + bIt++; + } } } From 6de56615aa52b2faa7739c408305a3b76d5a1f6c Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 9 Jul 2013 23:50:44 -0700 Subject: [PATCH 13/36] Avoid casting shadows in first-person view --- apps/openmw/mwrender/npcanimation.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index d39b339df6..ee27f710aa 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -371,7 +371,12 @@ NifOgre::ObjectList NpcAnimation::insertBoundedPart(const std::string &model, in setRenderProperties(objects, mVisibilityFlags, RQG_Main, RQG_Alpha); for(size_t i = 0;i < objects.mEntities.size();i++) - objects.mEntities[i]->getUserObjectBindings().setUserAny(Ogre::Any(group)); + { + Ogre::Entity *ent = objects.mEntities[i]; + ent->getUserObjectBindings().setUserAny(Ogre::Any(group)); + if(mViewMode == VM_FirstPerson) + ent->setCastShadows(false); + } for(size_t i = 0;i < objects.mParticles.size();i++) objects.mParticles[i]->getUserObjectBindings().setUserAny(Ogre::Any(group)); From 4df1f198a7c06723e817ee7503db731e84407319 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 12 Jul 2013 22:30:25 -0700 Subject: [PATCH 14/36] Avoid a map lookup for every skin body part when updating --- apps/openmw/mwrender/npcanimation.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index ee27f710aa..b905a8bdce 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -357,11 +357,15 @@ void NpcAnimation::updateParts(bool forceupdate) } } - for (int part = ESM::PRT_Neck; part < ESM::PRT_Count; ++part) + const std::vector &parts = sRaceMapping[thisCombination]; + for(int part = ESM::PRT_Neck; part < ESM::PRT_Count; ++part) { - const ESM::BodyPart* bodypart = sRaceMapping[thisCombination][part]; - if (mPartPriorities[part] < 1 && bodypart) - addOrReplaceIndividualPart(part, -1,1, "meshes\\"+bodypart->mModel); + if(mPartPriorities[part] < 1) + { + const ESM::BodyPart* bodypart = parts[part]; + if(bodypart) + addOrReplaceIndividualPart(part, -1,1, "meshes\\"+bodypart->mModel); + } } } From 4cce466dc76feea1ae9a3e85e8da1db00bc803a2 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 13 Jul 2013 16:34:10 +0200 Subject: [PATCH 15/36] inverted region map y-axis --- apps/opencs/model/world/regionmap.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/world/regionmap.cpp b/apps/opencs/model/world/regionmap.cpp index a071bded86..697c5f4500 100644 --- a/apps/opencs/model/world/regionmap.cpp +++ b/apps/opencs/model/world/regionmap.cpp @@ -27,13 +27,15 @@ CSMWorld::RegionMap::CellDescription::CellDescription (const Record& cell) CSMWorld::RegionMap::CellIndex CSMWorld::RegionMap::getIndex (const QModelIndex& index) const { - return CellIndex (index.column()+mMin.first, index.row()+mMin.second); + return CellIndex (index.column()+mMin.first, + (mMax.second-mMin.second - index.row()-1)+mMin.second); } QModelIndex CSMWorld::RegionMap::getIndex (const CellIndex& index) const { // I hate you, Qt API naming scheme! - return QAbstractTableModel::index (index.second-mMin.second, index.first-mMin.first); + return QAbstractTableModel::index (mMax.second-mMin.second - (index.second-mMin.second)-1, + index.first-mMin.first); } void CSMWorld::RegionMap::buildRegions() From 4a562b585c7873f9598d3b4c96f57a38e4a26d13 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 13 Jul 2013 20:24:19 +0200 Subject: [PATCH 16/36] Fix journal window with background image != 512x256 --- files/mygui/openmw_journal.layout | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/files/mygui/openmw_journal.layout b/files/mygui/openmw_journal.layout index 58d2c2690a..34421d4313 100644 --- a/files/mygui/openmw_journal.layout +++ b/files/mygui/openmw_journal.layout @@ -4,9 +4,10 @@ - - - + + + + @@ -53,7 +54,7 @@ - + @@ -103,7 +104,6 @@ - From d6faad3dd299d218e0ffd140e65caf5fcf9768cd Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 13 Jul 2013 20:26:35 +0200 Subject: [PATCH 17/36] Same fix for the book window --- files/mygui/openmw_book.layout | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/files/mygui/openmw_book.layout b/files/mygui/openmw_book.layout index d9ac2f43ae..3d6322e74a 100644 --- a/files/mygui/openmw_book.layout +++ b/files/mygui/openmw_book.layout @@ -4,9 +4,9 @@ - - - + + + @@ -44,6 +44,7 @@ + From 2d7620e774e3dfc24d513ef28b466758cfe1b71e Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 13 Jul 2013 12:39:06 -0700 Subject: [PATCH 18/36] Rotate the neck when looking up and down in first-person --- apps/openmw/mwrender/animation.cpp | 1 + apps/openmw/mwrender/animation.hpp | 5 +++++ apps/openmw/mwrender/camera.cpp | 4 ++++ apps/openmw/mwrender/npcanimation.cpp | 8 ++++++++ 4 files changed, 18 insertions(+) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 286da2bc13..2d3b5942d0 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -45,6 +45,7 @@ void Animation::destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectL Animation::Animation(const MWWorld::Ptr &ptr) : mPtr(ptr) + , mCamera(NULL) , mInsert(NULL) , mSkelBase(NULL) , mAccumRoot(NULL) diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 9fd4a606c6..050aa925ae 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -11,6 +11,7 @@ namespace MWRender { +class Camera; class Animation { @@ -80,6 +81,7 @@ protected: typedef std::map AnimStateMap; MWWorld::Ptr mPtr; + Camera *mCamera; Ogre::SceneNode *mInsert; Ogre::Entity *mSkelBase; @@ -210,6 +212,9 @@ public: virtual void showWeapons(bool showWeapon); + void setCamera(Camera *cam) + { mCamera = cam; } + Ogre::Node *getNode(const std::string &name); }; diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index e71e694f96..42c78f7531 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -283,10 +283,14 @@ namespace MWRender // If we're switching to a new NpcAnimation, ensure the old one is // using a normal view mode if(mAnimation && mAnimation != anim) + { mAnimation->setViewMode(NpcAnimation::VM_Normal); + mAnimation->setCamera(NULL); + } mAnimation = anim; mAnimation->setViewMode(isFirstPerson() ? NpcAnimation::VM_FirstPerson : NpcAnimation::VM_Normal); + mAnimation->setCamera(this); } void Camera::setHeight(float height) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index b905a8bdce..dbefb8b3c5 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -14,6 +14,7 @@ #include "../mwbase/mechanicsmanager.hpp" #include "renderconst.hpp" +#include "camera.hpp" namespace MWRender @@ -415,6 +416,13 @@ Ogre::Vector3 NpcAnimation::runAnimation(float timepassed) Ogre::Vector3 ret = Animation::runAnimation(timepassed); Ogre::SkeletonInstance *baseinst = mSkelBase->getSkeleton(); + if(mViewMode == VM_FirstPerson && mCamera) + { + float pitch = mCamera->getPitch(); + Ogre::Node *node = baseinst->getBone("Bip01 Neck"); + node->pitch(Ogre::Radian(pitch*0.75f), Ogre::Node::TS_WORLD); + } + for(size_t i = 0;i < sPartListSize;i++) { Ogre::Entity *ent = mObjectParts[i].mSkelBase; From 5ee889e8b64d937c92a901e4f3310e7084b242ca Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 13 Jul 2013 16:12:38 -0700 Subject: [PATCH 19/36] Attach the camera to the Head node in first-person --- apps/openmw/mwrender/animation.cpp | 36 ++++++++++++++++ apps/openmw/mwrender/animation.hpp | 10 +++++ apps/openmw/mwrender/camera.cpp | 66 +++++++++++++++--------------- apps/openmw/mwrender/camera.hpp | 5 +-- 4 files changed, 80 insertions(+), 37 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 2d3b5942d0..a5ca5b81cb 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -115,7 +115,23 @@ void Animation::setObjectRoot(Ogre::SceneNode *node, const std::string &model, b Ogre::Skeleton::BoneIterator boneiter = skelinst->getBoneIterator(); while(boneiter.hasMoreElements()) boneiter.getNext()->setManuallyControlled(true); + + // Reattach any objects that have been attached to this one + ObjectAttachMap::iterator iter = mAttachedObjects.begin(); + while(iter != mAttachedObjects.end()) + { + if(!skelinst->hasBone(iter->second)) + mAttachedObjects.erase(iter++); + else + { + mSkelBase->attachObjectToBone(iter->second, iter->first); + iter++; + } + } } + else + mAttachedObjects.clear(); + for(size_t i = 0;i < mObjectRoot.mControllers.size();i++) { if(mObjectRoot.mControllers[i].getSource().isNull()) @@ -741,4 +757,24 @@ bool Animation::isPriorityActive(int priority) const return false; } +Ogre::TagPoint *Animation::attachObjectToBone(const Ogre::String &bonename, Ogre::MovableObject *obj) +{ + Ogre::TagPoint *tag = NULL; + Ogre::SkeletonInstance *skel = (mSkelBase ? mSkelBase->getSkeleton() : NULL); + if(skel && skel->hasBone(bonename)) + { + tag = mSkelBase->attachObjectToBone(bonename, obj); + mAttachedObjects[obj] = bonename; + } + return tag; +} + +void Animation::detachObjectFromBone(Ogre::MovableObject *obj) +{ + ObjectAttachMap::iterator iter = mAttachedObjects.find(obj); + if(iter != mAttachedObjects.end()) + mAttachedObjects.erase(iter); + mSkelBase->detachObjectFromBone(obj); +} + } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 050aa925ae..f87fade556 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -80,6 +80,8 @@ protected: }; typedef std::map AnimStateMap; + typedef std::map ObjectAttachMap; + MWWorld::Ptr mPtr; Camera *mCamera; @@ -96,6 +98,8 @@ protected: Ogre::SharedPtr mAnimationValuePtr[sNumGroups]; + ObjectAttachMap mAttachedObjects; + float mAnimVelocity; float mAnimSpeedMult; @@ -216,6 +220,12 @@ public: { mCamera = cam; } Ogre::Node *getNode(const std::string &name); + + // Attaches the given object to a bone on this object's base skeleton. If the bone doesn't + // exist, the object isn't attached and NULL is returned. The returned TagPoint is only + // valid until the next setObjectRoot call. + Ogre::TagPoint *attachObjectToBone(const Ogre::String &bonename, Ogre::MovableObject *obj); + void detachObjectFromBone(Ogre::MovableObject *obj); }; } diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index 42c78f7531..d226c577cd 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" @@ -57,10 +58,10 @@ namespace MWRender Ogre::Quaternion xr(Ogre::Radian(getPitch() + Ogre::Math::HALF_PI), Ogre::Vector3::UNIT_X); if (!mVanity.enabled && !mPreviewMode) { - mCameraNode->setOrientation(xr); + mCamera->getParentNode()->setOrientation(xr); } else { Ogre::Quaternion zr(Ogre::Radian(getYaw()), Ogre::Vector3::NEGATIVE_UNIT_Z); - mCameraNode->setOrientation(zr * xr); + mCamera->getParentNode()->setOrientation(zr * xr); } } @@ -112,14 +113,12 @@ namespace MWRender void Camera::toggleViewMode() { mFirstPersonView = !mFirstPersonView; - mAnimation->setViewMode(isFirstPerson() ? NpcAnimation::VM_FirstPerson : - NpcAnimation::VM_Normal); + processViewChange(); + if (mFirstPersonView) { mCamera->setPosition(0.f, 0.f, 0.f); - setLowHeight(false); } else { mCamera->setPosition(0.f, 0.f, mCameraDistance); - setLowHeight(true); } } @@ -139,21 +138,16 @@ namespace MWRender return true; mVanity.enabled = enable; - mAnimation->setViewMode(isFirstPerson() ? NpcAnimation::VM_FirstPerson : - NpcAnimation::VM_Normal); + processViewChange(); float offset = mPreviewCam.offset; Ogre::Vector3 rot(0.f, 0.f, 0.f); if (mVanity.enabled) { rot.x = Ogre::Degree(-30.f).valueRadians(); mMainCam.offset = mCamera->getPosition().z; - - setLowHeight(true); } else { rot.x = getPitch(); offset = mMainCam.offset; - - setLowHeight(!mFirstPersonView); } rot.z = getYaw(); @@ -169,20 +163,15 @@ namespace MWRender return; mPreviewMode = enable; - mAnimation->setViewMode(isFirstPerson() ? NpcAnimation::VM_FirstPerson : - NpcAnimation::VM_Normal); + processViewChange(); float offset = mCamera->getPosition().z; if (mPreviewMode) { mMainCam.offset = offset; offset = mPreviewCam.offset; - - setLowHeight(true); } else { mPreviewCam.offset = offset; offset = mMainCam.offset; - - setLowHeight(!mFirstPersonView); } mCamera->setPosition(0.f, 0.f, offset); @@ -286,27 +275,45 @@ namespace MWRender { mAnimation->setViewMode(NpcAnimation::VM_Normal); mAnimation->setCamera(NULL); + mAnimation->detachObjectFromBone(mCamera); } mAnimation = anim; - mAnimation->setViewMode(isFirstPerson() ? NpcAnimation::VM_FirstPerson : - NpcAnimation::VM_Normal); mAnimation->setCamera(this); + + processViewChange(); } - void Camera::setHeight(float height) + void Camera::processViewChange() { - mHeight = height; - mCameraNode->setPosition(0.f, 0.f, mHeight); + mAnimation->detachObjectFromBone(mCamera); + mCamera->detachFromParent(); + + if(isFirstPerson()) + { + mAnimation->setViewMode(NpcAnimation::VM_FirstPerson); + Ogre::TagPoint *tag = mAnimation->attachObjectToBone("Head", mCamera); + tag->setInheritOrientation(false); + } + else + { + mAnimation->setViewMode(NpcAnimation::VM_Normal); + mCameraNode->attachObject(mCamera); + } } float Camera::getHeight() { - return mHeight * mTrackingPtr.getRefData().getBaseNode()->getScale().z; + if(mCamera->isParentTagPoint()) + { + Ogre::TagPoint *tag = static_cast(mCamera->getParentNode()); + return tag->_getFullLocalTransform().getTrans().z; + } + return mCamera->getParentNode()->getPosition().z; } bool Camera::getPosition(Ogre::Vector3 &player, Ogre::Vector3 &camera) { - mCamera->getParentSceneNode ()->needUpdate(true); + mCamera->getParentSceneNode()->needUpdate(true); camera = mCamera->getRealPosition(); player = mTrackingPtr.getRefData().getBaseNode()->getPosition(); @@ -329,15 +336,6 @@ namespace MWRender mFreeLook = enable; } - void Camera::setLowHeight(bool low) - { - if (low) { - mCameraNode->setPosition(0.f, 0.f, mHeight * 0.85); - } else { - mCameraNode->setPosition(0.f, 0.f, mHeight); - } - } - bool Camera::isVanityOrPreviewModeEnabled() { return mPreviewMode || mVanity.enabled; diff --git a/apps/openmw/mwrender/camera.hpp b/apps/openmw/mwrender/camera.hpp index ad5e35f939..3418efcc91 100644 --- a/apps/openmw/mwrender/camera.hpp +++ b/apps/openmw/mwrender/camera.hpp @@ -46,8 +46,6 @@ namespace MWRender /// Updates sound manager listener data void updateListener(); - void setLowHeight(bool low = true); - public: Camera(Ogre::Camera *camera); ~Camera(); @@ -80,6 +78,8 @@ namespace MWRender bool isFirstPerson() const { return !(mVanity.enabled || mPreviewMode || !mFirstPersonView); } + void processViewChange(); + void update(float duration); /// Set camera distance for current mode. Don't work on 1st person view. @@ -93,7 +93,6 @@ namespace MWRender void setAnimation(NpcAnimation *anim); - void setHeight(float height); float getHeight(); /// Stores player and camera world positions in passed arguments From 3771e5839e9b007c4a984a87a9337e2810611b2c Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Sat, 13 Jul 2013 17:03:10 -0700 Subject: [PATCH 20/36] Allow some more third person fallbacks in first person --- apps/openmw/mwrender/npcanimation.cpp | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index dbefb8b3c5..92218a3f2b 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -336,9 +336,10 @@ void NpcAnimation::updateParts(bool forceupdate) { if(mViewMode == VM_FirstPerson && (bodypart.mData.mPart == ESM::BodyPart::MP_Hand || bodypart.mData.mPart == ESM::BodyPart::MP_Wrist || - bodypart.mData.mPart == ESM::BodyPart::MP_Forearm)) + bodypart.mData.mPart == ESM::BodyPart::MP_Forearm || + bodypart.mData.mPart == ESM::BodyPart::MP_Upperarm)) { - /* Allow 3rd person skins as a fallback for the forearms if 1st person is missing. */ + /* Allow 3rd person skins as a fallback for the arms if 1st person is missing. */ BodyPartMapType::const_iterator bIt = sBodyPartMap.lower_bound(BodyPartMapType::key_type(bodypart.mData.mPart)); while(bIt != sBodyPartMap.end() && bIt->first == bodypart.mData.mPart) { @@ -499,9 +500,31 @@ void NpcAnimation::addPartGroup(int group, int priority, const std::vectorisMale() && !part->mFemale.empty()) + { bodypart = partStore.search(part->mFemale+ext); + if(!bodypart && mViewMode == VM_FirstPerson) + { + bodypart = partStore.search(part->mFemale); + if(bodypart && !(bodypart->mData.mPart == ESM::BodyPart::MP_Hand || + bodypart->mData.mPart == ESM::BodyPart::MP_Wrist || + bodypart->mData.mPart == ESM::BodyPart::MP_Forearm || + bodypart->mData.mPart == ESM::BodyPart::MP_Upperarm)) + bodypart = NULL; + } + } if(!bodypart && !part->mMale.empty()) + { bodypart = partStore.search(part->mMale+ext); + if(!bodypart && mViewMode == VM_FirstPerson) + { + bodypart = partStore.search(part->mMale); + if(bodypart && !(bodypart->mData.mPart == ESM::BodyPart::MP_Hand || + bodypart->mData.mPart == ESM::BodyPart::MP_Wrist || + bodypart->mData.mPart == ESM::BodyPart::MP_Forearm || + bodypart->mData.mPart == ESM::BodyPart::MP_Upperarm)) + bodypart = NULL; + } + } if(bodypart) addOrReplaceIndividualPart(part->mPart, group, priority, "meshes\\"+bodypart->mModel); From a049638e7f86e83a8db6e04aaa6db022da6f2d15 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 14 Jul 2013 14:54:40 +0200 Subject: [PATCH 21/36] Fixes character previews getting shadowed randomly --- apps/openmw/mwrender/characterpreview.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index e9ecedc551..c6e6e158e7 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -44,9 +44,14 @@ namespace MWRender { mSceneMgr = Ogre::Root::getSingleton().createSceneManager(Ogre::ST_GENERIC); - /// \todo Read the fallback values from INIImporter (Inventory:Directional*) + // This is a dummy light to turn off shadows without having to use a separate set of shaders Ogre::Light* l = mSceneMgr->createLight(); l->setType (Ogre::Light::LT_DIRECTIONAL); + l->setDiffuseColour (Ogre::ColourValue(0,0,0)); + + /// \todo Read the fallback values from INIImporter (Inventory:Directional*) + l = mSceneMgr->createLight(); + l->setType (Ogre::Light::LT_DIRECTIONAL); l->setDirection (Ogre::Vector3(0.3, -0.7, 0.3)); l->setDiffuseColour (Ogre::ColourValue(1,1,1)); From 61661c865336eedf814be19ef4983ffe55c26331 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 14 Jul 2013 14:55:07 +0200 Subject: [PATCH 22/36] Fix first person meshes casting shadows --- apps/openmw/mwrender/npcanimation.cpp | 4 +--- apps/openmw/mwrender/renderconst.hpp | 3 +++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 92218a3f2b..f2df5ccd5f 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -374,14 +374,12 @@ void NpcAnimation::updateParts(bool forceupdate) NifOgre::ObjectList NpcAnimation::insertBoundedPart(const std::string &model, int group, const std::string &bonename) { NifOgre::ObjectList objects = NifOgre::Loader::createObjects(mSkelBase, bonename, mInsert, model); - setRenderProperties(objects, mVisibilityFlags, RQG_Main, RQG_Alpha); + setRenderProperties(objects, (mViewMode == VM_FirstPerson) ? RV_FirstPerson : mVisibilityFlags, RQG_Main, RQG_Alpha); for(size_t i = 0;i < objects.mEntities.size();i++) { Ogre::Entity *ent = objects.mEntities[i]; ent->getUserObjectBindings().setUserAny(Ogre::Any(group)); - if(mViewMode == VM_FirstPerson) - ent->setCastShadows(false); } for(size_t i = 0;i < objects.mParticles.size();i++) objects.mParticles[i]->getUserObjectBindings().setUserAny(Ogre::Any(group)); diff --git a/apps/openmw/mwrender/renderconst.hpp b/apps/openmw/mwrender/renderconst.hpp index 1d2cdf1ead..44599ebee2 100644 --- a/apps/openmw/mwrender/renderconst.hpp +++ b/apps/openmw/mwrender/renderconst.hpp @@ -59,6 +59,9 @@ enum VisibilityFlags // overlays, we only want these on the main render target RV_Overlay = 1024, + // First person meshes do not cast shadows + RV_FirstPerson = 2048, + RV_Map = RV_Terrain + RV_Statics + RV_StaticsSmall + RV_Misc + RV_Water }; From fa5198d7b24cc2f444d5379f20ca9b7c2873658a Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 14 Jul 2013 14:59:24 +0200 Subject: [PATCH 23/36] Fix an assertion --- apps/openmw/mwrender/animation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index a5ca5b81cb..f958b82860 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -73,7 +73,7 @@ Animation::~Animation() void Animation::setObjectRoot(Ogre::SceneNode *node, const std::string &model, bool baseonly) { - OgreAssert(mAnimSources.size() != 0, "Setting object root while animation sources are set!"); + OgreAssert(mAnimSources.size() == 0, "Setting object root while animation sources are set!"); if(!mInsert) mInsert = node->createChildSceneNode(); From 8407e2b3aa3a14edd5ef9f06266d0cfb914a198d Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 14 Jul 2013 23:54:28 +0200 Subject: [PATCH 24/36] Fixes the console sometimes receiving text after it was closed --- apps/openmw/mwgui/console.cpp | 71 +++++++++++++------------- apps/openmw/mwgui/console.hpp | 21 ++++---- apps/openmw/mwgui/windowmanagerimp.cpp | 4 +- files/mygui/openmw_console.layout | 1 + 4 files changed, 48 insertions(+), 49 deletions(-) diff --git a/apps/openmw/mwgui/console.cpp b/apps/openmw/mwgui/console.cpp index f438d5e093..3bc0de4cb3 100644 --- a/apps/openmw/mwgui/console.cpp +++ b/apps/openmw/mwgui/console.cpp @@ -101,59 +101,58 @@ namespace MWGui } Console::Console(int w, int h, bool consoleOnlyScripts) - : Layout("openmw_console.layout"), + : WindowBase("openmw_console.layout"), mCompilerContext (MWScript::CompilerContext::Type_Console), mConsoleOnlyScripts (consoleOnlyScripts) { setCoord(10,10, w-10, h/2); - getWidget(command, "edit_Command"); - getWidget(history, "list_History"); + getWidget(mCommandLine, "edit_Command"); + getWidget(mHistory, "list_History"); // Set up the command line box - command->eventEditSelectAccept += + mCommandLine->eventEditSelectAccept += newDelegate(this, &Console::acceptCommand); - command->eventKeyButtonPressed += + mCommandLine->eventKeyButtonPressed += newDelegate(this, &Console::keyPress); // Set up the log window - history->setOverflowToTheLeft(true); - history->setEditStatic(true); - history->setVisibleVScroll(true); + mHistory->setOverflowToTheLeft(true); + mHistory->setEditStatic(true); + mHistory->setVisibleVScroll(true); // compiler MWScript::registerExtensions (mExtensions, mConsoleOnlyScripts); mCompilerContext.setExtensions (&mExtensions); } - void Console::enable() + void Console::open() { - setVisible(true); - // Give keyboard focus to the combo box whenever the console is // turned on - MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(command); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCommandLine); } - void Console::disable() + void Console::close() { - setVisible(false); + // Apparently, hidden widgets can retain key focus + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(NULL); } void Console::setFont(const std::string &fntName) { - history->setFontName(fntName); - command->setFontName(fntName); + mHistory->setFontName(fntName); + mCommandLine->setFontName(fntName); } void Console::clearHistory() { - history->setCaption(""); + mHistory->setCaption(""); } void Console::print(const std::string &msg) { - history->addText(msg); + mHistory->addText(msg); } void Console::printOK(const std::string &msg) @@ -215,7 +214,7 @@ namespace MWGui { std::vector matches; listNames(); - command->setCaption(complete( command->getCaption(), matches )); + mCommandLine->setCaption(complete( mCommandLine->getCaption(), matches )); #if 0 int i = 0; for(std::vector::iterator it=matches.begin(); it < matches.end(); it++,i++ ) @@ -227,50 +226,50 @@ namespace MWGui #endif } - if(command_history.empty()) return; + if(mCommandHistory.empty()) return; // Traverse history with up and down arrows if(key == MyGUI::KeyCode::ArrowUp) { // If the user was editing a string, store it for later - if(current == command_history.end()) - editString = command->getCaption(); + if(mCurrent == mCommandHistory.end()) + mEditString = mCommandLine->getCaption(); - if(current != command_history.begin()) + if(mCurrent != mCommandHistory.begin()) { - current--; - command->setCaption(*current); + mCurrent--; + mCommandLine->setCaption(*mCurrent); } } else if(key == MyGUI::KeyCode::ArrowDown) { - if(current != command_history.end()) + if(mCurrent != mCommandHistory.end()) { - current++; + mCurrent++; - if(current != command_history.end()) - command->setCaption(*current); + if(mCurrent != mCommandHistory.end()) + mCommandLine->setCaption(*mCurrent); else // Restore the edit string - command->setCaption(editString); + mCommandLine->setCaption(mEditString); } } } void Console::acceptCommand(MyGUI::EditBox* _sender) { - const std::string &cm = command->getCaption(); + const std::string &cm = mCommandLine->getCaption(); if(cm.empty()) return; // Add the command to the history, and set the current pointer to // the end of the list - command_history.push_back(cm); - current = command_history.end(); - editString.clear(); + mCommandHistory.push_back(cm); + mCurrent = mCommandHistory.end(); + mEditString.clear(); execute (cm); - command->setCaption(""); + mCommandLine->setCaption(""); } std::string Console::complete( std::string input, std::vector &matches ) @@ -412,7 +411,7 @@ namespace MWGui setTitle("#{sConsoleTitle}"); mPtr = MWWorld::Ptr(); } - MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(command); + MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCommandLine); } void Console::onReferenceUnavailable() diff --git a/apps/openmw/mwgui/console.hpp b/apps/openmw/mwgui/console.hpp index b1d961ed20..8901763630 100644 --- a/apps/openmw/mwgui/console.hpp +++ b/apps/openmw/mwgui/console.hpp @@ -1,7 +1,6 @@ #ifndef MWGUI_CONSOLE_H #define MWGUI_CONSOLE_H -#include #include #include #include @@ -18,10 +17,11 @@ #include "../mwscript/interpretercontext.hpp" #include "referenceinterface.hpp" +#include "windowbase.hpp" namespace MWGui { - class Console : private OEngine::GUI::Layout, private Compiler::ErrorHandler, public ReferenceInterface + class Console : public WindowBase, private Compiler::ErrorHandler, public ReferenceInterface { private: @@ -55,21 +55,20 @@ namespace MWGui public: - MyGUI::EditBox* command; - MyGUI::EditBox* history; + MyGUI::EditBox* mCommandLine; + MyGUI::EditBox* mHistory; typedef std::list StringList; // History of previous entered commands - StringList command_history; - StringList::iterator current; - std::string editString; + StringList mCommandHistory; + StringList::iterator mCurrent; + std::string mEditString; Console(int w, int h, bool consoleOnlyScripts); - void enable(); - - void disable(); + virtual void open(); + virtual void close(); void setFont(const std::string &fntName); @@ -91,7 +90,7 @@ namespace MWGui void execute (const std::string& command); - void executeFile (const std::string& command); + void executeFile (const std::string& path); private: diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 5dbdbf90a0..38bc0481d4 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -329,7 +329,7 @@ namespace MWGui mMap->setVisible(false); mMenu->setVisible(false); mStatsWindow->setVisible(false); - mConsole->disable(); + mConsole->setVisible(false); mJournal->setVisible(false); mDialogueWindow->setVisible(false); mContainerWindow->setVisible(false); @@ -398,7 +398,7 @@ namespace MWGui mInventoryWindow->setVisible(mInventoryWindow->pinned()); mSpellWindow->setVisible(mSpellWindow->pinned()); - mConsole->enable(); + mConsole->setVisible(true); break; case GM_Scroll: mScrollWindow->setVisible(true); diff --git a/files/mygui/openmw_console.layout b/files/mygui/openmw_console.layout index 732684ad12..6b0bb9f17d 100644 --- a/files/mygui/openmw_console.layout +++ b/files/mygui/openmw_console.layout @@ -4,6 +4,7 @@ + From c81b85207167ccc532e105b2362e0b990e3385fc Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 15 Jul 2013 00:31:18 +0200 Subject: [PATCH 25/36] Fixes inaccurate ray casts; rely on getCameraToViewportRay --- apps/openmw/mwrender/renderingmanager.cpp | 7 ----- apps/openmw/mwrender/renderingmanager.hpp | 2 -- apps/openmw/mwworld/physicssystem.cpp | 33 +++++++---------------- apps/openmw/mwworld/physicssystem.hpp | 6 ----- apps/openmw/mwworld/worldimp.cpp | 5 ---- 5 files changed, 10 insertions(+), 43 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index c8b496f6b1..c4b086b891 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -884,13 +884,6 @@ void RenderingManager::renderPlayer(const MWWorld::Ptr &ptr) MWBase::Environment::get().getWorld()->scaleObject(ptr, 1.f); } -void RenderingManager::getCameraData(Ogre::Vector3 &eyepos, float &pitch, float &yaw) -{ - eyepos = mCamera->getPosition(); - eyepos.z += mCamera->getHeight(); - mCamera->getSightAngles(pitch, yaw); -} - bool RenderingManager::vanityRotateCamera(const float *rot) { if(!mCamera->isVanityOrPreviewModeEnabled()) diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index b492a0db90..2111b89f19 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -87,8 +87,6 @@ public: bool vanityRotateCamera(const float *rot); - void getCameraData(Ogre::Vector3 &eyepos, float &pitch, float &yaw); - void setupPlayer(const MWWorld::Ptr &ptr); void renderPlayer(const MWWorld::Ptr &ptr); diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 7d63a2362e..3881523769 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -254,15 +254,12 @@ namespace MWWorld std::pair PhysicsSystem::getFacedHandle (MWWorld::World& world, float queryDistance) { - btVector3 dir(0, 1, 0); - dir = dir.rotate(btVector3(1, 0, 0), mCameraData.pitch); - dir = dir.rotate(btVector3(0, 0, 1), mCameraData.yaw); - dir.setX(-dir.x()); + Ray ray = mRender.getCamera()->getCameraToViewportRay(0.5, 0.5); - btVector3 origin(mCameraData.eyepos.x, - mCameraData.eyepos.y, - mCameraData.eyepos.z); - origin += dir * 5; + Ogre::Vector3 origin_ = ray.getOrigin(); + btVector3 origin(origin_.x, origin_.y, origin_.z); + Ogre::Vector3 dir_ = ray.getDirection().normalisedCopy(); + btVector3 dir(dir_.x, dir_.y, dir_.z); btVector3 dest = origin + dir * queryDistance; std::pair result; @@ -273,15 +270,12 @@ namespace MWWorld std::vector < std::pair > PhysicsSystem::getFacedHandles (float queryDistance) { - btVector3 dir(0, 1, 0); - dir = dir.rotate(btVector3(1, 0, 0), mCameraData.pitch); - dir = dir.rotate(btVector3(0, 0, 1), mCameraData.yaw); - dir.setX(-dir.x()); + Ray ray = mRender.getCamera()->getCameraToViewportRay(0.5, 0.5); - btVector3 origin(mCameraData.eyepos.x, - mCameraData.eyepos.y, - mCameraData.eyepos.z); - origin += dir * 5; + Ogre::Vector3 origin_ = ray.getOrigin(); + btVector3 origin(origin_.x, origin_.y, origin_.z); + Ogre::Vector3 dir_ = ray.getDirection().normalisedCopy(); + btVector3 dir(dir_.x, dir_.y, dir_.z); btVector3 dest = origin + dir * queryDistance; std::vector < std::pair > results; @@ -543,11 +537,4 @@ namespace MWWorld return true; } - - void PhysicsSystem::updateCameraData(const Ogre::Vector3 &eyepos, float pitch, float yaw) - { - mCameraData.eyepos = eyepos; - mCameraData.pitch = pitch; - mCameraData.yaw = yaw; - } } diff --git a/apps/openmw/mwworld/physicssystem.hpp b/apps/openmw/mwworld/physicssystem.hpp index 7b48f880ee..d7e8532601 100644 --- a/apps/openmw/mwworld/physicssystem.hpp +++ b/apps/openmw/mwworld/physicssystem.hpp @@ -77,13 +77,7 @@ namespace MWWorld bool getObjectAABB(const MWWorld::Ptr &ptr, Ogre::Vector3 &min, Ogre::Vector3 &max); - void updateCameraData(const Ogre::Vector3 &eyepos, float pitch, float yaw); - private: - struct { - Ogre::Vector3 eyepos; - float pitch, yaw; - } mCameraData; OEngine::Render::OgreRenderer &mRender; OEngine::Physic::PhysicEngine* mEngine; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 5c7a400503..a479c9a141 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1234,11 +1234,6 @@ namespace MWWorld mWorldScene->update (duration, paused); - float pitch, yaw; - Ogre::Vector3 eyepos; - mRendering->getCameraData(eyepos, pitch, yaw); - mPhysics->updateCameraData(eyepos, pitch, yaw); - performUpdateSceneQueries (); updateWindowManager (); From d4739a451aad8fd551c9c02f19d7afc4ed005746 Mon Sep 17 00:00:00 2001 From: vorenon Date: Mon, 15 Jul 2013 02:18:24 +0200 Subject: [PATCH 26/36] added ability to close container windows with the activation key --- apps/openmw/mwinput/inputmanagerimp.cpp | 35 ++++++++++++++++++++----- apps/openmw/mwinput/inputmanagerimp.hpp | 1 + 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index f19f634589..59e92eded4 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -172,8 +172,8 @@ namespace MWInput exitNow(); break; case A_Screenshot: - screenshot(); - break; + screenshot() +; break; case A_Inventory: toggleInventory (); break; @@ -182,11 +182,14 @@ namespace MWInput break; case A_Activate: resetIdleTime(); - if( MWBase::Environment::get().getWindowManager()->isGuiMode()) { - // Pressing the activation key when a messagebox is prompting for "ok" will activate the ok button - MWBase::Environment::get().getWindowManager()->enterPressed(); - } - activate(); + + if (mWindows.getMode() == MWGui::GM_Container) { + toggleContainer (); + } else if (MWBase::Environment::get().getWindowManager()->isGuiMode()) { + MWBase::Environment::get().getWindowManager()->enterPressed(); + } else { + activate(); + } break; case A_Journal: toggleJournal (); @@ -674,6 +677,24 @@ namespace MWInput // .. but don't touch any other mode, except container. } + void InputManager::toggleContainer() + { + if (MyGUI::InputManager::getInstance ().isModalAny()) + return; + + bool gameMode = !mWindows.isGuiMode(); + + if(!gameMode) + { + if (mWindows.getMode() == MWGui::GM_Container) + mWindows.popGuiMode(); + else + mWindows.pushGuiMode(MWGui::GM_Container); + } + + } + + void InputManager::toggleConsole() { if (MyGUI::InputManager::getInstance ().isModalAny()) diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index f463de811a..0c7940fd65 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -170,6 +170,7 @@ namespace MWInput void toggleSpell(); void toggleWeapon(); void toggleInventory(); + void toggleContainer(); void toggleConsole(); void screenshot(); void toggleJournal(); From 33779ae23c3deec5f43d6c61f3a97b45f4a0f6c4 Mon Sep 17 00:00:00 2001 From: vorenon Date: Mon, 15 Jul 2013 02:23:18 +0200 Subject: [PATCH 27/36] oops --- apps/openmw/mwinput/inputmanagerimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 59e92eded4..4d4d97adbd 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -173,7 +173,7 @@ namespace MWInput break; case A_Screenshot: screenshot() -; break; + break; case A_Inventory: toggleInventory (); break; From 66eb82b36323be86fc70ff5a454a4ed14a80c224 Mon Sep 17 00:00:00 2001 From: vorenon Date: Mon, 15 Jul 2013 02:26:22 +0200 Subject: [PATCH 28/36] oops again --- apps/openmw/mwinput/inputmanagerimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 4d4d97adbd..d2aa6e51e9 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -172,7 +172,7 @@ namespace MWInput exitNow(); break; case A_Screenshot: - screenshot() + screenshot(); break; case A_Inventory: toggleInventory (); From daf9dca12196874db2d541e16ab34269e1c19c56 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 15 Jul 2013 20:37:36 +0200 Subject: [PATCH 29/36] Fix exception when starting a new game --- apps/openmw/mwrender/camera.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index d226c577cd..b763d84ac9 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -82,6 +82,7 @@ namespace MWRender mCameraNode->getCreator()->destroySceneNode(mCameraNode); } mCameraNode = node; + mCamera->detachFromParent(); mCameraNode->attachObject(mCamera); } From 94d45e1518d0650ef5940325d320dc92df3b4493 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 15 Jul 2013 23:21:01 +0200 Subject: [PATCH 30/36] Fix twohanded weapons not unequipping when equipping a torch --- apps/openmw/mwclass/light.cpp | 23 +++++++++++++++++++++++ apps/openmw/mwclass/light.hpp | 2 ++ 2 files changed, 25 insertions(+) diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index ccc9d1de6d..d88eec71be 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -215,4 +215,27 @@ namespace MWClass ptr.get(); return ref->mBase->mData.mWeight; } + + std::pair Light::canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const + { + MWWorld::InventoryStore& invStore = MWWorld::Class::get(npc).getInventoryStore(npc); + MWWorld::ContainerStoreIterator weapon = invStore.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + + if(weapon == invStore.end()) + return std::make_pair(1,""); + + /// \todo the 2h check is repeated many times; put it in a function + if(weapon->getTypeName() == typeid(ESM::Weapon).name() && + (weapon->get()->mBase->mData.mType == ESM::Weapon::LongBladeTwoHand || + weapon->get()->mBase->mData.mType == ESM::Weapon::BluntTwoClose || + weapon->get()->mBase->mData.mType == ESM::Weapon::BluntTwoWide || + weapon->get()->mBase->mData.mType == ESM::Weapon::SpearTwoWide || + weapon->get()->mBase->mData.mType == ESM::Weapon::AxeTwoHand || + weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanBow || + weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow)) + { + return std::make_pair(3,""); + } + return std::make_pair(1,""); + } } diff --git a/apps/openmw/mwclass/light.hpp b/apps/openmw/mwclass/light.hpp index 7f747ef483..79d662763b 100644 --- a/apps/openmw/mwclass/light.hpp +++ b/apps/openmw/mwclass/light.hpp @@ -61,6 +61,8 @@ namespace MWClass virtual float getWeight (const MWWorld::Ptr& ptr) const; virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; + + std::pair canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const; }; } From 4ae65c20e630a136f1f8e6431ba4f1fee25d54e1 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 15 Jul 2013 22:56:23 -0700 Subject: [PATCH 31/36] Separate movement and idle states This allows us to better handle cases of "missing" animations. Mainly for first-person, but also for spells and certain weapon types. --- apps/openmw/mwmechanics/character.cpp | 338 ++++++++++++++++---------- apps/openmw/mwmechanics/character.hpp | 11 +- 2 files changed, 214 insertions(+), 135 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index d3dbb93256..0e15652cc7 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -39,62 +39,65 @@ namespace MWMechanics { -static const struct StateInfo { +struct StateInfo { CharacterState state; const char groupname[32]; - Priority priority; - bool loops; -} sStateList[] = { - { CharState_Idle, "idle", Priority_Default, true }, - { CharState_Idle2, "idle2", Priority_Default, true }, - { CharState_Idle3, "idle3", Priority_Default, true }, - { CharState_Idle4, "idle4", Priority_Default, true }, - { CharState_Idle5, "idle5", Priority_Default, true }, - { CharState_Idle6, "idle6", Priority_Default, true }, - { CharState_Idle7, "idle7", Priority_Default, true }, - { CharState_Idle8, "idle8", Priority_Default, true }, - { CharState_Idle9, "idle9", Priority_Default, true }, - { CharState_IdleSwim, "idleswim", Priority_Default, true }, - { CharState_IdleSneak, "idlesneak", Priority_Default, true }, +}; - { CharState_WalkForward, "walkforward", Priority_Default, true }, - { CharState_WalkBack, "walkback", Priority_Default, true }, - { CharState_WalkLeft, "walkleft", Priority_Default, true }, - { CharState_WalkRight, "walkright", Priority_Default, true }, +static const StateInfo sStateList[] = { + { CharState_Idle, "idle" }, + { CharState_Idle2, "idle2" }, + { CharState_Idle3, "idle3" }, + { CharState_Idle4, "idle4" }, + { CharState_Idle5, "idle5" }, + { CharState_Idle6, "idle6" }, + { CharState_Idle7, "idle7" }, + { CharState_Idle8, "idle8" }, + { CharState_Idle9, "idle9" }, + { CharState_IdleSwim, "idleswim" }, + { CharState_IdleSneak, "idlesneak" }, - { CharState_SwimWalkForward, "swimwalkforward", Priority_Default, true }, - { CharState_SwimWalkBack, "swimwalkback", Priority_Default, true }, - { CharState_SwimWalkLeft, "swimwalkleft", Priority_Default, true }, - { CharState_SwimWalkRight, "swimwalkright", Priority_Default, true }, - - { CharState_RunForward, "runforward", Priority_Default, true }, - { CharState_RunBack, "runback", Priority_Default, true }, - { CharState_RunLeft, "runleft", Priority_Default, true }, - { CharState_RunRight, "runright", Priority_Default, true }, - - { CharState_SwimRunForward, "swimrunforward", Priority_Default, true }, - { CharState_SwimRunBack, "swimrunback", Priority_Default, true }, - { CharState_SwimRunLeft, "swimrunleft", Priority_Default, true }, - { CharState_SwimRunRight, "swimrunright", Priority_Default, true }, - - { CharState_SneakForward, "sneakforward", Priority_Default, true }, - { CharState_SneakBack, "sneakback", Priority_Default, true }, - { CharState_SneakLeft, "sneakleft", Priority_Default, true }, - { CharState_SneakRight, "sneakright", Priority_Default, true }, - - { CharState_TurnLeft, "turnleft", Priority_Default, true }, - { CharState_TurnRight, "turnright", Priority_Default, true }, - - { CharState_Jump, "jump", Priority_Default, true }, - - { CharState_Death1, "death1", Priority_Death, false }, - { CharState_Death2, "death2", Priority_Death, false }, - { CharState_Death3, "death3", Priority_Death, false }, - { CharState_Death4, "death4", Priority_Death, false }, - { CharState_Death5, "death5", Priority_Death, false }, + { CharState_Death1, "death1" }, + { CharState_Death2, "death2" }, + { CharState_Death3, "death3" }, + { CharState_Death4, "death4" }, + { CharState_Death5, "death5" }, }; static const StateInfo *sStateListEnd = &sStateList[sizeof(sStateList)/sizeof(sStateList[0])]; + +static const StateInfo sMovementList[] = { + { CharState_WalkForward, "walkforward" }, + { CharState_WalkBack, "walkback" }, + { CharState_WalkLeft, "walkleft" }, + { CharState_WalkRight, "walkright" }, + + { CharState_SwimWalkForward, "swimwalkforward" }, + { CharState_SwimWalkBack, "swimwalkback" }, + { CharState_SwimWalkLeft, "swimwalkleft" }, + { CharState_SwimWalkRight, "swimwalkright" }, + + { CharState_RunForward, "runforward" }, + { CharState_RunBack, "runback" }, + { CharState_RunLeft, "runleft" }, + { CharState_RunRight, "runright" }, + + { CharState_SwimRunForward, "swimrunforward" }, + { CharState_SwimRunBack, "swimrunback" }, + { CharState_SwimRunLeft, "swimrunleft" }, + { CharState_SwimRunRight, "swimrunright" }, + + { CharState_SneakForward, "sneakforward" }, + { CharState_SneakBack, "sneakback" }, + { CharState_SneakLeft, "sneakleft" }, + { CharState_SneakRight, "sneakright" }, + + { CharState_TurnLeft, "turnleft" }, + { CharState_TurnRight, "turnright" }, +}; +static const StateInfo *sMovementListEnd = &sMovementList[sizeof(sMovementList)/sizeof(sMovementList[0])]; + + class FindCharState { CharacterState state; @@ -108,19 +111,18 @@ public: static const struct WeaponInfo { WeaponType type; - const char idlegroup[16]; - const char movementgroup[16]; - const char actiongroup[16]; + const char shortgroup[16]; + const char longgroup[16]; } sWeaponTypeList[] = { - { WeapType_HandToHand, "hh", "hh", "handtohand" }, - { WeapType_OneHand, "1h", "1h", "weapononehand" }, - { WeapType_TwoHand, "2c", "2c", "weapontwohand" }, - { WeapType_TwoWide, "2w", "2w", "weapontwowide" }, - { WeapType_BowAndArrow, "1h", "1h", "bowandarrow" }, - { WeapType_Crossbow, "crossbow", "1h", "crossbow" }, - { WeapType_ThowWeapon, "1h", "1h", "throwweapon" }, - { WeapType_PickProbe, "1h", "1h", "pickprobe" }, - { WeapType_Spell, "spell", "", "spellcast" }, + { WeapType_HandToHand, "hh", "handtohand" }, + { WeapType_OneHand, "1h", "weapononehand" }, + { WeapType_TwoHand, "2c", "weapontwohand" }, + { WeapType_TwoWide, "2w", "weapontwowide" }, + { WeapType_BowAndArrow, "1h", "bowandarrow" }, + { WeapType_Crossbow, "crossbow", "crossbow" }, + { WeapType_ThowWeapon, "1h", "throwweapon" }, + { WeapType_PickProbe, "1h", "pickprobe" }, + { WeapType_Spell, "spell", "spellcast" }, }; static const WeaponInfo *sWeaponTypeListEnd = &sWeaponTypeList[sizeof(sWeaponTypeList)/sizeof(sWeaponTypeList[0])]; @@ -135,31 +137,79 @@ public: }; -void CharacterController::getCurrentGroup(std::string &group, Priority &priority, bool &loops) const +void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force) { - std::string name; - const StateInfo *state = std::find_if(sStateList, sStateListEnd, FindCharState(mCharState)); - if(state == sStateListEnd) - throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(mCharState)); + const WeaponInfo *weap = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(mWeaponType)); - name = state->groupname; - priority = state->priority; - loops = state->loops; - - if(!(mCharState >= CharState_Death1) && mWeaponType != WeapType_None) + if(force || idle != mIdleState) { - const WeaponInfo *weap = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(mWeaponType)); - if(weap != sWeaponTypeListEnd) + mIdleState = idle; + + std::string idle; + // Only play "idleswim" or "idlesneak" if they exist. Otherwise, fallback to + // "idle"+weapon or "idle". + if(mIdleState == CharState_IdleSwim && mAnimation->hasAnimation("idleswim")) + idle = "idleswim"; + else if(mIdleState == CharState_IdleSneak && mAnimation->hasAnimation("idlesneak")) + idle = "idlesneak"; + else { - if(mCharState == CharState_Idle) - (group=name) += weap->idlegroup; - else - (group=name) += weap->movementgroup; + idle = "idle"; + if(weap != sWeaponTypeListEnd) + { + idle += weap->shortgroup; + if(!mAnimation->hasAnimation(idle)) + idle = "idle"; + } } + + mAnimation->disable(mCurrentIdle); + mCurrentIdle = idle; + mAnimation->play(mCurrentIdle, Priority_Default, MWRender::Animation::Group_All, false, + "start", "stop", 0.0f, ~0ul); } - if(group.empty() || !mAnimation->hasAnimation(group)) - group = (mAnimation->hasAnimation(name) ? name : std::string()); + if(force || movement != mMovementState) + { + mMovementState = movement; + + std::string movement; + MWRender::Animation::Group movegroup = MWRender::Animation::Group_All; + const StateInfo *movestate = std::find_if(sMovementList, sMovementListEnd, FindCharState(mMovementState)); + if(movestate != sMovementListEnd) + { + movement = movestate->groupname; + if(weap != sWeaponTypeListEnd && movement.find("swim") == std::string::npos) + { + movement += weap->shortgroup; + if(!mAnimation->hasAnimation(movement)) + { + movegroup = MWRender::Animation::Group_LowerBody; + movement = movestate->groupname; + } + } + + if(!mAnimation->hasAnimation(movement)) + { + std::string::size_type sneakpos = movement.find("sneak"); + if(sneakpos == std::string::npos) + movement.clear(); + else + { + movegroup = MWRender::Animation::Group_LowerBody; + movement.erase(sneakpos, 5); + if(!mAnimation->hasAnimation(movement)) + movement.clear(); + } + } + } + + mAnimation->disable(mCurrentMovement); + mCurrentMovement = movement; + if(!mCurrentMovement.empty()) + mAnimation->play(mCurrentMovement, Priority_Movement, movegroup, false, + "start", "stop", 0.0f, ~0ul); + } } @@ -167,13 +217,15 @@ void CharacterController::getWeaponGroup(WeaponType weaptype, std::string &group { const WeaponInfo *info = std::find_if(sWeaponTypeList, sWeaponTypeListEnd, FindWeaponType(weaptype)); if(info != sWeaponTypeListEnd) - group = info->actiongroup; + group = info->longgroup; } CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim, CharacterState state) : mPtr(ptr) , mAnimation(anim) + , mIdleState(CharState_Idle) + , mMovementState(CharState_None) , mCharState(state) , mWeaponType(WeapType_None) , mSkipAnim(false) @@ -196,12 +248,16 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim mAnimation->setAccumulation(Ogre::Vector3(0.0f)); } - std::string group; - Priority prio; - bool loops; - getCurrentGroup(group, prio, loops); - mAnimation->play(group, prio, MWRender::Animation::Group_All, false, - "start", "stop", 1.0f, loops ? (~(size_t)0) : 0); + refreshCurrentAnims(mIdleState, mMovementState, true); + if(mCharState >= CharState_Death1) + { + const StateInfo *state = std::find_if(sStateList, sStateListEnd, FindCharState(mCharState)); + if(state == sStateListEnd) + throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(mCharState)); + + mAnimation->play(state->groupname, Priority_Death, MWRender::Animation::Group_All, + false, "start", "stop", 1.0f, 0); + } } CharacterController::~CharacterController() @@ -248,9 +304,9 @@ void CharacterController::update(float duration, Movement &movement) speed = cls.getSpeed(mPtr); // advance athletics - if (vec.squaredLength() > 0 && mPtr.getRefData().getHandle() == "player") + if(vec.squaredLength() > 0 && mPtr.getRefData().getHandle() == "player") { - if (inwater) + if(inwater) { mSecondsOfSwimming += duration; while(mSecondsOfSwimming > 1) @@ -259,7 +315,7 @@ void CharacterController::update(float duration, Movement &movement) mSecondsOfSwimming -= 1; } } - else if (isrunning) + else if(isrunning) { mSecondsOfRunning += duration; while(mSecondsOfRunning > 1) @@ -292,54 +348,60 @@ void CharacterController::update(float duration, Movement &movement) //decrease fatigue by fFatigueJumpBase + (1 - normalizedEncumbrance) * fFatigueJumpMult; } - if(std::abs(vec.x/2.0f) > std::abs(vec.y) && speed > 0.0f) + vec.x *= speed; + vec.y *= speed; + + CharacterState movestate = CharState_None; + CharacterState idlestate = CharState_SpecialIdle; + bool forcestateupdate = false; + + if(std::abs(vec.x/2.0f) > std::abs(vec.y)) { if(vec.x > 0.0f) - setState(inwater ? (isrunning ? CharState_SwimRunRight : CharState_SwimWalkRight) - : (sneak ? CharState_SneakRight : (isrunning ? CharState_RunRight : CharState_WalkRight))); + movestate = (inwater ? (isrunning ? CharState_SwimRunRight : CharState_SwimWalkRight) + : (sneak ? CharState_SneakRight + : (isrunning ? CharState_RunRight : CharState_WalkRight))); else if(vec.x < 0.0f) - setState(inwater ? (isrunning ? CharState_SwimRunLeft : CharState_SwimWalkLeft) - : (sneak ? CharState_SneakLeft : (isrunning ? CharState_RunLeft : CharState_WalkLeft))); - - vec.x *= speed; - vec.y *= speed; + movestate = (inwater ? (isrunning ? CharState_SwimRunLeft : CharState_SwimWalkLeft) + : (sneak ? CharState_SneakLeft + : (isrunning ? CharState_RunLeft : CharState_WalkLeft))); } - else if(vec.y != 0.0f && speed > 0.0f) + else if(vec.y != 0.0f) { if(vec.y > 0.0f) - setState(inwater ? (isrunning ? CharState_SwimRunForward : CharState_SwimWalkForward) - : (sneak ? CharState_SneakForward : (isrunning ? CharState_RunForward : CharState_WalkForward))); + movestate = (inwater ? (isrunning ? CharState_SwimRunForward : CharState_SwimWalkForward) + : (sneak ? CharState_SneakForward + : (isrunning ? CharState_RunForward : CharState_WalkForward))); else if(vec.y < 0.0f) - setState(inwater ? (isrunning ? CharState_SwimRunBack : CharState_SwimWalkBack) - : (sneak ? CharState_SneakBack : (isrunning ? CharState_RunBack : CharState_WalkBack))); - - vec.x *= speed; - vec.y *= speed; + movestate = (inwater ? (isrunning ? CharState_SwimRunBack : CharState_SwimWalkBack) + : (sneak ? CharState_SneakBack + : (isrunning ? CharState_RunBack : CharState_WalkBack))); } else if(rot.z != 0.0f && !inwater && !sneak) { if(rot.z > 0.0f) - setState(CharState_TurnRight); + movestate = CharState_TurnRight; else if(rot.z < 0.0f) - setState(CharState_TurnLeft); + movestate = CharState_TurnLeft; } - else if(mAnimQueue.size() > 0) - { - if(mAnimQueue.size() > 1) - { - if(mAnimation->isPlaying(mAnimQueue.front().first) == false) - { - mAnimation->disable(mAnimQueue.front().first); - mAnimQueue.pop_front(); - mAnimation->play(mAnimQueue.front().first, Priority_Default, - MWRender::Animation::Group_All, false, - "start", "stop", 0.0f, mAnimQueue.front().second); - } + if(movestate != CharState_None) + clearAnimQueue(); + + if(mAnimQueue.size() == 0) + idlestate = (inwater ? CharState_IdleSwim : (sneak ? CharState_IdleSneak : CharState_Idle)); + else if(mAnimQueue.size() > 1) + { + if(mAnimation->isPlaying(mAnimQueue.front().first) == false) + { + mAnimation->disable(mAnimQueue.front().first); + mAnimQueue.pop_front(); + + mAnimation->play(mAnimQueue.front().first, Priority_Default, + MWRender::Animation::Group_All, false, + "start", "stop", 0.0f, mAnimQueue.front().second); } } - else - setState((inwater ? CharState_IdleSwim : (sneak ? CharState_IdleSneak : CharState_Idle))); vec *= duration; movement.mPosition[0] += vec.x; @@ -410,13 +472,15 @@ void CharacterController::update(float duration, Movement &movement) if(mUpdateWeapon) { + forcestateupdate = (mWeaponType != weaptype); mWeaponType = weaptype; - forceStateUpdate(); mUpdateWeapon = false; } if(weaptype != mWeaponType) { + forcestateupdate = true; + std::string weapgroup; if(weaptype == WeapType_None) { @@ -435,7 +499,6 @@ void CharacterController::update(float duration, Movement &movement) } mWeaponType = weaptype; - forceStateUpdate(); if(weapon != inv.end()) { @@ -461,8 +524,10 @@ void CharacterController::update(float duration, Movement &movement) else if(mAnimation->isPlaying("torch")) mAnimation->disable("torch"); } + + refreshCurrentAnims(idlestate, movestate, forcestateupdate); } - else if (cls.getCreatureStats(mPtr).isDead()) + else if(cls.getCreatureStats(mPtr).isDead()) { MWBase::Environment::get().getWorld()->enableActorCollision(mPtr, false); } @@ -506,7 +571,10 @@ void CharacterController::playGroup(const std::string &groupname, int mode, int clearAnimQueue(); mAnimQueue.push_back(std::make_pair(groupname, count-1)); - mCharState = CharState_SpecialIdle; + mAnimation->disable(mCurrentIdle); + mCurrentIdle.clear(); + + mIdleState = CharState_SpecialIdle; mAnimation->play(groupname, Priority_Default, MWRender::Animation::Group_All, false, ((mode==2) ? "loop start" : "start"), "stop", 0.0f, count-1); @@ -528,8 +596,7 @@ bool CharacterController::isAnimPlaying(const std::string &groupName) { if(mAnimation == NULL) return false; - else - return mAnimation->isPlaying(groupName); + return mAnimation->isPlaying(groupName); } @@ -556,12 +623,17 @@ void CharacterController::forceStateUpdate() return; clearAnimQueue(); - std::string group; - Priority prio; - bool loops; - getCurrentGroup(group, prio, loops); - mAnimation->play(group, prio, MWRender::Animation::Group_All, false, - "start", "stop", 0.0f, loops ? (~(size_t)0) : 0); + refreshCurrentAnims(mIdleState, mMovementState, true); + if(mCharState >= CharState_Death1) + { + const StateInfo *state = std::find_if(sStateList, sStateListEnd, FindCharState(mCharState)); + if(state == sStateListEnd) + throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(mCharState)); + + if(!mAnimation->getInfo(state->groupname)) + mAnimation->play(state->groupname, Priority_Death, MWRender::Animation::Group_All, + false, "start", "stop", 0.0f, 0); + } } } diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index cf5d6e8231..4f23101bb8 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -17,6 +17,7 @@ class Movement; enum Priority { Priority_Default, + Priority_Movement, Priority_Weapon, Priority_Torch, @@ -26,6 +27,8 @@ enum Priority { }; enum CharacterState { + CharState_None, + CharState_SpecialIdle, CharState_Idle, CharState_Idle2, @@ -100,6 +103,11 @@ class CharacterController typedef std::deque > AnimationQueue; AnimationQueue mAnimQueue; + CharacterState mIdleState; + std::string mCurrentIdle; + CharacterState mMovementState; + std::string mCurrentMovement; + CharacterState mCharState; WeaponType mWeaponType; bool mSkipAnim; @@ -111,8 +119,7 @@ class CharacterController float mSecondsOfSwimming; float mSecondsOfRunning; - // Gets an animation group name from the current character state, and whether it should loop. - void getCurrentGroup(std::string &group, Priority &prio, bool &loops) const; + void refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force=false); static void getWeaponGroup(WeaponType weaptype, std::string &group); From 06e631f2133ac028c847b670407ad3e1459e8a94 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 15 Jul 2013 23:43:33 -0700 Subject: [PATCH 32/36] Track death separately in the character controller --- apps/openmw/mwmechanics/actors.cpp | 15 +++--- apps/openmw/mwmechanics/character.cpp | 71 +++++++++++++++++++-------- apps/openmw/mwmechanics/character.hpp | 13 +++-- apps/openmw/mwmechanics/objects.cpp | 3 +- 4 files changed, 66 insertions(+), 36 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 082100b278..7af7e063cb 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -168,13 +168,10 @@ namespace MWMechanics void Actors::addActor (const MWWorld::Ptr& ptr) { // erase previous death events since we are currently only tracking them while in an active cell - MWWorld::Class::get (ptr).getCreatureStats (ptr).clearHasDied(); + MWWorld::Class::get(ptr).getCreatureStats(ptr).clearHasDied(); MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr); - if(!MWWorld::Class::get(ptr).getCreatureStats(ptr).isDead()) - mActors.insert(std::make_pair(ptr, new CharacterController(ptr, anim, CharState_Idle))); - else - mActors.insert(std::make_pair(ptr, new CharacterController(ptr, anim, CharState_Death1))); + mActors.insert(std::make_pair(ptr, new CharacterController(ptr, anim))); } void Actors::removeActor (const MWWorld::Ptr& ptr) @@ -228,8 +225,8 @@ namespace MWMechanics { if(!MWWorld::Class::get(iter->first).getCreatureStats(iter->first).isDead()) { - if(iter->second->getState() >= CharState_Death1) - iter->second->setState(CharState_Idle); + if(iter->second->isDead()) + iter->second->resurrect(); updateActor(iter->first, totalDuration); if(iter->first.getTypeName() == typeid(ESM::NPC).name()) @@ -256,10 +253,10 @@ namespace MWMechanics continue; } - if(iter->second->getState() >= CharState_Death1) + if(iter->second->isDead()) continue; - iter->second->setState(CharState_Death1); + iter->second->kill(); ++mDeathCount[MWWorld::Class::get(iter->first).getId(iter->first)]; diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 0e15652cc7..8c231107e6 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -221,12 +221,12 @@ void CharacterController::getWeaponGroup(WeaponType weaptype, std::string &group } -CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim, CharacterState state) +CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim) : mPtr(ptr) , mAnimation(anim) , mIdleState(CharState_Idle) , mMovementState(CharState_None) - , mCharState(state) + , mDeathState(CharState_None) , mWeaponType(WeapType_None) , mSkipAnim(false) , mSecondsOfRunning(0) @@ -241,6 +241,12 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim /* Accumulate along X/Y only for now, until we can figure out how we should * handle knockout and death which moves the character down. */ mAnimation->setAccumulation(Ogre::Vector3(1.0f, 1.0f, 0.0f)); + + if(MWWorld::Class::get(mPtr).getCreatureStats(mPtr).isDead()) + { + /* FIXME: Get the actual death state used. */ + mDeathState = CharState_Death1; + } } else { @@ -249,13 +255,14 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim } refreshCurrentAnims(mIdleState, mMovementState, true); - if(mCharState >= CharState_Death1) + if(mDeathState != CharState_None) { - const StateInfo *state = std::find_if(sStateList, sStateListEnd, FindCharState(mCharState)); + const StateInfo *state = std::find_if(sStateList, sStateListEnd, FindCharState(mDeathState)); if(state == sStateListEnd) - throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(mCharState)); + throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(mDeathState)); - mAnimation->play(state->groupname, Priority_Death, MWRender::Animation::Group_All, + mCurrentDeath = state->groupname; + mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All, false, "start", "stop", 1.0f, 0); } } @@ -608,15 +615,6 @@ void CharacterController::clearAnimQueue() } -void CharacterController::setState(CharacterState state) -{ - if(mCharState == state) - return; - mCharState = state; - - forceStateUpdate(); -} - void CharacterController::forceStateUpdate() { if(!mAnimation) @@ -624,16 +622,49 @@ void CharacterController::forceStateUpdate() clearAnimQueue(); refreshCurrentAnims(mIdleState, mMovementState, true); - if(mCharState >= CharState_Death1) + if(mDeathState != CharState_None) { - const StateInfo *state = std::find_if(sStateList, sStateListEnd, FindCharState(mCharState)); + const StateInfo *state = std::find_if(sStateList, sStateListEnd, FindCharState(mDeathState)); if(state == sStateListEnd) - throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(mCharState)); + throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(mDeathState)); - if(!mAnimation->getInfo(state->groupname)) - mAnimation->play(state->groupname, Priority_Death, MWRender::Animation::Group_All, + mCurrentDeath = state->groupname; + if(!mAnimation->getInfo(mCurrentDeath)) + mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All, false, "start", "stop", 0.0f, 0); } } +void CharacterController::kill() +{ + static const CharacterState deathstates[] = { + CharState_Death1, CharState_Death2, CharState_Death3, CharState_Death4, CharState_Death5 + }; + + if(mDeathState != CharState_None) + return; + + mDeathState = deathstates[(int)(rand()/((double)RAND_MAX+1.0)*5.0)]; + const StateInfo *state = std::find_if(sStateList, sStateListEnd, FindCharState(mDeathState)); + if(state == sStateListEnd) + throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(mDeathState)); + + mCurrentDeath = state->groupname; + if(mAnimation && !mAnimation->getInfo(mCurrentDeath)) + mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All, + false, "start", "stop", 0.0f, 0); +} + +void CharacterController::resurrect() +{ + if(mDeathState == CharState_None) + return; + + if(mAnimation) + mAnimation->disable(mCurrentDeath); + mCurrentDeath.empty(); + mDeathState = CharState_None; +} + + } diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 4f23101bb8..84f324fad7 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -108,7 +108,9 @@ class CharacterController CharacterState mMovementState; std::string mCurrentMovement; - CharacterState mCharState; + CharacterState mDeathState; + std::string mCurrentDeath; + WeaponType mWeaponType; bool mSkipAnim; @@ -126,7 +128,7 @@ class CharacterController void clearAnimQueue(); public: - CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim, CharacterState state); + CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim); virtual ~CharacterController(); void updatePtr(const MWWorld::Ptr &ptr); @@ -137,9 +139,10 @@ public: void skipAnim(); bool isAnimPlaying(const std::string &groupName); - void setState(CharacterState state); - CharacterState getState() const - { return mCharState; } + void kill(); + void resurrect(); + bool isDead() const + { return mDeathState != CharState_None; } void forceStateUpdate(); }; diff --git a/apps/openmw/mwmechanics/objects.cpp b/apps/openmw/mwmechanics/objects.cpp index 24d8a8bf7a..45760b68b6 100644 --- a/apps/openmw/mwmechanics/objects.cpp +++ b/apps/openmw/mwmechanics/objects.cpp @@ -17,8 +17,7 @@ Objects::Objects() void Objects::addObject(const MWWorld::Ptr& ptr) { MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr); - if(anim != NULL) - mObjects.insert(std::make_pair(ptr, CharacterController(ptr, anim, CharState_Idle))); + if(anim) mObjects.insert(std::make_pair(ptr, CharacterController(ptr, anim))); } void Objects::removeObject(const MWWorld::Ptr& ptr) From 3a1facefdfdb69793ac7d3ead533ecfaae0ea6e2 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 15 Jul 2013 23:47:04 -0700 Subject: [PATCH 33/36] Store a pointer to the character controller for non-actor objects --- apps/openmw/mwmechanics/objects.cpp | 18 ++++++++++++------ apps/openmw/mwmechanics/objects.hpp | 2 +- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwmechanics/objects.cpp b/apps/openmw/mwmechanics/objects.cpp index 45760b68b6..deebd8b39e 100644 --- a/apps/openmw/mwmechanics/objects.cpp +++ b/apps/openmw/mwmechanics/objects.cpp @@ -17,14 +17,17 @@ Objects::Objects() void Objects::addObject(const MWWorld::Ptr& ptr) { MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr); - if(anim) mObjects.insert(std::make_pair(ptr, CharacterController(ptr, anim))); + if(anim) mObjects.insert(std::make_pair(ptr, new CharacterController(ptr, anim))); } void Objects::removeObject(const MWWorld::Ptr& ptr) { PtrControllerMap::iterator iter = mObjects.find(ptr); if(iter != mObjects.end()) + { + delete iter->second; mObjects.erase(iter); + } } void Objects::updateObject(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr) @@ -32,10 +35,10 @@ void Objects::updateObject(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr) PtrControllerMap::iterator iter = mObjects.find(old); if(iter != mObjects.end()) { - CharacterController ctrl = iter->second; + CharacterController *ctrl = iter->second; mObjects.erase(iter); - ctrl.updatePtr(ptr); + ctrl->updatePtr(ptr); mObjects.insert(std::make_pair(ptr, ctrl)); } } @@ -46,7 +49,10 @@ void Objects::dropObjects (const MWWorld::Ptr::CellStore *cellStore) while(iter != mObjects.end()) { if(iter->first.getCell()==cellStore) + { + delete iter->second; mObjects.erase(iter++); + } else ++iter; } @@ -59,7 +65,7 @@ void Objects::update(float duration, bool paused) for(PtrControllerMap::iterator iter(mObjects.begin());iter != mObjects.end();++iter) { Movement movement; - iter->second.update(duration, movement); + iter->second->update(duration, movement); } } } @@ -68,13 +74,13 @@ void Objects::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& gro { PtrControllerMap::iterator iter = mObjects.find(ptr); if(iter != mObjects.end()) - iter->second.playGroup(groupName, mode, number); + iter->second->playGroup(groupName, mode, number); } void Objects::skipAnimation(const MWWorld::Ptr& ptr) { PtrControllerMap::iterator iter = mObjects.find(ptr); if(iter != mObjects.end()) - iter->second.skipAnim(); + iter->second->skipAnim(); } } diff --git a/apps/openmw/mwmechanics/objects.hpp b/apps/openmw/mwmechanics/objects.hpp index 7b1185a29a..5cdcdaa0af 100644 --- a/apps/openmw/mwmechanics/objects.hpp +++ b/apps/openmw/mwmechanics/objects.hpp @@ -16,7 +16,7 @@ namespace MWMechanics { class Objects { - typedef std::map PtrControllerMap; + typedef std::map PtrControllerMap; PtrControllerMap mObjects; public: From f296d13c20e89ec49869aa8f4194bfc9cf738e52 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 16 Jul 2013 00:43:31 -0700 Subject: [PATCH 34/36] Add a speed multiplier to the animation state --- apps/openmw/mwmechanics/character.cpp | 22 +++++++++++----------- apps/openmw/mwrender/animation.cpp | 9 ++++++--- apps/openmw/mwrender/animation.hpp | 9 ++++++--- apps/openmw/mwrender/characterpreview.cpp | 8 ++++---- apps/openmw/mwworld/player.cpp | 4 ++-- 5 files changed, 29 insertions(+), 23 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 8c231107e6..0d62e546a1 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -166,7 +166,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat mAnimation->disable(mCurrentIdle); mCurrentIdle = idle; mAnimation->play(mCurrentIdle, Priority_Default, MWRender::Animation::Group_All, false, - "start", "stop", 0.0f, ~0ul); + 1.0f, "start", "stop", 0.0f, ~0ul); } if(force || movement != mMovementState) @@ -208,7 +208,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat mCurrentMovement = movement; if(!mCurrentMovement.empty()) mAnimation->play(mCurrentMovement, Priority_Movement, movegroup, false, - "start", "stop", 0.0f, ~0ul); + 1.0f, "start", "stop", 0.0f, ~0ul); } } @@ -263,7 +263,7 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim mCurrentDeath = state->groupname; mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All, - false, "start", "stop", 1.0f, 0); + false, 1.0f, "start", "stop", 1.0f, 0); } } @@ -294,7 +294,7 @@ void CharacterController::update(float duration, Movement &movement) mAnimation->play(mAnimQueue.front().first, Priority_Default, MWRender::Animation::Group_All, false, - "start", "stop", 0.0f, mAnimQueue.front().second); + 1.0f, "start", "stop", 0.0f, mAnimQueue.front().second); } } } @@ -406,7 +406,7 @@ void CharacterController::update(float duration, Movement &movement) mAnimation->play(mAnimQueue.front().first, Priority_Default, MWRender::Animation::Group_All, false, - "start", "stop", 0.0f, mAnimQueue.front().second); + 1.0f, "start", "stop", 0.0f, mAnimQueue.front().second); } } @@ -494,7 +494,7 @@ void CharacterController::update(float duration, Movement &movement) getWeaponGroup(mWeaponType, weapgroup); mAnimation->play(weapgroup, Priority_Weapon, MWRender::Animation::Group_UpperBody, true, - "unequip start", "unequip stop", 0.0f, 0); + 1.0f, "unequip start", "unequip stop", 0.0f, 0); } else { @@ -502,7 +502,7 @@ void CharacterController::update(float duration, Movement &movement) mAnimation->showWeapons(false); mAnimation->play(weapgroup, Priority_Weapon, MWRender::Animation::Group_UpperBody, true, - "equip start", "equip stop", 0.0f, 0); + 1.0f, "equip start", "equip stop", 0.0f, 0); } mWeaponType = weaptype; @@ -526,7 +526,7 @@ void CharacterController::update(float duration, Movement &movement) if(!mAnimation->isPlaying("torch")) mAnimation->play("torch", Priority_Torch, MWRender::Animation::Group_LeftArm, false, - "start", "stop", 0.0f, (~(size_t)0)); + 1.0f, "start", "stop", 0.0f, (~(size_t)0)); } else if(mAnimation->isPlaying("torch")) mAnimation->disable("torch"); @@ -583,7 +583,7 @@ void CharacterController::playGroup(const std::string &groupname, int mode, int mIdleState = CharState_SpecialIdle; mAnimation->play(groupname, Priority_Default, - MWRender::Animation::Group_All, false, + MWRender::Animation::Group_All, false, 1.0f, ((mode==2) ? "loop start" : "start"), "stop", 0.0f, count-1); } else if(mode == 0) @@ -631,7 +631,7 @@ void CharacterController::forceStateUpdate() mCurrentDeath = state->groupname; if(!mAnimation->getInfo(mCurrentDeath)) mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All, - false, "start", "stop", 0.0f, 0); + false, 1.0f, "start", "stop", 0.0f, 0); } } @@ -652,7 +652,7 @@ void CharacterController::kill() mCurrentDeath = state->groupname; if(mAnimation && !mAnimation->getInfo(mCurrentDeath)) mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All, - false, "start", "stop", 0.0f, 0); + false, 1.0f, "start", "stop", 0.0f, 0); } void CharacterController::resurrect() diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index f958b82860..0888d391b3 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -517,7 +517,7 @@ bool Animation::handleTextKey(AnimState &state, const std::string &groupname, co } -void Animation::play(const std::string &groupname, int priority, int groups, bool autodisable, const std::string &start, const std::string &stop, float startpoint, size_t loops) +void Animation::play(const std::string &groupname, int priority, int groups, bool autodisable, float speedmult, const std::string &start, const std::string &stop, float startpoint, size_t loops) { if(!mSkelBase) return; @@ -555,6 +555,7 @@ void Animation::play(const std::string &groupname, int priority, int groups, boo if(reset(state, (*iter)->mTextKeys, groupname, start, stop, startpoint)) { state.mSource = *iter; + state.mSpeedMult = speedmult; state.mLoopCount = loops; state.mPlaying = true; state.mPriority = priority; @@ -650,12 +651,13 @@ void Animation::resetActiveGroups() } -bool Animation::getInfo(const std::string &groupname, float *complete, std::string *start, std::string *stop) const +bool Animation::getInfo(const std::string &groupname, float *complete, float *speedmult, std::string *start, std::string *stop) const { AnimStateMap::const_iterator iter = mStates.find(groupname); if(iter == mStates.end()) { if(complete) *complete = 0.0f; + if(speedmult) *speedmult = 0.0f; if(start) *start = ""; if(stop) *stop = ""; return false; @@ -663,6 +665,7 @@ bool Animation::getInfo(const std::string &groupname, float *complete, std::stri if(complete) *complete = (iter->second.mTime - iter->second.mStartKey->first) / (iter->second.mStopKey->first - iter->second.mStartKey->first); + if(speedmult) *speedmult = iter->second.mSpeedMult; if(start) *start = iter->second.mStartKey->second.substr(groupname.size()+2); if(stop) *stop = iter->second.mStopKey->second.substr(groupname.size()+2); return true; @@ -687,7 +690,7 @@ Ogre::Vector3 Animation::runAnimation(float duration) while(stateiter != mStates.end()) { AnimState &state = stateiter->second; - float timepassed = duration; + float timepassed = duration * state.mSpeedMult; while(state.mPlaying) { float targetTime = state.mTime + timepassed; diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index f87fade556..4955acb3bd 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -66,6 +66,7 @@ protected: NifOgre::TextKeyMap::const_iterator mNextKey; float mTime; + float mSpeedMult; bool mPlaying; size_t mLoopCount; @@ -74,7 +75,7 @@ protected: int mGroups; bool mAutoDisable; - AnimState() : mTime(0.0f), mPlaying(false), mLoopCount(0), + AnimState() : mTime(0.0f), mSpeedMult(1.0f), mPlaying(false), mLoopCount(0), mPriority(0), mGroups(0), mAutoDisable(true) { } }; @@ -183,6 +184,7 @@ public: * \param groups Bone groups to play the animation on. * \param autodisable Automatically disable the animation when it stops * playing. + * \param speedmult Speed multiplier for the animation. * \param start Key marker from which to start. * \param stop Key marker to stop at. * \param startpoint How far in between the two markers to start. 0 starts @@ -192,7 +194,7 @@ public: * otherwise it will use "start" and "stop". */ void play(const std::string &groupname, int priority, int groups, bool autodisable, - const std::string &start, const std::string &stop, + float speedmult, const std::string &start, const std::string &stop, float startpoint, size_t loops); /** Returns true if the named animation group is playing. */ @@ -201,11 +203,12 @@ public: /** Gets info about the given animation group. * \param groupname Animation group to check. * \param complete Stores completion amount (0 = at start key, 0.5 = half way between start and stop keys), etc. + * \param speedmult Stores the animation speed multiplier * \param start Stores the start key * \param stop Stores the stop key * \return True if the animation is active, false otherwise. */ - bool getInfo(const std::string &groupname, float *complete=NULL, std::string *start=NULL, std::string *stop=NULL) const; + bool getInfo(const std::string &groupname, float *complete=NULL, float *speedmult=NULL, std::string *start=NULL, std::string *stop=NULL) const; /** Disables the specified animation group; * \param groupname Animation group to disable. diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index c6e6e158e7..88d8704451 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -177,7 +177,7 @@ namespace MWRender if(groupname != mCurrentAnimGroup) { mCurrentAnimGroup = groupname; - mAnimation->play(mCurrentAnimGroup, 1, Animation::Group_All, false, "start", "stop", 0.0f, 0); + mAnimation->play(mCurrentAnimGroup, 1, Animation::Group_All, false, 1.0f, "start", "stop", 0.0f, 0); } MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); @@ -185,7 +185,7 @@ namespace MWRender { if(!mAnimation->getInfo("torch")) mAnimation->play("torch", 2, MWRender::Animation::Group_LeftArm, false, - "start", "stop", 0.0f, (~(size_t)0)); + 1.0f, "start", "stop", 0.0f, ~0ul); } else if(mAnimation->getInfo("torch")) mAnimation->disable("torch"); @@ -215,7 +215,7 @@ namespace MWRender mAnimation->showWeapons(true); mCurrentAnimGroup = "inventoryhandtohand"; - mAnimation->play(mCurrentAnimGroup, 1, Animation::Group_All, false, "start", "stop", 0.0f, 0); + mAnimation->play(mCurrentAnimGroup, 1, Animation::Group_All, false, 1.0f, "start", "stop", 0.0f, 0); } // -------------------------------------------------------------------------------------------------- @@ -252,7 +252,7 @@ namespace MWRender void RaceSelectionPreview::onSetup () { - mAnimation->play("idle", 1, Animation::Group_All, false, "start", "stop", 0.0f, 0); + mAnimation->play("idle", 1, Animation::Group_All, false, 1.0f, "start", "stop", 0.0f, 0); updateCamera(); } diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index cd1eb823da..4d6f50ffd2 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -163,13 +163,13 @@ namespace MWWorld { if (!target.isEmpty()) MWMechanics::Security(getPlayer()).pickLock(target, item, resultMessage, resultSound); - anim->play("pickprobe", MWMechanics::Priority_Weapon, MWRender::Animation::Group_UpperBody, true, "start", "stop", 0.0, 0); + anim->play("pickprobe", MWMechanics::Priority_Weapon, MWRender::Animation::Group_UpperBody, true, 1.0f, "start", "stop", 0.0, 0); } else if (item.getTypeName() == typeid(ESM::Probe).name()) { if (!target.isEmpty()) MWMechanics::Security(getPlayer()).probeTrap(target, item, resultMessage, resultSound); - anim->play("pickprobe", MWMechanics::Priority_Weapon, MWRender::Animation::Group_UpperBody, true, "start", "stop", 0.0, 0); + anim->play("pickprobe", MWMechanics::Priority_Weapon, MWRender::Animation::Group_UpperBody, true, 1.0f, "start", "stop", 0.0, 0); } if (!resultMessage.empty()) From a932a89e02d874bc4ae4ec0df17d3d7ef24d9dc4 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Tue, 16 Jul 2013 01:30:03 -0700 Subject: [PATCH 35/36] Specify the velocity-based animation speed multiplier when playing it --- apps/openmw/mwmechanics/character.cpp | 19 +++--- apps/openmw/mwmechanics/character.hpp | 2 + apps/openmw/mwrender/animation.cpp | 93 ++++++++++++++++----------- apps/openmw/mwrender/animation.hpp | 8 +-- 4 files changed, 71 insertions(+), 51 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 0d62e546a1..355a8f93d3 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -207,8 +207,13 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat mAnimation->disable(mCurrentMovement); mCurrentMovement = movement; if(!mCurrentMovement.empty()) + { + float vel, speed = 0.0f; + if(mMovementSpeed > 0.0f && (vel=mAnimation->getVelocity(mCurrentMovement)) > 1.0f) + speed = mMovementSpeed / vel; mAnimation->play(mCurrentMovement, Priority_Movement, movegroup, false, - 1.0f, "start", "stop", 0.0f, ~0ul); + speed, "start", "stop", 0.0f, ~0ul); + } } } @@ -226,6 +231,7 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim , mAnimation(anim) , mIdleState(CharState_Idle) , mMovementState(CharState_None) + , mMovementSpeed(0.0f) , mDeathState(CharState_None) , mWeaponType(WeapType_None) , mSkipAnim(false) @@ -281,7 +287,6 @@ void CharacterController::updatePtr(const MWWorld::Ptr &ptr) void CharacterController::update(float duration, Movement &movement) { const MWWorld::Class &cls = MWWorld::Class::get(mPtr); - float speed = 0.0f; if(!cls.isActor()) { @@ -308,7 +313,7 @@ void CharacterController::update(float duration, Movement &movement) bool sneak = cls.getStance(mPtr, MWWorld::Class::Sneak); Ogre::Vector3 vec = cls.getMovementVector(mPtr); Ogre::Vector3 rot = cls.getRotationVector(mPtr); - speed = cls.getSpeed(mPtr); + mMovementSpeed = cls.getSpeed(mPtr); // advance athletics if(vec.squaredLength() > 0 && mPtr.getRefData().getHandle() == "player") @@ -355,8 +360,8 @@ void CharacterController::update(float duration, Movement &movement) //decrease fatigue by fFatigueJumpBase + (1 - normalizedEncumbrance) * fFatigueJumpMult; } - vec.x *= speed; - vec.y *= speed; + vec.x *= mMovementSpeed; + vec.y *= mMovementSpeed; CharacterState movestate = CharState_None; CharacterState idlestate = CharState_SpecialIdle; @@ -541,11 +546,9 @@ void CharacterController::update(float duration, Movement &movement) if(mAnimation && !mSkipAnim) { - mAnimation->setSpeed(speed); - Ogre::Vector3 moved = mAnimation->runAnimation(duration); // Ensure we're moving in generally the right direction - if (speed > 0.f) + if(mMovementSpeed > 0.f) { if((movement.mPosition[0] < 0.0f && movement.mPosition[0] < moved.x*2.0f) || (movement.mPosition[0] > 0.0f && movement.mPosition[0] > moved.x*2.0f)) diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 84f324fad7..6dfd7e6ca6 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -105,8 +105,10 @@ class CharacterController CharacterState mIdleState; std::string mCurrentIdle; + CharacterState mMovementState; std::string mCurrentMovement; + float mMovementSpeed; CharacterState mDeathState; std::string mCurrentDeath; diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 0888d391b3..147e7c90ed 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -52,8 +52,6 @@ Animation::Animation(const MWWorld::Ptr &ptr) , mNonAccumRoot(NULL) , mNonAccumCtrl(NULL) , mAccumulate(0.0f) - , mAnimVelocity(0.0f) - , mAnimSpeedMult(1.0f) { for(size_t i = 0;i < sNumGroups;i++) mAnimationValuePtr[i].bind(OGRE_NEW AnimationValue(this)); @@ -245,7 +243,6 @@ void Animation::clearAnimSources() mAnimationValuePtr[i]->setAnimName(std::string()); mNonAccumCtrl = NULL; - mAnimVelocity = 0.0f; mAccumRoot = NULL; mNonAccumRoot = NULL; @@ -298,13 +295,6 @@ void Animation::setAccumulation(const Ogre::Vector3 &accum) mAccumulate = accum; } -void Animation::setSpeed(float speed) -{ - mAnimSpeedMult = 1.0f; - if(speed > 0.0f && mAnimVelocity > 1.0f) - mAnimSpeedMult = speed / mAnimVelocity; -} - void Animation::updatePtr(const MWWorld::Ptr &ptr) { @@ -344,6 +334,61 @@ float Animation::calcAnimVelocity(const NifOgre::TextKeyMap &keys, NifOgre::Node return 0.0f; } +float Animation::getVelocity(const std::string &groupname) const +{ + /* Look in reverse; last-inserted source has priority. */ + AnimSourceList::const_reverse_iterator animsrc(mAnimSources.rbegin()); + for(;animsrc != mAnimSources.rend();animsrc++) + { + const NifOgre::TextKeyMap &keys = (*animsrc)->mTextKeys; + if(findGroupStart(keys, groupname) != keys.end()) + break; + } + if(animsrc == mAnimSources.rend()) + return 0.0f; + + float velocity = 0.0f; + const NifOgre::TextKeyMap &keys = (*animsrc)->mTextKeys; + const std::vector >&ctrls = (*animsrc)->mControllers[0]; + for(size_t i = 0;i < ctrls.size();i++) + { + NifOgre::NodeTargetValue *dstval; + dstval = static_cast*>(ctrls[i].getDestination().getPointer()); + if(dstval->getNode() == mNonAccumRoot) + { + velocity = calcAnimVelocity(keys, dstval, mAccumulate, groupname); + break; + } + } + + // If there's no velocity, keep looking + if(!(velocity > 1.0f)) + { + AnimSourceList::const_reverse_iterator animiter = mAnimSources.rbegin(); + while(*animiter != *animsrc) + ++animiter; + + while(!(velocity > 1.0f) && ++animiter != mAnimSources.rend()) + { + const NifOgre::TextKeyMap &keys = (*animiter)->mTextKeys; + const std::vector >&ctrls = (*animiter)->mControllers[0]; + for(size_t i = 0;i < ctrls.size();i++) + { + NifOgre::NodeTargetValue *dstval; + dstval = static_cast*>(ctrls[i].getDestination().getPointer()); + if(dstval->getNode() == mNonAccumRoot) + { + velocity = calcAnimVelocity(keys, dstval, mAccumulate, groupname); + break; + } + } + } + } + + return velocity; +} + + static void updateBoneTree(const Ogre::SkeletonInstance *skelsrc, Ogre::Bone *bone) { if(skelsrc->hasBone(bone->getName())) @@ -599,9 +644,7 @@ void Animation::resetActiveGroups() mAnimationValuePtr[grp]->setAnimName((active == mStates.end()) ? std::string() : active->first); } - mNonAccumCtrl = NULL; - mAnimVelocity = 0.0f; if(!mNonAccumRoot || mAccumulate == Ogre::Vector3(0.0f)) return; @@ -619,35 +662,10 @@ void Animation::resetActiveGroups() dstval = static_cast*>(ctrls[i].getDestination().getPointer()); if(dstval->getNode() == mNonAccumRoot) { - mAnimVelocity = calcAnimVelocity(keys, dstval, mAccumulate, state->first); mNonAccumCtrl = dstval; break; } } - - // If there's no velocity, keep looking - if(!(mAnimVelocity > 1.0f)) - { - AnimSourceList::const_reverse_iterator animiter = mAnimSources.rbegin(); - while(*animiter != animsrc) - ++animiter; - - while(!(mAnimVelocity > 1.0f) && ++animiter != mAnimSources.rend()) - { - const NifOgre::TextKeyMap &keys = (*animiter)->mTextKeys; - const std::vector >&ctrls = (*animiter)->mControllers[0]; - for(size_t i = 0;i < ctrls.size();i++) - { - NifOgre::NodeTargetValue *dstval; - dstval = static_cast*>(ctrls[i].getDestination().getPointer()); - if(dstval->getNode() == mNonAccumRoot) - { - mAnimVelocity = calcAnimVelocity(keys, dstval, mAccumulate, state->first); - break; - } - } - } - } } @@ -685,7 +703,6 @@ Ogre::Vector3 Animation::runAnimation(float duration) { Ogre::Vector3 movement(0.0f); - duration *= mAnimSpeedMult; AnimStateMap::iterator stateiter = mStates.begin(); while(stateiter != mStates.end()) { diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 4955acb3bd..bc7fd3d22f 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -101,9 +101,6 @@ protected: ObjectAttachMap mAttachedObjects; - float mAnimVelocity; - float mAnimSpeedMult; - /* Sets the appropriate animations on the bone groups based on priority. */ void resetActiveGroups(); @@ -174,8 +171,6 @@ public: // should be on the scale of 0 to 1. void setAccumulation(const Ogre::Vector3 &accum); - void setSpeed(float speed); - /** Plays an animation. * \param groupname Name of the animation group to play. * \param priority Priority of the animation. The animation will play on @@ -215,6 +210,9 @@ public: */ void disable(const std::string &groupname); + /** Retrieves the velocity (in units per second) that the animation will move. */ + float getVelocity(const std::string &groupname) const; + virtual Ogre::Vector3 runAnimation(float duration); virtual void showWeapons(bool showWeapon); From 47f7bbd48a57a5a579e81a8bc4afdeaf2a651552 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 16 Jul 2013 12:08:35 +0200 Subject: [PATCH 36/36] fixed OpenCS crash during cell loading when loading multiple ESX files --- apps/opencs/model/world/refcollection.cpp | 2 -- components/esm/loadcell.cpp | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/opencs/model/world/refcollection.cpp b/apps/opencs/model/world/refcollection.cpp index fd191ba139..6a1e8045b3 100644 --- a/apps/opencs/model/world/refcollection.cpp +++ b/apps/opencs/model/world/refcollection.cpp @@ -16,8 +16,6 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool Cell& cell2 = base ? cell.mBase : cell.mModified; - cell2.restore (reader, 0); /// \todo fix the index - CellRef ref; while (cell2.getNextRef (reader, ref)) diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index b1f9986be4..dbd1fed6f0 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -122,7 +122,7 @@ void Cell::save(ESMWriter &esm) void Cell::restore(ESMReader &esm, int iCtx) const { - esm.restoreContext(mContextList[iCtx]); + esm.restoreContext(mContextList.at (iCtx)); } std::string Cell::getDescription() const