diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index cd75db705e..41d44ca18c 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -19,6 +19,7 @@ opencs_hdrs_noqt (model/doc opencs_units (model/world idtable idtableproxymodel regionmap data commanddispatcher idtablebase resourcetable nestedtableproxymodel idtree infotableproxymodel + pathgridcommands ) @@ -91,6 +92,7 @@ opencs_units (view/render opencs_units_noqt (view/render navigation navigation1st navigationfree navigationorbit lighting lightingday lightingnight lightingbright object cell terrainstorage textoverlay overlaymask overlaysystem mousestate + pathgridpoint ) opencs_hdrs_noqt (view/render diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 3721936ce9..79f32619e1 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -425,6 +425,9 @@ std::auto_ptr CS::Editor::setupGraphics() // for font used in overlays Ogre::Root::getSingleton().addResourceLocation ((mResources / "mygui").string(), "FileSystem", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, true); + // for pathgrid point nif + Ogre::Root::getSingleton().addResourceLocation ((mResources / "materials").string(), + "FileSystem", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, true); if (!boost::filesystem::exists (mCfgMgr.getCachePath())) boost::filesystem::create_directories (mCfgMgr.getCachePath()); diff --git a/apps/opencs/model/world/commands.hpp b/apps/opencs/model/world/commands.hpp index 23ffccbd7e..05f21017c0 100644 --- a/apps/opencs/model/world/commands.hpp +++ b/apps/opencs/model/world/commands.hpp @@ -12,7 +12,6 @@ #include #include "universalid.hpp" -#include "nestedtablewrapper.hpp" class QModelIndex; class QAbstractItemModel; @@ -205,7 +204,8 @@ namespace CSMWorld public: - DeleteNestedCommand (IdTree& model, const std::string& id, int nestedRow, int parentColumn, QUndoCommand* parent = 0); + DeleteNestedCommand (IdTree& model, + const std::string& id, int nestedRow, int parentColumn, QUndoCommand* parent = 0); virtual void redo(); @@ -227,7 +227,8 @@ namespace CSMWorld public: - AddNestedCommand(IdTree& model, const std::string& id, int nestedRow, int parentColumn, QUndoCommand* parent = 0); + AddNestedCommand(IdTree& model, + const std::string& id, int nestedRow, int parentColumn, QUndoCommand* parent = 0); virtual void redo(); diff --git a/apps/opencs/model/world/idtree.cpp b/apps/opencs/model/world/idtree.cpp index 56d83d9ed1..9fee014431 100644 --- a/apps/opencs/model/world/idtree.cpp +++ b/apps/opencs/model/world/idtree.cpp @@ -259,6 +259,10 @@ void CSMWorld::IdTree::setNestedTable(const QModelIndex& index, const CSMWorld:: { emit resetEnd(this->index(index.row(), 0).data().toString()); } + // FIXME: Removing pathgrid points will also remove pathgrid edges, but we have + // no way of emitting signals to indicate those changes. In addition, the + // removed edges can be any index (and there can be several edges removed + // but always by a factor of two) } CSMWorld::NestedTableWrapperBase* CSMWorld::IdTree::nestedTable(const QModelIndex& index) const diff --git a/apps/opencs/model/world/nestedcoladapterimp.cpp b/apps/opencs/model/world/nestedcoladapterimp.cpp index 3ebc637b5b..82409f26b7 100644 --- a/apps/opencs/model/world/nestedcoladapterimp.cpp +++ b/apps/opencs/model/world/nestedcoladapterimp.cpp @@ -7,6 +7,7 @@ #include "pathgrid.hpp" #include "info.hpp" #include "usertype.hpp" +#include "pathgridpointswrap.hpp" namespace CSMWorld { @@ -148,6 +149,8 @@ namespace CSMWorld PathgridEdgeListAdapter::PathgridEdgeListAdapter () {} // ToDo: seems to be auto-sorted in the dialog table display after insertion + // + // FIXME: edges should be added in pairs void PathgridEdgeListAdapter::addRow(Record& record, int position) const { Pathgrid pathgrid = record.get(); @@ -169,6 +172,7 @@ namespace CSMWorld record.setModified (pathgrid); } + // FIXME: edges should be removed in pairs and Point.mConnectionNum updated void PathgridEdgeListAdapter::removeRow(Record& record, int rowToRemove) const { Pathgrid pathgrid = record.get(); @@ -219,6 +223,8 @@ namespace CSMWorld } // ToDo: detect duplicates in mEdges + // + // FIXME: Point.mConnectionNum needs to be updated void PathgridEdgeListAdapter::setData(Record& record, const QVariant& value, int subRowIndex, int subColIndex) const { diff --git a/apps/opencs/model/world/nestedcoladapterimp.hpp b/apps/opencs/model/world/nestedcoladapterimp.hpp index 2fd569bd0d..15203eed91 100644 --- a/apps/opencs/model/world/nestedcoladapterimp.hpp +++ b/apps/opencs/model/world/nestedcoladapterimp.hpp @@ -3,7 +3,6 @@ #include -#include #include #include // for converting magic effect id to string & back #include // for converting skill names @@ -25,21 +24,6 @@ namespace CSMWorld struct Pathgrid; struct Info; - struct PathgridPointsWrap : public NestedTableWrapperBase - { - ESM::Pathgrid mRecord; - - PathgridPointsWrap(ESM::Pathgrid pathgrid) - : mRecord(pathgrid) {} - - virtual ~PathgridPointsWrap() {} - - virtual int size() const - { - return mRecord.mPoints.size(); // used in IdTree::setNestedTable() - } - }; - class PathgridPointListAdapter : public NestedColumnAdapter { public: diff --git a/apps/opencs/model/world/pathgridcommands.cpp b/apps/opencs/model/world/pathgridcommands.cpp new file mode 100644 index 0000000000..193ab44571 --- /dev/null +++ b/apps/opencs/model/world/pathgridcommands.cpp @@ -0,0 +1,48 @@ +#include "pathgridcommands.hpp" + +#include "../../view/render/cell.hpp" + +#include "idtree.hpp" +#include "nestedtablewrapper.hpp" + +// 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) + , QUndoCommand(parent), NestedTableStoring(model, id, parentColumn) +{ + setText (("Modify Pathgrid record " + mId).c_str()); // FIXME: better description +} + +void CSMWorld::ModifyPathgridCommand::redo() +{ + const QModelIndex& parentIndex = mModel.getModelIndex(mId, mParentColumn); + + mModel.setNestedTable(parentIndex, *mRecord); +} + +void CSMWorld::ModifyPathgridCommand::undo() +{ + const QModelIndex& parentIndex = mModel.getModelIndex(mId, mParentColumn); + + mModel.setNestedTable(parentIndex, getOld()); + + emit undoActioned(); +} + +void CSMWorld::SignalHandler::rebuildPathgrid() +{ + mParent->clearPathgrid(); + mParent->buildPathgrid(); + + emit flagAsModified(); +} + +CSMWorld::SignalHandler::SignalHandler (CSVRender::Cell *parent) : mParent(parent) +{} + +void CSMWorld::SignalHandler::connectToCommand(const CSMWorld::ModifyPathgridCommand *command) +{ + connect (command, SIGNAL(undoActioned()), this, SLOT(rebuildPathgrid())); +} diff --git a/apps/opencs/model/world/pathgridcommands.hpp b/apps/opencs/model/world/pathgridcommands.hpp new file mode 100644 index 0000000000..3acf0865bf --- /dev/null +++ b/apps/opencs/model/world/pathgridcommands.hpp @@ -0,0 +1,65 @@ +#ifndef CSM_WOLRD_PATHGRIDCOMMANDS_H +#define CSM_WOLRD_PATHGRIDCOMMANDS_H + +#include + +#include "commands.hpp" + +namespace CSVRender +{ + class Cell; +} + +namespace CSMWorld +{ + class IdTree; + class NestedTableWrapperBase; + + class ModifyPathgridCommand : public QObject, public QUndoCommand, private NestedTableStoring + { + Q_OBJECT + + IdTree& mModel; + std::string mId; + + int mParentColumn; + + NestedTableWrapperBase* mRecord; + + public: + + ModifyPathgridCommand(IdTree& model, + const std::string& id, int parentColumn, NestedTableWrapperBase* newRecord, + QUndoCommand* parent = 0); + + virtual void redo(); + + virtual void undo(); + + signals: + + void undoActioned(); + }; + + class SignalHandler : public QObject + { + Q_OBJECT + + CSVRender::Cell *mParent; + + public: + + SignalHandler (CSVRender::Cell *parent); + + void connectToCommand(const ModifyPathgridCommand *command); + + public slots: + + void rebuildPathgrid(); + + signals: + + void flagAsModified(); + }; +} +#endif // CSM_WOLRD_PATHGRIDCOMMANDS_H diff --git a/apps/opencs/model/world/pathgridpointswrap.hpp b/apps/opencs/model/world/pathgridpointswrap.hpp new file mode 100644 index 0000000000..6f1f61fc61 --- /dev/null +++ b/apps/opencs/model/world/pathgridpointswrap.hpp @@ -0,0 +1,26 @@ +#ifndef CSM_WOLRD_PATHGRIDPOINTSWRAP_H +#define CSM_WOLRD_PATHGRIDPOINTSWRAP_H + +#include + +#include "nestedtablewrapper.hpp" + +namespace CSMWorld +{ + struct PathgridPointsWrap : public NestedTableWrapperBase + { + ESM::Pathgrid mRecord; + + PathgridPointsWrap(ESM::Pathgrid pathgrid) + : mRecord(pathgrid) {} + + virtual ~PathgridPointsWrap() {} + + virtual int size() const + { + return mRecord.mPoints.size(); // used in IdTree::setNestedTable() + } + }; +} + +#endif // CSM_WOLRD_PATHGRIDPOINTSWRAP_H diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index c6da1bef8e..f81b0708fa 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -2,18 +2,83 @@ #include #include +#include #include #include +#include "../../model/doc/document.hpp" #include "../../model/world/idtable.hpp" +#include "../../model/world/idtree.hpp" #include "../../model/world/columns.hpp" #include "../../model/world/data.hpp" #include "../../model/world/refcollection.hpp" +#include "../../model/world/pathgrid.hpp" +#include "../../model/world/commands.hpp" +#include "../../model/world/pathgridcommands.hpp" +#include "../../model/world/pathgridpointswrap.hpp" +#include "../../model/world/nestedtableproxymodel.hpp" #include "../world/physicssystem.hpp" #include "elements.hpp" #include "terrainstorage.hpp" +#include "pathgridpoint.hpp" + +namespace CSVRender +{ + // PLEASE NOTE: pathgrid edge code copied and adapted from mwrender/debugging + static const std::string PG_LINE_MATERIAL = "pathgridLineMaterial"; + static const int POINT_MESH_BASE = 35; + static const std::string DEBUGGING_GROUP = "debugging"; +} + +void CSVRender::Cell::createGridMaterials() +{ + if(!Ogre::ResourceGroupManager::getSingleton().resourceGroupExists(DEBUGGING_GROUP)) + Ogre::ResourceGroupManager::getSingleton().createResourceGroup(DEBUGGING_GROUP); + + if(Ogre::MaterialManager::getSingleton().getByName(PG_LINE_MATERIAL, DEBUGGING_GROUP).isNull()) + { + Ogre::MaterialPtr lineMatPtr = + Ogre::MaterialManager::getSingleton().create(PG_LINE_MATERIAL, DEBUGGING_GROUP); + lineMatPtr->setReceiveShadows(false); + lineMatPtr->getTechnique(0)->setLightingEnabled(true); + lineMatPtr->getTechnique(0)->getPass(0)->setDiffuse(1,1,0,0); + lineMatPtr->getTechnique(0)->getPass(0)->setAmbient(1,1,0); + lineMatPtr->getTechnique(0)->getPass(0)->setSelfIllumination(1,1,0); + } +} + +void CSVRender::Cell::destroyGridMaterials() +{ + if(Ogre::ResourceGroupManager::getSingleton().resourceGroupExists(DEBUGGING_GROUP)) + { + if(!Ogre::MaterialManager::getSingleton().getByName(PG_LINE_MATERIAL, DEBUGGING_GROUP).isNull()) + Ogre::MaterialManager::getSingleton().remove(PG_LINE_MATERIAL); + + Ogre::ResourceGroupManager::getSingleton().destroyResourceGroup(DEBUGGING_GROUP); + } +} + +Ogre::ManualObject *CSVRender::Cell::createPathgridEdge(const std::string &name, + const Ogre::Vector3 &start, const Ogre::Vector3 &end) +{ + Ogre::ManualObject *result = mSceneMgr->createManualObject(name); + + createGridMaterials(); + result->begin(PG_LINE_MATERIAL, Ogre::RenderOperation::OT_LINE_LIST); + + Ogre::Vector3 direction = (end - start); + Ogre::Vector3 lineDisplacement = direction.crossProduct(Ogre::Vector3::UNIT_Z).normalisedCopy(); + // move lines up a little, so they will be less covered by meshes/landscape + lineDisplacement = lineDisplacement * POINT_MESH_BASE + Ogre::Vector3(0, 0, 10); + result->position(start + lineDisplacement); + result->position(end + lineDisplacement); + + result->end(); + + return result; +} bool CSVRender::Cell::removeObject (const std::string& id) { @@ -32,7 +97,7 @@ bool CSVRender::Cell::addObjects (int start, int end) { bool modified = false; - const CSMWorld::RefCollection& collection = mData.getReferences(); + const CSMWorld::RefCollection& collection = mDocument.getData().getReferences(); for (int i=start; i<=end; ++i) { @@ -44,7 +109,7 @@ bool CSVRender::Cell::addObjects (int start, int end) { std::string id = Misc::StringUtils::lowerCase (collection.getRecord (i).get().mId); - mObjects.insert (std::make_pair (id, new Object (mData, mCellNode, id, false, mPhysics))); + mObjects.insert (std::make_pair (id, new Object (mDocument.getData(), mCellNode, id, false, mPhysics))); modified = true; } } @@ -52,21 +117,23 @@ bool CSVRender::Cell::addObjects (int start, int end) return modified; } -CSVRender::Cell::Cell (CSMWorld::Data& data, Ogre::SceneManager *sceneManager, +CSVRender::Cell::Cell (CSMDoc::Document& document, Ogre::SceneManager *sceneManager, const std::string& id, boost::shared_ptr physics, const Ogre::Vector3& origin) -: mData (data), mId (Misc::StringUtils::lowerCase (id)), mPhysics(physics), mSceneMgr(sceneManager), mX(0), mY(0) +: mDocument (document), mId (Misc::StringUtils::lowerCase (id)) +, mProxyModel(0), mModel(0), mPgIndex(-1), mHandler(new CSMWorld::SignalHandler(this)) +, mPhysics(physics), mSceneMgr(sceneManager), mX(0), mY(0) { mCellNode = sceneManager->getRootSceneNode()->createChildSceneNode(); mCellNode->setPosition (origin); CSMWorld::IdTable& references = dynamic_cast ( - *mData.getTableModel (CSMWorld::UniversalId::Type_References)); + *mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_References)); int rows = references.rowCount(); addObjects (0, rows-1); - const CSMWorld::IdCollection& land = mData.getLand(); + const CSMWorld::IdCollection& land = mDocument.getData().getLand(); int landIndex = land.searchId(mId); if (landIndex != -1) { @@ -74,7 +141,7 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, Ogre::SceneManager *sceneManager, if (esmLand.getLandData (ESM::Land::DATA_VHGT)) { - mTerrain.reset(new Terrain::TerrainGrid(sceneManager, new TerrainStorage(mData), Element_Terrain, true, + mTerrain.reset(new Terrain::TerrainGrid(sceneManager, new TerrainStorage(mDocument.getData()), Element_Terrain, true, Terrain::Align_XY)); mTerrain->loadCell(esmLand.mX, esmLand.mY); @@ -88,10 +155,19 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, Ogre::SceneManager *sceneManager, esmLand.getLandData(ESM::Land::DATA_VHGT)->mHeights, mX, mY, 0, worldsize / (verts-1), verts); } } + + setupPathgrid(); + buildPathgrid(); } CSVRender::Cell::~Cell() { + clearPathgrid(); + destroyGridMaterials(); + + delete mProxyModel; + delete mHandler; + if (mTerrain.get()) mPhysics->removeHeightField(mSceneMgr, mX, mY); @@ -135,7 +211,7 @@ bool CSVRender::Cell::referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { CSMWorld::IdTable& references = dynamic_cast ( - *mData.getTableModel (CSMWorld::UniversalId::Type_References)); + *mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_References)); int idColumn = references.findColumnIndex (CSMWorld::Columns::ColumnId_Id); int cellColumn = references.findColumnIndex (CSMWorld::Columns::ColumnId_Cell); @@ -187,7 +263,7 @@ bool CSVRender::Cell::referenceDataChanged (const QModelIndex& topLeft, for (std::map::iterator iter (ids.begin()); iter!=ids.end(); ++iter) { mObjects.insert (std::make_pair ( - iter->first, new Object (mData, mCellNode, iter->first, false, mPhysics))); + iter->first, new Object (mDocument.getData(), mCellNode, iter->first, false, mPhysics))); modified = true; } @@ -202,7 +278,7 @@ bool CSVRender::Cell::referenceAboutToBeRemoved (const QModelIndex& parent, int return false; CSMWorld::IdTable& references = dynamic_cast ( - *mData.getTableModel (CSMWorld::UniversalId::Type_References)); + *mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_References)); int idColumn = references.findColumnIndex (CSMWorld::Columns::ColumnId_Id); @@ -231,3 +307,287 @@ float CSVRender::Cell::getTerrainHeightAt(const Ogre::Vector3 &pos) const else return -std::numeric_limits::max(); } + +void CSVRender::Cell::pathgridDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) +{ + CSMWorld::IdTree *pathgrids = dynamic_cast( + mDocument.getData().getTableModel(CSMWorld::UniversalId::Type_Pathgrid)); + + int idColumn = pathgrids->findColumnIndex(CSMWorld::Columns::ColumnId_Id); + int colPaths = pathgrids->findColumnIndex(CSMWorld::Columns::ColumnId_PathgridPoints); + //int colEdges = pathgrids->findColumnIndex(CSMWorld::Columns::ColumnId_PathgridEdges); + + // FIXME: how to detect adds/deletes/modifies? + + for (int i=topLeft.row(); i<=bottomRight.row(); ++i) + { + std::string cell = Misc::StringUtils::lowerCase (pathgrids->data ( + pathgrids->index (i, idColumn)).toString().toUtf8().constData()); + + if (cell==mId && colPaths >= topLeft.column() && colPaths <= bottomRight.column()) + { + if (!mModel) + setupPathgrid(); + + mHandler->rebuildPathgrid(); + } + } +} + +// FIXME: +// - adding edges (need the ability to select a pathgrid and highlight) +// - repainting edges while moving +void CSVRender::Cell::setupPathgrid() +{ + const CSMWorld::SubCellCollection& pathgrids = mDocument.getData().getPathgrids(); + int index = pathgrids.searchId(mId); + if(index != -1) + { + 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); + + } +} + +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)) + { + 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(); + + // destroy points + for(std::map::iterator iter (mPgPoints.begin()); + iter!=mPgPoints.end(); ++iter) + { + delete iter->second; + } + mPgPoints.clear(); +} + +// 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; + + const CSMWorld::SubCellCollection& pathgrids = mDocument.getData().getPathgrids(); + const CSMWorld::Pathgrid &pathgrid = pathgrids.getRecord(mPgIndex).get(); + + 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)); + } +} + +// NOTE: pos is in world coordinates +void CSVRender::Cell::pathgridPointAdded(const Ogre::Vector3 &pos, bool interior) +{ + const CSMWorld::SubCellCollection& pathgrids = mDocument.getData().getPathgrids(); + CSMWorld::Pathgrid pathgrid = pathgrids.getRecord(mPgIndex).get(); + + std::string name = PathgridPoint::getName(mId, pathgrid.mPoints.size()); // generate a new name + + mPgPoints.insert(std::make_pair(name, new PathgridPoint(name, mCellNode, pos, mPhysics))); + + // store to document + int worldsize = ESM::Land::REAL_SIZE; + + int x = pos.x; + int y = pos.y; + if(!interior) + { + x = x - (worldsize * mX); + y = y - (worldsize * mY); + } + + ESM::Pathgrid::Point point(x, y, (int)pos.z); + point.mConnectionNum = 0; + pathgrid.mPoints.push_back(point); + // FIXME: update other scene managers + + pathgrid.mData.mS2 += 1; // increment the number of points + + // TODO: check for possible issue if this cell is deleted and undo() is actioned afterwards + CSMWorld::ModifyPathgridCommand *cmd = new CSMWorld::ModifyPathgridCommand(*mModel, + mProxyModel->getParentId(), mProxyModel->getParentColumn(), + new CSMWorld::PathgridPointsWrap(pathgrid)); + mHandler->connectToCommand(cmd); + mDocument.getUndoStack().push(cmd); + // emit signal here? +} + +void CSVRender::Cell::pathgridPointRemoved(const std::string &name) +{ + std::pair result = PathgridPoint::getIdAndIndex(name); + if(result.first == "") + return; + + std::string pathgridId = result.first; + int index = result.second; + + const CSMWorld::SubCellCollection& pathgrids = mDocument.getData().getPathgrids(); + CSMWorld::Pathgrid pathgrid = pathgrids.getRecord(mPgIndex).get(); + + // check if the point exists + if(index < 0 || (unsigned int)index >= pathgrid.mPoints.size()) + return; + + int numToDelete = pathgrid.mPoints[index].mConnectionNum * 2; // for sanity check later + int deletedEdgeCount = 0; + + // update edge indicies to account for the deleted pathgrid point + std::vector::iterator iter = pathgrid.mEdges.begin(); + for (; iter != pathgrid.mEdges.end();) + { + if (((*iter).mV0 == index) || ((*iter).mV1 == index)) + { + 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 + + 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; + } + + // TODO: check for possible issue if this cell is deleted and undo() is actioned afterwards + CSMWorld::ModifyPathgridCommand *cmd = new CSMWorld::ModifyPathgridCommand(*mModel, + mProxyModel->getParentId(), mProxyModel->getParentColumn(), + new CSMWorld::PathgridPointsWrap(pathgrid)); + mHandler->connectToCommand(cmd); + mDocument.getUndoStack().push(cmd); + + clearPathgrid(); + buildPathgrid(); +} + +// NOTE: newPos is in world coordinates +void CSVRender::Cell::pathgridPointMoved(const std::string &name, + const Ogre::Vector3 &newPos, bool interior) +{ + std::pair result = PathgridPoint::getIdAndIndex(name); + if(result.first == "") + return; + + std::string pathgridId = result.first; + int index = result.second; + + const CSMWorld::SubCellCollection& pathgrids = mDocument.getData().getPathgrids(); + CSMWorld::Pathgrid pathgrid = pathgrids.getRecord(mPgIndex).get(); + + // check if the point exists + if(index < 0 || (unsigned int)index >= pathgrid.mPoints.size()) + return; + + int worldsize = ESM::Land::REAL_SIZE; + + int x = newPos.x; + int y = newPos.y; + if(!interior) + { + x = x - (worldsize * mX); + y = y - (worldsize * mY); + } + + pathgrid.mPoints[index].mX = x; + pathgrid.mPoints[index].mY = y; + pathgrid.mPoints[index].mZ = newPos.z; + + // TODO: check for possible issue if this cell is deleted and undo() is actioned afterwards + CSMWorld::ModifyPathgridCommand *cmd = new CSMWorld::ModifyPathgridCommand(*mModel, + mProxyModel->getParentId(), mProxyModel->getParentColumn(), + new CSMWorld::PathgridPointsWrap(pathgrid)); + mHandler->connectToCommand(cmd); + mDocument.getUndoStack().push(cmd); + + clearPathgrid(); + buildPathgrid(); +} + +// FIXME: save to the document +void CSVRender::Cell::addPathgridEdge() +{ + // check if the points exist + // update the edges + // store to document + // FIXME: update other scene managers +} + +// FIXME: save to the document +void CSVRender::Cell::removePathgridEdge() +{ +} + +CSMWorld::SignalHandler *CSVRender::Cell::getSignalHandler() +{ + return mHandler; +} diff --git a/apps/opencs/view/render/cell.hpp b/apps/opencs/view/render/cell.hpp index 73d7949482..dbf680dd4f 100644 --- a/apps/opencs/view/render/cell.hpp +++ b/apps/opencs/view/render/cell.hpp @@ -21,11 +21,20 @@ namespace Ogre { class SceneManager; class SceneNode; + class ManualObject; +} + +namespace CSMDoc +{ + class Document; } namespace CSMWorld { - class Data; + class Pathgrid; + class NestedTableProxyModel; + class IdTree; + class SignalHandler; } namespace CSVWorld @@ -35,12 +44,22 @@ namespace CSVWorld namespace CSVRender { + class PathgridPoint; + class Cell { - CSMWorld::Data& mData; + CSMDoc::Document& mDocument; std::string mId; Ogre::SceneNode *mCellNode; std::map mObjects; + std::map mPgPoints; + std::map, std::string> mPgEdges; + + CSMWorld::NestedTableProxyModel *mProxyModel; + CSMWorld::IdTree *mModel; + int mPgIndex; + CSMWorld::SignalHandler *mHandler; + std::auto_ptr mTerrain; boost::shared_ptr mPhysics; Ogre::SceneManager *mSceneMgr; @@ -59,7 +78,7 @@ namespace CSVRender public: - Cell (CSMWorld::Data& data, Ogre::SceneManager *sceneManager, const std::string& id, + Cell (CSMDoc::Document& document, Ogre::SceneManager *sceneManager, const std::string& id, boost::shared_ptr physics, const Ogre::Vector3& origin = Ogre::Vector3 (0, 0, 0)); ~Cell(); @@ -86,6 +105,31 @@ namespace CSVRender bool referenceAdded (const QModelIndex& parent, int start, int end); float getTerrainHeightAt(const Ogre::Vector3 &pos) const; + + void pathgridPointAdded(const Ogre::Vector3 &pos, bool interior = false); + void pathgridPointMoved(const std::string &name, + const Ogre::Vector3 &newPos, bool interior = false); + void pathgridPointRemoved(const std::string &name); + + void pathgridDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); + + private: + + // for drawing pathgrid points & lines + void createGridMaterials(); + void destroyGridMaterials(); + 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(); + CSMWorld::SignalHandler *getSignalHandler(); }; } diff --git a/apps/opencs/view/render/mousestate.cpp b/apps/opencs/view/render/mousestate.cpp index 8aecc4a414..f8258f9c35 100644 --- a/apps/opencs/view/render/mousestate.cpp +++ b/apps/opencs/view/render/mousestate.cpp @@ -59,9 +59,9 @@ namespace CSVRender MouseState::MouseState(WorldspaceWidget *parent) : mMouseState(Mouse_Default), mParent(parent), mPhysics(parent->mDocument.getPhysics()) - , mSceneManager(parent->getSceneManager()), mOldPos(0,0), mCurrentObj(""), mGrabbedSceneNode("") + , mSceneManager(parent->getSceneManager()), mOldCursorPos(0,0), mCurrentObj(""), mGrabbedSceneNode(""), mGrabbedRefId("") , mMouseEventTimer(0), mPlane(0), mOrigObjPos(Ogre::Vector3()), mOrigMousePos(Ogre::Vector3()) - , mCurrentMousePos(Ogre::Vector3()), mOffset(0.0f), mIdTableModel(0), mColIndexPosX(0) + , mOldMousePos(Ogre::Vector3()), mIdTableModel(0), mColIndexPosX(0) , mColIndexPosY(0), mColIndexPosZ(0) { const CSMWorld::RefCollection& references = mParent->mDocument.getData().getReferences(); @@ -81,11 +81,11 @@ namespace CSVRender Ogre::MeshPtr mesh = Ogre::MeshManager::getSingleton().createPlane("mouse", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, *mPlane, - 300000,300000, // FIXME: use far clip dist? - 1,1, // segments - true, // normals - 1, // numTexCoordSets - 1,1, // uTile, vTile + 300000,300000, // FIXME: use far clip dist? + 1,1, // segments + true, // normals + 1, // numTexCoordSets + 1,1, // uTile, vTile planeRes.second // upVector ); } @@ -113,24 +113,22 @@ namespace CSVRender } case Mouse_Drag: { - if(event->pos() != mOldPos) // TODO: maybe don't update less than a quantum? + if(event->pos() != mOldCursorPos) // TODO: maybe don't update less than a quantum? { - mOldPos = event->pos(); + mOldCursorPos = event->pos(); // ray test against the plane to provide feedback to the user the - // relative movement of the object on the x-y plane - std::pair planeResult = mousePositionOnPlane(event->pos(), *mPlane); + // relative movement of the object on the movement plane + std::pair planeResult = mousePosOnPlane(event->pos(), *mPlane); if(planeResult.first) { - if(mGrabbedSceneNode != "") - { - std::pair planeRes = planeAxis(); - Ogre::Vector3 pos = mOrigObjPos + planeRes.first*mOffset; - mSceneManager->getSceneNode(mGrabbedSceneNode)->setPosition(pos+planeResult.second-mOrigMousePos); - mCurrentMousePos = planeResult.second; - mPhysics->moveSceneNodes(mGrabbedSceneNode, pos+planeResult.second-mOrigMousePos); - updateSceneWidgets(); - } + Ogre::Vector3 pos = mOrigObjPos + (planeResult.second-mOrigMousePos); + + mSceneManager->getSceneNode(mGrabbedSceneNode)->setPosition(pos); + mPhysics->moveSceneNodes(mGrabbedSceneNode, pos); + updateSceneWidgets(); + + mOldMousePos = planeResult.second; } } break; @@ -158,21 +156,27 @@ namespace CSVRender { if(event->buttons() & Qt::RightButton) { - std::pair result = objectUnderCursor(event->x(), event->y()); + // get object or pathgrid + std::pair result = underCursor(event->x(), event->y(), + CSVRender::Element_Reference|CSVRender::Element_Pathgrid); + if(result.first == "") break; - mGrabbedSceneNode = result.first; + mGrabbedSceneNode = mPhysics->refIdToSceneNode(result.first, mSceneManager); + if(!mSceneManager->hasSceneNode(mGrabbedSceneNode)) + break; + + mGrabbedRefId = result.first; // ray test agaist the plane to get a starting position of the // mouse in relation to the object position std::pair planeRes = planeAxis(); mPlane->redefine(planeRes.first, result.second); - std::pair planeResult = mousePositionOnPlane(event->pos(), *mPlane); + std::pair planeResult = mousePosOnPlane(event->pos(), *mPlane); if(planeResult.first) { mOrigMousePos = planeResult.second; - mCurrentMousePos = planeResult.second; - mOffset = 0.0f; + mOldMousePos = planeResult.second; } mOrigObjPos = mSceneManager->getSceneNode(mGrabbedSceneNode)->getPosition(); @@ -192,7 +196,9 @@ namespace CSVRender { case Mouse_Grab: { - std::pair result = objectUnderCursor(event->x(), event->y()); + std::pair result = underCursor(event->x(), event->y(), + CSVRender::Element_Reference|CSVRender::Element_Pathgrid); + if(result.first != "") { if(result.first == mCurrentObj) @@ -208,56 +214,94 @@ namespace CSVRender mCurrentObj = result.first; } +//#if 0 + // print some debug info + std::cout << "result grab release: " << result.first << std::endl; + std::cout << " hit pos "+ QString::number(result.second.x).toStdString() + + ", " + QString::number(result.second.y).toStdString() + + ", " + QString::number(result.second.z).toStdString() << std::endl; +//#endif } break; } case Mouse_Drag: { // final placement - std::pair planeResult = mousePositionOnPlane(event->pos(), *mPlane); + std::pair planeResult = mousePosOnPlane(event->pos(), *mPlane); if(planeResult.first) { - if(mGrabbedSceneNode != "") - { - std::pair planeRes = planeAxis(); - Ogre::Vector3 pos = mOrigObjPos+planeRes.first*mOffset+planeResult.second-mOrigMousePos; - // use the saved scene node name since the physics model has not moved yet - std::string referenceId = mPhysics->sceneNodeToRefId(mGrabbedSceneNode); + Ogre::Vector3 pos = mOrigObjPos + (planeResult.second-mOrigMousePos); + // use the saved reference Id since the physics model has not moved yet + if(QString(mGrabbedRefId.c_str()).contains(QRegExp("^Pathgrid"))) + { + // FIXME: move pathgrid point, but don't save yet (need pathgrid + // table feature & its data structure to be completed) + // Also need to signal PathgridPoint object of change + std::pair result = + mPhysics->distToClosest(pos, + getCamera()->getViewport()->getVisibilityMask(), 600); // snap + + if(result.first != "" && // don't allow pathgrid points under the cursor + !QString(result.first.c_str()).contains(QRegExp("^Pathgrid"))) + { + pos.z -= result.second; + pos.z += 1; // arbitrary number, lift up slightly (maybe change the nif?) + // FIXME: rather than just updating at the end, should + // consider providing visual feedback of terrain height + // while dragging the pathgrid point (maybe check whether + // the object is a pathgrid point at the begging and set + // a flag?) + placeObject(mGrabbedSceneNode, pos); // result.second + mParent->pathgridMoved(mGrabbedRefId, pos); // result.second + } + else + cancelDrag(); // FIXME: does not allow editing if terrain not visible + } + else + { mParent->mDocument.getUndoStack().beginMacro (QObject::tr("Move Object")); mParent->mDocument.getUndoStack().push(new CSMWorld::ModifyCommand(*mIdTableModel, - mIdTableModel->getModelIndex(referenceId, mColIndexPosX), pos.x)); + mIdTableModel->getModelIndex(mGrabbedRefId, mColIndexPosX), pos.x)); mParent->mDocument.getUndoStack().push(new CSMWorld::ModifyCommand(*mIdTableModel, - mIdTableModel->getModelIndex(referenceId, mColIndexPosY), pos.y)); + mIdTableModel->getModelIndex(mGrabbedRefId, mColIndexPosY), pos.y)); mParent->mDocument.getUndoStack().push(new CSMWorld::ModifyCommand(*mIdTableModel, - mIdTableModel->getModelIndex(referenceId, mColIndexPosZ), pos.z)); + mIdTableModel->getModelIndex(mGrabbedRefId, mColIndexPosZ), pos.z)); mParent->mDocument.getUndoStack().endMacro(); - - // FIXME: highlight current object? - //mCurrentObj = mGrabbedSceneNode; // FIXME: doesn't work? - mCurrentObj = ""; // whether the object is selected - - mMouseState = Mouse_Edit; - - // reset states - mCurrentMousePos = Ogre::Vector3(); // mouse pos to use in wheel event - mOrigMousePos = Ogre::Vector3(); // starting pos of mouse in world space - mOrigObjPos = Ogre::Vector3(); // starting pos of object in world space - mGrabbedSceneNode = ""; // id of the object - mOffset = 0.0f; // used for z-axis movement - mOldPos = QPoint(0, 0); // to calculate relative movement of mouse } - } + + // FIXME: highlight current object? + //mCurrentObj = mGrabbedRefId; // FIXME: doesn't work? + mCurrentObj = ""; // whether the object is selected + + mMouseState = Mouse_Edit; + + // reset states + mGrabbedRefId = ""; // id of the object + mGrabbedSceneNode = ""; + mOrigMousePos = Ogre::Vector3(); // starting pos of mouse in world space + mOrigObjPos = Ogre::Vector3(); // starting pos of object in world space + mOldMousePos = Ogre::Vector3(); // mouse pos to use in wheel event + mOldCursorPos = QPoint(0, 0); // to calculate relative movement of mouse + } break; } case Mouse_Edit: case Mouse_Default: { // probably terrain, check - std::pair result = terrainUnderCursor(event->x(), event->y()); + std::pair result = underCursor(event->x(), event->y(), + CSVRender::Element_Terrain); + if(result.first != "") { // FIXME: terrain editing goes here +//#if 0 + std::cout << "result default/edit release: " << result.first << std::endl; + std::cout << " hit pos "+ QString::number(result.second.x).toStdString() + + ", " + QString::number(result.second.y).toStdString() + + ", " + QString::number(result.second.z).toStdString() << std::endl; +//#endif } break; } @@ -283,17 +327,49 @@ namespace CSVRender /* FALL_THROUGH */ case Mouse_Drag: { - // move the object along the z axis during Mouse_Drag or Mouse_Grab + // move the object along the axis normal to the plane during Mouse_Drag or Mouse_Grab if (event->delta()) { - // seems positive is up and negative is down - mOffset += (event->delta()/1); // FIXME: arbitrary number, make config option? + // The mouse point is where the mouse points the object when dragging starts. + // The object position is usually a little away from the mount point. + // Get the new world position of mouse on the plane offset from the wheel + // FIXME: make the sensitivity a user setting and/or allow modifiers std::pair planeRes = planeAxis(); - Ogre::Vector3 pos = mOrigObjPos + planeRes.first*mOffset; - mSceneManager->getSceneNode(mGrabbedSceneNode)->setPosition(pos+mCurrentMousePos-mOrigMousePos); - mPhysics->moveSceneNodes(mGrabbedSceneNode, pos+mCurrentMousePos-mOrigMousePos); + Ogre::Vector3 mousePos = mOldMousePos + planeRes.first*(event->delta()/1.5); + + // Move the movement plane to the new mouse position. The plane is created on + // the mouse point (i.e. not the object position) + mPlane->redefine(planeRes.first, mousePos); + + // Calculate the new screen position of the cursor + Ogre::Vector3 screenPos = + getCamera()->getProjectionMatrix() * getCamera()->getViewMatrix() * mousePos; + int screenX = (screenPos.x/2+0.5) * getViewport()->getActualWidth(); + int screenY = (1-(screenPos.y/2+0.5)) * getViewport()->getActualHeight(); + + // Move the cursor to the new screen position + QCursor::setPos(mParent->mapToGlobal(QPoint(screenX, screenY))); + + // Use the new position to check the world position of the mouse + std::pair planeResult = + mousePosOnPlane(QPoint(screenX, screenY), *mPlane); + { + if(planeResult.first) + mousePos = planeResult.second; + } + + // Find the final world position of the object + Ogre::Vector3 finalPos = mOrigObjPos + (mousePos-mOrigMousePos); + + // update Ogre and Bullet + mSceneManager->getSceneNode(mGrabbedSceneNode)->setPosition(finalPos); + mPhysics->moveSceneNodes(mGrabbedSceneNode, finalPos); updateSceneWidgets(); + + // remember positions for next time + mOldMousePos = mousePos; + mOldCursorPos = QPoint(screenX, screenY); } break; } @@ -323,14 +399,14 @@ namespace CSVRender // reset states mMouseState = Mouse_Default; - mCurrentMousePos = Ogre::Vector3(); + mOldMousePos = Ogre::Vector3(); mOrigMousePos = Ogre::Vector3(); mOrigObjPos = Ogre::Vector3(); + mGrabbedRefId = ""; mGrabbedSceneNode = ""; mCurrentObj = ""; - mOldPos = QPoint(0, 0); + mOldCursorPos = QPoint(0, 0); mMouseEventTimer->invalidate(); - mOffset = 0.0f; break; } @@ -375,7 +451,7 @@ namespace CSVRender } } - std::pair MouseState::mousePositionOnPlane(const QPoint &pos, const Ogre::Plane &plane) + std::pair MouseState::mousePosOnPlane(const QPoint &pos, const Ogre::Plane &plane) { // using a really small value seems to mess up with the projections float nearClipDistance = getCamera()->getNearClipDistance(); // save existing @@ -392,7 +468,8 @@ namespace CSVRender return std::make_pair(false, Ogre::Vector3()); // should only happen if the plane is too small } - std::pair MouseState::terrainUnderCursor(const int mouseX, const int mouseY) + std::pair MouseState::underCursor(const int mouseX, + const int mouseY, Ogre::uint32 elements) { if(!getViewport()) return std::make_pair("", Ogre::Vector3()); @@ -401,45 +478,19 @@ namespace CSVRender float y = (float) mouseY / getViewport()->getActualHeight(); std::pair result = mPhysics->castRay(x, y, mSceneManager, getCamera()); - if(result.first != "") + + if(result.first != "" && + ((elements & (Ogre::uint32)CSVRender::Element_Terrain && + QString(result.first.c_str()).contains(QRegExp("^Height"))) || + (elements & (Ogre::uint32)CSVRender::Element_Reference && + QString(result.first.c_str()).contains(QRegExp("^ref#"))) || + (elements & (Ogre::uint32)CSVRender::Element_Pathgrid && + QString(result.first.c_str()).contains(QRegExp("^Pathgrid"))))) { - // FIXME: is there a better way to distinguish terrain from objects? - QString name = QString(result.first.c_str()); - if(name.contains(QRegExp("^HeightField"))) - { - return result; - } + return result; } - - return std::make_pair("", Ogre::Vector3()); - } - - std::pair MouseState::objectUnderCursor(const int mouseX, const int mouseY) - { - if(!getViewport()) + else return std::make_pair("", Ogre::Vector3()); - - float x = (float) mouseX / getViewport()->getActualWidth(); - float y = (float) mouseY / getViewport()->getActualHeight(); - - std::pair result = mPhysics->castRay(x, y, mSceneManager, getCamera()); - if(result.first != "") - { - // NOTE: anything not terrain is assumed to be an object - QString name = QString(result.first.c_str()); - if(!name.contains(QRegExp("^HeightField"))) - { - uint32_t visibilityMask = getViewport()->getVisibilityMask(); - bool ignoreObjects = !(visibilityMask & (uint32_t)CSVRender::Element_Reference); - - if(!ignoreObjects && mSceneManager->hasSceneNode(result.first)) - { - return result; - } - } - } - - return std::make_pair("", Ogre::Vector3()); } void MouseState::updateSceneWidgets() @@ -462,4 +513,15 @@ namespace CSVRender { return mParent->getCamera()->getViewport(); } + + void MouseState::placeObject(const std::string sceneNodeName, const Ogre::Vector3 &pos) + { + mSceneManager->getSceneNode(sceneNodeName)->setPosition(pos); + + // update physics + mPhysics->replaceObject(sceneNodeName, 1, pos, Ogre::Quaternion::IDENTITY); + + // update all SceneWidgets and their SceneManagers + updateSceneWidgets(); + } } diff --git a/apps/opencs/view/render/mousestate.hpp b/apps/opencs/view/render/mousestate.hpp index 70e18427f3..02d54a8b99 100644 --- a/apps/opencs/view/render/mousestate.hpp +++ b/apps/opencs/view/render/mousestate.hpp @@ -47,15 +47,15 @@ namespace CSVRender boost::shared_ptr mPhysics; Ogre::SceneManager *mSceneManager; // local copy - QPoint mOldPos; + QPoint mOldCursorPos; std::string mCurrentObj; std::string mGrabbedSceneNode; + std::string mGrabbedRefId; QElapsedTimer *mMouseEventTimer; Ogre::Plane *mPlane; Ogre::Vector3 mOrigObjPos; Ogre::Vector3 mOrigMousePos; - Ogre::Vector3 mCurrentMousePos; - float mOffset; + Ogre::Vector3 mOldMousePos; CSMWorld::IdTable *mIdTableModel; int mColIndexPosX; @@ -73,16 +73,19 @@ namespace CSVRender void mouseDoubleClickEvent (QMouseEvent *event); bool wheelEvent (QWheelEvent *event); + std::pair underCursor(const int mouseX, + const int mouseY, Ogre::uint32 elements = 0xFFFFFFFF); + void cancelDrag(); private: - std::pair mousePositionOnPlane(const QPoint &pos, const Ogre::Plane &plane); - std::pair terrainUnderCursor(const int mouseX, const int mouseY); - std::pair objectUnderCursor(const int mouseX, const int mouseY); + std::pair mousePosOnPlane(const QPoint &pos, const Ogre::Plane &plane); std::pair planeAxis(); void updateSceneWidgets(); + void placeObject(const std::string sceneNodeName, const Ogre::Vector3 &pos); // FIXME + Ogre::Camera *getCamera(); // friend access Ogre::Viewport *getViewport(); // friend access }; diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp index 3607fb415d..9e39ee37bc 100644 --- a/apps/opencs/view/render/object.cpp +++ b/apps/opencs/view/render/object.cpp @@ -223,12 +223,10 @@ bool CSVRender::Object::referenceDataChanged (const QModelIndex& topLeft, { mReferenceableId = references.getData (index, columnIndex).toString().toUtf8().constData(); - - update(); } + update(); adjust(); - return true; } diff --git a/apps/opencs/view/render/object.hpp b/apps/opencs/view/render/object.hpp index c5a2b73c20..b4c3f9cda7 100644 --- a/apps/opencs/view/render/object.hpp +++ b/apps/opencs/view/render/object.hpp @@ -36,6 +36,7 @@ namespace CSVRender NifOgre::ObjectScenePtr mObject; bool mForceBaseToZero; boost::shared_ptr mPhysics; + std::string mPhysicsObject; /// Not implemented Object (const Object&); diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index bcaadbcea8..cd247afe52 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -20,11 +20,14 @@ #include "../../model/world/tablemimedata.hpp" #include "../../model/world/idtable.hpp" +#include "../../model/world/pathgridcommands.hpp" #include "../widget/scenetooltoggle.hpp" #include "../widget/scenetoolmode.hpp" #include "../widget/scenetooltoggle2.hpp" +#include "../world/physicssystem.hpp" +#include "pathgridpoint.hpp" #include "editmode.hpp" #include "elements.hpp" @@ -112,8 +115,9 @@ bool CSVRender::PagedWorldspaceWidget::adjustCells() if (index > 0 && cells.getRecord (index).mState!=CSMWorld::RecordBase::State_Deleted && mCells.find (*iter)==mCells.end()) { - Cell *cell = new Cell (mDocument.getData(), getSceneManager(), + Cell *cell = new Cell (mDocument, getSceneManager(), iter->getId (mWorldspace), mDocument.getPhysics()); + connect (cell->getSignalHandler(), SIGNAL(flagAsModified()), this, SLOT(flagAsModSlot())); mCells.insert (std::make_pair (*iter, cell)); float height = cell->getTerrainHeightAt(Ogre::Vector3( @@ -327,6 +331,106 @@ void CSVRender::PagedWorldspaceWidget::referenceAdded (const QModelIndex& parent flagAsModified(); } +void CSVRender::PagedWorldspaceWidget::pathgridDataChanged (const QModelIndex& topLeft, + const QModelIndex& bottomRight) +{ + for (std::map::iterator iter (mCells.begin()); + iter!=mCells.end(); ++iter) + iter->second->pathgridDataChanged (topLeft, bottomRight); +} + +CSVRender::Cell *CSVRender::PagedWorldspaceWidget::findCell(const std::string &cellId) +{ + const CSMWorld::IdCollection& cells = mDocument.getData().getCells(); + + std::map::iterator iter (mCells.begin()); + for(; iter!= mCells.end(); ++iter) + { + int index = cells.searchId(cellId); + + if (index != -1 && cellId == iter->first.getId (mWorldspace)) + { + return iter->second; + } + } + + return NULL; +} + +// NOTE: allow placing pathgrid points above objects and terrain +void CSVRender::PagedWorldspaceWidget::pathgridInserted (const std::string &referenceId, const Ogre::Vector3 &pos) +{ + QString id = QString(referenceId.c_str()); + + bool terrain = id.startsWith("HeightField_"); + bool object = QString(referenceId.c_str()).startsWith("ref#"); + // don't allow placing another one on top of a pathgrid point + if (id.isEmpty() || (!terrain && !object)) + return; + + std::string cellId; + if(terrain) + { + QRegExp nameRe("^HeightField_([\\d-]+)_([\\d-]+)$"); + if (nameRe.indexIn(id) == -1) + return; + + int cellX = nameRe.cap(1).toInt(); + int cellY = nameRe.cap(2).toInt(); + + std::ostringstream stream; + stream << "#" << cellX << " " << cellY; + cellId = stream.str(); + } + else + { + const CSMWorld::RefCollection& references = mDocument.getData().getReferences(); + int index = references.searchId(referenceId); + if(index == -1) + return; + + cellId = references.getData(index, references.findColumnIndex(CSMWorld::Columns::ColumnId_Cell)) + .toString().toUtf8().constData(); + } + + Cell *cell = findCell(cellId); + if(cell) + { + cell->pathgridPointAdded(pos); + flagAsModified(); + + return; + } +} + +void CSVRender::PagedWorldspaceWidget::pathgridMoved (const std::string &pgName, const Ogre::Vector3 &pos) +{ + std::pair result = PathgridPoint::getIdAndIndex(pgName); + + Cell *cell = findCell(result.first); + if(cell) + { + cell->pathgridPointMoved(pgName, pos); + flagAsModified(); + + return; + } +} + +void CSVRender::PagedWorldspaceWidget::pathgridAboutToBeRemoved (const std::string &pgName) +{ + std::pair result = PathgridPoint::getIdAndIndex(pgName); + + Cell *cell = findCell(result.first); + if(cell) + { + cell->pathgridPointRemoved(pgName); + flagAsModified(); + + return; + } +} + std::string CSVRender::PagedWorldspaceWidget::getStartupInstruction() { Ogre::Vector3 position = getCamera()->getPosition(); @@ -374,9 +478,9 @@ CSVRender::PagedWorldspaceWidget::~PagedWorldspaceWidget() if(mOverlayMask) { - removeRenderTargetListener(mOverlayMask); - delete mOverlayMask; - } + removeRenderTargetListener(mOverlayMask); + delete mOverlayMask; +} } void CSVRender::PagedWorldspaceWidget::useViewHint (const std::string& hint) @@ -526,3 +630,8 @@ void CSVRender::PagedWorldspaceWidget::cellAdded (const QModelIndex& index, int if (adjustCells()) flagAsModified(); } + +void CSVRender::PagedWorldspaceWidget::flagAsModSlot () +{ + flagAsModified(); +} diff --git a/apps/opencs/view/render/pagedworldspacewidget.hpp b/apps/opencs/view/render/pagedworldspacewidget.hpp index 3db6ee4edb..ea9449b2ff 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.hpp +++ b/apps/opencs/view/render/pagedworldspacewidget.hpp @@ -55,6 +55,8 @@ namespace CSVRender virtual std::string getStartupInstruction(); + Cell *findCell(const std::string &cellId); + public: PagedWorldspaceWidget (QWidget *parent, CSMDoc::Document& document); @@ -95,6 +97,10 @@ namespace CSVRender virtual void mouseDoubleClickEvent (QMouseEvent *event); + virtual void pathgridInserted (const std::string &referenceId, const Ogre::Vector3 &pos); + virtual void pathgridMoved (const std::string &pgName, const Ogre::Vector3 &pos); + virtual void pathgridAboutToBeRemoved (const std::string &pgName); + signals: void cellSelectionChanged (const CSMWorld::CellSelection& selection); @@ -107,6 +113,9 @@ namespace CSVRender virtual void cellAdded (const QModelIndex& index, int start, int end); + virtual void flagAsModSlot(); + + virtual void pathgridDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); }; } diff --git a/apps/opencs/view/render/pathgridpoint.cpp b/apps/opencs/view/render/pathgridpoint.cpp new file mode 100644 index 0000000000..ea6815fbe8 --- /dev/null +++ b/apps/opencs/view/render/pathgridpoint.cpp @@ -0,0 +1,70 @@ +#include "pathgridpoint.hpp" + +#include + +#include +#include + +#include "../world/physicssystem.hpp" + +#include "elements.hpp" + +namespace CSVRender +{ + PathgridPoint::PathgridPoint(const std::string &name, + Ogre::SceneNode *cellNode, const Ogre::Vector3 &pos, boost::shared_ptr physics) + : mBase(cellNode), mPhysics(physics) + { + mBase = cellNode->createChildSceneNode(); + + mPgPoint = NifOgre::Loader::createObjects(mBase, "pathgrid_pt.nif"); + mPgPoint->setVisibilityFlags(Element_Pathgrid); + mBase->setPosition(pos); + + physics->addObject("pathgrid_pt.nif", + mBase->getName(), name, 1, pos, Ogre::Quaternion::IDENTITY); + } + + PathgridPoint::~PathgridPoint() + { + mPgPoint.setNull(); + + mPhysics->removeObject(mBase->getName()); + + if (mBase) + mBase->getCreator()->destroySceneNode(mBase); + } + + // FIXME: Is there a way to identify the pathgrid point other than via the index? + // ESM::Pathgrid::Edge itself uses the indicies so any change (add/delete) must be + // propagated everywhere. + std::pair PathgridPoint::getIdAndIndex(const std::string &name) + { + // decode name + QString id = QString(name.c_str()); + QRegExp pathgridRe("^Pathgrid_(.+)_(\\d+)$"); + + if (id.isEmpty() || !id.startsWith("Pathgrid_")) + return std::make_pair("", -1); + + std::string pathgridId = ""; + int index = -1; + if (pathgridRe.indexIn(id) != -1) + { + pathgridId = pathgridRe.cap(1).toStdString(); + index = pathgridRe.cap(2).toInt(); + + return std::make_pair(pathgridId, index); + } + + return std::make_pair("", -1); + } + + std::string PathgridPoint::getName(const std::string &pathgridId, const int index) + { + std::ostringstream stream; + stream << "Pathgrid_" << pathgridId << "_" << index; + + return stream.str(); + } +} diff --git a/apps/opencs/view/render/pathgridpoint.hpp b/apps/opencs/view/render/pathgridpoint.hpp new file mode 100644 index 0000000000..54fe70e311 --- /dev/null +++ b/apps/opencs/view/render/pathgridpoint.hpp @@ -0,0 +1,42 @@ +#ifndef OPENCS_VIEW_PATHGRIDPOINT_H +#define OPENCS_VIEW_PATHGRIDPOINT_H + +#include + +#include + +namespace Ogre +{ + class Vector3; + class SceneNode; + class SceneManager; +} + +namespace CSVWorld +{ + class PhysicsSystem; +} + +namespace CSVRender +{ + class PathgridPoint + { + boost::shared_ptr mPhysics; // local copy + NifOgre::ObjectScenePtr mPgPoint; + Ogre::SceneNode *mBase; + + public: + + PathgridPoint(const std::string &name, + Ogre::SceneNode *cellNode, const Ogre::Vector3 &pos, + boost::shared_ptr physics); + + ~PathgridPoint(); + + static std::pair getIdAndIndex(const std::string &name); + + static std::string getName(const std::string &pathgridId, int index); + }; +} + +#endif // OPENCS_VIEW_PATHGRIDPOINT_H diff --git a/apps/opencs/view/render/unpagedworldspacewidget.cpp b/apps/opencs/view/render/unpagedworldspacewidget.cpp index 4f9dbb96c1..321c79460a 100644 --- a/apps/opencs/view/render/unpagedworldspacewidget.cpp +++ b/apps/opencs/view/render/unpagedworldspacewidget.cpp @@ -12,6 +12,7 @@ #include "../../model/world/data.hpp" #include "../../model/world/idtable.hpp" #include "../../model/world/tablemimedata.hpp" +#include "../../model/world/pathgridcommands.hpp" #include "../widget/scenetooltoggle.hpp" #include "../widget/scenetooltoggle2.hpp" @@ -48,7 +49,9 @@ CSVRender::UnpagedWorldspaceWidget::UnpagedWorldspaceWidget (const std::string& update(); - mCell.reset (new Cell (document.getData(), getSceneManager(), mCellId, document.getPhysics())); + Cell *cell = new Cell (document, getSceneManager(), mCellId, document.getPhysics()); + connect (cell->getSignalHandler(), SIGNAL(flagAsModified()), this, SLOT(flagAsModSlot())); + mCell.reset (cell); } void CSVRender::UnpagedWorldspaceWidget::cellDataChanged (const QModelIndex& topLeft, @@ -90,7 +93,9 @@ bool CSVRender::UnpagedWorldspaceWidget::handleDrop (const std::vectorgetId(); - mCell.reset (new Cell (getDocument().getData(), getSceneManager(), mCellId, getDocument().getPhysics())); + Cell *cell = new Cell (getDocument(), getSceneManager(), mCellId, getDocument().getPhysics()); + connect (cell->getSignalHandler(), SIGNAL(flagAsModified()), this, SLOT(flagAsModSlot())); + mCell.reset (cell); update(); emit cellChanged(*data.begin()); @@ -160,6 +165,12 @@ void CSVRender::UnpagedWorldspaceWidget::addVisibilitySelectorButtons ( tool->addButton (Element_Fog, "Fog"); } +void CSVRender::UnpagedWorldspaceWidget::pathgridDataChanged (const QModelIndex& topLeft, + const QModelIndex& bottomRight) +{ + // FIXME: +} + std::string CSVRender::UnpagedWorldspaceWidget::getStartupInstruction() { Ogre::Vector3 position = getCamera()->getPosition(); @@ -193,3 +204,8 @@ CSVRender::WorldspaceWidget::dropRequirments CSVRender::UnpagedWorldspaceWidget: return ignored; } } + +void CSVRender::UnpagedWorldspaceWidget::flagAsModSlot () +{ + flagAsModified(); +} diff --git a/apps/opencs/view/render/unpagedworldspacewidget.hpp b/apps/opencs/view/render/unpagedworldspacewidget.hpp index d01c3e7667..4e19efbf07 100644 --- a/apps/opencs/view/render/unpagedworldspacewidget.hpp +++ b/apps/opencs/view/render/unpagedworldspacewidget.hpp @@ -70,6 +70,10 @@ namespace CSVRender void cellRowsAboutToBeRemoved (const QModelIndex& parent, int start, int end); + virtual void flagAsModSlot(); + + virtual void pathgridDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); + signals: void cellChanged(const CSMWorld::UniversalId& id); diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp index 823a38c801..3c1ef5c64c 100644 --- a/apps/opencs/view/render/worldspacewidget.cpp +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include "../../model/world/universalid.hpp" #include "../../model/world/idtable.hpp" @@ -59,6 +60,12 @@ CSVRender::WorldspaceWidget::WorldspaceWidget (CSMDoc::Document& document, QWidg connect (debugProfiles, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), this, SLOT (debugProfileAboutToBeRemoved (const QModelIndex&, int, int))); + QAbstractItemModel *pathgrids = + document.getData().getTableModel (CSMWorld::UniversalId::Type_Pathgrid); + + connect (pathgrids, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), + this, SLOT (pathgridDataChanged (const QModelIndex&, const QModelIndex&))); + mPhysics = document.getPhysics(); // create physics if one doesn't exist mPhysics->addSceneManager(getSceneManager(), this); mMouse = new MouseState(this); @@ -415,12 +422,52 @@ void CSVRender::WorldspaceWidget::wheelEvent (QWheelEvent *event) SceneWidget::wheelEvent(event); } +// FIXME: mouse button events are processed in MouseState but key events are +// processed here - seems inconsistent void CSVRender::WorldspaceWidget::keyPressEvent (QKeyEvent *event) { if(event->key() == Qt::Key_Escape) { mMouse->cancelDrag(); } + else if(event->key() == Qt::Key_Delete) + { + QPoint p = this->mapFromGlobal(QCursor::pos()); + std::pair result = + mMouse->underCursor(p.x(), p.y(), CSVRender::Element_Pathgrid); + + if(result.first != "") + { + pathgridAboutToBeRemoved(result.first); + } + else + SceneWidget::keyPressEvent(event); + } + else if(event->key() == Qt::Key_Insert) + { + QPoint p = this->mapFromGlobal(QCursor::pos()); + std::pair result = + mMouse->underCursor(p.x(), p.y(), CSVRender::Element_Reference|CSVRender::Element_Terrain); + + if(result.first != "") + { + pathgridInserted(result.first, result.second); + } + else + SceneWidget::keyPressEvent(event); + } else SceneWidget::keyPressEvent(event); } + +void CSVRender::WorldspaceWidget::pathgridAboutToBeRemoved (const std::string &pgName) +{ +} + +void CSVRender::WorldspaceWidget::pathgridMoved (const std::string &pgName, const Ogre::Vector3 &newPos) +{ +} + +void CSVRender::WorldspaceWidget::pathgridInserted (const std::string &name, const Ogre::Vector3 &pos) +{ +} diff --git a/apps/opencs/view/render/worldspacewidget.hpp b/apps/opencs/view/render/worldspacewidget.hpp index b19197e36b..a250547b32 100644 --- a/apps/opencs/view/render/worldspacewidget.hpp +++ b/apps/opencs/view/render/worldspacewidget.hpp @@ -124,6 +124,11 @@ namespace CSVRender virtual void wheelEvent (QWheelEvent *event); virtual void keyPressEvent (QKeyEvent *event); + // FIXME: temporary only until the signals from the document are implemented + virtual void pathgridInserted (const std::string &terrain, const Ogre::Vector3 &pos); + virtual void pathgridMoved (const std::string &pgName, const Ogre::Vector3 &pos); + virtual void pathgridAboutToBeRemoved (const std::string &pgName); + private: void dragEnterEvent(QDragEnterEvent *event); @@ -158,6 +163,7 @@ namespace CSVRender void debugProfileAboutToBeRemoved (const QModelIndex& parent, int start, int end); + virtual void pathgridDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) = 0; protected slots: diff --git a/apps/opencs/view/world/physicssystem.cpp b/apps/opencs/view/world/physicssystem.cpp index 7dce611c48..9a19e03d05 100644 --- a/apps/opencs/view/world/physicssystem.cpp +++ b/apps/opencs/view/world/physicssystem.cpp @@ -72,9 +72,6 @@ namespace CSVWorld if(referenceId != "") { - mSceneNodeToRefId.erase(sceneNodeName); - mSceneNodeToMesh.erase(sceneNodeName); - // find which SceneManager has this object Ogre::SceneManager *sceneManager = findSceneManager(sceneNodeName); if(!sceneManager) @@ -99,7 +96,8 @@ namespace CSVWorld mRefIdToSceneNode.begin(); for(; itRef != mRefIdToSceneNode.end(); ++itRef) { - if((*itRef).second.find(sceneManager) != (*itRef).second.end()) + if((*itRef).first == referenceId && + (*itRef).second.find(sceneManager) != (*itRef).second.end()) { (*itRef).second.erase(sceneManager); break; @@ -107,11 +105,15 @@ namespace CSVWorld } // check whether the physics model should be deleted - if(mRefIdToSceneNode.find(referenceId) == mRefIdToSceneNode.end()) + itRef = mRefIdToSceneNode.find(referenceId); + if(itRef == mRefIdToSceneNode.end() || (*itRef).second.empty()) { mEngine->removeRigidBody(referenceId); mEngine->deleteRigidBody(referenceId); } + + mSceneNodeToRefId.erase(sceneNodeName); + mSceneNodeToMesh.erase(sceneNodeName); } } @@ -211,11 +213,6 @@ namespace CSVWorld } } - // sceneMgr: to lookup the scene node name from the object's referenceId - // camera: primarily used to get the visibility mask for the viewport - // - // returns the found object's scene node name and its position in the world space - // // WARNING: far clip distance is a global setting, if it changes in future // this method will need to be updated std::pair PhysicsSystem::castRay(float mouseX, @@ -241,27 +238,86 @@ namespace CSVWorld _from = btVector3(from.x, from.y, from.z); _to = btVector3(to.x, to.y, to.z); - uint32_t visibilityMask = camera->getViewport()->getVisibilityMask(); - bool ignoreHeightMap = !(visibilityMask & (uint32_t)CSVRender::Element_Terrain); - bool ignoreObjects = !(visibilityMask & (uint32_t)CSVRender::Element_Reference); + Ogre::uint32 visibilityMask = camera->getViewport()->getVisibilityMask(); + bool ignoreHeightMap = !(visibilityMask & (Ogre::uint32)CSVRender::Element_Terrain); + bool ignoreObjects = !(visibilityMask & (Ogre::uint32)CSVRender::Element_Reference); + bool ignorePathgrid = !(visibilityMask & (Ogre::uint32)CSVRender::Element_Pathgrid); - Ogre::Vector3 norm; // not used - std::pair result = - mEngine->rayTest(_from, _to, !ignoreObjects, ignoreHeightMap, &norm); + std::pair result = std::make_pair("", -1); + short mask = OEngine::Physic::CollisionType_Raycasting; + std::vector > objects = mEngine->rayTest2(_from, _to, mask); + + for (std::vector >::iterator it = objects.begin(); + it != objects.end(); ++it) + { + if(ignorePathgrid && QString((*it).second.c_str()).contains(QRegExp("^Pathgrid"))) + continue; + else if(ignoreObjects && QString((*it).second.c_str()).contains(QRegExp("^ref#"))) + continue; + else if(ignoreHeightMap && QString((*it).second.c_str()).contains(QRegExp("^Height"))) + continue; + + result = std::make_pair((*it).second, (*it).first); + break; + } // result.first is the object's referenceId if(result.first == "") - return std::make_pair("", Ogre::Vector3(0,0,0)); + return std::make_pair("", Ogre::Vector3()); else - { - std::string name = refIdToSceneNode(result.first, sceneMgr); - if(name == "") - name = result.first; - else - name = refIdToSceneNode(result.first, sceneMgr); + return std::make_pair(result.first, ray.getPoint(farClipDist*result.second)); + } - return std::make_pair(name, ray.getPoint(farClipDist*result.second)); + std::pair PhysicsSystem::distToGround(const Ogre::Vector3 &position, + Ogre::uint32 visibilityMask, const float limit) + { + btVector3 _from, _to; + _from = btVector3(position.x, position.y, position.z); + _to = btVector3(position.x, position.y, position.z-limit); + + bool ignoreHeightMap = !(visibilityMask & (Ogre::uint32)CSVRender::Element_Terrain); + bool ignoreObjects = !(visibilityMask & (Ogre::uint32)CSVRender::Element_Reference); + bool ignorePathgrid = !(visibilityMask & (Ogre::uint32)CSVRender::Element_Pathgrid); + + std::pair result = std::make_pair("", -1); + short mask = OEngine::Physic::CollisionType_Raycasting; + std::vector > objects = mEngine->rayTest2(_from, _to, mask); + + for (std::vector >::iterator it = objects.begin(); + it != objects.end(); ++it) + { + if(ignorePathgrid && QString((*it).second.c_str()).contains(QRegExp("^Pathgrid"))) + continue; + else if(ignoreObjects && QString((*it).second.c_str()).contains(QRegExp("^ref#"))) + continue; + else if(ignoreHeightMap && QString((*it).second.c_str()).contains(QRegExp("^Height"))) + continue; + + result = std::make_pair((*it).second, (*it).first); + break; } + + // result.first is the object's referenceId + if(result.first == "") + return std::make_pair("", -1); + else + return std::make_pair(result.first, limit*result.second); + } + + // tries to find the distance to the "top" of the closest object (ignores pathgrid points) + std::pair PhysicsSystem::distToClosest(const Ogre::Vector3 &position, + Ogre::uint32 visibilityMask, const float limit) + { + const float thickness = 50; // arbitrary number + + std::pair resDown = + distToGround(Ogre::Vector3(position.x, position.y, position.z+thickness), + visibilityMask&(~CSVRender::Element_Pathgrid), limit+thickness); + + if(resDown.first != "") + return std::make_pair(resDown.first, resDown.second-thickness); + else + return std::make_pair("", -1); } std::string PhysicsSystem::refIdToSceneNode(std::string referenceId, Ogre::SceneManager *sceneMgr) diff --git a/apps/opencs/view/world/physicssystem.hpp b/apps/opencs/view/world/physicssystem.hpp index 17661caa80..0d688fbc9c 100644 --- a/apps/opencs/view/world/physicssystem.hpp +++ b/apps/opencs/view/world/physicssystem.hpp @@ -4,6 +4,8 @@ #include #include +#include + namespace Ogre { class Vector3; @@ -73,7 +75,13 @@ namespace CSVWorld std::pair castRay(float mouseX, float mouseY, Ogre::SceneManager *sceneMgr, Ogre::Camera *camera); - std::string sceneNodeToRefId(std::string sceneNodeName); + std::pair distToGround(const Ogre::Vector3 &position, + Ogre::uint32 visibilityMask, const float limit = 300000); + + std::pair distToClosest(const Ogre::Vector3 &position, + Ogre::uint32 visibilityMask, const float limit = 100.0f); + + std::string refIdToSceneNode(std::string referenceId, Ogre::SceneManager *sceneMgr); // for multi-scene manager per physics engine std::map sceneWidgets(); @@ -83,9 +91,7 @@ namespace CSVWorld void moveSceneNodeImpl(const std::string sceneNodeName, const std::string referenceId, const Ogre::Vector3 &position); - void updateSelectionHighlight(std::string sceneNode, const Ogre::Vector3 &position); - - std::string refIdToSceneNode(std::string referenceId, Ogre::SceneManager *sceneMgr); + std::string sceneNodeToRefId(std::string sceneNodeName); Ogre::SceneManager *findSceneManager(std::string sceneNodeName); }; diff --git a/files/CMakeLists.txt b/files/CMakeLists.txt index da3451d93e..aa8e3f0509 100644 --- a/files/CMakeLists.txt +++ b/files/CMakeLists.txt @@ -17,6 +17,7 @@ set(MATERIAL_FILES objects.shader objects.shaderset openmw.configuration + pathgrid_pt.nif quad.mat quad.shader quad.shaderset diff --git a/files/materials/pathgrid_pt.nif b/files/materials/pathgrid_pt.nif new file mode 100644 index 0000000000..cc37e0ee37 Binary files /dev/null and b/files/materials/pathgrid_pt.nif differ