1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-02-01 07: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 opencs_units (model/world
idtable idtableproxymodel regionmap data commanddispatcher idtablebase resourcetable nestedtableproxymodel idtree infotableproxymodel idtable idtableproxymodel regionmap data commanddispatcher idtablebase resourcetable nestedtableproxymodel idtree infotableproxymodel
pathgridcommands
) )
@ -91,6 +92,7 @@ opencs_units (view/render
opencs_units_noqt (view/render opencs_units_noqt (view/render
navigation navigation1st navigationfree navigationorbit lighting lightingday lightingnight navigation navigation1st navigationfree navigationorbit lighting lightingday lightingnight
lightingbright object cell terrainstorage textoverlay overlaymask overlaysystem mousestate lightingbright object cell terrainstorage textoverlay overlaymask overlaysystem mousestate
pathgridpoint
) )
opencs_hdrs_noqt (view/render opencs_hdrs_noqt (view/render

View file

@ -425,6 +425,9 @@ std::auto_ptr<sh::Factory> CS::Editor::setupGraphics()
// for font used in overlays // for font used in overlays
Ogre::Root::getSingleton().addResourceLocation ((mResources / "mygui").string(), Ogre::Root::getSingleton().addResourceLocation ((mResources / "mygui").string(),
"FileSystem", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, true); "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())) if (!boost::filesystem::exists (mCfgMgr.getCachePath()))
boost::filesystem::create_directories (mCfgMgr.getCachePath()); boost::filesystem::create_directories (mCfgMgr.getCachePath());

View file

@ -12,7 +12,6 @@
#include <QModelIndex> #include <QModelIndex>
#include "universalid.hpp" #include "universalid.hpp"
#include "nestedtablewrapper.hpp"
class QModelIndex; class QModelIndex;
class QAbstractItemModel; class QAbstractItemModel;
@ -205,7 +204,8 @@ namespace CSMWorld
public: 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(); virtual void redo();
@ -227,7 +227,8 @@ namespace CSMWorld
public: 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(); 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()); 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 CSMWorld::NestedTableWrapperBase* CSMWorld::IdTree::nestedTable(const QModelIndex& index) const

View file

@ -7,6 +7,7 @@
#include "pathgrid.hpp" #include "pathgrid.hpp"
#include "info.hpp" #include "info.hpp"
#include "usertype.hpp" #include "usertype.hpp"
#include "pathgridpointswrap.hpp"
namespace CSMWorld namespace CSMWorld
{ {
@ -148,6 +149,8 @@ namespace CSMWorld
PathgridEdgeListAdapter::PathgridEdgeListAdapter () {} PathgridEdgeListAdapter::PathgridEdgeListAdapter () {}
// ToDo: seems to be auto-sorted in the dialog table display after insertion // 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 void PathgridEdgeListAdapter::addRow(Record<Pathgrid>& record, int position) const
{ {
Pathgrid pathgrid = record.get(); Pathgrid pathgrid = record.get();
@ -169,6 +172,7 @@ namespace CSMWorld
record.setModified (pathgrid); record.setModified (pathgrid);
} }
// FIXME: edges should be removed in pairs and Point.mConnectionNum updated
void PathgridEdgeListAdapter::removeRow(Record<Pathgrid>& record, int rowToRemove) const void PathgridEdgeListAdapter::removeRow(Record<Pathgrid>& record, int rowToRemove) const
{ {
Pathgrid pathgrid = record.get(); Pathgrid pathgrid = record.get();
@ -219,6 +223,8 @@ namespace CSMWorld
} }
// ToDo: detect duplicates in mEdges // ToDo: detect duplicates in mEdges
//
// FIXME: Point.mConnectionNum needs to be updated
void PathgridEdgeListAdapter::setData(Record<Pathgrid>& record, void PathgridEdgeListAdapter::setData(Record<Pathgrid>& record,
const QVariant& value, int subRowIndex, int subColIndex) const const QVariant& value, int subRowIndex, int subColIndex) const
{ {

View file

@ -3,7 +3,6 @@
#include <QVariant> #include <QVariant>
#include <components/esm/loadpgrd.hpp>
#include <components/esm/effectlist.hpp> #include <components/esm/effectlist.hpp>
#include <components/esm/loadmgef.hpp> // for converting magic effect id to string & back #include <components/esm/loadmgef.hpp> // for converting magic effect id to string & back
#include <components/esm/loadskil.hpp> // for converting skill names #include <components/esm/loadskil.hpp> // for converting skill names
@ -25,21 +24,6 @@ namespace CSMWorld
struct Pathgrid; struct Pathgrid;
struct Info; 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> class PathgridPointListAdapter : public NestedColumnAdapter<Pathgrid>
{ {
public: 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 <OgreSceneManager.h>
#include <OgreSceneNode.h> #include <OgreSceneNode.h>
#include <OgreManualObject.h>
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
#include <components/esm/loadland.hpp> #include <components/esm/loadland.hpp>
#include "../../model/doc/document.hpp"
#include "../../model/world/idtable.hpp" #include "../../model/world/idtable.hpp"
#include "../../model/world/idtree.hpp"
#include "../../model/world/columns.hpp" #include "../../model/world/columns.hpp"
#include "../../model/world/data.hpp" #include "../../model/world/data.hpp"
#include "../../model/world/refcollection.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 "../world/physicssystem.hpp"
#include "elements.hpp" #include "elements.hpp"
#include "terrainstorage.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) bool CSVRender::Cell::removeObject (const std::string& id)
{ {
@ -32,7 +97,7 @@ bool CSVRender::Cell::addObjects (int start, int end)
{ {
bool modified = false; bool modified = false;
const CSMWorld::RefCollection& collection = mData.getReferences(); const CSMWorld::RefCollection& collection = mDocument.getData().getReferences();
for (int i=start; i<=end; ++i) 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); 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; modified = true;
} }
} }
@ -52,21 +117,23 @@ bool CSVRender::Cell::addObjects (int start, int end)
return modified; 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) 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 = sceneManager->getRootSceneNode()->createChildSceneNode();
mCellNode->setPosition (origin); mCellNode->setPosition (origin);
CSMWorld::IdTable& references = dynamic_cast<CSMWorld::IdTable&> ( CSMWorld::IdTable& references = dynamic_cast<CSMWorld::IdTable&> (
*mData.getTableModel (CSMWorld::UniversalId::Type_References)); *mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_References));
int rows = references.rowCount(); int rows = references.rowCount();
addObjects (0, rows-1); 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); int landIndex = land.searchId(mId);
if (landIndex != -1) if (landIndex != -1)
{ {
@ -74,7 +141,7 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, Ogre::SceneManager *sceneManager,
if (esmLand.getLandData (ESM::Land::DATA_VHGT)) 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)); Terrain::Align_XY));
mTerrain->loadCell(esmLand.mX, mTerrain->loadCell(esmLand.mX,
esmLand.mY); 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); esmLand.getLandData(ESM::Land::DATA_VHGT)->mHeights, mX, mY, 0, worldsize / (verts-1), verts);
} }
} }
setupPathgrid();
buildPathgrid();
} }
CSVRender::Cell::~Cell() CSVRender::Cell::~Cell()
{ {
clearPathgrid();
destroyGridMaterials();
delete mProxyModel;
delete mHandler;
if (mTerrain.get()) if (mTerrain.get())
mPhysics->removeHeightField(mSceneMgr, mX, mY); mPhysics->removeHeightField(mSceneMgr, mX, mY);
@ -135,7 +211,7 @@ bool CSVRender::Cell::referenceDataChanged (const QModelIndex& topLeft,
const QModelIndex& bottomRight) const QModelIndex& bottomRight)
{ {
CSMWorld::IdTable& references = dynamic_cast<CSMWorld::IdTable&> ( 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 idColumn = references.findColumnIndex (CSMWorld::Columns::ColumnId_Id);
int cellColumn = references.findColumnIndex (CSMWorld::Columns::ColumnId_Cell); 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) for (std::map<std::string, bool>::iterator iter (ids.begin()); iter!=ids.end(); ++iter)
{ {
mObjects.insert (std::make_pair ( 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; modified = true;
} }
@ -202,7 +278,7 @@ bool CSVRender::Cell::referenceAboutToBeRemoved (const QModelIndex& parent, int
return false; return false;
CSMWorld::IdTable& references = dynamic_cast<CSMWorld::IdTable&> ( 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 idColumn = references.findColumnIndex (CSMWorld::Columns::ColumnId_Id);
@ -231,3 +307,287 @@ float CSVRender::Cell::getTerrainHeightAt(const Ogre::Vector3 &pos) const
else else
return -std::numeric_limits<float>::max(); 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 SceneManager;
class SceneNode; class SceneNode;
class ManualObject;
}
namespace CSMDoc
{
class Document;
} }
namespace CSMWorld namespace CSMWorld
{ {
class Data; class Pathgrid;
class NestedTableProxyModel;
class IdTree;
class SignalHandler;
} }
namespace CSVWorld namespace CSVWorld
@ -35,12 +44,22 @@ namespace CSVWorld
namespace CSVRender namespace CSVRender
{ {
class PathgridPoint;
class Cell class Cell
{ {
CSMWorld::Data& mData; CSMDoc::Document& mDocument;
std::string mId; std::string mId;
Ogre::SceneNode *mCellNode; Ogre::SceneNode *mCellNode;
std::map<std::string, Object *> mObjects; 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; std::auto_ptr<Terrain::TerrainGrid> mTerrain;
boost::shared_ptr<CSVWorld::PhysicsSystem> mPhysics; boost::shared_ptr<CSVWorld::PhysicsSystem> mPhysics;
Ogre::SceneManager *mSceneMgr; Ogre::SceneManager *mSceneMgr;
@ -59,7 +78,7 @@ namespace CSVRender
public: 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)); boost::shared_ptr<CSVWorld::PhysicsSystem> physics, const Ogre::Vector3& origin = Ogre::Vector3 (0, 0, 0));
~Cell(); ~Cell();
@ -86,6 +105,31 @@ namespace CSVRender
bool referenceAdded (const QModelIndex& parent, int start, int end); bool referenceAdded (const QModelIndex& parent, int start, int end);
float getTerrainHeightAt(const Ogre::Vector3 &pos) const; 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) MouseState::MouseState(WorldspaceWidget *parent)
: mMouseState(Mouse_Default), mParent(parent), mPhysics(parent->mDocument.getPhysics()) : 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()) , 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) , mColIndexPosY(0), mColIndexPosZ(0)
{ {
const CSMWorld::RefCollection& references = mParent->mDocument.getData().getReferences(); const CSMWorld::RefCollection& references = mParent->mDocument.getData().getReferences();
@ -113,24 +113,22 @@ namespace CSVRender
} }
case Mouse_Drag: 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 // ray test against the plane to provide feedback to the user the
// relative movement of the object on the x-y plane // relative movement of the object on the movement plane
std::pair<bool, Ogre::Vector3> planeResult = mousePositionOnPlane(event->pos(), *mPlane); std::pair<bool, Ogre::Vector3> planeResult = mousePosOnPlane(event->pos(), *mPlane);
if(planeResult.first) if(planeResult.first)
{ {
if(mGrabbedSceneNode != "") Ogre::Vector3 pos = mOrigObjPos + (planeResult.second-mOrigMousePos);
{
std::pair<Ogre::Vector3, Ogre::Vector3> planeRes = planeAxis(); mSceneManager->getSceneNode(mGrabbedSceneNode)->setPosition(pos);
Ogre::Vector3 pos = mOrigObjPos + planeRes.first*mOffset; mPhysics->moveSceneNodes(mGrabbedSceneNode, pos);
mSceneManager->getSceneNode(mGrabbedSceneNode)->setPosition(pos+planeResult.second-mOrigMousePos);
mCurrentMousePos = planeResult.second;
mPhysics->moveSceneNodes(mGrabbedSceneNode, pos+planeResult.second-mOrigMousePos);
updateSceneWidgets(); updateSceneWidgets();
}
mOldMousePos = planeResult.second;
} }
} }
break; break;
@ -158,21 +156,27 @@ namespace CSVRender
{ {
if(event->buttons() & Qt::RightButton) 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 == "") if(result.first == "")
break; 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 // ray test agaist the plane to get a starting position of the
// mouse in relation to the object position // mouse in relation to the object position
std::pair<Ogre::Vector3, Ogre::Vector3> planeRes = planeAxis(); std::pair<Ogre::Vector3, Ogre::Vector3> planeRes = planeAxis();
mPlane->redefine(planeRes.first, result.second); 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) if(planeResult.first)
{ {
mOrigMousePos = planeResult.second; mOrigMousePos = planeResult.second;
mCurrentMousePos = planeResult.second; mOldMousePos = planeResult.second;
mOffset = 0.0f;
} }
mOrigObjPos = mSceneManager->getSceneNode(mGrabbedSceneNode)->getPosition(); mOrigObjPos = mSceneManager->getSceneNode(mGrabbedSceneNode)->getPosition();
@ -192,7 +196,9 @@ namespace CSVRender
{ {
case Mouse_Grab: 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 != "")
{ {
if(result.first == mCurrentObj) if(result.first == mCurrentObj)
@ -208,45 +214,75 @@ namespace CSVRender
mCurrentObj = result.first; 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; break;
} }
case Mouse_Drag: case Mouse_Drag:
{ {
// final placement // 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(planeResult.first)
{ {
if(mGrabbedSceneNode != "") Ogre::Vector3 pos = mOrigObjPos + (planeResult.second-mOrigMousePos);
{
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);
// 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().beginMacro (QObject::tr("Move Object"));
mParent->mDocument.getUndoStack().push(new CSMWorld::ModifyCommand(*mIdTableModel, 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, 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, mParent->mDocument.getUndoStack().push(new CSMWorld::ModifyCommand(*mIdTableModel,
mIdTableModel->getModelIndex(referenceId, mColIndexPosZ), pos.z)); mIdTableModel->getModelIndex(mGrabbedRefId, mColIndexPosZ), pos.z));
mParent->mDocument.getUndoStack().endMacro(); mParent->mDocument.getUndoStack().endMacro();
}
// FIXME: highlight current object? // FIXME: highlight current object?
//mCurrentObj = mGrabbedSceneNode; // FIXME: doesn't work? //mCurrentObj = mGrabbedRefId; // FIXME: doesn't work?
mCurrentObj = ""; // whether the object is selected mCurrentObj = ""; // whether the object is selected
mMouseState = Mouse_Edit; mMouseState = Mouse_Edit;
// reset states // 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 mOrigMousePos = Ogre::Vector3(); // starting pos of mouse in world space
mOrigObjPos = Ogre::Vector3(); // starting pos of object in world space mOrigObjPos = Ogre::Vector3(); // starting pos of object in world space
mGrabbedSceneNode = ""; // id of the object mOldMousePos = Ogre::Vector3(); // mouse pos to use in wheel event
mOffset = 0.0f; // used for z-axis movement mOldCursorPos = QPoint(0, 0); // to calculate relative movement of mouse
mOldPos = QPoint(0, 0); // to calculate relative movement of mouse
}
} }
break; break;
} }
@ -254,10 +290,18 @@ namespace CSVRender
case Mouse_Default: case Mouse_Default:
{ {
// probably terrain, check // 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 != "") if(result.first != "")
{ {
// FIXME: terrain editing goes here // 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; break;
} }
@ -283,17 +327,49 @@ namespace CSVRender
/* FALL_THROUGH */ /* FALL_THROUGH */
case Mouse_Drag: 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()) if (event->delta())
{ {
// seems positive is up and negative is down // The mouse point is where the mouse points the object when dragging starts.
mOffset += (event->delta()/1); // FIXME: arbitrary number, make config option? // 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(); std::pair<Ogre::Vector3, Ogre::Vector3> planeRes = planeAxis();
Ogre::Vector3 pos = mOrigObjPos + planeRes.first*mOffset; Ogre::Vector3 mousePos = mOldMousePos + planeRes.first*(event->delta()/1.5);
mSceneManager->getSceneNode(mGrabbedSceneNode)->setPosition(pos+mCurrentMousePos-mOrigMousePos);
mPhysics->moveSceneNodes(mGrabbedSceneNode, pos+mCurrentMousePos-mOrigMousePos); // 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(); updateSceneWidgets();
// remember positions for next time
mOldMousePos = mousePos;
mOldCursorPos = QPoint(screenX, screenY);
} }
break; break;
} }
@ -323,14 +399,14 @@ namespace CSVRender
// reset states // reset states
mMouseState = Mouse_Default; mMouseState = Mouse_Default;
mCurrentMousePos = Ogre::Vector3(); mOldMousePos = Ogre::Vector3();
mOrigMousePos = Ogre::Vector3(); mOrigMousePos = Ogre::Vector3();
mOrigObjPos = Ogre::Vector3(); mOrigObjPos = Ogre::Vector3();
mGrabbedRefId = "";
mGrabbedSceneNode = ""; mGrabbedSceneNode = "";
mCurrentObj = ""; mCurrentObj = "";
mOldPos = QPoint(0, 0); mOldCursorPos = QPoint(0, 0);
mMouseEventTimer->invalidate(); mMouseEventTimer->invalidate();
mOffset = 0.0f;
break; 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 // using a really small value seems to mess up with the projections
float nearClipDistance = getCamera()->getNearClipDistance(); // save existing 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 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()) if(!getViewport())
return std::make_pair("", Ogre::Vector3()); return std::make_pair("", Ogre::Vector3());
@ -401,44 +478,18 @@ namespace CSVRender
float y = (float) mouseY / getViewport()->getActualHeight(); float y = (float) mouseY / getViewport()->getActualHeight();
std::pair<std::string, Ogre::Vector3> result = mPhysics->castRay(x, y, mSceneManager, getCamera()); std::pair<std::string, Ogre::Vector3> result = mPhysics->castRay(x, y, mSceneManager, getCamera());
if(result.first != "")
{ if(result.first != "" &&
// FIXME: is there a better way to distinguish terrain from objects? ((elements & (Ogre::uint32)CSVRender::Element_Terrain &&
QString name = QString(result.first.c_str()); QString(result.first.c_str()).contains(QRegExp("^Height"))) ||
if(name.contains(QRegExp("^HeightField"))) (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 result;
} }
} else
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;
}
}
}
return std::make_pair("", Ogre::Vector3()); return std::make_pair("", Ogre::Vector3());
} }
@ -462,4 +513,15 @@ namespace CSVRender
{ {
return mParent->getCamera()->getViewport(); 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; boost::shared_ptr<CSVWorld::PhysicsSystem> mPhysics;
Ogre::SceneManager *mSceneManager; // local copy Ogre::SceneManager *mSceneManager; // local copy
QPoint mOldPos; QPoint mOldCursorPos;
std::string mCurrentObj; std::string mCurrentObj;
std::string mGrabbedSceneNode; std::string mGrabbedSceneNode;
std::string mGrabbedRefId;
QElapsedTimer *mMouseEventTimer; QElapsedTimer *mMouseEventTimer;
Ogre::Plane *mPlane; Ogre::Plane *mPlane;
Ogre::Vector3 mOrigObjPos; Ogre::Vector3 mOrigObjPos;
Ogre::Vector3 mOrigMousePos; Ogre::Vector3 mOrigMousePos;
Ogre::Vector3 mCurrentMousePos; Ogre::Vector3 mOldMousePos;
float mOffset;
CSMWorld::IdTable *mIdTableModel; CSMWorld::IdTable *mIdTableModel;
int mColIndexPosX; int mColIndexPosX;
@ -73,16 +73,19 @@ namespace CSVRender
void mouseDoubleClickEvent (QMouseEvent *event); void mouseDoubleClickEvent (QMouseEvent *event);
bool wheelEvent (QWheelEvent *event); bool wheelEvent (QWheelEvent *event);
std::pair<std::string, Ogre::Vector3> underCursor(const int mouseX,
const int mouseY, Ogre::uint32 elements = 0xFFFFFFFF);
void cancelDrag(); void cancelDrag();
private: private:
std::pair<bool, Ogre::Vector3> mousePositionOnPlane(const QPoint &pos, const Ogre::Plane &plane); std::pair<bool, Ogre::Vector3> mousePosOnPlane(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<Ogre::Vector3, Ogre::Vector3> planeAxis(); std::pair<Ogre::Vector3, Ogre::Vector3> planeAxis();
void updateSceneWidgets(); void updateSceneWidgets();
void placeObject(const std::string sceneNodeName, const Ogre::Vector3 &pos); // FIXME
Ogre::Camera *getCamera(); // friend access Ogre::Camera *getCamera(); // friend access
Ogre::Viewport *getViewport(); // friend access Ogre::Viewport *getViewport(); // friend access
}; };

View file

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

View file

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

View file

@ -20,11 +20,14 @@
#include "../../model/world/tablemimedata.hpp" #include "../../model/world/tablemimedata.hpp"
#include "../../model/world/idtable.hpp" #include "../../model/world/idtable.hpp"
#include "../../model/world/pathgridcommands.hpp"
#include "../widget/scenetooltoggle.hpp" #include "../widget/scenetooltoggle.hpp"
#include "../widget/scenetoolmode.hpp" #include "../widget/scenetoolmode.hpp"
#include "../widget/scenetooltoggle2.hpp" #include "../widget/scenetooltoggle2.hpp"
#include "../world/physicssystem.hpp"
#include "pathgridpoint.hpp"
#include "editmode.hpp" #include "editmode.hpp"
#include "elements.hpp" #include "elements.hpp"
@ -112,8 +115,9 @@ bool CSVRender::PagedWorldspaceWidget::adjustCells()
if (index > 0 && cells.getRecord (index).mState!=CSMWorld::RecordBase::State_Deleted && if (index > 0 && cells.getRecord (index).mState!=CSMWorld::RecordBase::State_Deleted &&
mCells.find (*iter)==mCells.end()) mCells.find (*iter)==mCells.end())
{ {
Cell *cell = new Cell (mDocument.getData(), getSceneManager(), Cell *cell = new Cell (mDocument, getSceneManager(),
iter->getId (mWorldspace), mDocument.getPhysics()); iter->getId (mWorldspace), mDocument.getPhysics());
connect (cell->getSignalHandler(), SIGNAL(flagAsModified()), this, SLOT(flagAsModSlot()));
mCells.insert (std::make_pair (*iter, cell)); mCells.insert (std::make_pair (*iter, cell));
float height = cell->getTerrainHeightAt(Ogre::Vector3( float height = cell->getTerrainHeightAt(Ogre::Vector3(
@ -327,6 +331,106 @@ void CSVRender::PagedWorldspaceWidget::referenceAdded (const QModelIndex& parent
flagAsModified(); 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() std::string CSVRender::PagedWorldspaceWidget::getStartupInstruction()
{ {
Ogre::Vector3 position = getCamera()->getPosition(); Ogre::Vector3 position = getCamera()->getPosition();
@ -526,3 +630,8 @@ void CSVRender::PagedWorldspaceWidget::cellAdded (const QModelIndex& index, int
if (adjustCells()) if (adjustCells())
flagAsModified(); flagAsModified();
} }
void CSVRender::PagedWorldspaceWidget::flagAsModSlot ()
{
flagAsModified();
}

View file

@ -55,6 +55,8 @@ namespace CSVRender
virtual std::string getStartupInstruction(); virtual std::string getStartupInstruction();
Cell *findCell(const std::string &cellId);
public: public:
PagedWorldspaceWidget (QWidget *parent, CSMDoc::Document& document); PagedWorldspaceWidget (QWidget *parent, CSMDoc::Document& document);
@ -95,6 +97,10 @@ namespace CSVRender
virtual void mouseDoubleClickEvent (QMouseEvent *event); 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: signals:
void cellSelectionChanged (const CSMWorld::CellSelection& selection); void cellSelectionChanged (const CSMWorld::CellSelection& selection);
@ -107,6 +113,9 @@ namespace CSVRender
virtual void cellAdded (const QModelIndex& index, int start, int end); 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/data.hpp"
#include "../../model/world/idtable.hpp" #include "../../model/world/idtable.hpp"
#include "../../model/world/tablemimedata.hpp" #include "../../model/world/tablemimedata.hpp"
#include "../../model/world/pathgridcommands.hpp"
#include "../widget/scenetooltoggle.hpp" #include "../widget/scenetooltoggle.hpp"
#include "../widget/scenetooltoggle2.hpp" #include "../widget/scenetooltoggle2.hpp"
@ -48,7 +49,9 @@ CSVRender::UnpagedWorldspaceWidget::UnpagedWorldspaceWidget (const std::string&
update(); 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, void CSVRender::UnpagedWorldspaceWidget::cellDataChanged (const QModelIndex& topLeft,
@ -90,7 +93,9 @@ bool CSVRender::UnpagedWorldspaceWidget::handleDrop (const std::vector<CSMWorld:
return false; return false;
mCellId = data.begin()->getId(); 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(); update();
emit cellChanged(*data.begin()); emit cellChanged(*data.begin());
@ -160,6 +165,12 @@ void CSVRender::UnpagedWorldspaceWidget::addVisibilitySelectorButtons (
tool->addButton (Element_Fog, "Fog"); tool->addButton (Element_Fog, "Fog");
} }
void CSVRender::UnpagedWorldspaceWidget::pathgridDataChanged (const QModelIndex& topLeft,
const QModelIndex& bottomRight)
{
// FIXME:
}
std::string CSVRender::UnpagedWorldspaceWidget::getStartupInstruction() std::string CSVRender::UnpagedWorldspaceWidget::getStartupInstruction()
{ {
Ogre::Vector3 position = getCamera()->getPosition(); Ogre::Vector3 position = getCamera()->getPosition();
@ -193,3 +204,8 @@ CSVRender::WorldspaceWidget::dropRequirments CSVRender::UnpagedWorldspaceWidget:
return ignored; return ignored;
} }
} }
void CSVRender::UnpagedWorldspaceWidget::flagAsModSlot ()
{
flagAsModified();
}

View file

@ -70,6 +70,10 @@ namespace CSVRender
void cellRowsAboutToBeRemoved (const QModelIndex& parent, int start, int end); void cellRowsAboutToBeRemoved (const QModelIndex& parent, int start, int end);
virtual void flagAsModSlot();
virtual void pathgridDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
signals: signals:
void cellChanged(const CSMWorld::UniversalId& id); void cellChanged(const CSMWorld::UniversalId& id);

View file

@ -12,6 +12,7 @@
#include <QDropEvent> #include <QDropEvent>
#include <QMouseEvent> #include <QMouseEvent>
#include <QKeyEvent> #include <QKeyEvent>
#include <QPoint>
#include "../../model/world/universalid.hpp" #include "../../model/world/universalid.hpp"
#include "../../model/world/idtable.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)), connect (debugProfiles, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),
this, SLOT (debugProfileAboutToBeRemoved (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 = document.getPhysics(); // create physics if one doesn't exist
mPhysics->addSceneManager(getSceneManager(), this); mPhysics->addSceneManager(getSceneManager(), this);
mMouse = new MouseState(this); mMouse = new MouseState(this);
@ -415,12 +422,52 @@ void CSVRender::WorldspaceWidget::wheelEvent (QWheelEvent *event)
SceneWidget::wheelEvent(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) void CSVRender::WorldspaceWidget::keyPressEvent (QKeyEvent *event)
{ {
if(event->key() == Qt::Key_Escape) if(event->key() == Qt::Key_Escape)
{ {
mMouse->cancelDrag(); 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 else
SceneWidget::keyPressEvent(event); 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 wheelEvent (QWheelEvent *event);
virtual void keyPressEvent (QKeyEvent *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: private:
void dragEnterEvent(QDragEnterEvent *event); void dragEnterEvent(QDragEnterEvent *event);
@ -158,6 +163,7 @@ namespace CSVRender
void debugProfileAboutToBeRemoved (const QModelIndex& parent, int start, int end); void debugProfileAboutToBeRemoved (const QModelIndex& parent, int start, int end);
virtual void pathgridDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) = 0;
protected slots: protected slots:

View file

@ -72,9 +72,6 @@ namespace CSVWorld
if(referenceId != "") if(referenceId != "")
{ {
mSceneNodeToRefId.erase(sceneNodeName);
mSceneNodeToMesh.erase(sceneNodeName);
// find which SceneManager has this object // find which SceneManager has this object
Ogre::SceneManager *sceneManager = findSceneManager(sceneNodeName); Ogre::SceneManager *sceneManager = findSceneManager(sceneNodeName);
if(!sceneManager) if(!sceneManager)
@ -99,7 +96,8 @@ namespace CSVWorld
mRefIdToSceneNode.begin(); mRefIdToSceneNode.begin();
for(; itRef != mRefIdToSceneNode.end(); ++itRef) 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); (*itRef).second.erase(sceneManager);
break; break;
@ -107,11 +105,15 @@ namespace CSVWorld
} }
// check whether the physics model should be deleted // 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->removeRigidBody(referenceId);
mEngine->deleteRigidBody(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 // WARNING: far clip distance is a global setting, if it changes in future
// this method will need to be updated // this method will need to be updated
std::pair<std::string, Ogre::Vector3> PhysicsSystem::castRay(float mouseX, std::pair<std::string, Ogre::Vector3> PhysicsSystem::castRay(float mouseX,
@ -241,27 +238,86 @@ namespace CSVWorld
_from = btVector3(from.x, from.y, from.z); _from = btVector3(from.x, from.y, from.z);
_to = btVector3(to.x, to.y, to.z); _to = btVector3(to.x, to.y, to.z);
uint32_t visibilityMask = camera->getViewport()->getVisibilityMask(); Ogre::uint32 visibilityMask = camera->getViewport()->getVisibilityMask();
bool ignoreHeightMap = !(visibilityMask & (uint32_t)CSVRender::Element_Terrain); bool ignoreHeightMap = !(visibilityMask & (Ogre::uint32)CSVRender::Element_Terrain);
bool ignoreObjects = !(visibilityMask & (uint32_t)CSVRender::Element_Reference); 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 = std::make_pair("", -1);
std::pair<std::string, float> result = short mask = OEngine::Physic::CollisionType_Raycasting;
mEngine->rayTest(_from, _to, !ignoreObjects, ignoreHeightMap, &norm); 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 // result.first is the object's referenceId
if(result.first == "") if(result.first == "")
return std::make_pair("", Ogre::Vector3(0,0,0)); return std::make_pair("", Ogre::Vector3());
else else
{ return std::make_pair(result.first, ray.getPoint(farClipDist*result.second));
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));
} }
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) std::string PhysicsSystem::refIdToSceneNode(std::string referenceId, Ogre::SceneManager *sceneMgr)

View file

@ -4,6 +4,8 @@
#include <string> #include <string>
#include <map> #include <map>
#include <OgrePlatform.h>
namespace Ogre namespace Ogre
{ {
class Vector3; class Vector3;
@ -73,7 +75,13 @@ namespace CSVWorld
std::pair<std::string, Ogre::Vector3> castRay(float mouseX, std::pair<std::string, Ogre::Vector3> castRay(float mouseX,
float mouseY, Ogre::SceneManager *sceneMgr, Ogre::Camera *camera); 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 // for multi-scene manager per physics engine
std::map<Ogre::SceneManager*, CSVRender::SceneWidget *> sceneWidgets(); std::map<Ogre::SceneManager*, CSVRender::SceneWidget *> sceneWidgets();
@ -83,9 +91,7 @@ namespace CSVWorld
void moveSceneNodeImpl(const std::string sceneNodeName, void moveSceneNodeImpl(const std::string sceneNodeName,
const std::string referenceId, const Ogre::Vector3 &position); const std::string referenceId, const Ogre::Vector3 &position);
void updateSelectionHighlight(std::string sceneNode, const Ogre::Vector3 &position); std::string sceneNodeToRefId(std::string sceneNodeName);
std::string refIdToSceneNode(std::string referenceId, Ogre::SceneManager *sceneMgr);
Ogre::SceneManager *findSceneManager(std::string sceneNodeName); Ogre::SceneManager *findSceneManager(std::string sceneNodeName);
}; };

View file

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

Binary file not shown.