Files
OpenSpace/modules/touch/src/TouchInteraction.cpp

308 lines
11 KiB
C++

/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2017 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include <modules/touch/include/TouchInteraction.h>
#include <openspace/interaction/interactionmode.h>
#include <openspace/engine/openspaceengine.h>
#include <openspace/query/query.h>
#include <openspace/rendering/renderengine.h>
#include <openspace/scene/scenegraphnode.h>
#include <openspace/util/time.h>
#include <openspace/util/keys.h>
#include <ghoul/logging/logmanager.h>
#include <glm/gtx/quaternion.hpp>
#ifdef OPENSPACE_MODULE_GLOBEBROWSING_ENABLED
#include <modules/globebrowsing/globes/renderableglobe.h>
#include <modules/globebrowsing/globes/chunkedlodglobe.h>
#include <modules/globebrowsing/geometry/geodetic2.h>
#endif
#include <glm/ext.hpp>
namespace {
const std::string _loggerCat = "TouchInteraction";
}
using namespace TUIO;
using namespace openspace;
TouchInteraction::TouchInteraction()
: properties::PropertyOwner("TouchInteraction"),
_origin("origin", "Origin", ""), _camera{ OsEng.interactionHandler().camera() },
_baseSensitivity{ 0.1 }, _baseFriction{ 0.02 },
_vel{ 0.0, glm::dvec2(0.0), glm::dvec2(0.0), 0.0, 0.0 },
_friction{ _baseFriction, _baseFriction/2.0, _baseFriction, _baseFriction, _baseFriction },
_sensitivity{ 2.0, 0.1, 0.1, 0.1, 0.3 }, _minHeightFromSurface{ 6.6 * 1000000.0 }
{
_origin.onChange([this]() {
SceneGraphNode* node = sceneGraphNode(_origin.value());
if (!node) {
LWARNING("Could not find a node in scenegraph called '" << _origin.value() << "'");
return;
}
setFocusNode(node);
});
}
TouchInteraction::~TouchInteraction() { }
void TouchInteraction::update(const std::vector<TuioCursor>& list, std::vector<Point>& lastProcessed) {
TuioCursor cursor = list.at(0);
glm::dvec3 centroid;
_interactionMode = interpret(list, lastProcessed);
if (_interactionMode != ROT) {
centroid.x = std::accumulate(list.begin(), list.end(), 0.0f, [](double x, const TuioCursor& c) { return x + c.getX(); }) / list.size();
centroid.y = std::accumulate(list.begin(), list.end(), 0.0f, [](double y, const TuioCursor& c) { return y + c.getY(); }) / list.size();
}
switch (_interactionMode) {
case ROT: { // add rotation velocity
_vel.globalRot += glm::dvec2(cursor.getXSpeed(), cursor.getYSpeed()) * _sensitivity.globalRot;
break;
}
case PINCH: { // add zooming velocity
double distance = std::accumulate(list.begin(), list.end(), 0.0, [&](double d, const TuioCursor& c) {
return d + sqrt(pow(c.getX() - centroid.x, 2) + pow(c.getY() - centroid.y, 2));
});
double lastDistance = std::accumulate(lastProcessed.begin(), lastProcessed.end(), 0.0f, [&](float d, const Point& p) {
return d + sqrt(pow(p.second.getX() - centroid.x, 2) + pow(p.second.getY() - centroid.y, 2));
});
double zoomFactor = (distance - lastDistance) * glm::distance(_camera->positionVec3(), _camera->focusPositionVec3());
_vel.zoom += zoomFactor * _sensitivity.zoom;
break;
}
case PAN: { // add local rotation velocity
_vel.localRot += glm::dvec2(cursor.getXSpeed(), cursor.getYSpeed()) * _sensitivity.localRot;
break;
}
case ROLL: { // add global roll rotation velocity
double rollFactor = std::accumulate(list.begin(), list.end(), 0.0, [](double s, const TuioCursor& c) {
return s + c.getXSpeed();
});
_vel.localRoll += rollFactor * _sensitivity.localRoll;
break;
}
case PICK: { // pick something in the scene as focus node
break;
}
default:
LINFO("Couldn't interpret touch input" << "\n");
}
}
int TouchInteraction::interpret(const std::vector<TuioCursor>& list, const std::vector<Point>& lastProcessed) {
double dist = 0;
double lastDist = 0;
TuioCursor cursor = list.at(0);
for (const TuioCursor& c : list) {
dist += glm::length(glm::dvec2(c.getX(), c.getY()) - glm::dvec2(cursor.getX(), cursor.getY()));
cursor = c;
}
TuioPoint point = lastProcessed.at(0).second;
for (const Point& p : lastProcessed) {
dist += glm::length(glm::dvec2(p.second.getX(), p.second.getY()) - glm::dvec2(point.getX(), point.getY()));
point = p.second;
}
if (list.size() == 1)
return ROT;
else {
if (std::abs(dist - lastDist) / list.size() < 0.1 && list.size() == 2)
return PAN;
//else if (list.size() == 3)
//return ROLL;
else
return PINCH;
}
}
void TouchInteraction::step(double dt) {
using namespace glm;
if (_focusNode) {
// Create variables from current state
dvec3 camPos = _camera->positionVec3();
// Follow the focus node
dquat totRot = _camera->rotationQuaternion();
dvec3 centerPos = _focusNode->worldPosition();
dvec3 focusNodeDiff = centerPos - _previousFocusNodePosition;
_previousFocusNodePosition = centerPos;
camPos += focusNodeDiff;
dvec3 directionToCenter = normalize(centerPos - camPos);
dvec3 centerToCamera = camPos - centerPos;
dvec3 lookUp = _camera->lookUpVectorWorldSpace();
dvec3 camDirection = _camera->viewDirectionWorldSpace();
// Make a representation of the rotation quaternion with local and global rotations
dmat4 lookAtMat = lookAt(
dvec3(0, 0, 0),
directionToCenter,
normalize(camDirection + lookUp)); // To avoid problem with lookup in up direction
dquat globalCamRot = normalize(quat_cast(inverse(lookAtMat)));
dquat localCamRot = inverse(globalCamRot) * totRot;
double boundingSphere = _focusNode->boundingSphere().lengthf();
double minHeightAboveBoundingSphere = 1;
dvec3 centerToBoundingSphere;
{ // Roll
dquat camRollRot = angleAxis(_vel.localRoll*dt, dvec3(0.0, 0.0, 1.0));
localCamRot = localCamRot * camRollRot;
}
{ // Panning (local rotation)
dvec3 eulerAngles(-_vel.localRot.y*dt, -_vel.localRot.x*dt, 0);
dquat rotationDiff = dquat(eulerAngles);
localCamRot = localCamRot * rotationDiff;
}
{ // Orbit (global rotation)
dvec3 eulerAngles(_vel.globalRot.y*dt, _vel.globalRot.x*dt, 0);
dquat rotationDiffCamSpace = dquat(eulerAngles);
dquat rotationDiffWorldSpace = globalCamRot * rotationDiffCamSpace * inverse(globalCamRot);
dvec3 rotationDiffVec3 = centerToCamera * rotationDiffWorldSpace - centerToCamera;
camPos += rotationDiffVec3;
dvec3 centerToCamera = camPos - centerPos;
directionToCenter = normalize(-centerToCamera);
dvec3 lookUpWhenFacingCenter = globalCamRot * dvec3(_camera->lookUpVectorCameraSpace());
dmat4 lookAtMat = lookAt(
dvec3(0, 0, 0),
directionToCenter,
lookUpWhenFacingCenter);
globalCamRot = normalize(quat_cast(inverse(lookAtMat)));
}
{ // Zooming
centerToBoundingSphere = -directionToCenter * boundingSphere;
dvec3 centerToCamera = camPos - centerPos;
if (length(_vel.zoom*dt) < length(centerToCamera - centerToBoundingSphere) && length(centerToCamera + directionToCenter*_vel.zoom*dt) > length(centerToBoundingSphere)) // should get boundingsphere from focusnode
camPos += directionToCenter*_vel.zoom*dt;
else
_vel.zoom = 0.0;
}
{ // Roll around sphere normal
dquat camRollRot = angleAxis(_vel.globalRoll*dt, -directionToCenter);
globalCamRot = camRollRot * globalCamRot;
}
{ // Push up to surface
dvec3 sphereSurfaceToCamera = camPos - (centerPos + centerToBoundingSphere);
double distFromSphereSurfaceToCamera = length(sphereSurfaceToCamera);
camPos += -directionToCenter * max(minHeightAboveBoundingSphere - distFromSphereSurfaceToCamera, 0.0);
}
std::shared_ptr<interaction::GlobeBrowsingInteractionMode> gbim =
std::dynamic_pointer_cast<interaction::GlobeBrowsingInteractionMode> (OsEng.interactionHandler().interactionmode());
if (gbim) {
double dist = length(camPos - (centerPos + centerToBoundingSphere));
configSensitivities(dist);
}
decelerate();
// Update the camera state
_camera->setPositionVec3(camPos);
_camera->setRotation(globalCamRot * localCamRot);
}
else {
setFocusNode(OsEng.interactionHandler().focusNode());
std::cout << "focus node set\n";
}
}
void TouchInteraction::configSensitivities(double dist) {
// Configurates sensitivities to appropriate values when the camera is close to the focus node.
double close = 4.6 * 1000000;
if (dist < close) {
_sensitivity.zoom = 2.0 * std::max(dist, 100.0)/close;
_sensitivity.globalRot = 0.1 * std::max(dist, 100.0) /close;
//_sensitivity.localRot = 0.1;
//_sensitivity.globalRoll = 0.1;
//_sensitivity.localRoll = 0.1;
}
else {
_sensitivity.zoom = 2.0;
_sensitivity.globalRot = 0.1;
_sensitivity.localRot = 0.1;
_sensitivity.globalRoll = 0.1;
_sensitivity.localRoll = 0.3;
}
}
void TouchInteraction::decelerate() {
_vel.zoom *= (1 - _friction.zoom);
_vel.globalRot *= (1 - _friction.globalRot);
_vel.localRot *= (1 - _friction.localRot);
_vel.globalRoll *= (1 - _friction.globalRoll);
_vel.localRoll *= (1 - _friction.localRoll);
}
// Getters
Camera* TouchInteraction::getCamera() {
return _camera;
}
SceneGraphNode* TouchInteraction::getFocusNode() {
return _focusNode;
}
double TouchInteraction::getFriction() {
return _baseFriction;
}
double TouchInteraction::getSensitivity() {
return _baseSensitivity;
}
// Setters
void TouchInteraction::setCamera(Camera* camera) {
_camera = camera;
}
void TouchInteraction::setFocusNode(SceneGraphNode* focusNode) {
_focusNode = focusNode;
if (_focusNode != nullptr) {
_previousFocusNodePosition = _focusNode->worldPosition();
_previousFocusNodeRotation = glm::quat_cast(_focusNode->worldRotationMatrix());
}
}
void TouchInteraction::setFriction(double friction) {
_baseFriction = std::max(friction, 0.0);
}
void TouchInteraction::setSensitivity(double sensitivity) {
_baseSensitivity = sensitivity;
}