1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-02-01 04:45:36 +00:00

Merge pull request #12 from cc9cii/Feature-1278

Feature #1278 - part 1
This commit is contained in:
cc9cii 2015-11-07 15:20:13 +11:00
commit c7c0023ed2
27 changed files with 1147 additions and 174 deletions

View file

@ -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

View file

@ -425,6 +425,9 @@ std::auto_ptr<sh::Factory> 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());

View file

@ -12,7 +12,6 @@
#include <QModelIndex>
#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();

View file

@ -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

View file

@ -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<Pathgrid>& 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<Pathgrid>& 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<Pathgrid>& record,
const QVariant& value, int subRowIndex, int subColIndex) const
{

View file

@ -3,7 +3,6 @@
#include <QVariant>
#include <components/esm/loadpgrd.hpp>
#include <components/esm/effectlist.hpp>
#include <components/esm/loadmgef.hpp> // for converting magic effect id to string & back
#include <components/esm/loadskil.hpp> // 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<Pathgrid>
{
public:

View file

@ -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()));
}

View file

@ -0,0 +1,65 @@
#ifndef CSM_WOLRD_PATHGRIDCOMMANDS_H
#define CSM_WOLRD_PATHGRIDCOMMANDS_H
#include <QObject>
#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

View file

@ -0,0 +1,26 @@
#ifndef CSM_WOLRD_PATHGRIDPOINTSWRAP_H
#define CSM_WOLRD_PATHGRIDPOINTSWRAP_H
#include <components/esm/loadpgrd.hpp>
#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

View file

@ -2,18 +2,83 @@
#include <OgreSceneManager.h>
#include <OgreSceneNode.h>
#include <OgreManualObject.h>
#include <components/misc/stringops.hpp>
#include <components/esm/loadland.hpp>
#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<CSVWorld::PhysicsSystem> 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<CSMWorld::IdTable&> (
*mData.getTableModel (CSMWorld::UniversalId::Type_References));
*mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_References));
int rows = references.rowCount();
addObjects (0, rows-1);
const CSMWorld::IdCollection<CSMWorld::Land>& land = mData.getLand();
const CSMWorld::IdCollection<CSMWorld::Land>& 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<CSMWorld::IdTable&> (
*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<std::string, bool>::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<CSMWorld::IdTable&> (
*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<float>::max();
}
void CSVRender::Cell::pathgridDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
{
CSMWorld::IdTree *pathgrids = dynamic_cast<CSMWorld::IdTree *>(
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<CSMWorld::Pathgrid>& 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<CSMWorld::IdTree *>(
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::pair<int, int>, 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<std::string, PathgridPoint *>::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<CSMWorld::Pathgrid>& pathgrids = mDocument.getData().getPathgrids();
const CSMWorld::Pathgrid &pathgrid = pathgrids.getRecord(mPgIndex).get();
int worldsize = ESM::Land::REAL_SIZE;
std::vector<ESM::Pathgrid::Point>::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<CSMWorld::Pathgrid>& 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<std::string, int> result = PathgridPoint::getIdAndIndex(name);
if(result.first == "")
return;
std::string pathgridId = result.first;
int index = result.second;
const CSMWorld::SubCellCollection<CSMWorld::Pathgrid>& 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<ESM::Pathgrid::Edge>::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<std::string, int> result = PathgridPoint::getIdAndIndex(name);
if(result.first == "")
return;
std::string pathgridId = result.first;
int index = result.second;
const CSMWorld::SubCellCollection<CSMWorld::Pathgrid>& 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;
}

View file

@ -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<std::string, Object *> mObjects;
std::map<std::string, PathgridPoint *> mPgPoints;
std::map<std::pair<int, int>, std::string> mPgEdges;
CSMWorld::NestedTableProxyModel *mProxyModel;
CSMWorld::IdTree *mModel;
int mPgIndex;
CSMWorld::SignalHandler *mHandler;
std::auto_ptr<Terrain::TerrainGrid> mTerrain;
boost::shared_ptr<CSVWorld::PhysicsSystem> 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<CSVWorld::PhysicsSystem> 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();
};
}

View file

@ -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();
@ -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<bool, Ogre::Vector3> planeResult = mousePositionOnPlane(event->pos(), *mPlane);
// relative movement of the object on the movement plane
std::pair<bool, Ogre::Vector3> planeResult = mousePosOnPlane(event->pos(), *mPlane);
if(planeResult.first)
{
if(mGrabbedSceneNode != "")
{
std::pair<Ogre::Vector3, Ogre::Vector3> 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);
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<std::string, Ogre::Vector3> result = objectUnderCursor(event->x(), event->y());
// get object or pathgrid
std::pair<std::string, Ogre::Vector3> 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<Ogre::Vector3, Ogre::Vector3> planeRes = planeAxis();
mPlane->redefine(planeRes.first, result.second);
std::pair<bool, Ogre::Vector3> planeResult = mousePositionOnPlane(event->pos(), *mPlane);
std::pair<bool, Ogre::Vector3> 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<std::string, Ogre::Vector3> result = objectUnderCursor(event->x(), event->y());
std::pair<std::string, Ogre::Vector3> result = underCursor(event->x(), event->y(),
CSVRender::Element_Reference|CSVRender::Element_Pathgrid);
if(result.first != "")
{
if(result.first == mCurrentObj)
@ -208,45 +214,75 @@ 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<bool, Ogre::Vector3> planeResult = mousePositionOnPlane(event->pos(), *mPlane);
std::pair<bool, Ogre::Vector3> planeResult = mousePosOnPlane(event->pos(), *mPlane);
if(planeResult.first)
{
if(mGrabbedSceneNode != "")
{
std::pair<Ogre::Vector3, Ogre::Vector3> 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<std::string, float> 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 = mGrabbedRefId; // 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
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
mGrabbedSceneNode = ""; // id of the object
mOffset = 0.0f; // used for z-axis movement
mOldPos = QPoint(0, 0); // to calculate relative movement of mouse
}
mOldMousePos = Ogre::Vector3(); // mouse pos to use in wheel event
mOldCursorPos = QPoint(0, 0); // to calculate relative movement of mouse
}
break;
}
@ -254,10 +290,18 @@ namespace CSVRender
case Mouse_Default:
{
// probably terrain, check
std::pair<std::string, Ogre::Vector3> result = terrainUnderCursor(event->x(), event->y());
std::pair<std::string, Ogre::Vector3> 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<Ogre::Vector3, Ogre::Vector3> 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<bool, Ogre::Vector3> 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<bool, Ogre::Vector3> MouseState::mousePositionOnPlane(const QPoint &pos, const Ogre::Plane &plane)
std::pair<bool, Ogre::Vector3> 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<std::string, Ogre::Vector3> MouseState::terrainUnderCursor(const int mouseX, const int mouseY)
std::pair<std::string, Ogre::Vector3> MouseState::underCursor(const int mouseX,
const int mouseY, Ogre::uint32 elements)
{
if(!getViewport())
return std::make_pair("", Ogre::Vector3());
@ -401,44 +478,18 @@ namespace CSVRender
float y = (float) mouseY / getViewport()->getActualHeight();
std::pair<std::string, Ogre::Vector3> result = mPhysics->castRay(x, y, mSceneManager, getCamera());
if(result.first != "")
{
// FIXME: is there a better way to distinguish terrain from objects?
QString name = QString(result.first.c_str());
if(name.contains(QRegExp("^HeightField")))
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")))))
{
return result;
}
}
return std::make_pair("", Ogre::Vector3());
}
std::pair<std::string, Ogre::Vector3> MouseState::objectUnderCursor(const int mouseX, const int mouseY)
{
if(!getViewport())
return std::make_pair("", Ogre::Vector3());
float x = (float) mouseX / getViewport()->getActualWidth();
float y = (float) mouseY / getViewport()->getActualHeight();
std::pair<std::string, Ogre::Vector3> 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;
}
}
}
else
return std::make_pair("", Ogre::Vector3());
}
@ -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();
}
}

View file

@ -47,15 +47,15 @@ namespace CSVRender
boost::shared_ptr<CSVWorld::PhysicsSystem> 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<std::string, Ogre::Vector3> underCursor(const int mouseX,
const int mouseY, Ogre::uint32 elements = 0xFFFFFFFF);
void cancelDrag();
private:
std::pair<bool, Ogre::Vector3> mousePositionOnPlane(const QPoint &pos, const Ogre::Plane &plane);
std::pair<std::string, Ogre::Vector3> terrainUnderCursor(const int mouseX, const int mouseY);
std::pair<std::string, Ogre::Vector3> objectUnderCursor(const int mouseX, const int mouseY);
std::pair<bool, Ogre::Vector3> mousePosOnPlane(const QPoint &pos, const Ogre::Plane &plane);
std::pair<Ogre::Vector3, Ogre::Vector3> planeAxis();
void updateSceneWidgets();
void placeObject(const std::string sceneNodeName, const Ogre::Vector3 &pos); // FIXME
Ogre::Camera *getCamera(); // friend access
Ogre::Viewport *getViewport(); // friend access
};

View file

@ -223,12 +223,10 @@ bool CSVRender::Object::referenceDataChanged (const QModelIndex& topLeft,
{
mReferenceableId =
references.getData (index, columnIndex).toString().toUtf8().constData();
update();
}
update();
adjust();
return true;
}

View file

@ -36,6 +36,7 @@ namespace CSVRender
NifOgre::ObjectScenePtr mObject;
bool mForceBaseToZero;
boost::shared_ptr<CSVWorld::PhysicsSystem> mPhysics;
std::string mPhysicsObject;
/// Not implemented
Object (const Object&);

View file

@ -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<CSMWorld::CellCoordinates, Cell *>::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<CSMWorld::Cell>& cells = mDocument.getData().getCells();
std::map<CSMWorld::CellCoordinates, Cell *>::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<std::string, int> 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<std::string, int> 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();
@ -526,3 +630,8 @@ void CSVRender::PagedWorldspaceWidget::cellAdded (const QModelIndex& index, int
if (adjustCells())
flagAsModified();
}
void CSVRender::PagedWorldspaceWidget::flagAsModSlot ()
{
flagAsModified();
}

View file

@ -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);
};
}

View file

@ -0,0 +1,70 @@
#include "pathgridpoint.hpp"
#include <QRegExp>
#include <OgreSceneManager.h>
#include <OgreSceneNode.h>
#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<CSVWorld::PhysicsSystem> 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<std::string, int> 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();
}
}

View file

@ -0,0 +1,42 @@
#ifndef OPENCS_VIEW_PATHGRIDPOINT_H
#define OPENCS_VIEW_PATHGRIDPOINT_H
#include <boost/shared_ptr.hpp>
#include <components/nifogre/ogrenifloader.hpp>
namespace Ogre
{
class Vector3;
class SceneNode;
class SceneManager;
}
namespace CSVWorld
{
class PhysicsSystem;
}
namespace CSVRender
{
class PathgridPoint
{
boost::shared_ptr<CSVWorld::PhysicsSystem> 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<CSVWorld::PhysicsSystem> physics);
~PathgridPoint();
static std::pair<std::string, int> getIdAndIndex(const std::string &name);
static std::string getName(const std::string &pathgridId, int index);
};
}
#endif // OPENCS_VIEW_PATHGRIDPOINT_H

View file

@ -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::vector<CSMWorld:
return false;
mCellId = data.begin()->getId();
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();
}

View file

@ -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);

View file

@ -12,6 +12,7 @@
#include <QDropEvent>
#include <QMouseEvent>
#include <QKeyEvent>
#include <QPoint>
#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<std::string, Ogre::Vector3> 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<std::string, Ogre::Vector3> 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)
{
}

View file

@ -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:

View file

@ -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<std::string, Ogre::Vector3> 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<std::string, float> result =
mEngine->rayTest(_from, _to, !ignoreObjects, ignoreHeightMap, &norm);
std::pair<std::string, float> result = std::make_pair("", -1);
short mask = OEngine::Physic::CollisionType_Raycasting;
std::vector<std::pair<float, std::string> > objects = mEngine->rayTest2(_from, _to, mask);
for (std::vector<std::pair<float, std::string> >::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(name, ray.getPoint(farClipDist*result.second));
return std::make_pair(result.first, ray.getPoint(farClipDist*result.second));
}
std::pair<std::string, float> 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<std::string, float> result = std::make_pair("", -1);
short mask = OEngine::Physic::CollisionType_Raycasting;
std::vector<std::pair<float, std::string> > objects = mEngine->rayTest2(_from, _to, mask);
for (std::vector<std::pair<float, std::string> >::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<std::string, float> PhysicsSystem::distToClosest(const Ogre::Vector3 &position,
Ogre::uint32 visibilityMask, const float limit)
{
const float thickness = 50; // arbitrary number
std::pair<std::string, float> 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)

View file

@ -4,6 +4,8 @@
#include <string>
#include <map>
#include <OgrePlatform.h>
namespace Ogre
{
class Vector3;
@ -73,7 +75,13 @@ namespace CSVWorld
std::pair<std::string, Ogre::Vector3> castRay(float mouseX,
float mouseY, Ogre::SceneManager *sceneMgr, Ogre::Camera *camera);
std::string sceneNodeToRefId(std::string sceneNodeName);
std::pair<std::string, float> distToGround(const Ogre::Vector3 &position,
Ogre::uint32 visibilityMask, const float limit = 300000);
std::pair<std::string, float> 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<Ogre::SceneManager*, CSVRender::SceneWidget *> 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);
};

View file

@ -17,6 +17,7 @@ set(MATERIAL_FILES
objects.shader
objects.shaderset
openmw.configuration
pathgrid_pt.nif
quad.mat
quad.shader
quad.shaderset

Binary file not shown.