diff --git a/include/openspace/interaction/controller.h b/include/openspace/interaction/controller.h index a98b9dd557..3a25ff2174 100644 --- a/include/openspace/interaction/controller.h +++ b/include/openspace/interaction/controller.h @@ -25,6 +25,11 @@ #ifndef __CONTROLLER_H__ #define __CONTROLLER_H__ +#include + +#include +#include + namespace openspace { namespace interaction { @@ -32,20 +37,98 @@ class InteractionHandler; class Controller { public: + Controller() : + _handler(nullptr) + {} + void setHandler(InteractionHandler* handler) { _handler = handler; } protected: + SceneGraphNode* focusNode() const { + assert(_handler); + return _handler->_focusNode; + } + + Camera* camera() const { + assert(_handler); + return _handler->_camera; + } + + + double deltaTime() const { + assert(_handler); + return _handler->_deltaTime; + } + void orbitDelta(const glm::quat& rotation) { + assert(_handler); + _handler->lockControls(); + + // the camera position + psc relative = _handler->_camera->position(); + + // should be changed to something more dynamic =) + psc origin; + if (_handler->_focusNode) { + origin = _handler->_focusNode->worldPosition(); + } + + psc relative_origin_coordinate = relative - origin; + //glm::mat4 rotation_matrix = glm::mat4_cast(glm::inverse(rotation)); + //relative_origin_coordinate = relative_origin_coordinate.vec4() * glm::inverse(rotation); + relative_origin_coordinate = glm::inverse(rotation) * relative_origin_coordinate.vec4(); + relative = relative_origin_coordinate + origin; + + _handler->_camera->setPosition(relative); + //camera_->rotate(rotation); + //camera_->setRotation(glm::mat4_cast(rotation)); + + glm::mat4 la = glm::lookAt(_handler->_camera->position().vec3(), origin.vec3(), glm::rotate(rotation, _handler->_camera->lookUpVector())); + _handler->_camera->setRotation(la); + //camera_->setLookUpVector(); + + _handler->unlockControls(); } + void rotateDelta(const glm::quat& rotation) { + assert(_handler); + _handler->lockControls(); + _handler->_camera->rotate(rotation); + _handler->unlockControls(); } + void distanceDelta(const PowerScaledScalar& distance) { + assert(_handler); + _handler->lockControls(); + + psc relative = _handler->_camera->position(); + const psc origin = (_handler->_focusNode) ? _handler->_focusNode->worldPosition() : psc(); + + psc relative_origin_coordinate = relative - origin; + const glm::vec3 dir(relative_origin_coordinate.direction()); + glm:: vec3 newdir = dir * distance[0]; + relative_origin_coordinate = newdir; + relative_origin_coordinate[3] = distance[1]; + relative = relative + relative_origin_coordinate; + + relative_origin_coordinate = relative - origin; + newdir = relative_origin_coordinate.direction(); + + // update only if on the same side of the origin + if(glm::angle(newdir, dir) < 90.0f) + _handler->_camera->setPosition(relative); + + _handler->unlockControls(); } + void lookAt(const glm::quat& rotation) { + assert(_handler); } + void setRotation(const glm::quat& rotation) { + assert(_handler); } private: diff --git a/include/openspace/interaction/interactionhandler.h b/include/openspace/interaction/interactionhandler.h index 26f15ed6c7..821bdf6003 100644 --- a/include/openspace/interaction/interactionhandler.h +++ b/include/openspace/interaction/interactionhandler.h @@ -110,6 +110,8 @@ public: } private: + friend class Controller; + InteractionHandler(const InteractionHandler&) = delete; InteractionHandler& operator=(const InteractionHandler&) = delete; InteractionHandler(InteractionHandler&&) = delete; diff --git a/include/openspace/interaction/mousecontroller.h b/include/openspace/interaction/mousecontroller.h index ebdd455e0e..fa66b18f40 100644 --- a/include/openspace/interaction/mousecontroller.h +++ b/include/openspace/interaction/mousecontroller.h @@ -29,14 +29,130 @@ #include +#include + namespace openspace { namespace interaction { class MouseController : public Controller { public: + MouseController() + : _lastTrackballPos(0.f) + , _isMouseBeingPressedAndHeld(false) + {} + virtual void button(MouseAction action, MouseButton button) = 0; virtual void move(float x, float y) = 0; - virtual void scrollWheel(float z) = 0; + virtual void scrollWheel(int pos) = 0; + +protected: + glm::vec3 _lastTrackballPos; + bool _isMouseBeingPressedAndHeld; + + glm::vec3 mapToTrackball(glm::vec2 mousePos) { + const float RADIUS = 0.5; // Sphere radius + glm::vec3 out = glm::vec3(mousePos.x-0.5, -1.0*(mousePos.y-0.5), 0); + + // Mapping according to Holroyds trackball + // Piece-wise sphere + hyperbolic sheet + if (out.x*out.x + out.y*out.y <= RADIUS*RADIUS/2.0) { + //Spherical Region + out.z = RADIUS*RADIUS - (out.x*out.x + out.y*out.y); + out.z = out.z > 0.0 ? sqrtf(out.z) : 0.0; + } else { //Hyperbolic Region - for smooth z values + out.z = (RADIUS*RADIUS)/(2.0*sqrt(out.x*out.x + out.y*out.y)); + } + + return glm::normalize(out); + } + + glm::vec3 mapToCamera(glm::vec3 trackballPos) { + // return glm::vec3((sgct::Engine::instance()->getActiveViewMatrix() * glm::vec4(trackballPos,0))); + + //Get x,y,z axis vectors of current camera view + glm::vec3 currentViewYaxis = glm::normalize(camera()->lookUpVector()); + psc viewDir = camera()->position() - focusNode()->worldPosition(); + glm::vec3 currentViewZaxis = glm::normalize(viewDir.vec3()); + glm::vec3 currentViewXaxis = glm::normalize(glm::cross(currentViewYaxis, currentViewZaxis)); + + //mapping to camera co-ordinate + currentViewXaxis*=trackballPos.x; + currentViewYaxis*=trackballPos.y; + currentViewZaxis*=trackballPos.z; + return (currentViewXaxis + currentViewYaxis + currentViewZaxis); + } + + void trackballRotate(int x, int y) { + // Normalize mouse coordinates to [0,1] + float width = sgct::Engine::instance()->getActiveXResolution(); + float height = sgct::Engine::instance()->getActiveYResolution(); + glm::vec2 mousePos = glm::vec2((float)x/width, (float)y/height); + + mousePos = glm::clamp(mousePos, -0.5, 1.5); // Ugly fix #1: Camera position becomes NaN on mouse values outside [-0.5, 1.5] + //mousePos[1] = 0.5; // Ugly fix #2: Tempoarily only allow rotation around y + + glm::vec3 curTrackballPos = mapToTrackball(mousePos); + // LDEBUG(mousePos.x << ", " << mousePos.y << " = " << curTrackballPos.x << ", " << curTrackballPos.y << ", " << curTrackballPos.z); + + // Disable movement on the first click for extra smoothness + if (!_isMouseBeingPressedAndHeld) { + _lastTrackballPos = curTrackballPos; + _isMouseBeingPressedAndHeld = true; + } + + if (curTrackballPos != _lastTrackballPos) { + // calculate rotation angle (in radians) + float rotationAngle = glm::angle(curTrackballPos, _lastTrackballPos); + rotationAngle *= deltaTime() * 100.0f; + + // Map trackballpos to camera + // glm::vec3 trackballMappedToCamera = mapToCamera(_lastTrackballPos - curTrackballPos); + // psc currentCamPos = camera_->getPosition(); + // glm::vec3 nextCamPos = currentCamPos.getVec3f() + trackballMappedToCamera; + // glm::vec3 rotationAxis = glm::cross(currentCamPos.getVec3f(), nextCamPos); + + glm::vec3 rotationAxis = glm::cross(_lastTrackballPos, curTrackballPos); + rotationAxis = glm::normalize(rotationAxis); + glm::quat quaternion = glm::angleAxis(rotationAngle, rotationAxis); + + // Apply quaternion to camera + orbitDelta(quaternion); + + _lastTrackballPos = curTrackballPos; + } + } +}; + +class OrbitMouseController : public MouseController { +public: + void button(MouseAction action, MouseButton button) { + if (button == MouseButton::Left && action == MouseAction::Press) + _leftMouseButtonDown = true; + else if (button == MouseButton::Left && action == MouseAction::Release) { + _leftMouseButtonDown = false; + _isMouseBeingPressedAndHeld = false; + } + } + + void move(float x, float y) { + if (_leftMouseButtonDown) + trackballRotate(x,y); + } + + void scrollWheel(int pos) { + const double speed = 4.75; + const double dt = deltaTime(); + if (pos < 0) { + PowerScaledScalar dist(speed * dt, 0.0); + distanceDelta(dist); + } else if(pos > 0) { + PowerScaledScalar dist(-speed * dt, 0.0); + distanceDelta(dist); + } + } + +protected: + bool _leftMouseButtonDown; }; } // namespace interaction