From 8b4651f0556be383ffcd46de61a51804c1ab3a66 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sun, 2 Nov 2014 08:22:27 +1100 Subject: [PATCH] Move mouse related functions and states out of WorldspaceWidget. --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/model/settings/usersettings.cpp | 16 + apps/opencs/view/render/mousestate.cpp | 648 +++++++++++++++++++ apps/opencs/view/render/mousestate.hpp | 82 +++ apps/opencs/view/render/worldspacewidget.cpp | 588 +---------------- apps/opencs/view/render/worldspacewidget.hpp | 34 +- 6 files changed, 763 insertions(+), 607 deletions(-) create mode 100644 apps/opencs/view/render/mousestate.cpp create mode 100644 apps/opencs/view/render/mousestate.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 30e9203f5..ec6f802cf 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -82,7 +82,7 @@ opencs_units (view/render opencs_units_noqt (view/render navigation navigation1st navigationfree navigationorbit lighting lightingday lightingnight - lightingbright object cell terrainstorage textoverlay overlaymask overlaysystem + lightingbright object cell terrainstorage textoverlay overlaymask overlaysystem mousestate ) opencs_hdrs_noqt (view/render diff --git a/apps/opencs/model/settings/usersettings.cpp b/apps/opencs/model/settings/usersettings.cpp index 7dac660c3..1ecd39db0 100644 --- a/apps/opencs/model/settings/usersettings.cpp +++ b/apps/opencs/model/settings/usersettings.cpp @@ -161,6 +161,22 @@ void CSMSettings::UserSettings::buildSettingModelDefaults() ritd->setDeclaredValues (values); } + declareSection ("debug", "Debug Options"); + { + Setting *mousePicking = createSetting (Type_CheckBox, "mouse-picking", "Debug Render Mouse-Picking"); + mousePicking->setDefaultValue ("false"); + mousePicking->setToolTip ("Enable redering debug information for mouse picking. " + "This option may be removed in future once the mouse picking feature is completed."); + + QString defaultValue = "Closer/Further"; + QStringList values = QStringList() << defaultValue << "Up/Down" << "Left/Right"; + + Setting *mouseWheel = createSetting (Type_RadioButton, "mouse-wheel", + "For testing mouse movement directions."); + mouseWheel->setDefaultValue (defaultValue); + mouseWheel->setDeclaredValues (values); + } + declareSection ("table-input", "Table Input"); { QString inPlaceEdit ("Edit in Place"); diff --git a/apps/opencs/view/render/mousestate.cpp b/apps/opencs/view/render/mousestate.cpp new file mode 100644 index 000000000..059fab2a5 --- /dev/null +++ b/apps/opencs/view/render/mousestate.cpp @@ -0,0 +1,648 @@ +#include "mousestate.hpp" + +#include +#include +#include + +#include +#include // FIXME: for debugging +#include // FIXME: for debugging +#include // FIXME: for debugging + +#include +#include + +#include "../../model/settings/usersettings.hpp" +#include "../world/physicssystem.hpp" + +#include "elements.hpp" // FIXME: for debugging +#include "worldspacewidget.hpp" + +namespace +{ + // FIXME: this section should be removed once the debugging is completed + void showHitPoint(Ogre::SceneManager *sceneMgr, std::string name, Ogre::Vector3 point) + { + if(sceneMgr->hasManualObject("manual" + name)) + sceneMgr->destroyManualObject("manual" + name); + Ogre::ManualObject* manual = sceneMgr->createManualObject("manual" + name); + manual->begin("BaseWhite", Ogre::RenderOperation::OT_LINE_LIST); + manual-> position(point.x, point.y, point.z-100); + manual-> position(point.x, point.y, point.z+100); + manual-> position(point.x, point.y-100, point.z); + manual-> position(point.x, point.y+100, point.z); + manual-> position(point.x-100, point.y, point.z); + manual-> position(point.x+100, point.y, point.z); + manual->end(); + sceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(manual); + } + + void removeHitPoint(Ogre::SceneManager *sceneMgr, std::string name) + { + if(sceneMgr->hasManualObject("manual" + name)) + sceneMgr->destroyManualObject("manual" + name); + } + + void initDebug() + { + // material for visual cue on selected objects + Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton().getByName("DynamicTrans"); + if(texture.isNull()) + { + texture = Ogre::TextureManager::getSingleton().createManual( + "DynamicTrans", // name + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + Ogre::TEX_TYPE_2D, // type + 8, 8, // width & height + 0, // number of mipmaps + Ogre::PF_BYTE_BGRA, // pixel format + Ogre::TU_DEFAULT); // usage; should be TU_DYNAMIC_WRITE_ONLY_DISCARDABLE for + // textures updated very often (e.g. each frame) + + Ogre::HardwarePixelBufferSharedPtr pixelBuffer = texture->getBuffer(); + pixelBuffer->lock(Ogre::HardwareBuffer::HBL_NORMAL); + const Ogre::PixelBox& pixelBox = pixelBuffer->getCurrentLock(); + + uint8_t* pDest = static_cast(pixelBox.data); + + // Fill in some pixel data. This will give a semi-transparent colour, + // but this is of course dependent on the chosen pixel format. + for (size_t j = 0; j < 8; j++) + { + for(size_t i = 0; i < 8; i++) + { + *pDest++ = 255; // B + *pDest++ = 255; // G + *pDest++ = 127; // R + *pDest++ = 63; // A + } + + pDest += pixelBox.getRowSkip() * Ogre::PixelUtil::getNumElemBytes(pixelBox.format); + } + pixelBuffer->unlock(); + } + Ogre::MaterialPtr material = Ogre::MaterialManager::getSingleton().getByName( + "TransMaterial"); + if(material.isNull()) + { + Ogre::MaterialPtr material = Ogre::MaterialManager::getSingleton().create( + "TransMaterial", + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, true ); + Ogre::Pass *pass = material->getTechnique( 0 )->getPass( 0 ); + pass->setLightingEnabled( false ); + pass->setDepthWriteEnabled( false ); + pass->setSceneBlending( Ogre::SBT_TRANSPARENT_ALPHA ); + + Ogre::TextureUnitState *tex = pass->createTextureUnitState("CustomState", 0); + tex->setTextureName("DynamicTrans"); + tex->setTextureFiltering( Ogre::TFO_ANISOTROPIC ); + material->load(); + } + } + + //plane Z, upvector Y, mOffset z : x-y plane, wheel up/down + //plane Y, upvector X, mOffset y : y-z plane, wheel left/right + //plane X, upvector Y, mOffset x : x-z plane, wheel closer/further + std::pair planeAxis() + { + CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance(); + QString wheelDir = userSettings.setting("debug/mouse-wheel", QString("Closer/Further")); + if(wheelDir == "Up/Down") + return std::make_pair(Ogre::Vector3::UNIT_Z, Ogre::Vector3::UNIT_Y); + else if(wheelDir == "Left/Right") + return std::make_pair(Ogre::Vector3::UNIT_Y, Ogre::Vector3::UNIT_X); + else + return std::make_pair(Ogre::Vector3::UNIT_X, Ogre::Vector3::UNIT_Y); + } +} + +namespace CSVRender +{ + // mouse picking + // FIXME: need to virtualise mouse buttons + // + // State machine: + // + // [default] mousePressEvent->check if the mouse is pointing at an object + // if yes, create collision planes then go to [grab] + // else check for terrain + // + // [grab] mouseReleaseEvent->if same button and new obj, go to [edit] + // mouseMoveEvent->if same button, go to [drag] + // other mouse events or buttons, go back to [default] (i.e. like 'cancel') + // + // [drag] mouseReleaseEvent->if same button, place the object at the new + // location, update the document then go to [edit] + // mouseMoveEvent->update position to the user based on ray to the collision + // planes and render the object at the new location, but do not update + // the document yet + // + // [edit] TODO, probably fine positional adjustments or rotations; clone/delete? + // + // + // press press (obj) + // [default] --------> [grab] <-------------------- [edit] + // ^ (obj) | | ------> [drag] -----> ^ + // | | | move ^ | release | + // | | | | | | + // | | | +-+ | + // | | | move | + // +----------------+ +--------------------------+ + // release release + // (same obj) (new obj) + // + // + + MouseState::MouseState(WorldspaceWidget *parent) + : mParent(parent), mPhysics(parent->getPhysics()), mSceneManager(parent->getSceneManager()) + , mCurrentObj(""), mMouseState(Mouse_Default), mOldPos(0,0), mMouseEventTimer(0), mPlane(0) + , mGrabbedSceneNode(""), mOrigObjPos(Ogre::Vector3()), mOrigMousePos(Ogre::Vector3()) + , mCurrentMousePos(Ogre::Vector3()), mOffset(0.0f) + { + initDebug(); + + mMouseEventTimer = new QElapsedTimer(); + mMouseEventTimer->invalidate(); + + std::pair planeRes = planeAxis(); + mPlane = new Ogre::Plane(planeRes.first, 0); + Ogre::MeshPtr mesh = Ogre::MeshManager::getSingleton().createPlane("mouse", + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + *mPlane, + 300000,300000, // FIXME: use far clip dist? + 1,1, // segments + true, // normals + 1, // numTexCoordSets + 1,1, // uTile, vTile + planeRes.second // upVector + ); + } + + MouseState::~MouseState () + { + delete mMouseEventTimer; + + // For debugging only + std::map >::iterator iter = mSelectedEntities.begin(); + for(;iter != mSelectedEntities.end(); ++iter) + { + removeHitPoint(mSceneManager, iter->first); + + if(mSceneManager->hasSceneNode(iter->first)) + { + Ogre::SceneNode *scene = mSceneManager->getSceneNode(iter->first); + + if(scene) + { + scene->removeAndDestroyAllChildren(); + mSceneManager->destroySceneNode(iter->first); + } + } + } + + delete mPlane; + } + + void MouseState::mouseMoveEvent (QMouseEvent *event) + { + switch(mMouseState) + { + case Mouse_Grab: + { + // check if min elapsed time to stop false detection of drag + if(!mMouseEventTimer->isValid() || !mMouseEventTimer->hasExpired(100)) // ms + break; + + mMouseEventTimer->invalidate(); + mMouseState = Mouse_Drag; + + /* FALL_THROUGH */ + } + case Mouse_Drag: + { + if(event->pos() != mOldPos) // TODO: maybe don't update less than a quantum? + { + mOldPos = event->pos(); + + // ray test against the plane to provide feedback to the user the + // relative movement of the object on the x-y plane + std::pair planeResult = mousePositionOnPlane(event->pos(), *mPlane); + if(planeResult.first) + { + if(mGrabbedSceneNode != "") + { + std::pair planeRes = planeAxis(); + Ogre::Vector3 pos = mOrigObjPos + planeRes.first*mOffset; + //pos.z += mOffset; + mSceneManager->getSceneNode(mGrabbedSceneNode)->setPosition(pos+planeResult.second-mOrigMousePos); + mCurrentMousePos = planeResult.second; + mPhysics->moveSceneNodes(mGrabbedSceneNode, pos+planeResult.second-mOrigMousePos); + updateSceneWidgets(); + } + } + } + break; + } + case Mouse_Edit: + case Mouse_Default: + { + break; // error event, ignore + } + /* NO_DEFAULT_CASE */ + } + } + + void MouseState::mousePressEvent (QMouseEvent *event) + { + switch(mMouseState) + { + case Mouse_Grab: + case Mouse_Drag: + { + if(0 /*event->buttons() & ~Qt::RightButton*/) + { + // cancel operation & return the object to the original position + placeObject(mGrabbedSceneNode, mOrigObjPos); + mMouseState = Mouse_Default; + + // reset states + mCurrentMousePos = Ogre::Vector3(); + mOrigMousePos = Ogre::Vector3(); + mOrigObjPos = Ogre::Vector3(); + mGrabbedSceneNode = ""; + mCurrentObj = ""; + mOldPos = QPoint(0, 0); + mMouseEventTimer->invalidate(); + mOffset = 0.0f; + } + break; + } + case Mouse_Edit: + case Mouse_Default: + { + if(event->buttons() & Qt::RightButton) + { + std::pair result = objectUnderCursor(event->x(), event->y()); + if(result.first == "") + break; + + mGrabbedSceneNode = result.first; + // ray test agaist the plane to get a starting position of the + // mouse in relation to the object position + //mPlane->redefine(Ogre::Vector3::UNIT_Z, result.second); + std::pair planeRes = planeAxis(); + mPlane->redefine(planeRes.first, result.second); + std::pair planeResult = mousePositionOnPlane(event->pos(), *mPlane); + if(planeResult.first) + { + mOrigMousePos = planeResult.second; + mCurrentMousePos = planeResult.second; + mOffset = 0.0f; + } + + mOrigObjPos = mSceneManager->getSceneNode(mGrabbedSceneNode)->getPosition(); + mMouseEventTimer->start(); + + mMouseState = Mouse_Grab; + } + break; + } + /* NO_DEFAULT_CASE */ + } + } + + void MouseState::mouseReleaseEvent (QMouseEvent *event) + { + switch(mMouseState) + { + case Mouse_Grab: + { + std::pair result = objectUnderCursor(event->x(), event->y()); + if(result.first != "") + { + if(result.first == mCurrentObj) + { + // unselect object + mMouseState = Mouse_Default; + mCurrentObj = ""; + } + else + { + // select object + mMouseState = Mouse_Edit; + mCurrentObj = result.first; + + // print some debug info + if(isDebug()) + { + std::string referenceId = mPhysics->sceneNodeToRefId(result.first); + std::cout << "ReferenceId: " << referenceId << std::endl; + const CSMWorld::RefCollection& references = mParent->mDocument.getData().getReferences(); + int index = references.searchId(referenceId); + if (index != -1) + { + int columnIndex = + references.findColumnIndex(CSMWorld::Columns::ColumnId_ReferenceableId); + std::cout << " index: " + QString::number(index).toStdString() + +", column index: " + QString::number(columnIndex).toStdString() + << std::endl; + } + } + } + // update highlighting the current object + if(isDebug()) + updateSelectionHighlight(result.first, result.second); + } + break; + } + case Mouse_Drag: + { + // final placement + std::pair planeResult = mousePositionOnPlane(event->pos(), *mPlane); + if(planeResult.first) + { + if(mGrabbedSceneNode != "") + { + std::pair planeRes = planeAxis(); + //mOrigObjPos.z += mOffset; + Ogre::Vector3 pos = mOrigObjPos+planeRes.first*mOffset+planeResult.second-mOrigMousePos; + placeObject(mGrabbedSceneNode, pos); + //mCurrentObj = mGrabbedSceneNode; // FIXME + mCurrentObj = ""; // whether the object is selected + + // 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 + // on screen + + // FIXME: update document + // FIXME: highlight current object? + mMouseState = Mouse_Edit; + } + } + break; + } + case Mouse_Edit: + case Mouse_Default: + { + // probably terrain, check + std::pair result = terrainUnderCursor(event->x(), event->y()); + if(result.first != "") + { + if(isDebug()) + { + std::cout << "terrain: " << 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; + } + /* NO_DEFAULT_CASE */ + } + mMouseEventTimer->invalidate(); + } + + void MouseState::mouseDoubleClickEvent (QMouseEvent *event) + { + if(0 && isDebug()) // disable + { + // FIXME: OEngine::PhysicEngine creates only one child scene node for the + // debug drawer. Hence only the first subview that creates the debug drawer + // can view the debug lines. Will need to keep a map in OEngine if multiple + // subviews are to be supported. + mPhysics->addSceneManager(mSceneManager, mParent); + mPhysics->toggleDebugRendering(mSceneManager); + mParent->flagAsModified(); + } + } + + bool MouseState::wheelEvent (QWheelEvent *event) + { + switch(mMouseState) + { + case Mouse_Grab: + mMouseState = Mouse_Drag; + + /* FALL_THROUGH */ + case Mouse_Drag: + { + // move the object along the z axis 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? + + std::pair planeRes = planeAxis(); + Ogre::Vector3 pos = mOrigObjPos + planeRes.first*mOffset; + //pos.z += mOffset; + mSceneManager->getSceneNode(mGrabbedSceneNode)->setPosition(pos+mCurrentMousePos-mOrigMousePos); + mPhysics->moveSceneNodes(mGrabbedSceneNode, pos+mCurrentMousePos-mOrigMousePos); + updateSceneWidgets(); + } + break; + } + case Mouse_Edit: + case Mouse_Default: + { + return false; + } + /* NO_DEFAULT_CASE */ + } + + return true; + } + + std::pair MouseState::mousePositionOnPlane(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 + getCamera()->setNearClipDistance(10.0f); // arbitrary number + Ogre::Ray mouseRay = getCamera()->getCameraToViewportRay( + (float) pos.x() / getViewport()->getActualWidth(), + (float) pos.y() / getViewport()->getActualHeight()); + getCamera()->setNearClipDistance(nearClipDistance); // restore + std::pair planeResult = mouseRay.intersects(plane); + + if(planeResult.first) + return std::make_pair(true, mouseRay.getPoint(planeResult.second)); + else + return std::make_pair(false, Ogre::Vector3()); // should only happen if the plane is too small + } + + std::pair MouseState::terrainUnderCursor(const int mouseX, const int mouseY) + { + if(!getViewport()) + return std::make_pair("", Ogre::Vector3()); + + float x = (float) mouseX / getViewport()->getActualWidth(); + float y = (float) mouseY / getViewport()->getActualHeight(); + + std::pair result = mPhysics->castRay(x, y, mSceneManager, getCamera()); + if(result.first != "") + { + // FIXME: is there a better way to distinguish terrain from objects? + QString name = QString(result.first.c_str()); + if(name.contains(QRegExp("^HeightField"))) + { + return result; + } + } + + return std::make_pair("", Ogre::Vector3()); + } + + std::pair 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 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()); + } + + // FIXME: for debugging only + void MouseState::updateSelectionHighlight(const std::string sceneNode, const Ogre::Vector3 &position) + { + uint32_t visibilityMask = getViewport()->getVisibilityMask(); + bool ignoreObjects = !(visibilityMask & (uint32_t)CSVRender::Element_Reference); + + if(ignoreObjects || !mSceneManager->hasSceneNode(sceneNode) || !isDebug()) + return; + + CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance(); + bool debugCursor = userSettings.setting( + "debug/mouse-position", QString("false")) == "true" ? true : false; + + //TODO: Try http://www.ogre3d.org/tikiwiki/Create+outline+around+a+character + Ogre::SceneNode *scene = mSceneManager->getSceneNode(sceneNode); + std::map >::iterator iter = + mSelectedEntities.find(sceneNode); + if(iter != mSelectedEntities.end()) // currently selected + { + std::vector clonedEntities = mSelectedEntities[sceneNode]; + while(!clonedEntities.empty()) + { + if(mSceneManager->hasEntity(clonedEntities.back())) + { + scene->detachObject(clonedEntities.back()); + mSceneManager->destroyEntity(clonedEntities.back()); + } + clonedEntities.pop_back(); + } + mSelectedEntities.erase(iter); + + if(debugCursor) + removeHitPoint(mSceneManager, sceneNode); + } + else + { + std::vector clonedEntities; + Ogre::SceneNode::ObjectIterator iter = scene->getAttachedObjectIterator(); + iter.begin(); + while(iter.hasMoreElements()) + { + Ogre::MovableObject * element = iter.getNext(); + if(!element) + break; + + if(element->getMovableType() != "Entity") + continue; + + Ogre::Entity * entity = dynamic_cast(element); + if(mSceneManager->hasEntity(entity->getName()+"cover")) + { + // FIXME: this shouldn't really happen... but does :( + scene->detachObject(entity->getName()+"cover"); + mSceneManager->destroyEntity(entity->getName()+"cover"); + } + Ogre::Entity * clone = entity->clone(entity->getName()+"cover"); + + Ogre::MaterialPtr mat = + Ogre::MaterialManager::getSingleton().getByName("TransMaterial"); + if(!mat.isNull()) + { + clone->setMaterial(mat); + scene->attachObject(clone); + clonedEntities.push_back(entity->getName()+"cover"); + } + } + mSelectedEntities[sceneNode] = clonedEntities; + + if(debugCursor) + showHitPoint(mSceneManager, sceneNode, position); + } + mParent->flagAsModified(); + } + + void MouseState::placeObject(const std::string sceneNode, const Ogre::Vector3 &pos) + { + mSceneManager->getSceneNode(sceneNode)->setPosition(pos); + + // update physics + std::string refId = mPhysics->sceneNodeToRefId(sceneNode); + const CSMWorld::CellRef& cellref = mParent->mDocument.getData().getReferences().getRecord (refId).get(); + Ogre::Quaternion xr (Ogre::Radian (-cellref.mPos.rot[0]), Ogre::Vector3::UNIT_X); + Ogre::Quaternion yr (Ogre::Radian (-cellref.mPos.rot[1]), Ogre::Vector3::UNIT_Y); + Ogre::Quaternion zr (Ogre::Radian (-cellref.mPos.rot[2]), Ogre::Vector3::UNIT_Z); + + // FIXME: adjustRigidBody() seems to lose objects, work around by deleting and recreating objects + //mPhysics->moveObject(sceneNode, pos, xr*yr*zr); + mPhysics->replaceObject(sceneNode, refId, cellref.mScale, pos, xr*yr*zr); + + // update all SceneWidgets and their SceneManagers + updateSceneWidgets(); + } + + void MouseState::updateSceneWidgets() + { + std::map sceneWidgets = mPhysics->sceneWidgets(); + + std::map::iterator iter = sceneWidgets.begin(); + for(; iter != sceneWidgets.end(); ++iter) + { + (*iter).second->updateScene(); + } + } + + Ogre::Camera *MouseState::getCamera() + { + return mParent->getCamera(); + } + + Ogre::Viewport *MouseState::getViewport() + { + return mParent->getCamera()->getViewport(); + } + + bool MouseState::isDebug() + { + CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance(); + + return userSettings.setting("debug/mouse-picking", QString("false")) == "true" ? true : false; + } +} diff --git a/apps/opencs/view/render/mousestate.hpp b/apps/opencs/view/render/mousestate.hpp new file mode 100644 index 000000000..e7eedf1d4 --- /dev/null +++ b/apps/opencs/view/render/mousestate.hpp @@ -0,0 +1,82 @@ +#ifndef OPENCS_VIEW_MOUSESTATE_H +#define OPENCS_VIEW_MOUSESTATE_H + +#include +#include +#include + +class QElapsedTimer; +class QMouseEvent; +class QWheelEvent; + +namespace Ogre +{ + class Plane; + class SceneManager; + class Camera; + class Viewport; +} + +namespace CSVWorld +{ + class PhysicsSystem; +} + +namespace CSVRender +{ + class WorldspaceWidget; + + class MouseState + { + enum MouseStates + { + Mouse_Grab, + Mouse_Drag, + Mouse_Edit, + Mouse_Default + }; + MouseStates mMouseState; + + WorldspaceWidget *mParent; + CSVWorld::PhysicsSystem *mPhysics; // local copy + Ogre::SceneManager *mSceneManager; // local copy + + QPoint mOldPos; + std::string mCurrentObj; + std::string mGrabbedSceneNode; + QElapsedTimer *mMouseEventTimer; + Ogre::Plane *mPlane; + Ogre::Vector3 mOrigObjPos; + Ogre::Vector3 mOrigMousePos; + Ogre::Vector3 mCurrentMousePos; + float mOffset; + + std::map > mSelectedEntities; + + public: + + MouseState(WorldspaceWidget *parent); + ~MouseState(); + + void mouseMoveEvent (QMouseEvent *event); + void mousePressEvent (QMouseEvent *event); + void mouseReleaseEvent (QMouseEvent *event); + void mouseDoubleClickEvent (QMouseEvent *event); + bool wheelEvent (QWheelEvent *event); + + private: + + std::pair mousePositionOnPlane(const QPoint &pos, const Ogre::Plane &plane); + void placeObject(const std::string sceneNode, const Ogre::Vector3 &pos); + std::pair terrainUnderCursor(const int mouseX, const int mouseY); + std::pair objectUnderCursor(const int mouseX, const int mouseY); + void updateSelectionHighlight(const std::string sceneNode, const Ogre::Vector3 &position); + void updateSceneWidgets(); + bool isDebug(); + + Ogre::Camera *getCamera(); // friend access + Ogre::Viewport *getViewport(); // friend access + }; +} + +#endif // OPENCS_VIEW_MOUSESTATE_H diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp index fd6a23e2b..0503d4d63 100644 --- a/apps/opencs/view/render/worldspacewidget.cpp +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -7,18 +7,10 @@ #include #include -#include -#include // FIXME: for debugging -#include // FIXME: for debugging -#include // FIXME: for debugging - -#include -#include #include #include "../../model/world/universalid.hpp" #include "../../model/world/idtable.hpp" -#include "../../model/settings/usersettings.hpp" #include "../widget/scenetoolmode.hpp" #include "../widget/scenetooltoggle.hpp" @@ -27,96 +19,11 @@ #include "../world/physicsmanager.hpp" #include "../world/physicssystem.hpp" +#include "mousestate.hpp" #include "elements.hpp" -namespace -{ - // FIXME: this section should be removed once the debugging is completed - void showHitPoint(Ogre::SceneManager *sceneMgr, std::string name, Ogre::Vector3 point) - { - if(sceneMgr->hasManualObject("manual" + name)) - sceneMgr->destroyManualObject("manual" + name); - Ogre::ManualObject* manual = sceneMgr->createManualObject("manual" + name); - manual->begin("BaseWhite", Ogre::RenderOperation::OT_LINE_LIST); - manual-> position(point.x, point.y, point.z-100); - manual-> position(point.x, point.y, point.z+100); - manual-> position(point.x, point.y-100, point.z); - manual-> position(point.x, point.y+100, point.z); - manual-> position(point.x-100, point.y, point.z); - manual-> position(point.x+100, point.y, point.z); - manual->end(); - sceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(manual); - } - - void removeHitPoint(Ogre::SceneManager *sceneMgr, std::string name) - { - if(sceneMgr->hasManualObject("manual" + name)) - sceneMgr->destroyManualObject("manual" + name); - } - - void initDebug() - { - // material for visual cue on selected objects - Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton().getByName("DynamicTrans"); - if(texture.isNull()) - { - texture = Ogre::TextureManager::getSingleton().createManual( - "DynamicTrans", // name - Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - Ogre::TEX_TYPE_2D, // type - 8, 8, // width & height - 0, // number of mipmaps - Ogre::PF_BYTE_BGRA, // pixel format - Ogre::TU_DEFAULT); // usage; should be TU_DYNAMIC_WRITE_ONLY_DISCARDABLE for - // textures updated very often (e.g. each frame) - - Ogre::HardwarePixelBufferSharedPtr pixelBuffer = texture->getBuffer(); - pixelBuffer->lock(Ogre::HardwareBuffer::HBL_NORMAL); - const Ogre::PixelBox& pixelBox = pixelBuffer->getCurrentLock(); - - uint8_t* pDest = static_cast(pixelBox.data); - - // Fill in some pixel data. This will give a semi-transparent colour, - // but this is of course dependent on the chosen pixel format. - for (size_t j = 0; j < 8; j++) - { - for(size_t i = 0; i < 8; i++) - { - *pDest++ = 255; // B - *pDest++ = 255; // G - *pDest++ = 127; // R - *pDest++ = 63; // A - } - - pDest += pixelBox.getRowSkip() * Ogre::PixelUtil::getNumElemBytes(pixelBox.format); - } - pixelBuffer->unlock(); - } - Ogre::MaterialPtr material = Ogre::MaterialManager::getSingleton().getByName( - "TransMaterial"); - if(material.isNull()) - { - Ogre::MaterialPtr material = Ogre::MaterialManager::getSingleton().create( - "TransMaterial", - Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, true ); - Ogre::Pass *pass = material->getTechnique( 0 )->getPass( 0 ); - pass->setLightingEnabled( false ); - pass->setDepthWriteEnabled( false ); - pass->setSceneBlending( Ogre::SBT_TRANSPARENT_ALPHA ); - - Ogre::TextureUnitState *tex = pass->createTextureUnitState("CustomState", 0); - tex->setTextureName("DynamicTrans"); - tex->setTextureFiltering( Ogre::TFO_ANISOTROPIC ); - material->load(); - } - } -} - CSVRender::WorldspaceWidget::WorldspaceWidget (CSMDoc::Document& document, QWidget* parent) -: SceneWidget (parent), mDocument(document), mSceneElements(0), mRun(0), - mCurrentObj(""), mMouseState(Mouse_Default), mOldPos(0,0), mMouseEventTimer(0), mPlane(0), - mGrabbedSceneNode(""), mOrigObjPos(Ogre::Vector3()), mOrigMousePos(Ogre::Vector3()), - mCurrentMousePos(Ogre::Vector3()), mZOffset(0.0f) +: SceneWidget (parent), mDocument(document), mSceneElements(0), mRun(0), mPhysics(0), mMouse(0) { setAcceptDrops(true); @@ -148,53 +55,18 @@ CSVRender::WorldspaceWidget::WorldspaceWidget (CSMDoc::Document& document, QWidg connect (debugProfiles, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), this, SLOT (debugProfileAboutToBeRemoved (const QModelIndex&, int, int))); - // associate WorldSpaceWidgets (and their SceneManagers with Documents, then create physics if new document + // associate WorldSpaceWidgets (and their SceneManagers) with Documents + // then create physics if there is a new document mPhysics = CSVWorld::PhysicsManager::instance()->addSceneWidget(document, this); mPhysics->addSceneManager(getSceneManager(), this); - - initDebug(); - mMouseEventTimer = new QElapsedTimer(); - mMouseEventTimer->invalidate(); - - mPlane = new Ogre::Plane(Ogre::Vector3::UNIT_Z, 0); - Ogre::MeshPtr mesh = Ogre::MeshManager::getSingleton().createPlane("ground", - Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - *mPlane, - 300000,300000, // FIXME: use far clip dist? - 1,1, // segments - true, // normals - 1, // numTexCoordSets - 1,1, // uTile, vTile - Ogre::Vector3::UNIT_Y // upVector - ); + mMouse = new MouseState(this); } CSVRender::WorldspaceWidget::~WorldspaceWidget () { + delete mMouse; mPhysics->removeSceneManager(getSceneManager()); CSVWorld::PhysicsManager::instance()->removeSceneWidget(this); - - delete mMouseEventTimer; - - // For debugging only - std::map >::iterator iter = mSelectedEntities.begin(); - for(;iter != mSelectedEntities.end(); ++iter) - { - removeHitPoint(getSceneManager(), iter->first); - - if(getSceneManager()->hasSceneNode(iter->first)) - { - Ogre::SceneNode *scene = getSceneManager()->getSceneNode(iter->first); - - if(scene) - { - scene->removeAndDestroyAllChildren(); - getSceneManager()->destroySceneNode(iter->first); - } - } - } - - delete mPlane; } void CSVRender::WorldspaceWidget::selectNavigationMode (const std::string& mode) @@ -471,147 +343,18 @@ CSVWorld::PhysicsSystem *CSVRender::WorldspaceWidget::getPhysics() return mPhysics; } -// mouse picking -// FIXME: need to virtualise mouse buttons -// -// State machine: -// -// [default] mousePressEvent->check if the mouse is pointing at an object -// if yes, create collision planes then go to [grab] -// else check for terrain -// -// [grab] mouseReleaseEvent->if same button and new obj, go to [edit] -// mouseMoveEvent->if same button, go to [drag] -// other mouse events or buttons, go back to [default] (i.e. like 'cancel') -// -// [drag] mouseReleaseEvent->if same button, place the object at the new -// location, update the document then go to [edit] -// mouseMoveEvent->update position to the user based on ray to the collision -// planes and render the object at the new location, but do not update -// the document yet -// -// [edit] TODO, probably fine positional adjustments or rotations; clone/delete? -// -// -// press press (obj) -// [default] --------> [grab] <-------------------- [edit] -// ^ (obj) | | ------> [drag] -----> ^ -// | | | move ^ | release | -// | | | | | | -// | | | +-+ | -// | | | move | -// +----------------+ +--------------------------+ -// release release -// (same obj) (new obj) -// -// void CSVRender::WorldspaceWidget::mouseMoveEvent (QMouseEvent *event) { if(event->buttons() & Qt::RightButton) { - switch(mMouseState) - { - case Mouse_Grab: - { - // check if min elapsed time to stop false detection of drag - if(!mMouseEventTimer->isValid() || !mMouseEventTimer->hasExpired(100)) // ms - break; - - mMouseEventTimer->invalidate(); - mMouseState = Mouse_Drag; - - /* FALL_THROUGH */ - } - case Mouse_Drag: - { - if(event->pos() != mOldPos) // TODO: maybe don't update less than a quantum? - { - mOldPos = event->pos(); - - // ray test against the plane to provide feedback to the user the - // relative movement of the object on the x-y plane - std::pair planeResult = mousePositionOnPlane(event->pos(), *mPlane); - if(planeResult.first) - { - if(mGrabbedSceneNode != "") - { - Ogre::Vector3 pos = mOrigObjPos; - pos.z += mZOffset; - getSceneManager()->getSceneNode(mGrabbedSceneNode)->setPosition(pos+planeResult.second-mOrigMousePos); - mCurrentMousePos = planeResult.second; - mPhysics->moveSceneNodes(mGrabbedSceneNode, pos+planeResult.second-mOrigMousePos); - updateSceneWidgets(); - } - } - } - break; - } - case Mouse_Edit: - case Mouse_Default: - { - break; // error event, ignore - } - /* NO_DEFAULT_CASE */ - } + mMouse->mouseMoveEvent(event); } SceneWidget::mouseMoveEvent(event); } void CSVRender::WorldspaceWidget::mousePressEvent (QMouseEvent *event) { - switch(mMouseState) - { - case Mouse_Grab: - case Mouse_Drag: - { - if(0 /*event->buttons() & ~Qt::RightButton*/) - { - // cancel operation & return the object to the original position - placeObject(mGrabbedSceneNode, mOrigObjPos); - mMouseState = Mouse_Default; - - // reset states - mCurrentMousePos = Ogre::Vector3(); - mOrigMousePos = Ogre::Vector3(); - mOrigObjPos = Ogre::Vector3(); - mGrabbedSceneNode = ""; - mCurrentObj = ""; - mOldPos = QPoint(0, 0); - mMouseEventTimer->invalidate(); - mZOffset = 0.0f; - } - break; - } - case Mouse_Edit: - case Mouse_Default: - { - if(event->buttons() & Qt::RightButton) - { - std::pair result = objectUnderCursor(event->x(), event->y()); - if(result.first == "") - break; - - mGrabbedSceneNode = result.first; - // ray test agaist the plane to get a starting position of the - // mouse in relation to the object position - mPlane->redefine(Ogre::Vector3::UNIT_Z, result.second); - std::pair planeResult = mousePositionOnPlane(event->pos(), *mPlane); - if(planeResult.first) - { - mOrigMousePos = planeResult.second; - mCurrentMousePos = planeResult.second; - mZOffset = 0.0f; - } - - mOrigObjPos = getSceneManager()->getSceneNode(mGrabbedSceneNode)->getPosition(); - mMouseEventTimer->start(); - - mMouseState = Mouse_Grab; - } - break; - } - /* NO_DEFAULT_CASE */ - } + mMouse->mousePressEvent(event); //SceneWidget::mousePressEvent(event); } @@ -619,104 +362,12 @@ void CSVRender::WorldspaceWidget::mouseReleaseEvent (QMouseEvent *event) { if(event->button() == Qt::RightButton) { - if(!getCamera()->getViewport()) + if(!getViewport()) { SceneWidget::mouseReleaseEvent(event); return; } - - switch(mMouseState) - { - case Mouse_Grab: - { - std::pair result = objectUnderCursor(event->x(), event->y()); - if(result.first != "") - { - if(result.first == mCurrentObj) - { - // unselect object - mMouseState = Mouse_Default; - mCurrentObj = ""; - } - else - { - // select object - mMouseState = Mouse_Edit; - mCurrentObj = result.first; - - // print some debug info - if(isDebug()) - { - std::string referenceId = mPhysics->sceneNodeToRefId(result.first); - std::cout << "ReferenceId: " << referenceId << std::endl; - const CSMWorld::RefCollection& references = mDocument.getData().getReferences(); - int index = references.searchId(referenceId); - if (index != -1) - { - int columnIndex = - references.findColumnIndex(CSMWorld::Columns::ColumnId_ReferenceableId); - std::cout << " index: " + QString::number(index).toStdString() - +", column index: " + QString::number(columnIndex).toStdString() - << std::endl; - } - } - } - // update highlighting the current object - if(isDebug()) - updateSelectionHighlight(result.first, result.second); - } - break; - } - case Mouse_Drag: - { - // final placement - std::pair planeResult = mousePositionOnPlane(event->pos(), *mPlane); - if(planeResult.first) - { - if(mGrabbedSceneNode != "") - { - mOrigObjPos.z += mZOffset; - Ogre::Vector3 pos = mOrigObjPos+planeResult.second-mOrigMousePos; - placeObject(mGrabbedSceneNode, pos); - //mCurrentObj = mGrabbedSceneNode; // FIXME - mCurrentObj = ""; // whether the object is selected - - // 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 - mZOffset = 0.0f; // used for z-axis movement - mOldPos = QPoint(0, 0); // to calculate relative movement of mouse - // on screen - - // FIXME: update document - // FIXME: highlight current object? - mMouseState = Mouse_Edit; - } - } - break; - } - case Mouse_Edit: - case Mouse_Default: - { - // probably terrain, check - std::pair result = terrainUnderCursor(event->x(), event->y()); - if(result.first != "") - { - if(isDebug()) - { - std::cout << "terrain: " << 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; - } - /* NO_DEFAULT_CASE */ - } - mMouseEventTimer->invalidate(); + mMouse->mouseReleaseEvent(event); } SceneWidget::mouseReleaseEvent(event); } @@ -725,226 +376,13 @@ void CSVRender::WorldspaceWidget::mouseDoubleClickEvent (QMouseEvent *event) { if(event->button() == Qt::RightButton) { - if(isDebug()) - { - // FIXME: OEngine::PhysicEngine creates only one child scene node for the - // debug drawer. Hence only the first subview that creates the debug drawer - // can view the debug lines. Will need to keep a map in OEngine if multiple - // subviews are to be supported. - //mPhysics->setSceneManager(getSceneManager()); - mPhysics->toggleDebugRendering(getSceneManager()); - flagAsModified(); - } + mMouse->mouseDoubleClickEvent(event); } //SceneWidget::mouseDoubleClickEvent(event); } void CSVRender::WorldspaceWidget::wheelEvent (QWheelEvent *event) { - switch(mMouseState) - { - case Mouse_Grab: - mMouseState = Mouse_Drag; - - /* FALL_THROUGH */ - case Mouse_Drag: - { - // move the object along the z axis during Mouse_Drag or Mouse_Grab - if (event->delta()) - { - // seems positive is up and negative is down - mZOffset += (event->delta()/5); // FIXME: arbitrary number, make config option? - - Ogre::Vector3 pos = mOrigObjPos; - pos.z += mZOffset; - getSceneManager()->getSceneNode(mGrabbedSceneNode)->setPosition(pos+mCurrentMousePos-mOrigMousePos); - mPhysics->moveSceneNodes(mGrabbedSceneNode, pos+mCurrentMousePos-mOrigMousePos); - updateSceneWidgets(); - } - break; - } - case Mouse_Edit: - case Mouse_Default: - { - SceneWidget::wheelEvent(event); - break; - } - /* NO_DEFAULT_CASE */ - } -} - -// FIXME: for debugging only -void CSVRender::WorldspaceWidget::updateSelectionHighlight(const std::string sceneNode, const Ogre::Vector3 &position) -{ - uint32_t visibilityMask = getCamera()->getViewport()->getVisibilityMask(); - bool ignoreObjects = !(visibilityMask & (uint32_t)CSVRender::Element_Reference); - - if(ignoreObjects || !getSceneManager()->hasSceneNode(sceneNode) || !isDebug()) - return; - - CSMSettings::UserSettings &userSettings = CSMSettings::UserSettings::instance(); - bool debugCursor = userSettings.setting( - "debug/mouse-position", QString("false")) == "true" ? true : false; - - //TODO: Try http://www.ogre3d.org/tikiwiki/Create+outline+around+a+character - Ogre::SceneNode *scene = getSceneManager()->getSceneNode(sceneNode); - std::map >::iterator iter = - mSelectedEntities.find(sceneNode); - if(iter != mSelectedEntities.end()) // currently selected - { - std::vector clonedEntities = mSelectedEntities[sceneNode]; - while(!clonedEntities.empty()) - { - if(getSceneManager()->hasEntity(clonedEntities.back())) - { - scene->detachObject(clonedEntities.back()); - getSceneManager()->destroyEntity(clonedEntities.back()); - } - clonedEntities.pop_back(); - } - mSelectedEntities.erase(iter); - - if(debugCursor) - removeHitPoint(getSceneManager(), sceneNode); - } - else - { - std::vector clonedEntities; - Ogre::SceneNode::ObjectIterator iter = scene->getAttachedObjectIterator(); - iter.begin(); - while(iter.hasMoreElements()) - { - Ogre::MovableObject * element = iter.getNext(); - if(!element) - break; - - if(element->getMovableType() != "Entity") - continue; - - Ogre::Entity * entity = dynamic_cast(element); - if(getSceneManager()->hasEntity(entity->getName()+"cover")) - { - // FIXME: this shouldn't really happen... but does :( - scene->detachObject(entity->getName()+"cover"); - getSceneManager()->destroyEntity(entity->getName()+"cover"); - } - Ogre::Entity * clone = entity->clone(entity->getName()+"cover"); - - Ogre::MaterialPtr mat = - Ogre::MaterialManager::getSingleton().getByName("TransMaterial"); - if(!mat.isNull()) - { - clone->setMaterial(mat); - scene->attachObject(clone); - clonedEntities.push_back(entity->getName()+"cover"); - } - } - mSelectedEntities[sceneNode] = clonedEntities; - - if(debugCursor) - showHitPoint(getSceneManager(), sceneNode, position); - } - flagAsModified(); -} - -std::pair CSVRender::WorldspaceWidget::terrainUnderCursor(const int mouseX, const int mouseY) -{ - if(!getCamera()->getViewport()) - return std::make_pair("", Ogre::Vector3()); - - float x = (float) mouseX / getCamera()->getViewport()->getActualWidth(); - float y = (float) mouseY / getCamera()->getViewport()->getActualHeight(); - - std::pair result = mPhysics->castRay(x, y, getSceneManager(), 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"))) - { - return result; - } - } - - return std::make_pair("", Ogre::Vector3()); -} - -std::pair CSVRender::WorldspaceWidget::objectUnderCursor(const int mouseX, const int mouseY) -{ - if(!getCamera()->getViewport()) - return std::make_pair("", Ogre::Vector3()); - - float x = (float) mouseX / getCamera()->getViewport()->getActualWidth(); - float y = (float) mouseY / getCamera()->getViewport()->getActualHeight(); - - std::pair result = mPhysics->castRay(x, y, getSceneManager(), 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 = getCamera()->getViewport()->getVisibilityMask(); - bool ignoreObjects = !(visibilityMask & (uint32_t)CSVRender::Element_Reference); - - if(!ignoreObjects && getSceneManager()->hasSceneNode(result.first)) - { - return result; - } - } - } - - return std::make_pair("", Ogre::Vector3()); -} - -std::pair CSVRender::WorldspaceWidget::mousePositionOnPlane(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 - getCamera()->setNearClipDistance(10.0f); // arbitrary number - Ogre::Ray mouseRay = getCamera()->getCameraToViewportRay( - (float) pos.x() / getCamera()->getViewport()->getActualWidth(), - (float) pos.y() / getCamera()->getViewport()->getActualHeight()); - getCamera()->setNearClipDistance(nearClipDistance); // restore - std::pair planeResult = mouseRay.intersects(plane); - - if(planeResult.first) - return std::make_pair(true, mouseRay.getPoint(planeResult.second)); - else - return std::make_pair(false, Ogre::Vector3()); // should only happen if the plane is too small -} - -void CSVRender::WorldspaceWidget::placeObject(const std::string sceneNode, const Ogre::Vector3 &pos) -{ - getSceneManager()->getSceneNode(sceneNode)->setPosition(pos); - - // update physics - std::string refId = mPhysics->sceneNodeToRefId(sceneNode); - const CSMWorld::CellRef& cellref = mDocument.getData().getReferences().getRecord (refId).get(); - Ogre::Quaternion xr (Ogre::Radian (-cellref.mPos.rot[0]), Ogre::Vector3::UNIT_X); - Ogre::Quaternion yr (Ogre::Radian (-cellref.mPos.rot[1]), Ogre::Vector3::UNIT_Y); - Ogre::Quaternion zr (Ogre::Radian (-cellref.mPos.rot[2]), Ogre::Vector3::UNIT_Z); - - // FIXME: adjustRigidBody() seems to lose objects, work around by deleting and recreating objects - //mPhysics->moveObject(sceneNode, pos, xr*yr*zr); - mPhysics->replaceObject(sceneNode, refId, cellref.mScale, pos, xr*yr*zr); - - // update all SceneWidgets and their SceneManagers - updateSceneWidgets(); -} - -void CSVRender::WorldspaceWidget::updateSceneWidgets() -{ - std::map sceneWidgets = mPhysics->sceneWidgets(); - - std::map::iterator iter = sceneWidgets.begin(); - for(; iter != sceneWidgets.end(); ++iter) - { - (*iter).second->updateScene(); - } -} - -bool CSVRender::WorldspaceWidget::isDebug() -{ - return false; + if(!mMouse->wheelEvent(event)) + SceneWidget::wheelEvent(event); } diff --git a/apps/opencs/view/render/worldspacewidget.hpp b/apps/opencs/view/render/worldspacewidget.hpp index 431a023be..2dfe2b871 100644 --- a/apps/opencs/view/render/worldspacewidget.hpp +++ b/apps/opencs/view/render/worldspacewidget.hpp @@ -27,12 +27,12 @@ namespace CSVWorld class PhysicsSystem; } -class QElapsedTimer; - namespace CSVRender { class WorldspaceWidget : public SceneWidget { + friend class MouseState; + Q_OBJECT CSVRender::Navigation1st m1st; @@ -42,26 +42,7 @@ namespace CSVRender CSVWidget::SceneToolRun *mRun; CSMDoc::Document& mDocument; CSVWorld::PhysicsSystem *mPhysics; - - enum MouseState - { - Mouse_Grab, - Mouse_Drag, - Mouse_Edit, - Mouse_Default - }; - MouseState mMouseState; - - QPoint mOldPos; - std::string mCurrentObj; - std::string mGrabbedSceneNode; - QElapsedTimer *mMouseEventTimer; - Ogre::Plane *mPlane; - Ogre::Vector3 mOrigObjPos; - Ogre::Vector3 mOrigMousePos; - Ogre::Vector3 mCurrentMousePos; - float mZOffset; - std::map > mSelectedEntities; + MouseState *mMouse; public: @@ -138,15 +119,6 @@ namespace CSVRender virtual std::string getStartupInstruction() = 0; - void placeObject(const std::string sceneNode, const Ogre::Vector3 &pos); - std::pair terrainUnderCursor(const int mouseX, const int mouseY); - std::pair objectUnderCursor(const int mouseX, const int mouseY); - std::pair mousePositionOnPlane(const QPoint &pos, const Ogre::Plane &plane); - void updateSelectionHighlight(const std::string sceneNode, const Ogre::Vector3 &position); - - void updateSceneWidgets(); - bool isDebug(); - private slots: void selectNavigationMode (const std::string& mode);