mirror of
https://github.com/OpenMW/openmw.git
synced 2025-02-01 04:45:36 +00:00
commit
c7c0023ed2
27 changed files with 1147 additions and 174 deletions
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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:
|
||||
|
|
48
apps/opencs/model/world/pathgridcommands.cpp
Normal file
48
apps/opencs/model/world/pathgridcommands.cpp
Normal 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()));
|
||||
}
|
65
apps/opencs/model/world/pathgridcommands.hpp
Normal file
65
apps/opencs/model/world/pathgridcommands.hpp
Normal 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
|
26
apps/opencs/model/world/pathgridpointswrap.hpp
Normal file
26
apps/opencs/model/world/pathgridpointswrap.hpp
Normal 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
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -223,12 +223,10 @@ bool CSVRender::Object::referenceDataChanged (const QModelIndex& topLeft,
|
|||
{
|
||||
mReferenceableId =
|
||||
references.getData (index, columnIndex).toString().toUtf8().constData();
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
update();
|
||||
adjust();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -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&);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
70
apps/opencs/view/render/pathgridpoint.cpp
Normal file
70
apps/opencs/view/render/pathgridpoint.cpp
Normal 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();
|
||||
}
|
||||
}
|
42
apps/opencs/view/render/pathgridpoint.hpp
Normal file
42
apps/opencs/view/render/pathgridpoint.hpp
Normal 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
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -17,6 +17,7 @@ set(MATERIAL_FILES
|
|||
objects.shader
|
||||
objects.shaderset
|
||||
openmw.configuration
|
||||
pathgrid_pt.nif
|
||||
quad.mat
|
||||
quad.shader
|
||||
quad.shaderset
|
||||
|
|
BIN
files/materials/pathgrid_pt.nif
Normal file
BIN
files/materials/pathgrid_pt.nif
Normal file
Binary file not shown.
Loading…
Reference in a new issue