diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index ec6f802cf..29f93e566 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -83,6 +83,7 @@ opencs_units (view/render opencs_units_noqt (view/render navigation navigation1st navigationfree navigationorbit lighting lightingday lightingnight lightingbright object cell terrainstorage textoverlay overlaymask overlaysystem mousestate + pathgridpoint ) opencs_hdrs_noqt (view/render diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 591667ebb..beab0b607 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -308,6 +308,9 @@ std::auto_ptr CS::Editor::setupGraphics() // for font used in overlays Ogre::Root::getSingleton().addResourceLocation ((mResources / "mygui").string(), "FileSystem", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, true); + // for pathgrid point nif + Ogre::Root::getSingleton().addResourceLocation ((mResources / "materials").string(), + "FileSystem", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, true); if (!boost::filesystem::exists (mCfgMgr.getCachePath())) boost::filesystem::create_directories (mCfgMgr.getCachePath()); diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index 75e11cc10..6979a4042 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -10,10 +11,60 @@ #include "../../model/world/idtable.hpp" #include "../../model/world/columns.hpp" #include "../../model/world/data.hpp" +#include "../../model/world/pathgrid.hpp" #include "../world/physicssystem.hpp" #include "elements.hpp" #include "terrainstorage.hpp" +#include "pathgridpoint.hpp" + +// FIXME: redraw edges when pathgrid points are moved, added and deleted +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::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::MaterialManager::getSingleton().getByName(PG_LINE_MATERIAL, DEBUGGING_GROUP).isNull()) + Ogre::MaterialManager::getSingleton().remove(PG_LINE_MATERIAL); +} + +Ogre::ManualObject *CSVRender::Cell::createPathgridEdge(const std::string &name, + const Ogre::Vector3 &start, const Ogre::Vector3 &end) +{ + Ogre::ManualObject *result = mSceneMgr->createManualObject(name); + + 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) { @@ -94,10 +145,28 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, Ogre::SceneManager *sceneManager, esmLand->mLandData->mHeights, mX, mY, 0, worldsize / (verts-1), verts); } } + + addPathgrid(); } CSVRender::Cell::~Cell() { + // destroy manual objects + for(std::map, std::string>::iterator iter = mPgEdges.begin(); + iter != mPgEdges.end(); ++iter) + { + if(mSceneMgr->hasManualObject((*iter).second)) + mSceneMgr->destroyManualObject((*iter).second); + } + destroyGridMaterials(); + Ogre::ResourceGroupManager::getSingleton().destroyResourceGroup(DEBUGGING_GROUP); + + for(std::map::iterator iter (mPgPoints.begin()); + iter!=mPgPoints.end(); ++iter) + { + delete iter->second; + } + mPhysics->removeHeightField(mSceneMgr, mX, mY); for (std::map::iterator iter (mObjects.begin()); @@ -236,3 +305,54 @@ float CSVRender::Cell::getTerrainHeightAt(const Ogre::Vector3 &pos) const else return -std::numeric_limits::max(); } + +void CSVRender::Cell::addPathgrid() +{ + if(!Ogre::ResourceGroupManager::getSingleton().resourceGroupExists(DEBUGGING_GROUP)) + Ogre::ResourceGroupManager::getSingleton().createResourceGroup(DEBUGGING_GROUP); + createGridMaterials(); + + float worldsize = ESM::Land::REAL_SIZE; + + CSMWorld::SubCellCollection& pathgridCollection = mData.getPathgrids(); + int index = pathgridCollection.searchId(mId); + if(index != -1) + { + const CSMWorld::Pathgrid pathgrid = pathgridCollection.getRecord(index).get(); + + std::vector::const_iterator iter = pathgrid.mPoints.begin(); + for(index = 0; iter != pathgrid.mPoints.end(); ++iter, ++index) + { + std::ostringstream stream; + stream << "Pathgrid_" << mId << "_" << index; + std::string name = stream.str(); + + 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 << 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)); + } + } +} diff --git a/apps/opencs/view/render/cell.hpp b/apps/opencs/view/render/cell.hpp index d38f0c68d..c88b59353 100644 --- a/apps/opencs/view/render/cell.hpp +++ b/apps/opencs/view/render/cell.hpp @@ -17,6 +17,7 @@ namespace Ogre { class SceneManager; class SceneNode; + class ManualObject; } namespace CSMWorld @@ -31,12 +32,16 @@ namespace CSVWorld namespace CSVRender { + class PathgridPoint; + class Cell { CSMWorld::Data& mData; std::string mId; Ogre::SceneNode *mCellNode; std::map mObjects; + std::map mPgPoints; + std::map, std::string> mPgEdges; std::auto_ptr mTerrain; CSVWorld::PhysicsSystem *mPhysics; Ogre::SceneManager *mSceneMgr; @@ -82,6 +87,15 @@ namespace CSVRender bool referenceAdded (const QModelIndex& parent, int start, int end); float getTerrainHeightAt(const Ogre::Vector3 &pos) const; + + private: + + // for drawing pathgrid points & lines + void createGridMaterials(); + void destroyGridMaterials(); + void addPathgrid(); + Ogre::ManualObject *createPathgridEdge(const std::string &name, + const Ogre::Vector3 &start, const Ogre::Vector3 &end); }; } diff --git a/apps/opencs/view/render/mousestate.cpp b/apps/opencs/view/render/mousestate.cpp index 45e846f74..aa8f1a785 100644 --- a/apps/opencs/view/render/mousestate.cpp +++ b/apps/opencs/view/render/mousestate.cpp @@ -206,6 +206,15 @@ namespace CSVRender mCurrentObj = result.first; } + // print some debug info + std::string referenceId = mPhysics->sceneNodeToRefId(result.first); + if(referenceId != "") + std::cout << "result: " << referenceId << std::endl; + else + std::cout << "result: " << 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; } break; } @@ -213,15 +222,22 @@ namespace CSVRender { // final placement std::pair planeResult = mousePositionOnPlane(event->pos(), *mPlane); - if(planeResult.first) + if(planeResult.first && mGrabbedSceneNode != "") { - if(mGrabbedSceneNode != "") - { - std::pair planeRes = planeAxis(); - Ogre::Vector3 pos = mOrigObjPos+planeRes.first*mOffset+planeResult.second-mOrigMousePos; - // use the saved scene node name since the physics model has not moved yet - std::string referenceId = mPhysics->sceneNodeToRefId(mGrabbedSceneNode); + std::pair planeRes = planeAxis(); + Ogre::Vector3 pos = mOrigObjPos+planeRes.first*mOffset+planeResult.second-mOrigMousePos; + // use the saved scene node name since the physics model has not moved yet + std::string referenceId = mPhysics->sceneNodeToRefId(mGrabbedSceneNode); + if(QString(referenceId.c_str()).contains(QRegExp("^Pathgrid"))) + { + // move pathgrid point, but don't save yet (need pathgrid + // table feature & its data structure to be completed) + // FIXME: need to signal PathgridPoint object of change + placeObject(mGrabbedSceneNode, pos); + } + else + { mParent->mDocument.getUndoStack().beginMacro (QObject::tr("Move Object")); mParent->mDocument.getUndoStack().push(new CSMWorld::ModifyCommand(*mIdTableModel, mIdTableModel->getModelIndex(referenceId, mColIndexPosX), pos.x)); @@ -230,22 +246,22 @@ namespace CSVRender mParent->mDocument.getUndoStack().push(new CSMWorld::ModifyCommand(*mIdTableModel, mIdTableModel->getModelIndex(referenceId, mColIndexPosZ), pos.z)); mParent->mDocument.getUndoStack().endMacro(); - - // FIXME: highlight current object? - //mCurrentObj = mGrabbedSceneNode; // FIXME: doesn't work? - mCurrentObj = ""; // whether the object is selected - - mMouseState = Mouse_Edit; - - // reset states - mCurrentMousePos = Ogre::Vector3(); // mouse pos to use in wheel event - mOrigMousePos = Ogre::Vector3(); // starting pos of mouse in world space - mOrigObjPos = Ogre::Vector3(); // starting pos of object in world space - mGrabbedSceneNode = ""; // id of the object - mOffset = 0.0f; // used for z-axis movement - mOldPos = QPoint(0, 0); // to calculate relative movement of mouse } - } + + // FIXME: highlight current object? + //mCurrentObj = mGrabbedSceneNode; // FIXME: doesn't work? + mCurrentObj = ""; // whether the object is selected + + mMouseState = Mouse_Edit; + + // reset states + mCurrentMousePos = Ogre::Vector3(); // mouse pos to use in wheel event + mOrigMousePos = Ogre::Vector3(); // starting pos of mouse in world space + mOrigObjPos = Ogre::Vector3(); // starting pos of object in world space + mGrabbedSceneNode = ""; // id of the object + mOffset = 0.0f; // used for z-axis movement + mOldPos = QPoint(0, 0); // to calculate relative movement of mouse + } break; } case Mouse_Edit: @@ -266,9 +282,9 @@ namespace CSVRender void MouseState::mouseDoubleClickEvent (QMouseEvent *event) { - event->ignore(); - //mPhysics->toggleDebugRendering(mSceneManager); - //mParent->flagAsModified(); + //event->ignore(); + mPhysics->toggleDebugRendering(mSceneManager); + mParent->flagAsModified(); } bool MouseState::wheelEvent (QWheelEvent *event) @@ -401,6 +417,10 @@ namespace CSVRender std::pair result = mPhysics->castRay(x, y, mSceneManager, getCamera()); if(result.first != "") { + std::cout << "result: " << 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; // FIXME: is there a better way to distinguish terrain from objects? QString name = QString(result.first.c_str()); if(name.contains(QRegExp("^HeightField"))) @@ -423,7 +443,7 @@ namespace CSVRender std::pair result = mPhysics->castRay(x, y, mSceneManager, getCamera()); if(result.first != "") { - // NOTE: anything not terrain is assumed to be an object + // NOTE: anything not terrain is assumed to be an object, e.g pathgrid points QString name = QString(result.first.c_str()); if(!name.contains(QRegExp("^HeightField"))) { @@ -460,4 +480,17 @@ namespace CSVRender { return mParent->getCamera()->getViewport(); } + + void MouseState::placeObject(const std::string sceneNode, const Ogre::Vector3 &pos) + { + mSceneManager->getSceneNode(sceneNode)->setPosition(pos); + + // update physics + std::string refId = mPhysics->sceneNodeToRefId(sceneNode); + + mPhysics->replaceObject(sceneNode, 1, pos, Ogre::Quaternion::IDENTITY); + + // update all SceneWidgets and their SceneManagers + updateSceneWidgets(); + } } diff --git a/apps/opencs/view/render/mousestate.hpp b/apps/opencs/view/render/mousestate.hpp index 27907bb33..36d04ef6b 100644 --- a/apps/opencs/view/render/mousestate.hpp +++ b/apps/opencs/view/render/mousestate.hpp @@ -82,6 +82,8 @@ namespace CSVRender std::pair planeAxis(); void updateSceneWidgets(); + void placeObject(const std::string sceneNode, const Ogre::Vector3 &pos); // FIXME + Ogre::Camera *getCamera(); // friend access Ogre::Viewport *getViewport(); // friend access }; diff --git a/apps/opencs/view/render/pathgridpoint.cpp b/apps/opencs/view/render/pathgridpoint.cpp new file mode 100644 index 000000000..2455ea1a8 --- /dev/null +++ b/apps/opencs/view/render/pathgridpoint.cpp @@ -0,0 +1,38 @@ +#include "pathgridpoint.hpp" + +#include // FIXME + +#include +#include + +//#include "../../model/world/pathgrid.hpp" +#include "../world/physicssystem.hpp" + +#include "elements.hpp" + +namespace CSVRender +{ + PathgridPoint::PathgridPoint(const std::string &name, + Ogre::SceneNode *cellNode, const Ogre::Vector3 &pos, 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); + } +} diff --git a/apps/opencs/view/render/pathgridpoint.hpp b/apps/opencs/view/render/pathgridpoint.hpp new file mode 100644 index 000000000..f4fa42790 --- /dev/null +++ b/apps/opencs/view/render/pathgridpoint.hpp @@ -0,0 +1,35 @@ +#ifndef OPENCS_VIEW_PATHGRIDPOINT_H +#define OPENCS_VIEW_PATHGRIDPOINT_H + +#include + +namespace Ogre +{ + class Vector3; + class SceneNode; + class SceneManager; +} + +namespace CSVWorld +{ + class PhysicsSystem; +} + +namespace CSVRender +{ + class PathgridPoint + { + CSVWorld::PhysicsSystem *mPhysics; // local copy + NifOgre::ObjectScenePtr mPgPoint; + Ogre::SceneNode *mBase; + + public: + + PathgridPoint(const std::string &name, + Ogre::SceneNode *cellNode, const Ogre::Vector3 &pos, CSVWorld::PhysicsSystem *physics); + + ~PathgridPoint(); + }; +} + +#endif // OPENCS_VIEW_PATHGRIDPOINT_H diff --git a/files/materials/pathgrid_pt.nif b/files/materials/pathgrid_pt.nif new file mode 100644 index 000000000..579b03036 Binary files /dev/null and b/files/materials/pathgrid_pt.nif differ