diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index ec4737d16..79876224d 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -89,8 +89,8 @@ opencs_units (view/render ) opencs_units_noqt (view/render - lighting lightingday lightingnight - lightingbright object cell terrainstorage tagbase cellarrow cellmarker cellborder + lighting lightingday lightingnight lightingbright object cell terrainstorage tagbase + cellarrow cellmarker cellborder cameracontroller ) opencs_hdrs_noqt (view/render diff --git a/apps/opencs/view/render/cameracontroller.cpp b/apps/opencs/view/render/cameracontroller.cpp new file mode 100644 index 000000000..fa2ad8560 --- /dev/null +++ b/apps/opencs/view/render/cameracontroller.cpp @@ -0,0 +1,458 @@ +#include "cameracontroller.hpp" + +#include + +#include +#include +#include + +namespace CSVRender +{ + + /* + Camera Controller + */ + + const osg::Vec3d CameraController::LocalUp = osg::Vec3d(0, 1, 0); + const osg::Vec3d CameraController::LocalLeft = osg::Vec3d(1, 0, 0); + const osg::Vec3d CameraController::LocalForward = osg::Vec3d(0, 0, 1); + + const double CameraController::LinearSpeed = 1000; + const double CameraController::RotationalSpeed = osg::PI / 2.f; + const double CameraController::SpeedMultiplier = 8; + + CameraController::CameraController() + : mActive(false) + , mModified(false) + , mMouseScalar(-1/350.f) + , mCamera(NULL) + { + } + + CameraController::~CameraController() + { + } + + bool CameraController::isActive() const + { + return mActive; + } + + bool CameraController::isModified() const + { + return mModified; + } + + osg::Camera* CameraController::getCamera() const + { + return mCamera; + } + + double CameraController::getMouseScalar() const + { + return mMouseScalar; + } + + void CameraController::setCamera(osg::Camera* camera) + { + mCamera = camera; + mActive = (mCamera != NULL); + + if (mActive) + onActivate(); + } + + void CameraController::setMouseScalar(double value) + { + mMouseScalar = value; + } + + void CameraController::setModified() + { + mModified = true; + } + + void CameraController::resetModified() + { + mModified = false; + } + + /* + Free Camera Controller + */ + + FreeCameraController::FreeCameraController() + : mLockUpright(false) + , mFast(false) + , mLeft(false) + , mRight(false) + , mForward(false) + , mBackward(false) + , mRollLeft(false) + , mRollRight(false) + , mUp(LocalUp) + { + } + + void FreeCameraController::fixUpAxis(const osg::Vec3d& up) + { + mLockUpright = true; + mUp = up; + } + + void FreeCameraController::unfixUpAxis() + { + mLockUpright = false; + } + + bool FreeCameraController::handleKeyEvent(QKeyEvent* event, bool pressed) + { + if (!isActive()) + return false; + + if (event->key() == Qt::Key_Q) + { + mRollLeft = pressed; + setModified(); + } + else if (event->key() == Qt::Key_E) + { + mRollRight = pressed; + setModified(); + } + else if (event->key() == Qt::Key_A) + { + mLeft = pressed; + setModified(); + } + else if (event->key() == Qt::Key_D) + { + mRight = pressed; + setModified(); + } + else if (event->key() == Qt::Key_W) + { + mForward = pressed; + setModified(); + } + else if (event->key() == Qt::Key_S) + { + mBackward = pressed; + setModified(); + } + else if (event->key() == Qt::Key_Shift) + { + mFast = pressed; + setModified(); + } + else + { + return false; + } + + return true; + } + + bool FreeCameraController::handleMouseMoveEvent(std::string mode, int x, int y) + { + if (!isActive()) + return false; + + if (mode == "p-navi") + { + yaw(x * getMouseScalar()); + pitch(y * getMouseScalar()); + setModified(); + } + else if (mode == "s-navi") + { + translate(LocalLeft * x + LocalUp * -y); + setModified(); + } + else if (mode == "t-navi") + { + translate(LocalForward * x * (mFast ? SpeedMultiplier : 1)); + } + else + { + return false; + } + + return true; + } + + void FreeCameraController::update(double dt) + { + if (!isActive()) + return; + + double linDist = LinearSpeed * dt; + double rotDist = RotationalSpeed * dt; + + if (mFast) + linDist *= SpeedMultiplier; + + if (mLeft) + translate(LocalLeft * linDist); + if (mRight) + translate(LocalLeft * -linDist); + if (mForward) + translate(LocalForward * linDist); + if (mBackward) + translate(LocalForward * -linDist); + + if (!mLockUpright) + { + if (mRollLeft) + roll(-rotDist); + if (mRollRight) + roll(rotDist); + } + else if(isModified()) + { + stabilize(); + } + + // Normalize the matrix to counter drift + getCamera()->getViewMatrix().orthoNormal(getCamera()->getViewMatrix()); + + resetModified(); + } + + void FreeCameraController::yaw(double value) + { + getCamera()->getViewMatrix() *= osg::Matrixd::rotate(value, LocalUp); + } + + void FreeCameraController::pitch(double value) + { + getCamera()->getViewMatrix() *= osg::Matrixd::rotate(value, LocalLeft); + } + + void FreeCameraController::roll(double value) + { + getCamera()->getViewMatrix() *= osg::Matrixd::rotate(value, LocalForward); + } + + void FreeCameraController::translate(const osg::Vec3d& offset) + { + getCamera()->getViewMatrix() *= osg::Matrixd::translate(offset); + } + + void FreeCameraController::stabilize() + { + osg::Vec3d eye, center, up; + getCamera()->getViewMatrixAsLookAt(eye, center, up); + getCamera()->setViewMatrixAsLookAt(eye, center, mUp); + } + + /* + Orbit Camera Controller + */ + + OrbitCameraController::OrbitCameraController() + : mInitialized(false) + , mFast(false) + , mLeft(false) + , mRight(false) + , mUp(false) + , mDown(false) + , mRollLeft(false) + , mRollRight(false) + , mCenter(0,0,0) + { + } + + bool OrbitCameraController::handleKeyEvent(QKeyEvent* event, bool pressed) + { + if (!isActive()) + return false; + + if (!mInitialized) + initialize(); + + if (event->key() == Qt::Key_Q) + { + mRollLeft = pressed; + setModified(); + } + else if (event->key() == Qt::Key_E) + { + mRollRight = pressed; + setModified(); + } + else if (event->key() == Qt::Key_A) + { + mLeft = pressed; + setModified(); + } + else if (event->key() == Qt::Key_D) + { + mRight = pressed; + setModified(); + } + else if (event->key() == Qt::Key_W) + { + mUp = pressed; + setModified(); + } + else if (event->key() == Qt::Key_S) + { + mDown = pressed; + setModified(); + } + else if (event->key() == Qt::Key_Shift) + { + mFast = pressed; + setModified(); + } + else + { + return false; + } + + return true; + } + + bool OrbitCameraController::handleMouseMoveEvent(std::string mode, int x, int y) + { + if (!isActive()) + return false; + + if (!mInitialized) + initialize(); + + if (mode == "p-navi") + { + rotateHorizontal(x * getMouseScalar()); + rotateVertical(y * getMouseScalar()); + setModified(); + } + else if (mode == "s-navi") + { + translate(LocalLeft * x + LocalUp * -y); + setModified(); + } + else if (mode == "t-navi") + { + zoom(x * (mFast ? SpeedMultiplier : 1)); + } + else + { + return false; + } + + return true; + } + + void OrbitCameraController::update(double dt) + { + if (!isActive()) + return; + + if (!mInitialized) + initialize(); + + double rotDist = RotationalSpeed * dt; + + if (mFast) + rotDist *= SpeedMultiplier; + + if (mLeft) + rotateHorizontal(-rotDist); + if (mRight) + rotateHorizontal(rotDist); + if (mUp) + rotateVertical(rotDist); + if (mDown) + rotateVertical(-rotDist); + + if (mRollLeft) + roll(-rotDist); + if (mRollRight) + roll(rotDist); + + lookAtCenter(); + + // Normalize the matrix to counter drift + getCamera()->getViewMatrix().orthoNormal(getCamera()->getViewMatrix()); + + resetModified(); + } + + void OrbitCameraController::onActivate() + { + mInitialized = false; + } + + void OrbitCameraController::initialize() + { + static const int DefaultStartDistance = 10000.f; + + osg::Quat rotation = getCamera()->getViewMatrix().getRotate(); + osg::Vec3d position = getCamera()->getViewMatrix().getTrans(); + osg::Vec3d offset = rotation * (LocalForward * DefaultStartDistance); + + mCenter = position + offset; + + mInitialized = true; + } + + void OrbitCameraController::rotateHorizontal(double value) + { + osg::Vec3d position = getCamera()->getViewMatrix().getTrans(); + osg::Vec3d offset = position - mCenter; + osg::Quat rotation = getCamera()->getViewMatrix().getRotate(); + + osg::Quat offsetRotation = osg::Quat(value, LocalUp); + osg::Vec3d newOffset = (rotation * offsetRotation) * (rotation.inverse() * offset); + + getCamera()->getViewMatrix().setTrans(mCenter + newOffset); + } + + void OrbitCameraController::rotateVertical(double value) + { + osg::Vec3d position = getCamera()->getViewMatrix().getTrans(); + osg::Vec3d offset = position - mCenter; + osg::Quat rotation = getCamera()->getViewMatrix().getRotate(); + + osg::Quat offsetRotation = osg::Quat(value, LocalLeft); + osg::Vec3d newOffset = (rotation * offsetRotation) * (rotation.inverse() * offset); + + getCamera()->getViewMatrix().setTrans(mCenter + newOffset); + } + + void OrbitCameraController::roll(double value) + { + getCamera()->getViewMatrix() *= osg::Matrixd::rotate(value, LocalForward); + } + + void OrbitCameraController::translate(const osg::Vec3d& offset) + { + mCenter += offset; + getCamera()->getViewMatrix() *= osg::Matrixd::translate(offset); + } + + void OrbitCameraController::zoom(double value) + { + osg::Vec3d dir = mCenter - getCamera()->getViewMatrix().getTrans(); + double distance = dir.normalize(); + + if (distance > 1 || value < 0) + { + getCamera()->getViewMatrix() *= osg::Matrixd::translate(dir * value); + } + } + + void OrbitCameraController::lookAtCenter() + { + osg::Vec3d position = getCamera()->getViewMatrix().getTrans(); + osg::Vec3d offset = mCenter - position; + osg::Quat rotation = getCamera()->getViewMatrix().getRotate(); + + osg::Quat newRotation; + newRotation.makeRotate(LocalForward, offset); + + getCamera()->getViewMatrix().setRotate(newRotation); + } +} diff --git a/apps/opencs/view/render/cameracontroller.hpp b/apps/opencs/view/render/cameracontroller.hpp new file mode 100644 index 000000000..e45e7bead --- /dev/null +++ b/apps/opencs/view/render/cameracontroller.hpp @@ -0,0 +1,121 @@ +#ifndef OPENCS_VIEW_CAMERACONTROLLER_H +#define OPENCS_VIEW_CAMERACONTROLLER_H + +#include + +#include +#include + +class QKeyEvent; + +namespace osg +{ + class Camera; +} + +namespace CSVRender +{ + class CameraController + { + public: + + static const osg::Vec3d LocalUp; + static const osg::Vec3d LocalLeft; + static const osg::Vec3d LocalForward; + + static const double LinearSpeed; + static const double RotationalSpeed; + static const double SpeedMultiplier; + + CameraController(); + virtual ~CameraController(); + + bool isActive() const; + bool isModified() const; + + osg::Camera* getCamera() const; + double getMouseScalar() const; + + void setCamera(osg::Camera*); + void setMouseScalar(double value); + + virtual bool handleKeyEvent(QKeyEvent* event, bool pressed) = 0; + virtual bool handleMouseMoveEvent(std::string mode, int x, int y) = 0; + + virtual void update(double dt) = 0; + + protected: + + void setModified(); + void resetModified(); + + virtual void onActivate(){} + + private: + + bool mActive, mModified; + double mMouseScalar; + + osg::Camera* mCamera; + }; + + class FreeCameraController : public CameraController + { + public: + + FreeCameraController(); + + void fixUpAxis(const osg::Vec3d& up); + void unfixUpAxis(); + + bool handleKeyEvent(QKeyEvent* event, bool pressed); + bool handleMouseMoveEvent(std::string mode, int x, int y); + + void update(double dt); + + private: + + void yaw(double value); + void pitch(double value); + void roll(double value); + void translate(const osg::Vec3d& offset); + + void stabilize(); + + bool mLockUpright; + bool mFast, mLeft, mRight, mForward, mBackward, mRollLeft, mRollRight; + osg::Vec3d mUp; + }; + + class OrbitCameraController : public CameraController + { + public: + + OrbitCameraController(); + + bool handleKeyEvent(QKeyEvent* event, bool pressed); + bool handleMouseMoveEvent(std::string mode, int x, int y); + + void update(double dt); + + private: + + void onActivate(); + + void initialize(); + + void rotateHorizontal(double value); + void rotateVertical(double value); + void roll(double value); + void translate(const osg::Vec3d& offset); + void zoom(double value); + + void lookAtCenter(); + + bool mInitialized; + bool mFast, mLeft, mRight, mUp, mDown, mRollLeft, mRollRight; + osg::Vec3d mCenter; + }; +} + +#endif diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index 6115a9bc3..c3b4b9a19 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -25,6 +25,7 @@ #include "lighting.hpp" #include "mask.hpp" +#include "cameracontroller.hpp" namespace CSVRender { @@ -141,8 +142,12 @@ CompositeViewer &CompositeViewer::get() void CompositeViewer::update() { - mSimulationTime += mFrameTimer.time_s(); + double dt = mFrameTimer.time_s(); mFrameTimer.setStartTick(); + + emit simulationUpdated(dt); + + mSimulationTime += dt; frame(mSimulationTime); } @@ -154,7 +159,14 @@ SceneWidget::SceneWidget(boost::shared_ptr resourceSys , mResourceSystem(resourceSystem) , mLighting(NULL) , mHasDefaultAmbient(false) + , mPrevMouseX(0) + , mPrevMouseY(0) + , mFreeCamControl(new FreeCameraController()) + , mOrbitCamControl(new OrbitCameraController()) + , mCurrentCamControl(mFreeCamControl.get()) { + selectNavigationMode("free"); + // we handle lighting manually mView->setLightingMode(osgViewer::View::NO_LIGHT); @@ -175,6 +187,8 @@ SceneWidget::SceneWidget(boost::shared_ptr resourceSys CSMPrefs::get()["3D Scene Input"].update(); CSMPrefs::get()["Tooltips"].update(); } + + connect (&CompositeViewer::get(), SIGNAL (simulationUpdated(double)), this, SLOT (update(double))); } SceneWidget::~SceneWidget() @@ -256,28 +270,43 @@ void SceneWidget::setDefaultAmbient (const osg::Vec4f& colour) void SceneWidget::mousePressEvent (QMouseEvent *event) { - std::string button = mapButton(event); + mMouseMode = mapButton(event); - // TODO placeholders - if (button == "p-navi") - { - } - else if (button == "s-navi") - { - } + mPrevMouseX = event->x(); + mPrevMouseY = event->y(); } void SceneWidget::mouseReleaseEvent (QMouseEvent *event) { - std::string button = mapButton(event); + mMouseMode = ""; +} + +void SceneWidget::mouseMoveEvent (QMouseEvent *event) +{ + mCurrentCamControl->handleMouseMoveEvent(mMouseMode, event->x() - mPrevMouseX, event->y() - mPrevMouseY); - // TODO placeholders - if (button == "p-navi") - { - } - else if (button == "s-navi") - { - } + mPrevMouseX = event->x(); + mPrevMouseY = event->y(); +} + +void SceneWidget::wheelEvent(QWheelEvent *event) +{ + mCurrentCamControl->handleMouseMoveEvent("t-navi", event->delta(), 0); +} + +void SceneWidget::keyPressEvent (QKeyEvent *event) +{ + mCurrentCamControl->handleKeyEvent(event, true); +} + +void SceneWidget::keyReleaseEvent (QKeyEvent *event) +{ + mCurrentCamControl->handleKeyEvent(event, false); +} + +void SceneWidget::update(double dt) +{ + mCurrentCamControl->update(dt); } void SceneWidget::settingChanged (const CSMPrefs::Setting *setting) @@ -288,13 +317,31 @@ void SceneWidget::settingChanged (const CSMPrefs::Setting *setting) void SceneWidget::selectNavigationMode (const std::string& mode) { if (mode=="1st") - mView->setCameraManipulator(new osgGA::FirstPersonManipulator); + { + mCurrentCamControl->setCamera(NULL); + mCurrentCamControl = mFreeCamControl.get(); + mCurrentCamControl->setCamera(getCamera()); + mFreeCamControl->fixUpAxis(osg::Vec3d(0,0,1)); + } else if (mode=="free") - mView->setCameraManipulator(new osgGA::FirstPersonManipulator); + { + mCurrentCamControl->setCamera(NULL); + mCurrentCamControl = mFreeCamControl.get(); + mCurrentCamControl->setCamera(getCamera()); + mFreeCamControl->unfixUpAxis(); + } else if (mode=="orbit") - mView->setCameraManipulator(new osgGA::OrbitManipulator); + { + mCurrentCamControl->setCamera(NULL); + mCurrentCamControl = mOrbitCamControl.get(); + mCurrentCamControl->setCamera(getCamera()); + } else if (mode=="trackball") - mView->setCameraManipulator(new osgGA::TrackballManipulator); + { + mCurrentCamControl->setCamera(NULL); + mCurrentCamControl = mOrbitCamControl.get(); + mCurrentCamControl->setCamera(getCamera()); + } } bool SceneWidget::storeMappingSetting (const CSMPrefs::Setting *setting) diff --git a/apps/opencs/view/render/scenewidget.hpp b/apps/opencs/view/render/scenewidget.hpp index 6870c06ab..43f969aca 100644 --- a/apps/opencs/view/render/scenewidget.hpp +++ b/apps/opencs/view/render/scenewidget.hpp @@ -2,6 +2,7 @@ #define OPENCS_VIEW_SCENEWIDGET_H #include +#include #include #include @@ -39,6 +40,9 @@ namespace CSMPrefs namespace CSVRender { + class CameraController; + class FreeCameraController; + class OrbitCameraController; class Lighting; class RenderWidget : public QWidget @@ -88,6 +92,10 @@ namespace CSVRender virtual void mousePressEvent (QMouseEvent *event); virtual void mouseReleaseEvent (QMouseEvent *event); + virtual void mouseMoveEvent (QMouseEvent *event); + virtual void wheelEvent (QWheelEvent *event); + virtual void keyPressEvent (QKeyEvent *event); + virtual void keyReleaseEvent (QKeyEvent *event); /// \return Is \a key a button mapping setting? (ignored otherwise) virtual bool storeMappingSetting (const CSMPrefs::Setting *setting); @@ -104,8 +112,17 @@ namespace CSVRender LightingNight mLightingNight; LightingBright mLightingBright; + int mPrevMouseX, mPrevMouseY; + std::string mMouseMode; + std::auto_ptr mFreeCamControl; + std::auto_ptr mOrbitCamControl; + CameraController* mCurrentCamControl; + std::map, std::string> mButtonMapping; + public slots: + void update(double dt); + protected slots: virtual void settingChanged (const CSMPrefs::Setting *setting); @@ -139,6 +156,9 @@ namespace CSVRender public slots: void update(); + + signals: + void simulationUpdated(double dt); }; }