diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 11a675db0c..172211d7b5 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -6,6 +6,7 @@ #include "idtree.hpp" #include #include "nestedtablewrapper.hpp" +#include "../../view/render/cell.hpp" CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index, const QVariant& new_, QUndoCommand* parent) @@ -248,8 +249,9 @@ const CSMWorld::NestedTableWrapperBase& CSMWorld::NestedTableStoring::getOld() c // Current interface does not allow adding a non-blank row, so we're forced to modify // the whole record. CSMWorld::ModifyPathgridCommand::ModifyPathgridCommand(IdTree& model, - const std::string& id, int parentColumn, NestedTableWrapperBase* newRecord, QUndoCommand* parent) - : mModel(model), mId(id), mParentColumn(parentColumn), mRecord(newRecord) + const std::string& id, int parentColumn, CSVRender::Cell *cell, + NestedTableWrapperBase* newRecord, QUndoCommand* parent) + : mModel(model), mId(id), mParentColumn(parentColumn), mRecord(newRecord), mCell(cell) , QUndoCommand(parent), NestedTableStoring(model, id, parentColumn) { setText (("Modify Pathgrid record " + mId).c_str()); // FIXME: better description @@ -268,5 +270,6 @@ void CSMWorld::ModifyPathgridCommand::undo() mModel.setNestedTable(parentIndex, getOld()); - // FIXME: needs to tell the cell to redraw, possibly using signals + mCell->clearPathgrid(); + mCell->buildPathgrid(); } diff --git a/apps/opencs/model/world/commands.hpp b/apps/opencs/model/world/commands.hpp index 4e4de25c75..8661f987ad 100644 --- a/apps/opencs/model/world/commands.hpp +++ b/apps/opencs/model/world/commands.hpp @@ -17,6 +17,11 @@ class QModelIndex; class QAbstractItemModel; +namespace CSVRender +{ + class Cell; +} + namespace CSMWorld { class IdTable; @@ -204,11 +209,13 @@ namespace CSMWorld int mParentColumn; NestedTableWrapperBase* mRecord; + CSVRender::Cell *mCell; public: // if newEdges is NULL, only the paths are updated - ModifyPathgridCommand(IdTree& model, const std::string& id, int parentColumn, + ModifyPathgridCommand(IdTree& model, + const std::string& id, int parentColumn, CSVRender::Cell *cell, NestedTableWrapperBase* newRecord, QUndoCommand* parent = 0); virtual void redo(); diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index 698912344b..87f70b48eb 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -120,7 +120,7 @@ bool CSVRender::Cell::addObjects (int start, int end) CSVRender::Cell::Cell (CSMDoc::Document& document, Ogre::SceneManager *sceneManager, const std::string& id, boost::shared_ptr physics, const Ogre::Vector3& origin) : mDocument (document), mId (Misc::StringUtils::lowerCase (id)), mSceneMgr(sceneManager) -, mPhysics(physics), mX(0), mY(0), mPgIndex(-1), mModel(0), mProxyModel(0)// ,mPathgridId("") +, mPhysics(physics), mX(0), mY(0), mPgIndex(-1), mModel(0), mProxyModel(0) { mCellNode = sceneManager->getRootSceneNode()->createChildSceneNode(); mCellNode->setPosition (origin); @@ -153,32 +153,15 @@ CSVRender::Cell::Cell (CSMDoc::Document& document, Ogre::SceneManager *sceneMana } } - loadPathgrid(); + setupPathgrid(); + buildPathgrid(); } CSVRender::Cell::~Cell() { - // destroy manual objects - for(std::map, std::string>::iterator iter = mPgEdges.begin(); - iter != mPgEdges.end(); ++iter) - { - if(mSceneMgr->hasManualObject((*iter).second)) - { - Ogre::ManualObject *manual = mSceneMgr->getManualObject((*iter).second); - Ogre::SceneNode *node = manual->getParentSceneNode(); - mSceneMgr->destroyManualObject((*iter).second); - if(mSceneMgr->hasSceneNode(node->getName())) - mSceneMgr->destroySceneNode(node); - } - } + clearPathgrid(); destroyGridMaterials(); - for(std::map::iterator iter (mPgPoints.begin()); - iter!=mPgPoints.end(); ++iter) - { - delete iter->second; - } - delete mProxyModel; if (mTerrain.get()) @@ -322,62 +305,101 @@ float CSVRender::Cell::getTerrainHeightAt(const Ogre::Vector3 &pos) const } // FIXME: -// - updating indicies, including mData // - adding edges (need the ability to select a pathgrid and highlight) // - save to document & signals // - repainting edges while moving -void CSVRender::Cell::loadPathgrid() +void CSVRender::Cell::setupPathgrid() { - int worldsize = ESM::Land::REAL_SIZE; - const CSMWorld::SubCellCollection& pathgrids = mDocument.getData().getPathgrids(); int index = pathgrids.searchId(mId); if(index != -1) { - mPgIndex = index; // keep a copy to save from searching mId all the time - int col = pathgrids.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridPoints); + mPgIndex = index; // keep a copy to save from searching mId all the time + mModel = dynamic_cast( mDocument.getData().getTableModel(CSMWorld::UniversalId::Type_Pathgrid)); mProxyModel = new CSMWorld::NestedTableProxyModel (mModel->index(mPgIndex, col), CSMWorld::ColumnBase::Display_NestedHeader, mModel); - const CSMWorld::Pathgrid &pathgrid = pathgrids.getRecord(index).get(); + } +} - std::vector::const_iterator iter = pathgrid.mPoints.begin(); - for(index = 0; iter != pathgrid.mPoints.end(); ++iter, ++index) +void CSVRender::Cell::clearPathgrid() +{ + // destroy manual objects (edges) + for(std::map, std::string>::iterator iter = mPgEdges.begin(); + iter != mPgEdges.end(); ++iter) + { + if(mSceneMgr->hasManualObject((*iter).second)) { - std::string name = PathgridPoint::getName(pathgrid.mId, index); - - Ogre::Vector3 pos = - Ogre::Vector3(worldsize*mX+(*iter).mX, worldsize*mY+(*iter).mY, (*iter).mZ); - - mPgPoints.insert(std::make_pair(name, new PathgridPoint(name, mCellNode, pos, mPhysics))); + Ogre::ManualObject *manual = mSceneMgr->getManualObject((*iter).second); + Ogre::SceneNode *node = manual->getParentSceneNode(); + mSceneMgr->destroyManualObject((*iter).second); + if(mSceneMgr->hasSceneNode(node->getName())) + mSceneMgr->destroySceneNode(node); } + } + mPgEdges.clear(); - for(ESM::Pathgrid::EdgeList::const_iterator it = pathgrid.mEdges.begin(); - it != pathgrid.mEdges.end(); - ++it) - { - Ogre::SceneNode *node = mCellNode->createChildSceneNode(); - const ESM::Pathgrid::Edge &edge = *it; - const ESM::Pathgrid::Point &p0 = pathgrid.mPoints[edge.mV0]; - const ESM::Pathgrid::Point &p1 = pathgrid.mPoints[edge.mV1]; + // destroy points + for(std::map::iterator iter (mPgPoints.begin()); + iter!=mPgPoints.end(); ++iter) + { + delete iter->second; + } + mPgPoints.clear(); +} - std::ostringstream stream; - stream << pathgrid.mId << "_" << edge.mV0 << " " << edge.mV1; - std::string name = stream.str(); +// NOTE: getName() generates a string representation of mId+index to uniquely identify a +// pathgrid point. The trouble is that the index can change when a pathgrid point is deleted. +// Need a new way of uniquely identifying a pathgrid point. +// +// A workaround is to re-generate the pathgrids and edges each time a point is deleted or +// undo() is called (probably via a signal) +void CSVRender::Cell::buildPathgrid() +{ + if (!mModel) + return; - Ogre::ManualObject *line = createPathgridEdge(name, - Ogre::Vector3(worldsize*mX+p0.mX, worldsize*mY+p0.mY, p0.mZ), - Ogre::Vector3(worldsize*mX+p1.mX, worldsize*mY+p1.mY, p1.mZ)); - line->setVisibilityFlags(Element_Pathgrid); - node->attachObject(line); + const CSMWorld::SubCellCollection& pathgrids = mDocument.getData().getPathgrids(); + const CSMWorld::Pathgrid &pathgrid = pathgrids.getRecord(mPgIndex).get(); - mPgEdges.insert(std::make_pair(std::make_pair(edge.mV0, edge.mV1), name)); - } + int worldsize = ESM::Land::REAL_SIZE; + + std::vector::const_iterator iter = pathgrid.mPoints.begin(); + for(int index = 0; iter != pathgrid.mPoints.end(); ++iter, ++index) + { + std::string name = PathgridPoint::getName(pathgrid.mId, index); + + Ogre::Vector3 pos = + Ogre::Vector3(worldsize*mX+(*iter).mX, worldsize*mY+(*iter).mY, (*iter).mZ); + + mPgPoints.insert(std::make_pair(name, new PathgridPoint(name, mCellNode, pos, mPhysics))); + } + + for(ESM::Pathgrid::EdgeList::const_iterator it = pathgrid.mEdges.begin(); + it != pathgrid.mEdges.end(); + ++it) + { + Ogre::SceneNode *node = mCellNode->createChildSceneNode(); + const ESM::Pathgrid::Edge &edge = *it; + const ESM::Pathgrid::Point &p0 = pathgrid.mPoints[edge.mV0]; + const ESM::Pathgrid::Point &p1 = pathgrid.mPoints[edge.mV1]; + + std::ostringstream stream; + stream << pathgrid.mId << "_" << edge.mV0 << " " << edge.mV1; + std::string name = stream.str(); + + Ogre::ManualObject *line = createPathgridEdge(name, + Ogre::Vector3(worldsize*mX+p0.mX, worldsize*mY+p0.mY, p0.mZ), + Ogre::Vector3(worldsize*mX+p1.mX, worldsize*mY+p1.mY, p1.mZ)); + line->setVisibilityFlags(Element_Pathgrid); + node->attachObject(line); + + mPgEdges.insert(std::make_pair(std::make_pair(edge.mV0, edge.mV1), name)); } } @@ -411,7 +433,7 @@ void CSVRender::Cell::pathgridPointAdded(const Ogre::Vector3 &pos, bool interior // FIXME: probably will crash if this cell is deleted and undo() is actioned afterwards mDocument.getUndoStack().push(new CSMWorld::ModifyPathgridCommand(*mModel, - mProxyModel->getParentId(), mProxyModel->getParentColumn(), + mProxyModel->getParentId(), mProxyModel->getParentColumn(), this, new CSMWorld::PathgridPointsWrap(pathgrid))); // emit signal here? } @@ -433,81 +455,46 @@ void CSVRender::Cell::pathgridPointRemoved(const std::string &name) return; int numToDelete = pathgrid.mPoints[index].mConnectionNum * 2; // for sanity check later - int edgeCount = 0; + int deletedEdgeCount = 0; - // find edges to delete - std::vector > edges; - for(unsigned i = 0; i < pathgrid.mEdges.size(); ++i) + // update edge indicies to account for the deleted pathgrid point + std::vector::iterator iter = pathgrid.mEdges.begin(); + for (; iter != pathgrid.mEdges.end();) { - if(pathgrid.mEdges[i].mV0 == index || pathgrid.mEdges[i].mV1 == index) + if (((*iter).mV0 == index) || ((*iter).mV1 == index)) { - for(std::map, std::string>::iterator iter = mPgEdges.begin(); - iter != mPgEdges.end(); ++iter) - { - if((*iter).first.first == index || (*iter).first.second == index) - { - edges.push_back(std::make_pair((*iter).first.first, (*iter).first.second)); - } - } + iter = pathgrid.mEdges.erase(iter); + pathgrid.mPoints[index].mConnectionNum -= 1; + deletedEdgeCount++; // for sanity check later + } + else + { + if ((*iter).mV0 > index) + (*iter).mV0--; + + if ((*iter).mV1 > index) + (*iter).mV1--; + + ++iter; } } + pathgrid.mPoints.erase(pathgrid.mPoints.begin()+index); + pathgrid.mData.mS2 -= 1; // decrement the number of points - // delete the edges - for(std::vector >::iterator iter = edges.begin(); - iter != edges.end(); ++iter) - { - std::string name = mPgEdges[*iter]; - if(mSceneMgr->hasManualObject(name)) - { - // remove manual objects - Ogre::ManualObject *manual = mSceneMgr->getManualObject(name); - Ogre::SceneNode *node = manual->getParentSceneNode(); - mSceneMgr->destroyManualObject(name); - if(mSceneMgr->hasSceneNode(node->getName())) - mSceneMgr->destroySceneNode(node); - - edgeCount++; // for sanity check later - - // update map - mPgEdges.erase(*iter); - - // update document - assert(pathgrid.mPoints[(*iter).first].mConnectionNum > 0); - pathgrid.mPoints[(*iter).first].mConnectionNum -= 1; - for(unsigned i = pathgrid.mEdges.size() - 1; i > 0; --i) - { - if(pathgrid.mEdges[i].mV0 == index || pathgrid.mEdges[i].mV1 == index) - pathgrid.mEdges.erase(pathgrid.mEdges.begin() + i); - } - } - } - - if(edgeCount != numToDelete) + if(deletedEdgeCount != numToDelete) { // WARNING: continue anyway? Or should this be an exception? std::cerr << "The no of edges del does not match the no of conn for: " << pathgridId + "_" + QString::number(index).toStdString() << std::endl; } - if(edgeCount || pathgrid.mPoints[index].mConnectionNum == 0) - { - // remove the point - delete mPgPoints[name]; - mPgPoints.erase(name); - // FIXME: update other scene managers - } - - // store to document - //mPoints.erase(mPoints.begin() + index); // WARNING: Can't erase because the index will change - // FIXME: it should be possible to refresh indicies but that means index values - // can't be stored in maps, names, etc - - - - // FIXME: probably will crash if this cell is deleted and undo() is actioned afterwards mDocument.getUndoStack().push(new CSMWorld::ModifyPathgridCommand(*mModel, - mProxyModel->getParentId(), mProxyModel->getParentColumn(), new CSMWorld::PathgridPointsWrap(pathgrid))); + mProxyModel->getParentId(), mProxyModel->getParentColumn(), this, + new CSMWorld::PathgridPointsWrap(pathgrid))); + + clearPathgrid(); + buildPathgrid(); } // NOTE: newPos is in world coordinates @@ -542,50 +529,13 @@ void CSVRender::Cell::pathgridPointMoved(const std::string &name, pathgrid.mPoints[index].mY = y; pathgrid.mPoints[index].mZ = newPos.z; - // delete then recreate the edges - for(unsigned i = 0; i < pathgrid.mEdges.size(); ++i) - { - if(pathgrid.mEdges[i].mV0 == index || pathgrid.mEdges[i].mV1 == index) - { - std::ostringstream stream; - stream << pathgridId << "_" << pathgrid.mEdges[i].mV0 << " " << pathgrid.mEdges[i].mV1; - std::string name = stream.str(); - - if(mSceneMgr->hasManualObject(name)) - { - // remove manual objects - Ogre::ManualObject *manual = mSceneMgr->getManualObject(name); - Ogre::SceneNode *node = manual->getParentSceneNode(); - mSceneMgr->destroyManualObject(name); - - if(pathgrid.mEdges[i].mV0 == index) - { - const ESM::Pathgrid::Point &p1 = pathgrid.mPoints[pathgrid.mEdges[i].mV1]; - - Ogre::ManualObject *line = createPathgridEdge(name, - newPos, - Ogre::Vector3(worldsize*mX+p1.mX, worldsize*mY+p1.mY, p1.mZ)); - line->setVisibilityFlags(Element_Pathgrid); - node->attachObject(line); - } - else if(pathgrid.mEdges[i].mV1 == index) - { - const ESM::Pathgrid::Point &p0 = pathgrid.mPoints[pathgrid.mEdges[i].mV0]; - - Ogre::ManualObject *line = createPathgridEdge(name, - Ogre::Vector3(worldsize*mX+p0.mX, worldsize*mY+p0.mY, p0.mZ), - newPos); - line->setVisibilityFlags(Element_Pathgrid); - node->attachObject(line); - } - } - } - } - // FIXME: probably will crash if this cell is deleted and undo() is actioned afterwards mDocument.getUndoStack().push(new CSMWorld::ModifyPathgridCommand(*mModel, - mProxyModel->getParentId(), mProxyModel->getParentColumn(), + mProxyModel->getParentId(), mProxyModel->getParentColumn(), this, new CSMWorld::PathgridPointsWrap(pathgrid))); + + clearPathgrid(); + buildPathgrid(); } // FIXME: save to the document diff --git a/apps/opencs/view/render/cell.hpp b/apps/opencs/view/render/cell.hpp index 2658ff1adc..fac4ded59e 100644 --- a/apps/opencs/view/render/cell.hpp +++ b/apps/opencs/view/render/cell.hpp @@ -11,7 +11,6 @@ #ifndef Q_MOC_RUN #include -#include // FIXME: temporaty storage until saving to document #endif #include "object.hpp" @@ -116,12 +115,17 @@ namespace CSVRender // for drawing pathgrid points & lines void createGridMaterials(); void destroyGridMaterials(); - void loadPathgrid(); + void setupPathgrid(); Ogre::ManualObject *createPathgridEdge(const std::string &name, const Ogre::Vector3 &start, const Ogre::Vector3 &end); void addPathgridEdge(); void removePathgridEdge(); + + public: + + void clearPathgrid(); + void buildPathgrid(); }; } diff --git a/apps/opencs/view/render/pathgridpoint.cpp b/apps/opencs/view/render/pathgridpoint.cpp index 85b03beea9..ea6815fbe8 100644 --- a/apps/opencs/view/render/pathgridpoint.cpp +++ b/apps/opencs/view/render/pathgridpoint.cpp @@ -1,13 +1,10 @@ #include "pathgridpoint.hpp" -#include // FIXME - #include #include #include -//#include "../../model/world/pathgrid.hpp" #include "../world/physicssystem.hpp" #include "elements.hpp"