Something went wrong. Try again.
OpenMW is an open implementation of the Morrowind engine
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803#include "cameracontroller.hpp"
#include <algorithm>#include <cmath>#include <set>
#include <QWidget>
#include <osg/BoundingBox>#include <osg/Camera>#include <osg/ComputeBoundsVisitor>#include <osg/Group>#include <osg/Math>#include <osg/Matrixd>#include <osg/Quat>#include <osg/Vec3>#include <osg/ref_ptr>
#include <osgUtil/IntersectionVisitor>#include <osgUtil/LineSegmentIntersector>
#include "../../model/prefs/shortcut.hpp"
namespace CSVRender{
/* Camera Controller */
const osg::Vec3d CameraController::WorldUp = osg::Vec3d(0, 0, 1);
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);
CameraController::CameraController(QObject* parent) : QObject(parent) , mActive(false) , mInverted(false) , mCameraSensitivity(1 / 650.f) , mSecondaryMoveMult(50) , mWheelMoveMult(8) , mCamera(nullptr) { }
bool CameraController::isActive() const { return mActive; }
osg::Camera* CameraController::getCamera() const { return mCamera; }
double CameraController::getCameraSensitivity() const { return mCameraSensitivity; }
bool CameraController::getInverted() const { return mInverted; }
double CameraController::getSecondaryMovementMultiplier() const { return mSecondaryMoveMult; }
double CameraController::getWheelMovementMultiplier() const { return mWheelMoveMult; }
void CameraController::setCamera(osg::Camera* camera) { bool wasActive = mActive;
mCamera = camera; mActive = (mCamera != nullptr);
if (mActive != wasActive) { for (std::vector<CSMPrefs::Shortcut*>::iterator it = mShortcuts.begin(); it != mShortcuts.end(); ++it) { CSMPrefs::Shortcut* shortcut = *it; shortcut->enable(mActive); } } }
void CameraController::setCameraSensitivity(double value) { mCameraSensitivity = value; }
void CameraController::setInverted(bool value) { mInverted = value; }
void CameraController::setSecondaryMovementMultiplier(double value) { mSecondaryMoveMult = value; }
void CameraController::setWheelMovementMultiplier(double value) { mWheelMoveMult = value; }
void CameraController::setup(osg::Group* root, unsigned int mask, const osg::Vec3d& up) { // Find World bounds osg::ComputeBoundsVisitor boundsVisitor; osg::BoundingBox& boundingBox = boundsVisitor.getBoundingBox();
boundsVisitor.setTraversalMask(mask); root->accept(boundsVisitor);
if (!boundingBox.valid()) { // Try again without any mask boundsVisitor.reset(); boundsVisitor.setTraversalMask(~0u); root->accept(boundsVisitor);
// Last resort, set a default if (!boundingBox.valid()) { boundingBox.set(-1, -1, -1, 1, 1, 1); } }
// Calculate a good starting position osg::Vec3d minBounds = boundingBox.corner(0) - boundingBox.center(); osg::Vec3d maxBounds = boundingBox.corner(7) - boundingBox.center();
osg::Vec3d camOffset = up * maxBounds > 0 ? maxBounds : minBounds; camOffset *= 2;
osg::Vec3d eye = camOffset + boundingBox.center(); osg::Vec3d center = boundingBox.center();
getCamera()->setViewMatrixAsLookAt(eye, center, up); }
void CameraController::addShortcut(CSMPrefs::Shortcut* shortcut) { mShortcuts.push_back(shortcut); }
/* Free Camera Controller */
FreeCameraController::FreeCameraController(QWidget* widget) : CameraController(widget) , mLockUpright(false) , mModified(false) , mNaviPrimary(false) , mNaviSecondary(false) , mFast(false) , mFastAlternate(false) , mLeft(false) , mRight(false) , mForward(false) , mBackward(false) , mRollLeft(false) , mRollRight(false) , mUp(LocalUp) , mLinSpeed(1000) , mRotSpeed(osg::PI / 2) , mSpeedMult(8) { CSMPrefs::Shortcut* naviPrimaryShortcut = new CSMPrefs::Shortcut("scene-navi-primary", widget); naviPrimaryShortcut->enable(false); connect(naviPrimaryShortcut, qOverload<bool>(&CSMPrefs::Shortcut::activated), this, &FreeCameraController::naviPrimary);
addShortcut(naviPrimaryShortcut);
CSMPrefs::Shortcut* naviSecondaryShortcut = new CSMPrefs::Shortcut("scene-navi-secondary", widget); naviSecondaryShortcut->enable(false); connect(naviSecondaryShortcut, qOverload<bool>(&CSMPrefs::Shortcut::activated), this, &FreeCameraController::naviSecondary);
addShortcut(naviSecondaryShortcut);
CSMPrefs::Shortcut* forwardShortcut = new CSMPrefs::Shortcut("free-forward", "scene-speed-modifier", CSMPrefs::Shortcut::SM_Detach, widget); forwardShortcut->enable(false); connect(forwardShortcut, qOverload<bool>(&CSMPrefs::Shortcut::activated), this, &FreeCameraController::forward); connect(forwardShortcut, qOverload<bool>(&CSMPrefs::Shortcut::secondary), this, &FreeCameraController::alternateFast);
addShortcut(forwardShortcut);
CSMPrefs::Shortcut* leftShortcut = new CSMPrefs::Shortcut("free-left", widget); leftShortcut->enable(false); connect(leftShortcut, qOverload<bool>(&CSMPrefs::Shortcut::activated), this, &FreeCameraController::left);
addShortcut(leftShortcut);
CSMPrefs::Shortcut* backShortcut = new CSMPrefs::Shortcut("free-backward", widget); backShortcut->enable(false); connect(backShortcut, qOverload<bool>(&CSMPrefs::Shortcut::activated), this, &FreeCameraController::backward);
addShortcut(backShortcut);
CSMPrefs::Shortcut* rightShortcut = new CSMPrefs::Shortcut("free-right", widget); rightShortcut->enable(false); connect(rightShortcut, qOverload<bool>(&CSMPrefs::Shortcut::activated), this, &FreeCameraController::right);
addShortcut(rightShortcut);
CSMPrefs::Shortcut* rollLeftShortcut = new CSMPrefs::Shortcut("free-roll-left", widget); rollLeftShortcut->enable(false); connect( rollLeftShortcut, qOverload<bool>(&CSMPrefs::Shortcut::activated), this, &FreeCameraController::rollLeft);
addShortcut(rollLeftShortcut);
CSMPrefs::Shortcut* rollRightShortcut = new CSMPrefs::Shortcut("free-roll-right", widget); rollRightShortcut->enable(false); connect( rollRightShortcut, qOverload<bool>(&CSMPrefs::Shortcut::activated), this, &FreeCameraController::rollRight);
addShortcut(rollRightShortcut);
CSMPrefs::Shortcut* speedModeShortcut = new CSMPrefs::Shortcut("free-speed-mode", widget); speedModeShortcut->enable(false); connect( speedModeShortcut, qOverload<>(&CSMPrefs::Shortcut::activated), this, &FreeCameraController::swapSpeedMode);
addShortcut(speedModeShortcut); }
double FreeCameraController::getLinearSpeed() const { return mLinSpeed; }
double FreeCameraController::getRotationalSpeed() const { return mRotSpeed; }
double FreeCameraController::getSpeedMultiplier() const { return mSpeedMult; }
void FreeCameraController::setLinearSpeed(double value) { mLinSpeed = value; }
void FreeCameraController::setRotationalSpeed(double value) { mRotSpeed = value; }
void FreeCameraController::setSpeedMultiplier(double value) { mSpeedMult = value; }
void FreeCameraController::fixUpAxis(const osg::Vec3d& up) { mLockUpright = true; mUp = up; mModified = true; }
void FreeCameraController::unfixUpAxis() { mLockUpright = false; }
void FreeCameraController::handleMouseMoveEvent(int x, int y) { if (!isActive()) return;
if (mNaviPrimary) { double scalar = getCameraSensitivity() * (getInverted() ? -1.0 : 1.0); yaw(x * scalar); pitch(y * scalar); } else if (mNaviSecondary) { osg::Vec3d movement; movement += LocalLeft * -x * getSecondaryMovementMultiplier(); movement += LocalUp * y * getSecondaryMovementMultiplier();
translate(movement); } }
void FreeCameraController::handleMouseScrollEvent(int x) { if (!isActive()) return;
translate(LocalForward * x * ((mFast ^ mFastAlternate) ? getWheelMovementMultiplier() : 1)); }
void FreeCameraController::update(double dt) { if (!isActive()) return;
double linDist = mLinSpeed * dt; double rotDist = mRotSpeed * dt;
if (mFast ^ mFastAlternate) linDist *= mSpeedMult;
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 (mModified) { stabilize(); mModified = false; }
// Normalize the matrix to counter drift getCamera()->getViewMatrix().orthoNormal(getCamera()->getViewMatrix()); }
void FreeCameraController::yaw(double value) { getCamera()->getViewMatrix() *= osg::Matrixd::rotate(value, LocalUp); mModified = true; }
void FreeCameraController::pitch(double value) { const double constraint = osg::PI / 2 - 0.1;
if (mLockUpright) { osg::Vec3d eye, center, up; getCamera()->getViewMatrixAsLookAt(eye, center, up);
osg::Vec3d forward = center - eye; osg::Vec3d left = up ^ forward;
double pitchAngle = std::acos(up * mUp); if ((mUp ^ up) * left < 0) pitchAngle *= -1;
if (std::abs(pitchAngle + value) > constraint) value = (pitchAngle > 0 ? 1 : -1) * constraint - pitchAngle; }
getCamera()->getViewMatrix() *= osg::Matrixd::rotate(value, LocalLeft); mModified = true; }
void FreeCameraController::roll(double value) { getCamera()->getViewMatrix() *= osg::Matrixd::rotate(value, LocalForward); mModified = true; }
void FreeCameraController::translate(const osg::Vec3d& offset) { getCamera()->getViewMatrix() *= osg::Matrixd::translate(offset); mModified = true; }
void FreeCameraController::stabilize() { osg::Vec3d eye, center, up; getCamera()->getViewMatrixAsLookAt(eye, center, up); getCamera()->setViewMatrixAsLookAt(eye, center, mUp); }
void FreeCameraController::naviPrimary(bool active) { mNaviPrimary = active; }
void FreeCameraController::naviSecondary(bool active) { mNaviSecondary = active; }
void FreeCameraController::forward(bool active) { mForward = active; }
void FreeCameraController::left(bool active) { mLeft = active; }
void FreeCameraController::backward(bool active) { mBackward = active; }
void FreeCameraController::right(bool active) { mRight = active; }
void FreeCameraController::rollLeft(bool active) { mRollLeft = active; }
void FreeCameraController::rollRight(bool active) { mRollRight = active; }
void FreeCameraController::alternateFast(bool active) { mFastAlternate = active; }
void FreeCameraController::swapSpeedMode() { mFast = !mFast; }
/* Orbit Camera Controller */
OrbitCameraController::OrbitCameraController(QWidget* widget) : CameraController(widget) , mInitialized(false) , mNaviPrimary(false) , mNaviSecondary(false) , mFast(false) , mFastAlternate(false) , mLeft(false) , mRight(false) , mUp(false) , mDown(false) , mRollLeft(false) , mRollRight(false) , mPickingMask(~0u) , mCenter(0, 0, 0) , mDistance(0) , mOrbitSpeed(osg::PI / 4) , mOrbitSpeedMult(4) , mConstRoll(false) { CSMPrefs::Shortcut* naviPrimaryShortcut = new CSMPrefs::Shortcut("scene-navi-primary", widget); naviPrimaryShortcut->enable(false); connect(naviPrimaryShortcut, qOverload<bool>(&CSMPrefs::Shortcut::activated), this, &OrbitCameraController::naviPrimary);
addShortcut(naviPrimaryShortcut);
CSMPrefs::Shortcut* naviSecondaryShortcut = new CSMPrefs::Shortcut("scene-navi-secondary", widget); naviSecondaryShortcut->enable(false); connect(naviSecondaryShortcut, qOverload<bool>(&CSMPrefs::Shortcut::activated), this, &OrbitCameraController::naviSecondary);
addShortcut(naviSecondaryShortcut);
CSMPrefs::Shortcut* upShortcut = new CSMPrefs::Shortcut("orbit-up", "scene-speed-modifier", CSMPrefs::Shortcut::SM_Detach, widget); upShortcut->enable(false); connect(upShortcut, qOverload<bool>(&CSMPrefs::Shortcut::activated), this, &OrbitCameraController::up); connect( upShortcut, qOverload<bool>(&CSMPrefs::Shortcut::secondary), this, &OrbitCameraController::alternateFast);
addShortcut(upShortcut);
CSMPrefs::Shortcut* leftShortcut = new CSMPrefs::Shortcut("orbit-left", widget); leftShortcut->enable(false); connect(leftShortcut, qOverload<bool>(&CSMPrefs::Shortcut::activated), this, &OrbitCameraController::left);
addShortcut(leftShortcut);
CSMPrefs::Shortcut* downShortcut = new CSMPrefs::Shortcut("orbit-down", widget); downShortcut->enable(false); connect(downShortcut, qOverload<bool>(&CSMPrefs::Shortcut::activated), this, &OrbitCameraController::down);
addShortcut(downShortcut);
CSMPrefs::Shortcut* rightShortcut = new CSMPrefs::Shortcut("orbit-right", widget); rightShortcut->enable(false); connect(rightShortcut, qOverload<bool>(&CSMPrefs::Shortcut::activated), this, &OrbitCameraController::right);
addShortcut(rightShortcut);
CSMPrefs::Shortcut* rollLeftShortcut = new CSMPrefs::Shortcut("orbit-roll-left", widget); rollLeftShortcut->enable(false); connect( rollLeftShortcut, qOverload<bool>(&CSMPrefs::Shortcut::activated), this, &OrbitCameraController::rollLeft);
addShortcut(rollLeftShortcut);
CSMPrefs::Shortcut* rollRightShortcut = new CSMPrefs::Shortcut("orbit-roll-right", widget); rollRightShortcut->enable(false); connect(rollRightShortcut, qOverload<bool>(&CSMPrefs::Shortcut::activated), this, &OrbitCameraController::rollRight);
addShortcut(rollRightShortcut);
CSMPrefs::Shortcut* speedModeShortcut = new CSMPrefs::Shortcut("orbit-speed-mode", widget); speedModeShortcut->enable(false); connect(speedModeShortcut, qOverload<>(&CSMPrefs::Shortcut::activated), this, &OrbitCameraController::swapSpeedMode);
addShortcut(speedModeShortcut); }
osg::Vec3d OrbitCameraController::getCenter() const { return mCenter; }
double OrbitCameraController::getOrbitSpeed() const { return mOrbitSpeed; }
double OrbitCameraController::getOrbitSpeedMultiplier() const { return mOrbitSpeedMult; }
unsigned int OrbitCameraController::getPickingMask() const { return mPickingMask; }
void OrbitCameraController::setCenter(const osg::Vec3d& value) { osg::Vec3d eye, center, up; getCamera()->getViewMatrixAsLookAt(eye, center, up);
mCenter = value; mDistance = (eye - mCenter).length();
getCamera()->setViewMatrixAsLookAt(eye, mCenter, up);
mInitialized = true; }
void OrbitCameraController::setOrbitSpeed(double value) { mOrbitSpeed = value; }
void OrbitCameraController::setOrbitSpeedMultiplier(double value) { mOrbitSpeedMult = value; }
void OrbitCameraController::setPickingMask(unsigned int value) { mPickingMask = value; }
void OrbitCameraController::handleMouseMoveEvent(int x, int y) { if (!isActive()) return;
if (!mInitialized) initialize();
if (mNaviPrimary) { double scalar = getCameraSensitivity() * (getInverted() ? -1.0 : 1.0); rotateHorizontal(x * scalar); rotateVertical(-y * scalar); } else if (mNaviSecondary) { osg::Vec3d movement; movement += LocalLeft * x * getSecondaryMovementMultiplier(); movement += LocalUp * -y * getSecondaryMovementMultiplier();
translate(movement); } }
void OrbitCameraController::handleMouseScrollEvent(int x) { if (!isActive()) return;
zoom(-x * ((mFast ^ mFastAlternate) ? getWheelMovementMultiplier() : 1)); }
void OrbitCameraController::update(double dt) { if (!isActive()) return;
if (!mInitialized) initialize();
double rotDist = mOrbitSpeed * dt;
if (mFast ^ mFastAlternate) rotDist *= mOrbitSpeedMult;
if (mLeft) rotateHorizontal(-rotDist); if (mRight) rotateHorizontal(rotDist); if (mUp) rotateVertical(rotDist); if (mDown) rotateVertical(-rotDist);
if (mRollLeft) roll(-rotDist); if (mRollRight) roll(rotDist);
// Normalize the matrix to counter drift getCamera()->getViewMatrix().orthoNormal(getCamera()->getViewMatrix()); }
void OrbitCameraController::reset() { mInitialized = false; }
void OrbitCameraController::initialize() { const int defaultStartDistance = 10000.f;
// Try to intelligently pick focus object osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector( new osgUtil::LineSegmentIntersector(osgUtil::Intersector::PROJECTION, osg::Vec3d(0, 0, 0), LocalForward));
intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::LIMIT_NEAREST); osgUtil::IntersectionVisitor visitor(intersector);
visitor.setTraversalMask(mPickingMask);
getCamera()->accept(visitor);
osg::Vec3d eye, center, up; getCamera()->getViewMatrixAsLookAt(eye, center, up, defaultStartDistance);
if (intersector->getIntersections().begin() != intersector->getIntersections().end()) { mCenter = intersector->getIntersections().begin()->getWorldIntersectPoint(); mDistance = (eye - mCenter).length(); } else { mCenter = center; mDistance = defaultStartDistance; }
mInitialized = true; }
void OrbitCameraController::setConstRoll(bool enabled) { mConstRoll = enabled; }
void OrbitCameraController::rotateHorizontal(double value) { osg::Vec3d eye, center, up; getCamera()->getViewMatrixAsLookAt(eye, center, up); osg::Vec3d absoluteUp = osg::Vec3(0, 0, 1);
osg::Quat rotation = osg::Quat(value, mConstRoll ? absoluteUp : up); osg::Vec3d oldOffset = eye - mCenter; osg::Vec3d newOffset = rotation * oldOffset;
if (mConstRoll) up = rotation * up;
getCamera()->setViewMatrixAsLookAt(mCenter + newOffset, mCenter, up); }
void OrbitCameraController::rotateVertical(double value) { osg::Vec3d eye, center, up; getCamera()->getViewMatrixAsLookAt(eye, center, up);
osg::Vec3d forward = center - eye; osg::Vec3d axis = up ^ forward;
osg::Quat rotation = osg::Quat(value, axis); osg::Vec3d oldOffset = eye - mCenter; osg::Vec3d newOffset = rotation * oldOffset;
if (mConstRoll) up = rotation * up;
getCamera()->setViewMatrixAsLookAt(mCenter + newOffset, mCenter, up); }
void OrbitCameraController::roll(double value) { getCamera()->getViewMatrix() *= osg::Matrixd::rotate(value, LocalForward); }
void OrbitCameraController::translate(const osg::Vec3d& offset) { osg::Vec3d eye, center, up; getCamera()->getViewMatrixAsLookAt(eye, center, up);
osg::Vec3d newOffset = getCamera()->getViewMatrix().getRotate().inverse() * offset; mCenter += newOffset; eye += newOffset;
getCamera()->setViewMatrixAsLookAt(eye, mCenter, up); }
void OrbitCameraController::zoom(double value) { mDistance = std::max(10., mDistance + value);
osg::Vec3d eye, center, up; getCamera()->getViewMatrixAsLookAt(eye, center, up, 1.f);
osg::Vec3d offset = (eye - center) * mDistance;
getCamera()->setViewMatrixAsLookAt(mCenter + offset, mCenter, up); }
void OrbitCameraController::naviPrimary(bool active) { mNaviPrimary = active; }
void OrbitCameraController::naviSecondary(bool active) { mNaviSecondary = active; }
void OrbitCameraController::up(bool active) { mUp = active; }
void OrbitCameraController::left(bool active) { mLeft = active; }
void OrbitCameraController::down(bool active) { mDown = active; }
void OrbitCameraController::right(bool active) { mRight = active; }
void OrbitCameraController::rollLeft(bool active) { if (isActive()) mRollLeft = active; }
void OrbitCameraController::rollRight(bool active) { mRollRight = active; }
void OrbitCameraController::alternateFast(bool active) { mFastAlternate = active; }
void OrbitCameraController::swapSpeedMode() { mFast = !mFast; }}