Merge branch 'master' into feature/AALines2

This commit is contained in:
Jonathas Costa
2020-01-22 12:41:28 -05:00
25 changed files with 1218 additions and 831 deletions

View File

@@ -27,6 +27,7 @@
#include <openspace/util/keys.h>
#include <openspace/util/mouse.h>
#include <openspace/util/touch.h>
#include <functional>
#include <vector>
@@ -53,6 +54,10 @@ std::vector<std::function<bool(MouseButton, MouseAction, KeyModifier)>>& gMouseB
std::vector<std::function<void(double, double)>>& gMousePosition();
std::vector<std::function<bool(double, double)>>& gMouseScrollWheel();
std::vector<std::function<bool(TouchInput)>>& gTouchDetected();
std::vector<std::function<bool(TouchInput)>>& gTouchUpdated();
std::vector<std::function<void(TouchInput)>>& gTouchExit();
} // namespace detail
namespace callback {
@@ -76,7 +81,12 @@ static std::vector<std::function<void(double, double)>>& mousePosition =
detail::gMousePosition();
static std::vector<std::function<bool(double, double)>>& mouseScrollWheel =
detail::gMouseScrollWheel();
static std::vector<std::function<bool(TouchInput)>>& touchDetected =
detail::gTouchDetected();
static std::vector<std::function<bool(TouchInput)>>& touchUpdated =
detail::gTouchUpdated();
static std::vector<std::function<void(TouchInput)>>& touchExit =
detail::gTouchExit();
/**
* If the framerate becomes slow, Chromium Embedded Framework (used in Web Browser Module)

View File

@@ -28,6 +28,7 @@
#include <openspace/properties/stringproperty.h>
#include <openspace/util/keys.h>
#include <openspace/util/mouse.h>
#include <openspace/util/touch.h>
#include <openspace/util/versionchecker.h>
#include <ghoul/glm.h>
#include <memory>
@@ -80,7 +81,9 @@ public:
void mouseButtonCallback(MouseButton button, MouseAction action, KeyModifier mods);
void mousePositionCallback(double x, double y);
void mouseScrollWheelCallback(double posX, double posY);
void externalControlCallback(const char* receivedChars, int size, int clientId);
void touchDetectionCallback(TouchInput input);
void touchUpdateCallback(TouchInput input);
void touchExitCallback(TouchInput input);
std::vector<char> encode();
void decode(std::vector<char> data);
@@ -105,7 +108,6 @@ private:
void loadFonts();
void runGlobalCustomizationScripts();
void configureLogging();
std::unique_ptr<Scene> _scene;
std::unique_ptr<AssetManager> _assetManager;

View File

@@ -99,7 +99,6 @@ public:
// Right now this function returns the actual combined matrix which makes some
// of the old calls to the function wrong..
const glm::dmat4& combinedViewMatrix() const;
const glm::dmat4& combinedViewMatrixNoScale() const;
void invalidateCache();

View File

@@ -0,0 +1,88 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2020 *
* *
* 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. *
****************************************************************************************/
#ifndef __OPENSPACE_CORE___TOUCH___H__
#define __OPENSPACE_CORE___TOUCH___H__
#include <glm/detail/type_vec2.hpp>
#include <cstdint>
#include <deque>
namespace openspace {
struct TouchInput {
TouchInput(size_t touchDeviceId, size_t fingerId, float x, float y, double timestamp);
glm::vec2 screenCoordinates(glm::vec2 resolution) const;
glm::vec2 currentWindowCoordinates() const;
bool isMoving() const;
float distanceToPos(float otherX, float otherY) const;
float angleToPos(float otherX, float otherY) const;
size_t touchDeviceId;
size_t fingerId;
float x;
float y;
float dx = 0.f; // movement in x direction since last touch input
float dy = 0.f; // movement in y direction since last touch input
double timestamp; // timestamp in seconds from global touch initialization
};
class TouchInputHolder {
public:
TouchInputHolder(TouchInput input);
// tryAddInput:
// Succeeds upon a different input than last.
// Fails upon a too similar input as last.
bool tryAddInput(TouchInput input);
void clearInputs();
bool holdsInput(const TouchInput &input) const;
size_t touchDeviceId() const;
size_t fingerId() const;
float speedX() const;
float speedY() const;
bool isMoving() const;
float gestureDistance() const;
double gestureTime() const;
size_t numInputs() const;
const TouchInput& latestInput() const;
const std::deque<TouchInput>& peekInputs() const;
private:
//A deque of recorded inputs. Adding newer points to the front of the queue
std::deque<TouchInput> _inputs;
size_t _touchDeviceId;
size_t _fingerId;
};
} // namespace openspace
#endif // __OPENSPACE_CORE___TOUCH___H__

View File

@@ -25,8 +25,8 @@
#ifndef __OPENSPACE_MODULE_TOUCH___DIRECTINPUT_SOLVER___H__
#define __OPENSPACE_MODULE_TOUCH___DIRECTINPUT_SOLVER___H__
#include <openspace/util/touch.h>
#include <modules/touch/ext/levmarq.h>
#include <modules/touch/ext/libTUIO11/TUIO/TuioCursor.h>
#include <vector>
@@ -35,23 +35,35 @@ namespace openspace {
class Camera;
class SceneGraphNode;
/**
* The DirectInputSolver is used to minimize the L2 error of touch input
* to 3D camera position. It uses the levmarq algorithm in order to do this.
* */
class DirectInputSolver {
public:
// Stores the selected node, the cursor ID as well as the surface coordinates the
// cursor touched
struct SelectedBody {
long id;
size_t id;
SceneGraphNode* node;
glm::dvec3 coordinates;
};
DirectInputSolver();
bool solve(const std::vector<TUIO::TuioCursor>& list,
/**
* Returns true if the error could be minimized within certain bounds.
* If the error is found to be outside the bounds after a certain amount of
* iterations, this function fails.
* */
bool solve(const std::vector<TouchInputHolder>& list,
const std::vector<SelectedBody>& selectedBodies,
std::vector<double>* calculatedValues, const Camera& camera);
int getNDof() const;
const LMstat& getLevMarqStat();
int nDof() const;
const LMstat& levMarqStat();
void setLevMarqVerbosity(bool verbose);
private:

View File

@@ -28,8 +28,6 @@
#include <openspace/properties/propertyowner.h>
#include <modules/touch/include/directinputsolver.h>
#include <modules/touch/include/tuioear.h>
#include <openspace/properties/scalar/boolproperty.h>
#include <openspace/properties/scalar/floatproperty.h>
#include <openspace/properties/scalar/doubleproperty.h>
@@ -37,7 +35,7 @@
#include <openspace/properties/stringproperty.h>
#include <openspace/properties/vector/ivec2property.h>
#include <openspace/properties/vector/vec4property.h>
#include <chrono>
#include <memory>
//#define TOUCH_DEBUG_PROPERTIES
@@ -66,8 +64,6 @@ private:
class TouchInteraction : public properties::PropertyOwner {
public:
using Point = std::pair<int, TUIO::TuioPoint>;
TouchInteraction();
// for interpretInteraction()
@@ -95,15 +91,13 @@ public:
* 8 Evaluate if directControl should be called next frame- true if all contact points
* select the same node and said node is larger than _nodeRadiusThreshold
*/
void updateStateFromInput(const std::vector<TUIO::TuioCursor>& list,
std::vector<Point>& lastProcessed);
void updateStateFromInput(const std::vector<TouchInputHolder>& list,
std::vector<TouchInput>& lastProcessed);
// Calculates the new camera state with velocities and time since last frame
void step(double dt);
// Used to save LMA data for one frame if the user chose to
void unitTest();
// Called each frame we have no new input, used to reset data
void resetAfterInput();
@@ -120,37 +114,32 @@ public:
void setCamera(Camera* camera);
private:
/* Returns true if the clicked position contains WebGui content and the event will
* be parsed to the webbrowser
*/
bool webContent(const std::vector<TUIO::TuioCursor>& list);
/* Returns true if we have the GUI window open. If so, emulates the incoming touch
* input to a mouse such that we can interact with the GUI
*/
bool guiMode(const std::vector<TUIO::TuioCursor>& list);
bool isGuiMode(glm::dvec2 screenPosition, size_t numFingers);
/* Function that calculates the new camera state such that it minimizes the L2 error
* in screenspace
* between contact points and surface coordinates projected to clip space using LMA
*/
void directControl(const std::vector<TUIO::TuioCursor>& list);
void directControl(const std::vector<TouchInputHolder>& list);
/* Traces each contact point into the scene as a ray
* if the ray hits a node, save the id, node and surface coordinates the cursor hit
* in the list _selected
*/
void findSelectedNode(const std::vector<TUIO::TuioCursor>& list);
void findSelectedNode(const std::vector<TouchInputHolder>& list);
/* Returns an int (ROT = 0, PINCH, PAN, ROLL, PICK) for what interaction to be used,
* depending on what input was gotten
*/
int interpretInteraction(const std::vector<TUIO::TuioCursor>& list,
const std::vector<Point>& lastProcessed);
int interpretInteraction(const std::vector<TouchInputHolder>& list,
const std::vector<TouchInput>& lastProcessed);
// Compute new velocity according to the interpreted action
void computeVelocities(const std::vector<TUIO::TuioCursor>& list,
const std::vector<Point>& lastProcessed);
void computeVelocities(const std::vector<TouchInputHolder>& list,
const std::vector<TouchInput>& lastProcessed);
//Compute velocity based on double-tap for zooming
double computeTapZoomDistance(double zoomGain);
@@ -158,11 +147,6 @@ private:
//Compute coefficient for velocity decay to be applied in decceleration
double computeConstTimeDecayCoefficient(double velocity);
//Compute coefficient of decay based on current frametime; if frametime has been
// longer than usual then multiple decay steps may be applied to keep the decay
// relative to user time
double computeDecayCoeffFromFrametime(double coeff, int times);
/* Decelerate the velocities. Function is called in step() but is dereferenced from
* frame time to assure same behaviour on all systems
*/
@@ -224,25 +208,25 @@ private:
VelocityStates _lastVel;
VelocityStates _sensitivity;
double _projectionScaleFactor;
double _currentRadius;
double _slerpdT;
double _timeSlack;
int _numOfTests;
TUIO::TuioTime _time;
bool _directTouchMode;
bool _wasPrevModeDirectTouch;
bool _tap;
bool _doubleTap;
bool _zoomOutTap;
bool _lmSuccess;
bool _guiON;
double _projectionScaleFactor = 1.000004;
double _currentRadius = 1.0;
double _slerpdT = 10001.0;
double _timeSlack = 0.0;
int _numOfTests = 0;
std::chrono::milliseconds _time;
bool _directTouchMode = false;
bool _wasPrevModeDirectTouch = false;
bool _tap = false;
bool _doubleTap = false;
bool _zoomOutTap = false;
bool _lmSuccess = true;
bool _guiON = false;
std::vector<DirectInputSolver::SelectedBody> _selected;
SceneGraphNode* _pickingSelected = nullptr;
DirectInputSolver _solver;
glm::dquat _toSlerp;
glm::dvec3 _centroid;
glm::vec2 _centroid = glm::vec2(0.f);
FrameTimeAverage _frameTimeAvg;

View File

@@ -25,9 +25,6 @@
#ifndef __OPENSPACE_MODULE_TOUCH___TOUCH_MARKER___H__
#define __OPENSPACE_MODULE_TOUCH___TOUCH_MARKER___H__
#include <modules/touch/include/tuioear.h>
#include <ghoul/opengl/ghoul_gl.h>
#include <openspace/rendering/renderable.h>
#include <openspace/properties/propertyowner.h>
#include <openspace/properties/vector/vec3property.h>
@@ -35,10 +32,10 @@
#include <openspace/properties/scalar/boolproperty.h>
#include <openspace/properties/scalar/floatproperty.h>
#include <openspace/properties/vector/vec3property.h>
#include <openspace/util/touch.h>
#include <ghoul/glm.h>
#include <ghoul/opengl/ghoul_gl.h>
#include <ghoul/opengl/uniformcache.h>
#include <memory>
#include <vector>
@@ -54,10 +51,10 @@ public:
void initialize();
void deinitialize();
void render(const std::vector<TUIO::TuioCursor>& list);
void render(const std::vector<openspace::TouchInputHolder>& list);
private:
void createVertexList(const std::vector<TUIO::TuioCursor>& list);
void createVertexList(const std::vector<openspace::TouchInputHolder>& list);
properties::BoolProperty _visible;
properties::FloatProperty _radiusSize;

View File

@@ -38,8 +38,6 @@
#include <modules/touch/ext/libTUIO11/TUIO/TuioListener.h>
#include <modules/touch/ext/libTUIO11/TUIO/TuioClient.h>
#include <modules/touch/ext/libTUIO11/TUIO/UdpReceiver.h>
#include <modules/touch/ext/libTUIO11/TUIO/TcpReceiver.h>
#if (defined(__GNUC__) && !defined(__clang__))
#pragma GCC diagnostic pop
@@ -48,72 +46,52 @@
#pragma clang diagnostic pop
#endif // __clang__
#include <openspace/util/touch.h>
#include <ghoul/glm.h>
#include <algorithm>
#include <math.h>
#include <vector>
#include <mutex>
#include <numeric>
#include <algorithm>
#include <vector>
namespace openspace {
class TuioEar : public TUIO::TuioListener {
public:
TuioEar();
~TuioEar() {
_tuioClient.disconnect();
}
public:
TuioEar();
~TuioEar();
/**
* Callback functions, listens to the TUIO server
/**
* Callback functions, listens to the TUIO server
*/
void addTuioObject(TUIO::TuioObject *tobj);
void updateTuioObject(TUIO::TuioObject *tobj);
void removeTuioObject(TUIO::TuioObject *tobj);
void addTuioCursor(TUIO::TuioCursor *tcur);
void updateTuioCursor(TUIO::TuioCursor *tcur);
void removeTuioCursor(TUIO::TuioCursor *tcur);
void addTuioBlob(TUIO::TuioBlob *tblb);
void updateTuioBlob(TUIO::TuioBlob *tblb);
void removeTuioBlob(TUIO::TuioBlob *tblb);
void refresh(TUIO::TuioTime frameTime);
/**
* Lock-swap the containers of this listener
*/
void addTuioObject(TUIO::TuioObject *tobj);
void updateTuioObject(TUIO::TuioObject *tobj);
void removeTuioObject(TUIO::TuioObject *tobj);
std::vector<TouchInput> takeInput();
std::vector<TouchInput> takeRemovals();
void addTuioCursor(TUIO::TuioCursor *tcur);
void updateTuioCursor(TUIO::TuioCursor *tcur);
void removeTuioCursor(TUIO::TuioCursor *tcur);
private:
TUIO::TuioClient _tuioClient;
void addTuioBlob(TUIO::TuioBlob *tblb);
void updateTuioBlob(TUIO::TuioBlob *tblb);
void removeTuioBlob(TUIO::TuioBlob *tblb);
void refresh(TUIO::TuioTime frameTime);
/**
* Returns a list of all touch history that happened since the last frame
*/
std::vector<TUIO::TuioCursor> getInput();
/**
* Returns true if a tap occured since the last frame
*/
bool tap();
/**
* Returns tap's cursor coordinates and time information
*/
TUIO::TuioCursor getTap();
/**
* Clears the input list, function called after getInput() each frame
*/
void clearInput();
private:
bool _tap = false;
TUIO::TuioCursor _tapCo = TUIO::TuioCursor(-1, -1, -1.0f, -1.0f);
std::mutex _mx;
TUIO::TuioClient _tuioClient;
std::vector<TUIO::TuioCursor> _list;
/**
* A list that tracks all of the cursor ID's that got removed since last frame
*/
std::vector<long> _removeList;
std::vector<TouchInput> _inputList;
std::vector<TouchInput> _removalList;
std::mutex _mx;
};
} // namespace openspace
#endif // __OPENSPACE_MODULE_TOUCH___TUIO_EAR___H__

View File

@@ -37,7 +37,7 @@ namespace {
openspace::SceneGraphNode* node;
LMstat stats;
};
}
} // namespace
namespace openspace {
@@ -45,7 +45,8 @@ DirectInputSolver::DirectInputSolver() {
levmarq_init(&_lmstat);
}
// project back a 3D point in model view to clip space [-1,1] coordinates on the view plane
// project back a 3D point in model view to clip space [-1,1] coordinates on the view
// plane
glm::dvec2 castToNDC(const glm::dvec3& vec, Camera& camera, SceneGraphNode* node) {
glm::dvec3 posInCamSpace = glm::inverse(camera.rotationQuaternion()) *
(node->worldRotationMatrix() * vec +
@@ -82,20 +83,24 @@ double distToMinimize(double* par, int x, void* fdata, LMstat* lmstat) {
dvec3(0, 0, 0),
directionToCenter,
// To avoid problem with lookup in up direction
normalize(camDirection + lookUp));
normalize(camDirection + lookUp)
);
dquat globalCamRot = normalize(quat_cast(inverse(lookAtMat)));
dquat localCamRot = inverse(globalCamRot) * ptr->camera->rotationQuaternion();
{ // Roll
{
// Roll
dquat rollRot = angleAxis(q[3], dvec3(0.0, 0.0, 1.0));
localCamRot = localCamRot * rollRot;
}
{ // Panning (local rotation)
{
// Panning (local rotation)
dvec3 eulerAngles(q[5], q[4], 0);
dquat panRot = dquat(eulerAngles);
localCamRot = localCamRot * panRot;
}
{ // Orbit (global rotation)
{
// Orbit (global rotation)
dvec3 eulerAngles(q[1], q[0], 0);
dquat rotationDiffCamSpace = dquat(eulerAngles);
@@ -169,15 +174,17 @@ void gradient(double* g, double* par, int x, void* fdata, LMstat* lmstat) {
}
// calculate f1 with good h for finite difference
dPar.at(i) += h;
dPar[i] += h;
f1 = distToMinimize(dPar.data(), x, fdata, lmstat);
dPar.at(i) = par[i];
dPar[i] = par[i];
break;
}
else if ((f1 - f0) != 0 && lastG != 0) { // h too big
else if ((f1 - f0) != 0 && lastG != 0) {
// h too big
h /= scale;
}
else if ((f1 - f0) == 0) { // h too small
else if ((f1 - f0) == 0) {
// h too small
h *= scale;
}
lastG = f1 - f0;
@@ -201,9 +208,9 @@ void gradient(double* g, double* par, int x, void* fdata, LMstat* lmstat) {
}
}
bool DirectInputSolver::solve(const std::vector<TUIO::TuioCursor>& list,
bool DirectInputSolver::solve(const std::vector<TouchInputHolder>& list,
const std::vector<SelectedBody>& selectedBodies,
std::vector<double>* parameters, const Camera& camera)
std::vector<double>* parameters, const Camera& camera)
{
int nFingers = std::min(static_cast<int>(list.size()), 3);
_nDof = std::min(nFingers * 2, 6);
@@ -216,29 +223,9 @@ bool DirectInputSolver::solve(const std::vector<TUIO::TuioCursor>& list,
const SelectedBody& sb = selectedBodies.at(i);
selectedPoints.push_back(sb.coordinates);
screenPoints.emplace_back(
2 * (list[i].getX() - 0.5),
-2 * (list[i].getY() - 0.5)
2.0 * (list[i].latestInput().x - 0.5),
-2.0 * (list[i].latestInput().y - 0.5)
);
// This might be needed when we're directing the touchtable from another screen?
// std::vector<TuioCursor>::const_iterator c = std::find_if(
// list.begin(),
// list.end(),
// [&sb](const TuioCursor& c) { return c.getSessionID() == sb.id; }
// );
// if (c != list.end()) {
// // normalized -1 to 1 coordinates on screen
// screenPoints.emplace_back(2 * (c->getX() - 0.5), -2 * (c->getY() - 0.5));
// }
// else {
// global::moduleEngine.module<ImGUIModule>()->touchInput = {
// true,
// glm::dvec2(0.0, 0.0),
// 1
// };
// resetAfterInput();
// return;
// }
}
FunctionData fData = {
@@ -265,11 +252,11 @@ bool DirectInputSolver::solve(const std::vector<TUIO::TuioCursor>& list,
return result;
}
int DirectInputSolver::getNDof() const {
int DirectInputSolver::nDof() const {
return _nDof;
}
const LMstat& DirectInputSolver::getLevMarqStat() {
const LMstat& DirectInputSolver::levMarqStat() {
return _lmstat;
}

View File

@@ -24,39 +24,35 @@
#include <openspace/engine/globals.h>
#include <modules/touch/include/touchinteraction.h>
#include <modules/touch/include/directinputsolver.h>
#include <modules/imgui/imguimodule.h>
#include <openspace/interaction/orbitalnavigator.h>
#include <openspace/engine/globals.h>
#include <openspace/engine/moduleengine.h>
#include <openspace/query/query.h>
#include <openspace/rendering/renderengine.h>
#include <openspace/scene/scenegraphnode.h>
#include <openspace/scene/scene.h>
#include <openspace/util/time.h>
#include <openspace/util/keys.h>
#include <ghoul/misc/invariants.h>
#include <ghoul/logging/logmanager.h>
#include <openspace/util/camera.h>
#include <openspace/util/updatestructures.h>
#include <glm/gtx/quaternion.hpp>
#ifdef OPENSPACE_MODULE_GLOBEBROWSING_ENABLED
#include <modules/globebrowsing/src/basictypes.h>
#include <modules/globebrowsing/src/renderableglobe.h>
#endif
#ifdef OPENSPACE_MODULE_WEBBROWSER_ENABLED
#include <modules/webbrowser/webbrowsermodule.h>
#endif
#include <cmath>
#include <modules/imgui/imguimodule.h>
#include <modules/touch/include/touchinteraction.h>
#include <modules/touch/include/directinputsolver.h>
#include <openspace/engine/globals.h>
#include <openspace/engine/moduleengine.h>
#include <openspace/engine/windowdelegate.h>
#include <openspace/interaction/navigationhandler.h>
#include <openspace/interaction/orbitalnavigator.h>
#include <openspace/query/query.h>
#include <openspace/rendering/renderengine.h>
#include <openspace/scene/scene.h>
#include <openspace/scene/scenegraphnode.h>
#include <openspace/util/camera.h>
#include <openspace/util/keys.h>
#include <openspace/util/time.h>
#include <openspace/util/updatestructures.h>
#include <ghoul/fmt.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/misc/invariants.h>
#include <glm/gtx/quaternion.hpp>
#include <cmath>
#include <functional>
#include <fstream>
#include <numeric>
#ifdef WIN32
#pragma warning (push)
@@ -69,10 +65,6 @@
#pragma warning (pop)
#endif // WIN32
#include <openspace/engine/globals.h>
#include <openspace/engine/windowdelegate.h>
#include <openspace/interaction/navigationhandler.h>
namespace {
constexpr const char* _loggerCat = "TouchInteraction";
@@ -246,9 +238,19 @@ namespace {
"Its purpose is to limit zooming in on a node. If this value is not set it "
"defaults to the surface of the current anchor. "
};
} // namespace
using namespace TUIO;
// Compute coefficient of decay based on current frametime; if frametime has been
// longer than usual then multiple decay steps may be applied to keep the decay
// relative to user time
double computeDecayCoeffFromFrametime(double coeff, int times) {
if (coeff > 0.00001) {
return std::pow(coeff, times);
}
else {
return 0.0;
}
}
} // namespace
namespace openspace {
@@ -275,8 +277,8 @@ TouchInteraction::TouchInteraction()
0.25f
)
, _zoomBoundarySphereMultiplier(ZoomBoundarySphereMultiplierInfo, 1.001f, 1.f, 1.01f)
, _zoomOutLimit(ZoomOutLimitInfo, std::numeric_limits<double>::max(), 1000.0f, std::numeric_limits<double>::max())
, _zoomInLimit(ZoomInLimitInfo, -1.0f, 0.0f, std::numeric_limits<double>::max())
, _zoomOutLimit(ZoomOutLimitInfo, std::numeric_limits<double>::max(), 1000.0, std::numeric_limits<double>::max())
, _zoomInLimit(ZoomInLimitInfo, -1.0, 0.0, std::numeric_limits<double>::max())
, _inputStillThreshold(InputSensitivityInfo, 0.0005f, 0.f, 0.001f)
// used to void wrongly interpreted roll interactions
, _centroidStillThreshold(StationaryCentroidInfo, 0.0018f, 0.f, 0.01f)
@@ -310,22 +312,6 @@ TouchInteraction::TouchInteraction()
, _constTimeDecay_secs(ConstantTimeDecaySecsInfo, 1.75f, 0.1f, 4.0f)
// calculated with two vectors with known diff in length, then
// projDiffLength/diffLength.
, _projectionScaleFactor(1.000004)
, _currentRadius(1.0)
, _slerpdT(1000)
, _timeSlack(0.0)
, _numOfTests(0)
, _directTouchMode(false)
, _wasPrevModeDirectTouch(false)
, _tap(false)
, _doubleTap(false)
, _zoomOutTap(false)
, _lmSuccess(true)
, _guiON(false)
#ifdef TOUCH_DEBUG_PROPERTIES
, _debugProperties()
#endif
, _centroid(glm::dvec3(0.0))
{
addProperty(_touchActive);
addProperty(_unitTest);
@@ -370,41 +356,42 @@ TouchInteraction::TouchInteraction()
));
}
});
_time.initSession();
_time = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now().time_since_epoch()
);
}
// Called each frame if there is any input
void TouchInteraction::updateStateFromInput(const std::vector<TuioCursor>& list,
std::vector<Point>& lastProcessed)
void TouchInteraction::updateStateFromInput(const std::vector<TouchInputHolder>& list,
std::vector<TouchInput>& lastProcessed)
{
#ifdef TOUCH_DEBUG_PROPERTIES
_debugProperties.nFingers = list.size();
#endif
if (_tap) { // check for doubletap
if (_time.getSessionTime().getTotalMilliseconds() < _maxTapTime) {
if (_tap) {
// check for doubletap
using namespace std::chrono;
milliseconds timestamp = duration_cast<milliseconds>(
high_resolution_clock::now().time_since_epoch()
);
if ((timestamp - _time).count() < _maxTapTime) {
_doubleTap = true;
_tap = false;
}
_time.initSession();
_time = timestamp;
}
// Code for lower-right corner double-tap to zoom-out
const glm::vec2 res = global::windowDelegate.currentWindowSize();
const glm::vec2 pos = list[0].latestInput().screenCoordinates(res);
bool hasWebContent = webContent(list);
//Code for lower-right corner double-tap to zoom-out
glm::ivec2 res = global::windowDelegate.currentWindowSize();
glm::dvec2 pos = glm::vec2(
list.at(0).getScreenX(res.x),
list.at(0).getScreenY(res.y)
);
const float bottomCornerSizeForZoomTap_fraction = 0.08f;
int zoomTapThresholdX = static_cast<int>(
res.x * (1.0f - bottomCornerSizeForZoomTap_fraction)
const int zoomTapThresholdX = static_cast<int>(
res.x * (1.f - bottomCornerSizeForZoomTap_fraction)
);
int zoomTapThresholdY = static_cast<int>(
res.y * (1.0f - bottomCornerSizeForZoomTap_fraction)
const int zoomTapThresholdY = static_cast<int>(
res.y * (1.f - bottomCornerSizeForZoomTap_fraction)
);
bool isTapInLowerRightCorner =
const bool isTapInLowerRightCorner =
(std::abs(pos.x) > zoomTapThresholdX && std::abs(pos.y) > zoomTapThresholdY);
if (_doubleTap && isTapInLowerRightCorner) {
@@ -413,22 +400,18 @@ void TouchInteraction::updateStateFromInput(const std::vector<TuioCursor>& list,
_doubleTap = false;
}
if (!guiMode(list) && !hasWebContent) {
bool isThisFrameTransitionBetweenTouchModes
= (_wasPrevModeDirectTouch != _directTouchMode);
if (isThisFrameTransitionBetweenTouchModes) {
size_t numFingers = list.size();
if (!isGuiMode(pos, numFingers)) {
bool isTransitionBetweenModes = (_wasPrevModeDirectTouch != _directTouchMode);
if (isTransitionBetweenModes) {
_vel.orbit = glm::dvec2(0.0, 0.0);
_vel.zoom = 0.0;
_vel.roll = 0.0;
_vel.pan = glm::dvec2(0.0, 0.0);
resetAfterInput();
/*if( _directTouchMode )
LINFO("Touch -> Direct-touch");
else
LINFO("Direct-touch -> Touch");*/
}
if (_directTouchMode && _selected.size() > 0 && list.size() == _selected.size()) {
if (_directTouchMode && _selected.size() > 0 && numFingers == _selected.size()) {
#ifdef TOUCH_DEBUG_PROPERTIES
_debugProperties.interactionMode = "Direct";
#endif
@@ -448,41 +431,21 @@ void TouchInteraction::updateStateFromInput(const std::vector<TuioCursor>& list,
_wasPrevModeDirectTouch = _directTouchMode;
// evaluates if current frame is in directTouchMode (will be used next frame)
_directTouchMode =
(_currentRadius > _nodeRadiusThreshold && _selected.size() == list.size());
(_currentRadius > _nodeRadiusThreshold && _selected.size() == numFingers);
}
}
bool TouchInteraction::webContent(const std::vector<TuioCursor>& list) {
#ifdef OPENSPACE_MODULE_WEBBROWSER_ENABLED
glm::ivec2 res = global::windowDelegate.currentWindowSize();
glm::dvec2 pos = glm::vec2(
list.at(0).getScreenX(res.x),
list.at(0).getScreenY(res.y)
);
WebBrowserModule& module = *(global::moduleEngine.module<WebBrowserModule>());
return module.eventHandler().hasContentCallback(pos.x, pos.y);
#else
return false;
#endif
}
// Activates/Deactivates gui input mode (if active it voids all other interactions)
bool TouchInteraction::guiMode(const std::vector<TuioCursor>& list) {
bool TouchInteraction::isGuiMode(glm::dvec2 screenPosition, size_t numFingers) {
if (_ignoreGui) {
return false;
}
glm::ivec2 res = global::windowDelegate.currentWindowSize();
glm::dvec2 pos = glm::vec2(
list.at(0).getScreenX(res.x),
list.at(0).getScreenY(res.y)
);
ImGUIModule& module = *(global::moduleEngine.module<ImGUIModule>());
_guiON = module.gui.isEnabled();
if (_tap && list.size() == 1 &&
std::abs(pos.x) < _guiButton.value().x && std::abs(pos.y) < _guiButton.value().y)
if (_tap && numFingers == 1 &&
std::abs(screenPosition.x) < _guiButton.value().x &&
std::abs(screenPosition.y) < _guiButton.value().y)
{
// pressed invisible button
_guiON = !_guiON;
@@ -491,19 +454,19 @@ bool TouchInteraction::guiMode(const std::vector<TuioCursor>& list) {
LINFO(fmt::format(
"GUI mode is {}. Inside box by: ({}%, {}%)",
_guiON ? "activated" : "deactivated",
static_cast<int>(100 * (pos.x / _guiButton.value().x)),
static_cast<int>(100 * (pos.y / _guiButton.value().y))
static_cast<int>(100 * (screenPosition.x / _guiButton.value().x)),
static_cast<int>(100 * (screenPosition.y / _guiButton.value().y))
));
}
else if (_guiON) {
module.touchInput = { _guiON, pos, 1 }; // emulate touch input as a mouse
// emulate touch input as a mouse
module.touchInput = { _guiON, screenPosition, 1 };
}
return _guiON;
}
// Sets _vel to update _camera according to direct-manipulation (L2 error)
void TouchInteraction::directControl(const std::vector<TuioCursor>& list) {
void TouchInteraction::directControl(const std::vector<TouchInputHolder>& list) {
// Reset old velocities upon new interaction
_vel.orbit = glm::dvec2(0.0, 0.0);
_vel.zoom = 0.0;
@@ -515,10 +478,10 @@ void TouchInteraction::directControl(const std::vector<TuioCursor>& list) {
// finds best transform values for the new camera state and stores them in par
std::vector<double> par(6, 0.0);
par.at(0) = _lastVel.orbit.x; // use _lastVel for orbit
par.at(1) = _lastVel.orbit.y;
par[0] = _lastVel.orbit.x; // use _lastVel for orbit
par[1] = _lastVel.orbit.y;
_lmSuccess = _solver.solve(list, _selected, &par, *_camera);
int nDof = _solver.getNDof();
int nDof = _solver.nDof();
if (_lmSuccess && !_unitTest) {
// if good values were found set new camera state
@@ -543,19 +506,17 @@ void TouchInteraction::directControl(const std::vector<TuioCursor>& list) {
else {
// prevents touch to infinitely be active (due to windows bridge case where event
// doesnt get consumed sometimes when LMA fails to converge)
global::moduleEngine.module<ImGUIModule>()->touchInput = {
true,
glm::dvec2(0.0, 0.0),
1
};
Touch touch;
touch.active = true;
touch.pos = glm::dvec2(0.0, 0.0);
touch.action = 1;
global::moduleEngine.module<ImGUIModule>()->touchInput = touch;
resetAfterInput();
}
}
// Traces the touch input into the scene and finds the surface coordinates of touched
// planets (if occuring)
void TouchInteraction::findSelectedNode(const std::vector<TuioCursor>& list) {
//trim list to only contain visible nodes that make sense
void TouchInteraction::findSelectedNode(const std::vector<TouchInputHolder>& list) {
// trim list to only contain visible nodes that make sense
std::string selectables[30] = {
"Sun", "Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus",
"Neptune", "Pluto", "Moon", "Titan", "Rhea", "Mimas", "Iapetus", "Enceladus",
@@ -574,30 +535,31 @@ void TouchInteraction::findSelectedNode(const std::vector<TuioCursor>& list) {
glm::dquat camToWorldSpace = _camera->rotationQuaternion();
glm::dvec3 camPos = _camera->positionVec3();
std::vector<DirectInputSolver::SelectedBody> newSelected;
//node & distance
std::tuple<SceneGraphNode*, double> currentlyPicked = {
// node & distance
std::pair<SceneGraphNode*, double> currentlyPicked = {
nullptr,
std::numeric_limits<double>::max()
};
for (const TuioCursor& c : list) {
double xCo = 2 * (c.getX() - 0.5);
double yCo = -2 * (c.getY() - 0.5); // normalized -1 to 1 coordinates on screen
for (const TouchInputHolder& inputHolder : list) {
// normalized -1 to 1 coordinates on screen
double xCo = 2 * (inputHolder.latestInput().x - 0.5);
double yCo = -2 * (inputHolder.latestInput().y - 0.5);
glm::dvec3 cursorInWorldSpace = camToWorldSpace *
glm::dvec3(glm::inverse(_camera->projectionMatrix()) *
glm::dvec4(xCo, yCo, -1.0, 1.0));
glm::dvec3 raytrace = glm::normalize(cursorInWorldSpace);
long id = c.getSessionID();
size_t id = inputHolder.fingerId();
for (SceneGraphNode* node : selectableNodes) {
double boundingSphereSquared = static_cast<double>(node->boundingSphere()) *
static_cast<double>(node->boundingSphere());
glm::dvec3 camToSelectable = node->worldPosition() - camPos;
double intersectionDist = 0.0;
bool intersected = glm::intersectRaySphere(
const bool intersected = glm::intersectRaySphere(
camPos,
raytrace,
node->worldPosition(),
@@ -616,7 +578,7 @@ void TouchInteraction::findSelectedNode(const std::vector<TuioCursor>& list) {
[id](const DirectInputSolver::SelectedBody& s) { return s.id == id; }
);
if (oldNode != newSelected.end()) {
double oldNodeDist = glm::length(
const double oldNodeDist = glm::length(
oldNode->node->worldPosition() - camPos
);
if (glm::length(camToSelectable) < oldNodeDist) {
@@ -637,11 +599,12 @@ void TouchInteraction::findSelectedNode(const std::vector<TuioCursor>& list) {
glm::vec4(node->worldPosition(), 1.0);
glm::dvec2 ndc = clip / clip.w;
// If the object is not in the screen, we dont want to consider it at all
if (ndc.x >= -1.0 && ndc.x <= 1.0 && ndc.y >= -1.0 && ndc.y <= 1.0) {
const bool isVisibleX = (ndc.x >= -1.0 && ndc.x <= 1.0);
const bool isVisibleY = (ndc.y >= -1.0 && ndc.y <= 1.0);
if (isVisibleX && isVisibleY) {
glm::dvec2 cursor = { xCo, yCo };
double ndcDist = glm::length(ndc - cursor);
const double ndcDist = glm::length(ndc - cursor);
// We either want to select the object if it's bounding sphere as been
// touched (checked by the first part of this loop above) or if the touch
// point is within a minimum distance of the center
@@ -668,12 +631,9 @@ void TouchInteraction::findSelectedNode(const std::vector<TuioCursor>& list) {
"Picking candidate based on proximity"
);
#endif //#ifdef TOUCH_DEBUG_NODE_PICK_MESSAGES
double dist = length(camToSelectable);
if (dist < std::get<1>(currentlyPicked)) {
currentlyPicked = {
node,
dist
};
const double dist = length(camToSelectable);
if (dist < currentlyPicked.second) {
currentlyPicked = std::make_pair(node, dist);
}
}
}
@@ -681,7 +641,7 @@ void TouchInteraction::findSelectedNode(const std::vector<TuioCursor>& list) {
}
// If an item has been picked, it's in the first position of the vector now
if (SceneGraphNode* node = std::get<0>(currentlyPicked)) {
if (SceneGraphNode* node = currentlyPicked.first) {
_pickingSelected = node;
#ifdef TOUCH_DEBUG_NODE_PICK_MESSAGES
LINFOC("Picking", "Picked node: " + _pickingSelected->identifier());
@@ -691,65 +651,60 @@ void TouchInteraction::findSelectedNode(const std::vector<TuioCursor>& list) {
_selected = std::move(newSelected);
}
// Interprets the input gesture to a specific interaction
int TouchInteraction::interpretInteraction(const std::vector<TuioCursor>& list,
const std::vector<Point>& lastProcessed)
int TouchInteraction::interpretInteraction(const std::vector<TouchInputHolder>& list,
const std::vector<TouchInput>& lastProcessed)
{
glm::dvec3 lastCentroid = _centroid;
_centroid.x = std::accumulate(
list.begin(),
list.end(),
0.0,
[](double x, const TuioCursor& c) { return x + c.getX(); }
) / list.size();
_centroid.y = std::accumulate(
list.begin(),
list.end(),
0.0,
[](double y, const TuioCursor& c) { return y + c.getY(); }
) / list.size();
glm::fvec2 lastCentroid = _centroid;
_centroid = { 0.f, 0.f };
for (const TouchInputHolder& inputHolder : list) {
_centroid += glm::vec2(
inputHolder.latestInput().x,
inputHolder.latestInput().y
);
}
_centroid /= static_cast<float>(list.size());
// see if the distance between fingers changed - used in pan interpretation
double dist = 0;
double lastDist = 0;
TuioCursor cursor = list.at(0);
for (const TuioCursor& c : list) {
TouchInput distInput = list[0].latestInput();
for (const TouchInputHolder& inputHolder : list) {
const TouchInput& latestInput = inputHolder.latestInput();
dist += glm::length(
glm::dvec2(c.getX(), c.getY()) - glm::dvec2(cursor.getX(), cursor.getY())
glm::dvec2(latestInput.x, latestInput.y) -
glm::dvec2(distInput.x, distInput.y)
);
cursor = c;
distInput = latestInput;
}
TuioPoint point = lastProcessed.at(0).second;
for (const Point& p : lastProcessed) {
lastDist += glm::length(glm::dvec2(p.second.getX(), p.second.getY()) -
glm::dvec2(point.getX(), point.getY()));
point = p.second;
distInput = lastProcessed[0];
for (const TouchInput& p : lastProcessed) {
lastDist += glm::length(glm::dvec2(p.x, p.y) -
glm::dvec2(distInput.x, distInput.y));
distInput = p;
}
// find the slowest moving finger - used in roll interpretation
double minDiff = 1000;
long id = 0;
for (const TuioCursor& c : list) {
auto it = std::find_if(
lastProcessed.begin(),
lastProcessed.end(),
[&c](const Point& p) {
return p.first == c.getSessionID();
for (const TouchInputHolder& inputHolder : list) {
const auto it = std::find_if(
lastProcessed.cbegin(),
lastProcessed.cend(),
[&inputHolder](const TouchInput& input) {
return inputHolder.holdsInput(input);
});
if (it == lastProcessed.end()) {
if (it == lastProcessed.cend()) {
continue;
}
const TouchInput& latestInput = inputHolder.latestInput();
const TouchInput& prevInput = *it;
TuioPoint itPoint = it->second;
double diff = c.getX() - itPoint.getX() + c.getY() - itPoint.getY();
double diff = latestInput.x - prevInput.x + latestInput.y - prevInput.y;
if (!c.isMoving()) {
diff = minDiff = 0.0;
id = c.getSessionID();
if (!inputHolder.isMoving()) {
minDiff = 0.0;
}
else if (std::abs(diff) < std::abs(minDiff)) {
minDiff = diff;
id = c.getSessionID();
}
}
// find if all fingers angles are high - used in roll interpretation
@@ -757,26 +712,22 @@ int TouchInteraction::interpretInteraction(const std::vector<TuioCursor>& list,
list.begin(),
list.end(),
0.0,
[&](double diff, const TuioCursor& c) {
TuioPoint point = std::find_if(
[&](double diff, const TouchInputHolder& inputHolder) {
const TouchInput& lastPoint = *std::find_if(
lastProcessed.begin(),
lastProcessed.end(),
[&c](const Point& p) { return p.first == c.getSessionID(); }
)->second;
[&inputHolder](const TouchInput& input) {
return inputHolder.holdsInput(input);
});
double res = 0.0;
float lastAngle = point.getAngle(
static_cast<float>(_centroid.x),
static_cast<float>(_centroid.y)
);
float currentAngle = c.getAngle(
static_cast<float>(_centroid.x),
static_cast<float>(_centroid.y)
);
if (lastAngle > currentAngle + 1.5 * M_PI) {
res = currentAngle + (2 * M_PI - lastAngle);
float lastAngle = lastPoint.angleToPos(_centroid.x, _centroid.y);
float currentAngle = inputHolder.latestInput().angleToPos(_centroid.x, _centroid.y);
if (lastAngle > currentAngle + 1.5 * glm::pi<float>()) {
res = currentAngle + (2.0 * glm::pi<float>() - lastAngle);
}
else if (currentAngle > lastAngle + 1.5 * M_PI) {
res = (2 * M_PI - currentAngle) + lastAngle;
else if (currentAngle > lastAngle + 1.5 * glm::pi<float>()) {
res = (2.0 * glm::pi<float>() - currentAngle) + lastAngle;
}
else {
res = currentAngle - lastAngle;
@@ -810,9 +761,7 @@ int TouchInteraction::interpretInteraction(const std::vector<TuioCursor>& list,
return ROT;
}
else {
float avgDistance = static_cast<float>(
std::abs(dist - lastDist) / list.at(0).getMotionSpeed()
);
float avgDistance = static_cast<float>(std::abs(dist - lastDist));
// if average distance between 3 fingers are constant we have panning
if (_panEnabled && (avgDistance < _interpretPan && list.size() == 3)) {
return PAN;
@@ -833,11 +782,9 @@ int TouchInteraction::interpretInteraction(const std::vector<TuioCursor>& list,
}
}
// Calculate how much interpreted interaction (_vel) should change the camera state
void TouchInteraction::computeVelocities(const std::vector<TuioCursor>& list,
const std::vector<Point>& lastProcessed)
void TouchInteraction::computeVelocities(const std::vector<TouchInputHolder>& list,
const std::vector<TouchInput>& lastProcessed)
{
const TuioCursor& cursor = list.at(0);
const int action = interpretInteraction(list, lastProcessed);
const SceneGraphNode* anchor =
global::navigationHandler.orbitalNavigator().anchorNode();
@@ -868,14 +815,16 @@ void TouchInteraction::computeVelocities(const std::vector<TuioCursor>& list,
}
#endif
const TouchInputHolder& inputHolder = list.at(0);
switch (action) {
case ROT: { // add rotation velocity
_vel.orbit += glm::dvec2(cursor.getXSpeed() *
_sensitivity.orbit.x, cursor.getYSpeed() *
_vel.orbit += glm::dvec2(inputHolder.speedX() *
_sensitivity.orbit.x, inputHolder.speedY() *
_sensitivity.orbit.y);
double orbitVelocityAvg = glm::distance(_vel.orbit.x, _vel.orbit.y);
_constTimeDecayCoeff.orbit
= computeConstTimeDecayCoefficient(orbitVelocityAvg);
const double orbitVelocityAvg = glm::distance(_vel.orbit.x, _vel.orbit.y);
_constTimeDecayCoeff.orbit = computeConstTimeDecayCoefficient(
orbitVelocityAvg
);
break;
}
case PINCH: {
@@ -885,22 +834,18 @@ void TouchInteraction::computeVelocities(const std::vector<TuioCursor>& list,
list.begin(),
list.end(),
0.0,
[&](double d, const TuioCursor& c) {
return d + c.getDistance(
static_cast<float>(_centroid.x),
static_cast<float>(_centroid.y)
);
[&](double d, const TouchInputHolder& c) {
const glm::vec2 currPos = { c.latestInput().x, c.latestInput().y };
return d + glm::distance(currPos, _centroid);
}
) / list.size();
double lastDistance = std::accumulate(
lastProcessed.begin(),
lastProcessed.end(),
0.0f,
[&](float d, const Point& p) {
return d + p.second.getDistance(
static_cast<float>(_centroid.x),
static_cast<float>(_centroid.y)
);
0.f,
[&](float d, const TouchInput& p) {
const glm::vec2 lastPos = { p.x, p.y };
return d + glm::distance(lastPos, _centroid);
}
) / lastProcessed.size();
@@ -908,7 +853,7 @@ void TouchInteraction::computeVelocities(const std::vector<TuioCursor>& list,
glm::dvec3 centerPos = anchor->worldPosition();
glm::dvec3 currDistanceToFocusNode = camPos - centerPos;
double distanceFromFocusSurface =
const double distanceFromFocusSurface =
length(currDistanceToFocusNode) - anchor->boundingSphere();
double zoomFactor = (distance - lastDistance);
#ifdef TOUCH_DEBUG_PROPERTIES
@@ -918,11 +863,11 @@ void TouchInteraction::computeVelocities(const std::vector<TuioCursor>& list,
_constTimeDecayCoeff.zoom = computeConstTimeDecayCoefficient(_vel.zoom);
if (distanceFromFocusSurface > 0.1) {
double ratioOfDistanceToNodeVsSurface =
const double ratioOfDistanceToNodeVsSurface =
length(currDistanceToFocusNode) / distanceFromFocusSurface;
if (ratioOfDistanceToNodeVsSurface > _zoomSensitivityDistanceThreshold) {
zoomFactor *= pow(
std::abs(distanceFromFocusSurface),
std::abs(distanceFromFocusSurface),
static_cast<float>(_zoomSensitivityExponential)
);
}
@@ -940,27 +885,26 @@ void TouchInteraction::computeVelocities(const std::vector<TuioCursor>& list,
list.begin(),
list.end(),
0.0,
[&](double diff, const TuioCursor& c) {
TuioPoint point = std::find_if(
[&](double diff, const TouchInputHolder& inputHolder) {
TouchInput point = *std::find_if(
lastProcessed.begin(),
lastProcessed.end(),
[&c](const Point& p) { return p.first == c.getSessionID(); }
)->second;
double res = diff;
double lastAngle = point.getAngle(
static_cast<float>(_centroid.x),
static_cast<float>(_centroid.y)
[&inputHolder](const TouchInput& input) {
return inputHolder.holdsInput(input);
}
);
double currentAngle = c.getAngle(
static_cast<float>(_centroid.x),
static_cast<float>(_centroid.y)
double res = diff;
float lastAngle = point.angleToPos(_centroid.x, _centroid.y);
float currentAngle = inputHolder.latestInput().angleToPos(
_centroid.x,
_centroid.y
);
// if's used to set angles 359 + 1 = 0 and 0 - 1 = 359
if (lastAngle > currentAngle + 1.5 * M_PI) {
res += currentAngle + (2 * M_PI - lastAngle);
if (lastAngle > currentAngle + 1.5 * glm::pi<float>()) {
res += currentAngle + (2 * glm::pi<float>() - lastAngle);
}
else if (currentAngle > lastAngle + 1.5 * M_PI) {
res += (2 * M_PI - currentAngle) + lastAngle;
else if (currentAngle > lastAngle + 1.5 * glm::pi<float>()) {
res += (2 * glm::pi<float>() - currentAngle) + lastAngle;
}
else {
res += currentAngle - lastAngle;
@@ -968,15 +912,14 @@ void TouchInteraction::computeVelocities(const std::vector<TuioCursor>& list,
return res;
}
) / list.size();
_vel.roll += -rollFactor * _sensitivity.roll;
_constTimeDecayCoeff.roll = computeConstTimeDecayCoefficient(_vel.roll);
break;
}
case PAN: {
// add local rotation velocity
_vel.pan += glm::dvec2(cursor.getXSpeed() *
_sensitivity.pan.x, cursor.getYSpeed() * _sensitivity.pan.y);
_vel.pan += glm::dvec2(inputHolder.speedX() *
_sensitivity.pan.x, inputHolder.speedY() * _sensitivity.pan.y);
double panVelocityAvg = glm::distance(_vel.pan.x, _vel.pan.y);
_constTimeDecayCoeff.pan = computeConstTimeDecayCoefficient(panVelocityAvg);
break;
@@ -1015,8 +958,8 @@ void TouchInteraction::computeVelocities(const std::vector<TuioCursor>& list,
}
double TouchInteraction::computeConstTimeDecayCoefficient(double velocity) {
const double postDecayVelocityTarget = 1e-6;
double stepsToDecay = _constTimeDecay_secs / _frameTimeAvg.averageFrameTime();
constexpr const double postDecayVelocityTarget = 1e-6;
const double stepsToDecay = _constTimeDecay_secs / _frameTimeAvg.averageFrameTime();
if (stepsToDecay > 0.0 && std::abs(velocity) > postDecayVelocityTarget) {
return std::pow(postDecayVelocityTarget / std::abs(velocity), 1.0 / stepsToDecay);
@@ -1061,17 +1004,17 @@ void TouchInteraction::step(double dt) {
if (anchor && _camera) {
// Create variables from current state
dvec3 camPos = _camera->positionVec3();
dvec3 centerPos = anchor->worldPosition();
const dvec3 centerPos = anchor->worldPosition();
dvec3 directionToCenter = normalize(centerPos - camPos);
dvec3 centerToCamera = camPos - centerPos;
dvec3 lookUp = _camera->lookUpVectorWorldSpace();
dvec3 camDirection = _camera->viewDirectionWorldSpace();
const dvec3 centerToCamera = camPos - centerPos;
const dvec3 lookUp = _camera->lookUpVectorWorldSpace();
const dvec3 camDirection = _camera->viewDirectionWorldSpace();
// Make a representation of the rotation quaternion with local and global
// rotations
// To avoid problem with lookup in up direction
dmat4 lookAtMat = lookAt(
const dmat4 lookAtMat = lookAt(
dvec3(0, 0, 0),
directionToCenter,
normalize(camDirection + lookUp)
@@ -1079,60 +1022,59 @@ void TouchInteraction::step(double dt) {
dquat globalCamRot = normalize(quat_cast(inverse(lookAtMat)));
dquat localCamRot = inverse(globalCamRot) * _camera->rotationQuaternion();
double boundingSphere = anchor->boundingSphere();
double distance = std::max(length(centerToCamera) - boundingSphere, 0.0);
const double boundingSphere = anchor->boundingSphere();
const double distance = std::max(length(centerToCamera) - boundingSphere, 0.0);
_currentRadius = boundingSphere /
std::max(distance * _projectionScaleFactor, 1.0);
std::max(distance * _projectionScaleFactor, 1.0);
{
// Roll
dquat camRollRot = angleAxis(_vel.roll * dt, dvec3(0.0, 0.0, 1.0));
const dquat camRollRot = angleAxis(_vel.roll * dt, dvec3(0.0, 0.0, 1.0));
localCamRot = localCamRot * camRollRot;
}
{
// Panning (local rotation)
dvec3 eulerAngles(_vel.pan.y * dt, _vel.pan.x * dt, 0);
dquat rotationDiff = dquat(eulerAngles);
const dvec3 eulerAngles(_vel.pan.y * dt, _vel.pan.x * dt, 0);
const dquat rotationDiff = dquat(eulerAngles);
localCamRot = localCamRot * rotationDiff;
// if we have chosen a new focus node
if (_slerpdT < _slerpTime) {
_slerpdT += 0.1*dt;
_slerpdT += 0.1 * dt;
localCamRot = slerp(localCamRot, _toSlerp, _slerpdT / _slerpTime);
}
}
{
// Orbit (global rotation)
dvec3 eulerAngles(_vel.orbit.y*dt, _vel.orbit.x*dt, 0);
dquat rotationDiffCamSpace = dquat(eulerAngles);
const dvec3 eulerAngles(_vel.orbit.y*dt, _vel.orbit.x*dt, 0);
const dquat rotationDiffCamSpace = dquat(eulerAngles);
dquat rotationDiffWorldSpace = globalCamRot * rotationDiffCamSpace *
inverse(globalCamRot);
dvec3 rotationDiffVec3 = centerToCamera * rotationDiffWorldSpace -
centerToCamera;
const dquat rotationDiffWorldSpace = globalCamRot * rotationDiffCamSpace *
inverse(globalCamRot);
const dvec3 rotationDiffVec3 = centerToCamera * rotationDiffWorldSpace -
centerToCamera;
camPos += rotationDiffVec3;
dvec3 centerToCam = camPos - centerPos;
const dvec3 centerToCam = camPos - centerPos;
directionToCenter = normalize(-centerToCam);
dvec3 lookUpWhenFacingCenter = globalCamRot *
const dvec3 lookUpWhenFacingCenter = globalCamRot *
dvec3(_camera->lookUpVectorCameraSpace());
dmat4 lookAtMatrix = lookAt(
const dmat4 lookAtMatrix = lookAt(
dvec3(0, 0, 0),
directionToCenter,
lookUpWhenFacingCenter);
globalCamRot = normalize(quat_cast(inverse(lookAtMatrix)));
}
{ // Zooming
{
// Zooming
// This is a rough estimate of the node surface
// If nobody has set another zoom in limit, use this as default zoom in bounds
double zoomInBounds = boundingSphere * _zoomBoundarySphereMultiplier;
bool isZoomInLimitSet = (_zoomInLimit.value() >= 0.0);
// If nobody has set another zoom in limit, use the default zoom in bounds
if (_zoomInLimit.value() < 0) {
_zoomInLimit.setValue(zoomInBounds);
}
else if (_zoomInLimit.value() < zoomInBounds) {
// If zoom in limit is less than the estimated node radius we need to
if (isZoomInLimitSet && _zoomInLimit.value() < zoomInBounds) {
// If zoom in limit is less than the estimated node radius we need to
// make sure we do not get too close to possible height maps
SurfacePositionHandle posHandle = anchor->calculateSurfacePositionHandle(
camPos
@@ -1142,7 +1084,7 @@ void TouchInteraction::step(double dt) {
posHandle.referenceSurfaceOutDirection * posHandle.heightToSurface;
glm::dvec3 centerToActualSurface = glm::dmat3(anchor->modelTransform()) *
centerToActualSurfaceModelSpace;
double nodeRadius = length(centerToActualSurface);
const double nodeRadius = length(centerToActualSurface);
// Because of heightmaps we should make sure we do not go through the surface
if (_zoomInLimit.value() < nodeRadius) {
@@ -1150,32 +1092,33 @@ void TouchInteraction::step(double dt) {
LINFO(fmt::format("{}: Zoom In limit should be larger than anchor "
"center to surface, setting it to {}", _loggerCat, zoomInBounds));
#endif
_zoomInLimit.setValue(zoomInBounds);
}
zoomInBounds = _zoomInLimit.value();
}
}
// Make sure zoom in limit is not larger than zoom out limit
if (_zoomInLimit.value() > _zoomOutLimit.value()) {
if (zoomInBounds > _zoomOutLimit.value()) {
LWARNING(fmt::format(
"{}: Zoom In Limit should be smaller than Zoom Out Limit",
"{}: Zoom In Limit should be smaller than Zoom Out Limit",
_loggerCat, _zoomOutLimit.value()
));
}
//Apply the velocity to update camera position
glm::dvec3 zoomDistanceIncrement = directionToCenter * _vel.zoom * dt;
double newPosDistance = length(centerToCamera + zoomDistanceIncrement);
double currentPosDistance = length(centerToCamera);
const double newPosDistance = length(centerToCamera + zoomDistanceIncrement);
const double currentPosDistance = length(centerToCamera);
// Possible with other navigations performed outside touch interaction
bool currentPosViolatingZoomOutLimit =
const bool currentPosViolatingZoomOutLimit =
(currentPosDistance >= _zoomOutLimit.value());
bool willNewPositionViolateZoomOutLimit =
const bool willNewPositionViolateZoomOutLimit =
(newPosDistance >= _zoomOutLimit.value());
bool willNewPositionViolateZoomInLimit =
(newPosDistance < _zoomInLimit.value());
(newPosDistance < zoomInBounds);
if (!willNewPositionViolateZoomInLimit && !willNewPositionViolateZoomOutLimit){
if (!willNewPositionViolateZoomInLimit
&& !willNewPositionViolateZoomOutLimit) {
camPos += zoomDistanceIncrement;
}
else if (currentPosViolatingZoomOutLimit) {
@@ -1185,8 +1128,7 @@ void TouchInteraction::step(double dt) {
_loggerCat, _zoomOutLimit.value());
#endif
// Only allow zooming in if you are outside the zoom out limit
if (newPosDistance < currentPosDistance)
{
if (newPosDistance < currentPosDistance) {
camPos += zoomDistanceIncrement;
}
}
@@ -1224,46 +1166,6 @@ void TouchInteraction::step(double dt) {
}
}
void TouchInteraction::unitTest() {
if (_unitTest) {
_solver.setLevMarqVerbosity(true);
// set _selected pos and new pos (on screen)
std::vector<TuioCursor> lastFrame = {
{ TuioCursor(0, 10, 0.45f, 0.4f) }, // session id, cursor id, x, y
{ TuioCursor(1, 11, 0.55f, 0.6f) }
};
std::vector<TuioCursor> currFrame = {
{ TuioCursor(0, 10, 0.2f, 0.6f) }, // (-0.6,-0.2)
{ TuioCursor(1, 11, 0.8f, 0.4f) } // (0.6, 0.2)
};
// call update
findSelectedNode(lastFrame);
directControl(currFrame);
// save lmstats.data into a file and clear it
char buffer[32];
snprintf(buffer, sizeof(char) * 32, "lmdata%i.csv", _numOfTests);
_numOfTests++;
std::ofstream file(buffer);
file << _solver.getLevMarqStat().data;
// clear everything
_selected.clear();
_pickingSelected = nullptr;
_vel.orbit = glm::dvec2(0.0, 0.0);
_vel.zoom = 0.0;
_vel.roll = 0.0;
_vel.pan = glm::dvec2(0.0, 0.0);
_lastVel = _vel;
_unitTest = false;
_solver.setLevMarqVerbosity(false);
// could be the camera copy in func
}
}
// Decelerate velocities, called a set number of times per second to dereference it from
// frame time
// Example:
@@ -1289,15 +1191,6 @@ void TouchInteraction::decelerate(double dt) {
_vel.zoom *= computeDecayCoeffFromFrametime(_constTimeDecayCoeff.zoom, times);
}
double TouchInteraction::computeDecayCoeffFromFrametime(double coeff, int times) {
if (coeff > 0.00001) {
return std::pow(coeff, times);
}
else {
return 0.0;
}
}
// Called if all fingers are off the screen
void TouchInteraction::resetAfterInput() {
#ifdef TOUCH_DEBUG_PROPERTIES
@@ -1403,12 +1296,13 @@ void FrameTimeAverage::updateWithNewFrame(double sample) {
}
double FrameTimeAverage::averageFrameTime() const {
double ft;
if (_nSamples == 0)
ft = 1.0 / 60.0; //Just guess at 60fps if no data is available yet
else
ft = std::accumulate(_samples, _samples + _nSamples, 0.0) / (double)(_nSamples);
return ft;
if (_nSamples == 0) {
// Just guess at 60fps if no data is available yet
return 1.0 / 60.0;
}
else {
return std::accumulate(_samples, _samples + _nSamples, 0.0) / (double)(_nSamples);
}
}
#ifdef TOUCH_DEBUG_PROPERTIES

View File

@@ -27,9 +27,8 @@
#include <openspace/engine/globals.h>
#include <openspace/rendering/renderengine.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/opengl/programobject.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/opengl/programobject.h>
namespace {
constexpr const std::array<const char*, 4> UniformNames = {
@@ -63,6 +62,7 @@ namespace {
constexpr openspace::properties::Property::PropertyInfo ColorInfo = {
"MarkerColor", "Marker color", "" // @TODO Missing documentation
};
} // namespace
namespace openspace {
@@ -73,13 +73,7 @@ TouchMarker::TouchMarker()
, _radiusSize(RadiusInfo, 30.f, 0.f, 100.f)
, _transparency(TransparencyInfo, 0.8f, 0.f, 1.f)
, _thickness(ThicknessInfo, 2.f, 0.f, 4.f )
, _color(
ColorInfo,
glm::vec3(204.f / 255.f, 51.f / 255.f, 51.f / 255.f),
glm::vec3(0.f),
glm::vec3(1.f)
)
, _shader(nullptr)
, _color(ColorInfo, glm::vec3(0.96f, 0.2f, 0.2f), glm::vec3(0.f), glm::vec3(1.f))
{
addProperty(_visible);
addProperty(_radiusSize);
@@ -117,7 +111,7 @@ void TouchMarker::deinitialize() {
}
}
void TouchMarker::render(const std::vector<TUIO::TuioCursor>& list) {
void TouchMarker::render(const std::vector<openspace::TouchInputHolder>& list) {
if (_visible && !list.empty()) {
createVertexList(list);
_shader->activate();
@@ -131,7 +125,6 @@ void TouchMarker::render(const std::vector<TUIO::TuioCursor>& list) {
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_PROGRAM_POINT_SIZE); // Enable gl_PointSize in vertex shader
// glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); // When included this line makes the webgui disappear at touch interaction
glBindVertexArray(_quad);
glDrawArrays(GL_POINTS, 0, static_cast<int>(_vertexData.size() / 2));
@@ -139,13 +132,13 @@ void TouchMarker::render(const std::vector<TUIO::TuioCursor>& list) {
}
}
void TouchMarker::createVertexList(const std::vector<TUIO::TuioCursor>& list) {
void TouchMarker::createVertexList(const std::vector<openspace::TouchInputHolder>& list) {
_vertexData.resize(list.size() * 2);
int i = 0;
for (const TUIO::TuioCursor& c : list) {
_vertexData[i] = 2 * (c.getX() - 0.5f);
_vertexData[i + 1] = -2 * (c.getY() - 0.5f);
for (const openspace::TouchInputHolder& inputHolder : list) {
_vertexData[i] = 2 * (inputHolder.latestInput().x - 0.5f);
_vertexData[i + 1] = -2 * (inputHolder.latestInput().y - 0.5f);
i += 2;
}

View File

@@ -29,11 +29,12 @@
#include <openspace/interaction/navigationhandler.h>
#include <openspace/rendering/renderengine.h>
#include <openspace/rendering/screenspacerenderable.h>
#include <ghoul/logging/logmanager.h>
using namespace TUIO;
namespace openspace {
void TuioEar::addTuioObject(TuioObject*) { }
void TuioEar::updateTuioObject(TuioObject*) { }
@@ -42,67 +43,41 @@ void TuioEar::removeTuioObject(TuioObject*) { }
void TuioEar::addTuioCursor(TuioCursor* tcur) {
_mx.lock();
_tap = false;
// find same id in _list if it exists in _removeList (new input with same ID as a
// previously stored)
long i = tcur->getSessionID();
std::vector<long>::iterator foundID = std::find_if(
_removeList.begin(),
_removeList.end(),
[&i](long id) { return id == i; });
// if found, remove id from _removeList and update, otherwise add new id to list
if (foundID != _removeList.end()) {
std::find_if(
_list.begin(),
_list.end(),
[&i](const TuioCursor& cursor) {
return cursor.getSessionID() == i;
}
)->update(tcur);
_removeList.erase(foundID);
}
else {
_list.emplace_back(*tcur);
}
TouchInput input(
static_cast<size_t>(tcur->getTuioSourceID()),
static_cast<size_t>(tcur->getCursorID()),
tcur->getX(),
tcur->getY(),
static_cast<double>(tcur->getTuioTime().getTotalMilliseconds()) / 1000.0
);
_inputList.emplace_back(input);
_mx.unlock();
}
void TuioEar::updateTuioCursor(TuioCursor* tcur) {
_mx.lock();
_tap = false;
long i = tcur->getSessionID();
std::find_if(
_list.begin(),
_list.end(),
[&i](const TuioCursor& cursor) {
return cursor.getSessionID() == i;
}
)->update(tcur);
TouchInput input(
static_cast<size_t>(tcur->getTuioSourceID()),
static_cast<size_t>(tcur->getCursorID()),
tcur->getX(),
tcur->getY(),
static_cast<double>(tcur->getTuioTime().getTotalMilliseconds()) / 1000.0
);
_inputList.emplace_back(input);
_mx.unlock();
}
// save id to be removed and remove it in clearInput
void TuioEar::removeTuioCursor(TuioCursor* tcur) {
_mx.lock();
_removeList.push_back(tcur->getSessionID());
// Check if the cursor ID could be considered a tap
glm::dvec2 currPos = glm::dvec2(tcur->getX(), tcur->getY());
double dist = 0;
for (const TuioPoint& p : tcur->getPath()) {
dist += glm::length(glm::dvec2(p.getX(), p.getY()) - currPos);
}
dist /= tcur->getPath().size();
double heldTime =
tcur->getPath().back().getTuioTime().getTotalMilliseconds() -
tcur->getPath().front().getTuioTime().getTotalMilliseconds();
if (heldTime < 180 && dist < 0.0004 && _list.size() == 1 && _removeList.size() == 1) {
_tapCo = TuioCursor(*tcur);
_tap = true;
}
TouchInput input(
static_cast<size_t>(tcur->getTuioSourceID()),
static_cast<size_t>(tcur->getCursorID()),
tcur->getX(),
tcur->getY(),
static_cast<double>(tcur->getTuioTime().getTotalMilliseconds()) / 1000.0
);
_removalList.emplace_back(input);
_mx.unlock();
}
@@ -114,48 +89,22 @@ void TuioEar::removeTuioBlob(TuioBlob*) { }
void TuioEar::refresh(TuioTime) { } // about every 15ms
std::vector<TuioCursor> TuioEar::getInput() {
std::lock_guard lock(_mx);
return _list;
}
bool TuioEar::tap() {
std::lock_guard lock(_mx);
if (_tap) {
_tap = false;
return !_tap;
std::vector<TouchInput> TuioEar::takeInput() {
std::vector<TouchInput> outputList;
{
std::lock_guard lock(_mx);
outputList.swap(_inputList);
}
else {
return _tap;
return outputList;
}
std::vector<TouchInput> TuioEar::takeRemovals() {
std::vector<TouchInput> outputList;
{
std::lock_guard lock(_mx);
outputList.swap(_removalList);
}
}
TuioCursor TuioEar::getTap() {
std::lock_guard lock(_mx);
return _tapCo;
}
// Removes all cursor ID from list that exists in _removeList
void TuioEar::clearInput() {
_mx.lock();
_list.erase(
std::remove_if(
_list.begin(),
_list.end(),
[this](const TuioCursor& cursor) {
return std::find_if(
_removeList.begin(),
_removeList.end(),
[&cursor](long id) {
return cursor.getSessionID() == id;
}
) != _removeList.end();
}
),
_list.end()
);
_removeList.clear();
_mx.unlock();
return outputList;
}
// Standard UDP IP connection to port 3333
@@ -165,3 +114,9 @@ TuioEar::TuioEar()
_tuioClient.addTuioListener(this);
_tuioClient.connect();
}
TuioEar::~TuioEar() {
_tuioClient.disconnect();
}
} // namespace openspace

View File

@@ -26,84 +26,133 @@
#include <modules/touch/include/win32_touch.h>
#include <openspace/engine/globals.h>
#include <openspace/engine/openspaceengine.h>
#include <openspace/engine/windowdelegate.h>
#include <ghoul/logging/logmanager.h>
#include <TUIO/TuioServer.h>
#include <chrono>
#include <thread>
#include <tchar.h>
#include <tpcshrd.h>
// #define ENABLE_TUIOMESSAGES
#define ENABLE_DIRECTMSG
namespace {
constexpr const char* _loggerCat = "win32_touch";
HHOOK gTouchHook{ nullptr };
bool gStarted{ false };
TUIO::TuioServer* gTuioServer{ nullptr };
HHOOK gTouchHook = nullptr;
std::thread* gMouseHookThread;
HHOOK gMouseHook = nullptr;
bool gStarted = false;
std::chrono::microseconds gStartTime = std::chrono::microseconds(0);
std::unordered_map<
UINT32,
std::unique_ptr<openspace::TouchInputHolder>
> gTouchInputsMap;
#ifdef ENABLE_TUIOMESSAGES
TUIO::TuioServer* gTuioServer = nullptr;
std::unordered_map<UINT, TUIO::TuioCursor*> gCursorMap;
#endif
} // namespace
namespace openspace {
LRESULT CALLBACK LowLevelMouseProc(int nCode, WPARAM wParam, LPARAM lParam);
// This hook will only work for Win7+ Digitizers.
// This hook will only work for Win8+ Digitizers.
// - Once GLFW has native touch support, we can remove this windows-specific code
LRESULT CALLBACK HookCallback(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode < 0) {
if (nCode != HC_ACTION) {
return CallNextHookEx(0, nCode, wParam, lParam);
}
if (nCode == HC_ACTION) {
LPMSG pStruct = reinterpret_cast<LPMSG>(lParam);
const UINT message = pStruct->message;
switch (message) {
case WM_POINTERDOWN:
case WM_POINTERUPDATE:
case WM_POINTERUP:
{
POINTER_INFO pointerInfo = {};
if (GetPointerInfo(GET_POINTERID_WPARAM(pStruct->wParam), &pointerInfo)) {
RECT rect;
GetClientRect(pStruct->hwnd, reinterpret_cast<LPRECT>(&rect));
POINT p = pointerInfo.ptPixelLocation;
// native touch to screen conversion
ScreenToClient(pStruct->hwnd, reinterpret_cast<LPPOINT>(&p));
float xPos = static_cast<float>(p.x) /
static_cast<float>(rect.right - rect.left);
float yPos = static_cast<float>(p.y) /
static_cast<float>(rect.bottom - rect.top);
if (pointerInfo.pointerFlags & POINTER_FLAG_DOWN) {
// Handle new touchpoint
gTuioServer->initFrame(TUIO::TuioTime::getSessionTime());
gCursorMap[pointerInfo.pointerId] = gTuioServer->addTuioCursor(
xPos,
yPos
);
gTuioServer->commitFrame();
}
else if (pointerInfo.pointerFlags & POINTER_FLAG_UPDATE) {
// Handle update of touchpoint
TUIO::TuioTime frameTime = TUIO::TuioTime::getSessionTime();
if (gCursorMap[pointerInfo.pointerId]->getTuioTime() == frameTime)
{
break;
}
gTuioServer->initFrame(frameTime);
gTuioServer->updateTuioCursor(
gCursorMap[pointerInfo.pointerId],
xPos,
yPos
);
gTuioServer->commitFrame();
}
else if (pointerInfo.pointerFlags & POINTER_FLAG_UP) {
// Handle removed touchpoint
gTuioServer->initFrame(TUIO::TuioTime::getSessionTime());
gTuioServer->removeTuioCursor(gCursorMap[pointerInfo.pointerId]);
gTuioServer->commitFrame();
gCursorMap.erase(pointerInfo.pointerId);
}
}
LPMSG pStruct = reinterpret_cast<LPMSG>(lParam);
const UINT message = pStruct->message;
switch (message) {
case WM_POINTERDOWN:
case WM_POINTERUPDATE:
case WM_POINTERUP:
{
POINTER_INFO info = {};
BOOL hasInfo = GetPointerInfo(GET_POINTERID_WPARAM(pStruct->wParam), &info);
if (!hasInfo) {
break;
}
using namespace std::chrono;
const microseconds timestamp = duration_cast<microseconds>(
high_resolution_clock::now().time_since_epoch()
) - gStartTime;
RECT rect;
GetClientRect(pStruct->hwnd, reinterpret_cast<LPRECT>(&rect));
POINT p = info.ptPixelLocation;
// native touch to screen conversion
ScreenToClient(pStruct->hwnd, reinterpret_cast<LPPOINT>(&p));
float xPos = static_cast<float>(p.x) /
static_cast<float>(rect.right - rect.left);
float yPos = static_cast<float>(p.y) /
static_cast<float>(rect.bottom - rect.top);
TouchInput touchInput(
reinterpret_cast<size_t>(info.sourceDevice),
static_cast<size_t>(info.pointerId),
xPos,
yPos,
static_cast<double>(timestamp.count())/1'000'000.0
);
if (info.pointerFlags & POINTER_FLAG_DOWN) {
#ifdef ENABLE_DIRECTMSG
std::unique_ptr<TouchInputHolder> points =
std::make_unique<TouchInputHolder>(touchInput);
gTouchInputsMap.emplace(info.pointerId, std::move(points));
global::openSpaceEngine.touchDetectionCallback(touchInput);
#endif
#ifdef ENABLE_TUIOMESSAGES
// Handle new touchpoint
gTuioServer->initFrame(TUIO::TuioTime::getSessionTime());
gCursorMap[info.pointerId] = gTuioServer->addTuioCursor(
xPos,
yPos
);
gTuioServer->commitFrame();
#endif
}
else if (info.pointerFlags & POINTER_FLAG_UPDATE) {
// Handle update of touchpoint
#ifdef ENABLE_DIRECTMSG
TouchInputHolder* points = gTouchInputsMap[info.pointerId].get();
if (points->tryAddInput(touchInput)) {
global::openSpaceEngine.touchUpdateCallback(points->latestInput());
}
#endif
#ifdef ENABLE_TUIOMESSAGES
TUIO::TuioTime frameTime = TUIO::TuioTime::getSessionTime();
if (gCursorMap[info.pointerId]->getTuioTime() == frameTime) {
break;
}
gTuioServer->initFrame(frameTime);
gTuioServer->updateTuioCursor(gCursorMap[info.pointerId], xPos, yPos);
gTuioServer->commitFrame();
#endif
}
else if (info.pointerFlags & POINTER_FLAG_UP) {
#ifdef ENABLE_DIRECTMSG
gTouchInputsMap.erase(info.pointerId);
global::openSpaceEngine.touchExitCallback(touchInput);
#endif
#ifdef ENABLE_TUIOMESSAGES
// Handle removed touchpoint
gTuioServer->initFrame(TUIO::TuioTime::getSessionTime());
gTuioServer->removeTuioCursor(gCursorMap[info.pointerId]);
gTuioServer->commitFrame();
gCursorMap.erase(info.pointerId);
#endif
}
break;
}
}
@@ -119,9 +168,18 @@ Win32TouchHook::Win32TouchHook(void* nativeWindow)
return;
}
// HACK: This hack is required as long as our GLFW version is based on the touch
// branch. There is no convenient way to set a GLFWBool (uint32_t) which sets the
// state of touch-to-mouseinput interpretation. It happens to be 116 bytes into an
// internal glfw struct...
uint32_t* HACKY_PTR = (uint32_t *)GetPropW(hWnd, L"GLFW");
HACKY_PTR += 116/sizeof(uint32_t);
*HACKY_PTR = 1;
// Test for touch:
int value = GetSystemMetrics(SM_DIGITIZER);
if ((value & NID_READY) == 0) {
if ((value & NID_READY) == 0) {
// Don't bother setting up touch hooks?
return;
}
@@ -130,12 +188,12 @@ Win32TouchHook::Win32TouchHook(void* nativeWindow)
// Digitizer is multitouch
LINFO("Found Multitouch input digitizer!");
}
if (value & NID_INTEGRATED_TOUCH) {
if (value & NID_INTEGRATED_TOUCH) {
// Integrated touch
}
// This should be needed, but we seem to receive messages even without it,
// probably a Win7+ behaviour
// this ought to be part to the older (< win8) windows touch-api.
// Also - RegisterTouchWindow enables Windows gestures, which we don't want
// since they produce visual feedback for "press-and-tap" etc.
// RegisterTouchWindow(hWnd, TWF_FINETOUCH | TWF_WANTPALM);
@@ -143,7 +201,7 @@ Win32TouchHook::Win32TouchHook(void* nativeWindow)
// TODO: Would be nice to find out if the gesture "press-and-tap" can be disabled
// basically we don't really care for windows gestures for now...
// this disables press and hold (right-click) gesture
const DWORD dwHwndTabletProperty = TABLET_DISABLE_PRESSANDHOLD;
const UINT_PTR dwHwndTabletProperty = TABLET_DISABLE_PRESSANDHOLD;
ATOM atom = ::GlobalAddAtom(MICROSOFT_TABLETPENSERVICE_PROPERTY);
::SetProp(
@@ -155,28 +213,88 @@ Win32TouchHook::Win32TouchHook(void* nativeWindow)
if (!gStarted) {
gStarted = true;
gStartTime = std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::high_resolution_clock::now().time_since_epoch()
);
#ifdef ENABLE_TUIOMESSAGES
gTuioServer = new TUIO::TuioServer("localhost", 3333);
TUIO::TuioTime::initSession();
#endif
gTouchHook = SetWindowsHookExW(
WH_GETMESSAGE,
HookCallback,
GetModuleHandleW(NULL),
GetCurrentThreadId()
);
// In theory, if our UI is pumped from a different thread, we can
// handle Low-level mouse events in that thread as well.
// this might help reduce mouse lag while running OpenSpace?
// gMouseHookThread = new std::thread([](){
// gMouseHook = SetWindowsHookExW(
// WH_MOUSE_LL,
// LowLevelMouseProc,
// GetModuleHandleW(NULL),
// 0 //<- Global thread id (low-level mouse is global only)
// );
// if(!gMouseHook){
// LINFO("Could not setup mousehook!");
// }
// MSG msg;
// while (GetMessage(&msg, NULL, 0, 0))
// {
// DispatchMessage(&msg);
// }
// });
if (!gTouchHook) {
LINFO(fmt::format("Failed to setup WindowsHook for touch input redirection"));
#ifdef ENABLE_TUIOMESSAGES
delete gTuioServer;
#endif
gStarted = false;
}
}
}
Win32TouchHook::~Win32TouchHook() {
if (gStarted) {
UnhookWindowsHookEx(gTouchHook);
UnhookWindowsHookEx(gMouseHook);
#ifdef ENABLE_TUIOMESSAGES
delete gTuioServer;
#endif
}
}
// Low-level mouse hook is "needed" if we want to stop mousecursor from moving
// when we get a touch-input on our window A negative effect is that this
// function is for global threads, meaning our application will cause Windows to
// stall the mouse cursor when this function can't be scheduled. This is not yet
// fail-proof...might be a race-condition on message pumping?
// - Seems to move the cursor when we get two fingers as input..
// - If we ourselves would pump windows for events, we can handle this in the
// pump-loop
LRESULT CALLBACK LowLevelMouseProc(int nCode, WPARAM wParam, LPARAM lParam) {
constexpr const LONG_PTR SIGNATURE_MASK = 0xFFFFFF00;
constexpr const LONG_PTR MOUSEEVENTF_FROMTOUCH = 0xFF515700;
if (nCode < 0) {
// do not process message
return CallNextHookEx(0, nCode, wParam, lParam);
}
LPMSLLHOOKSTRUCT msg = reinterpret_cast<LPMSLLHOOKSTRUCT>(lParam);
// block injected events (in most cases generated by touches)
bool isFromTouch = (msg->dwExtraInfo || SIGNATURE_MASK) == MOUSEEVENTF_FROMTOUCH;
if (msg->flags & LLMHF_INJECTED || isFromTouch) {
return 1;
}
// forward event
return CallNextHookEx(0, nCode, wParam, lParam);
}
} // namespace openspace
#endif // WIN32

View File

@@ -23,95 +23,98 @@
****************************************************************************************/
#include <modules/touch/touchmodule.h>
#include <modules/touch/include/win32_touch.h>
#include <modules/webgui/webguimodule.h>
#include <modules/touch/include/tuioear.h>
#include <modules/touch/include/win32_touch.h>
#include <openspace/engine/globals.h>
#include <openspace/engine/globalscallbacks.h>
#include <openspace/engine/moduleengine.h>
#include <openspace/engine/windowdelegate.h>
#include <openspace/interaction/navigationhandler.h>
#include <openspace/interaction/interactionmonitor.h>
#include <openspace/interaction/navigationhandler.h>
#include <openspace/interaction/orbitalnavigator.h>
#include <openspace/rendering/renderengine.h>
#include <openspace/rendering/screenspacerenderable.h>
#include <ghoul/logging/logmanager.h>
#include <sstream>
#include <string>
#include <iostream>
#ifdef OPENSPACE_MODULE_WEBBROWSER_ENABLED
#include <modules/webbrowser/webbrowsermodule.h>
#endif
using namespace TUIO;
namespace {
constexpr openspace::properties::Property::PropertyInfo TouchActiveInfo = {
"TouchActive",
"True if we want to use touch input as 3d navigation",
"Use this if we want to turn on or off Touch input navigation. "
"Disabling this will reset all current touch inputs to the navigation. "
};
}
namespace openspace {
bool TouchModule::processNewInput() {
// Get new input from listener
std::vector<TouchInput> earInputs = _ear->takeInput();
std::vector<TouchInput> earRemovals = _ear->takeRemovals();
for(const TouchInput& input : earInputs) {
updateOrAddTouchInput(input);
}
for(const TouchInput& removal : earRemovals) {
removeTouchInput(removal);
}
_listOfContactPoints = _ear.getInput();
_ear.clearInput();
// Set touch property to active (to void mouse input, mainly for mtdev bridges)
_touch.touchActive(!_listOfContactPoints.empty());
_touch.touchActive(!_touchPoints.empty());
if (!_listOfContactPoints.empty()) {
if (!_touchPoints.empty()) {
global::interactionMonitor.markInteraction();
}
// Erase old input id's that no longer exists
_lastProcessed.erase(
_lastTouchInputs.erase(
std::remove_if(
_lastProcessed.begin(),
_lastProcessed.end(),
[this](const Point& point) {
return std::find_if(
_listOfContactPoints.begin(),
_listOfContactPoints.end(),
[&point](const TuioCursor& c) {
return point.first == c.getSessionID();
}
) == _listOfContactPoints.end(); }),
_lastProcessed.end());
_lastTouchInputs.begin(),
_lastTouchInputs.end(),
[this](const TouchInput& input) {
return !std::any_of(
_touchPoints.cbegin(),
_touchPoints.cend(),
[&input](const TouchInputHolder& holder) {
return holder.holdsInput(input);
}
);
}
),
_lastTouchInputs.end()
);
// if tap occured, we have new input
if (_listOfContactPoints.empty() && _lastProcessed.empty() && _ear.tap()) {
TuioCursor c = _ear.getTap();
_listOfContactPoints.push_back(c);
_lastProcessed.emplace_back(c.getSessionID(), c.getPath().back());
if (_tap) {
_touch.tap();
_tap = false;
return true;
}
// Check if we need to parse touchevent to the webgui
processNewWebInput(_listOfContactPoints);
// Return true if we got new input
if (_listOfContactPoints.size() == _lastProcessed.size() &&
!_listOfContactPoints.empty())
if (_touchPoints.size() == _lastTouchInputs.size() &&
!_touchPoints.empty())
{
bool newInput = true;
// go through list and check if the last registrered time is newer than the one in
// lastProcessed (last frame)
std::for_each(
_lastProcessed.begin(),
_lastProcessed.end(),
[this, &newInput](Point& p) {
std::vector<TuioCursor>::iterator cursor = std::find_if(
_listOfContactPoints.begin(),
_listOfContactPoints.end(),
[&p](const TuioCursor& c) { return c.getSessionID() == p.first; }
_lastTouchInputs.begin(),
_lastTouchInputs.end(),
[this, &newInput](TouchInput& input) {
std::vector<TouchInputHolder>::iterator holder = std::find_if(
_touchPoints.begin(),
_touchPoints.end(),
[&input](const TouchInputHolder& inputHolder) {
return inputHolder.holdsInput(input);
}
);
double now = cursor->getPath().back().getTuioTime().getTotalMilliseconds();
if (!cursor->isMoving()) {
// if current cursor isn't moving, we want to interpret that as new input
// for interaction purposes
if (!holder->isMoving()) {
newInput = true;
}
else if (p.second.getTuioTime().getTotalMilliseconds() == now) {
newInput = false;
}
});
return newInput;
}
@@ -120,50 +123,84 @@ bool TouchModule::processNewInput() {
}
}
void TouchModule::processNewWebInput(const std::vector<TuioCursor>& listOfContactPoints) {
bool isWebPositionCallbackZero =
(_webPositionCallback.x == 0 && _webPositionCallback.y == 0);
bool isSingleContactPoint = (listOfContactPoints.size() == 1);
if (isSingleContactPoint && isWebPositionCallbackZero) {
glm::ivec2 res = global::windowDelegate.currentWindowSize();
glm::dvec2 pos = glm::vec2(
listOfContactPoints.at(0).getScreenX(res.x),
listOfContactPoints.at(0).getScreenY(res.y)
);
#ifdef OPENSPACE_MODULE_WEBBROWSER_ENABLED
WebBrowserModule& module = *(global::moduleEngine.module<WebBrowserModule>());
if (module.eventHandler().hasContentCallback(pos.x, pos.y)) {
_webPositionCallback = glm::vec2(pos.x, pos.y);
module.eventHandler().touchPressCallback(pos.x, pos.y);
void TouchModule::clearInputs() {
for (const TouchInput& input : _deferredRemovals) {
for (TouchInputHolder& inputHolder : _touchPoints) {
if (inputHolder.holdsInput(input)) {
inputHolder = std::move(_touchPoints.back());
_touchPoints.pop_back();
break;
}
}
}
// Send mouse release if not same point input
else if (!isSingleContactPoint && !isWebPositionCallbackZero) {
WebBrowserModule& module = *(global::moduleEngine.module<WebBrowserModule>());
module.eventHandler().touchReleaseCallback(_webPositionCallback.x,
_webPositionCallback.y);
_webPositionCallback = glm::vec2(0, 0);
#endif
_deferredRemovals.clear();
}
void TouchModule::addTouchInput(TouchInput input) {
_touchPoints.emplace_back(input);
}
void TouchModule::updateOrAddTouchInput(TouchInput input) {
for (TouchInputHolder& inputHolder : _touchPoints) {
if (inputHolder.holdsInput(input)){
inputHolder.tryAddInput(input);
return;
}
}
_touchPoints.emplace_back(input);
}
void TouchModule::removeTouchInput(TouchInput input) {
_deferredRemovals.emplace_back(input);
//Check for "tap" gesture:
for (TouchInputHolder& inputHolder : _touchPoints) {
if (inputHolder.holdsInput(input)) {
inputHolder.tryAddInput(input);
const double totalTime = inputHolder.gestureTime();
const float totalDistance = inputHolder.gestureDistance();
//Magic values taken from tuioear.cpp:
const bool isWithinTapTime = totalTime < 0.18;
const bool wasStationary = totalDistance < 0.0004f;
if (isWithinTapTime && wasStationary && _touchPoints.size() == 1 &&
_deferredRemovals.size() == 1)
{
_tap = true;
}
return;
}
}
}
TouchModule::TouchModule()
: OpenSpaceModule("Touch")
, _touchActive(TouchActiveInfo, true)
{
addPropertySubOwner(_touch);
addPropertySubOwner(_markers);
addProperty(_touchActive);
_touchActive.onChange([&] {
_touch.resetAfterInput();
_lastTouchInputs.clear();
});
}
TouchModule::~TouchModule() {
// intentionally left empty
}
void TouchModule::internalInitialize(const ghoul::Dictionary& /*dictionary*/){
_ear.reset(new TuioEar());
global::callback::initializeGL.push_back([&]() {
LDEBUGC("TouchModule", "Initializing TouchMarker OpenGL");
_markers.initialize();
#ifdef WIN32
// We currently only support one window of touch input internally
// so here we grab the first window-handle and use it.
void* nativeWindowHandle = global::windowDelegate.getNativeWindowHandle(0);
if (nativeWindowHandle) {
_win32TouchHook.reset(new Win32TouchHook(nativeWindowHandle));
}
// We currently only support one window of touch input internally
// so here we grab the first window-handle and use it.
void* nativeWindowHandle = global::windowDelegate.getNativeWindowHandle(0);
if (nativeWindowHandle) {
_win32TouchHook = std::make_unique<Win32TouchHook>(nativeWindowHandle);
}
#endif
});
@@ -172,35 +209,51 @@ TouchModule::TouchModule()
_markers.deinitialize();
});
// These are handled in UI thread, which (as of 20th dec 2019) is in main/rendering
// thread so we don't need a mutex here
global::callback::touchDetected.push_back(
[this](TouchInput i) {
addTouchInput(i);
return true;
}
);
global::callback::touchUpdated.push_back(
[this](TouchInput i) {
updateOrAddTouchInput(i);
return true;
}
);
global::callback::touchExit.push_back(
std::bind(&TouchModule::removeTouchInput, this, std::placeholders::_1)
);
global::callback::preSync.push_back([&]() {
_touch.setCamera(global::navigationHandler.camera());
_touch.setFocusNode(global::navigationHandler.orbitalNavigator().anchorNode());
if (processNewInput() && global::windowDelegate.isMaster()) {
_touch.updateStateFromInput(_listOfContactPoints, _lastProcessed);
if (processNewInput() && global::windowDelegate.isMaster() && _touchActive) {
_touch.updateStateFromInput(_touchPoints, _lastTouchInputs);
}
else if (_listOfContactPoints.empty()) {
else if (_touchPoints.empty()) {
_touch.resetAfterInput();
}
// update lastProcessed
_lastProcessed.clear();
for (const TuioCursor& c : _listOfContactPoints) {
_lastProcessed.emplace_back(c.getSessionID(), c.getPath().back());
_lastTouchInputs.clear();
for (const TouchInputHolder& points : _touchPoints) {
_lastTouchInputs.emplace_back(points.latestInput());
}
// used to save data from solver, only calculated for one frame when user chooses
// in GUI
_touch.unitTest();
// calculate the new camera state for this frame
_touch.step(global::windowDelegate.deltaTime());
clearInputs();
});
global::callback::render.push_back([&]() { _markers.render(_listOfContactPoints); });
}
TouchModule::~TouchModule() {
//intentionally left empty
global::callback::render.push_back([&]() {
_markers.render(_touchPoints);
});
}
} // namespace openspace

View File

@@ -25,43 +25,53 @@
#ifndef __OPENSPACE_MODULE_TOUCH___TOUCHMODULE___H__
#define __OPENSPACE_MODULE_TOUCH___TOUCHMODULE___H__
#include <openspace/util/openspacemodule.h>
#include <modules/touch/include/touchmarker.h>
#include <modules/touch/include/touchinteraction.h>
#include <openspace/util/openspacemodule.h>
#include <openspace/util/touch.h>
#include <memory>
namespace openspace {
#ifdef WIN32
class Win32TouchHook;
#endif //WIN32
class TouchModule : public OpenSpaceModule {
using Point = std::pair<int, TUIO::TuioPoint>;
public:
TouchModule();
~TouchModule();
class TuioEar;
private:
/**
* Returns true if new touch input occured since the last frame
*/
bool processNewInput();
/**
* Checks if touchevent should be parsed to the webgui
*/
void processNewWebInput(const std::vector<TUIO::TuioCursor>& listOfContactPoints);
TuioEar _ear;
TouchInteraction _touch;
TouchMarker _markers;
std::vector<TUIO::TuioCursor> _listOfContactPoints;
// contains an id and the TuioPoint that was processed last frame
std::vector<Point> _lastProcessed;
glm::ivec2 _webPositionCallback = glm::ivec2(0,0);
#ifdef WIN32
std::unique_ptr<Win32TouchHook> _win32TouchHook;
class Win32TouchHook;
#endif //WIN32
};
class TouchModule : public OpenSpaceModule {
public:
TouchModule();
~TouchModule();
protected:
void internalInitialize(const ghoul::Dictionary& dictionary) override;
private:
/// Returns true if new touch input occured since the last frame
bool processNewInput();
void clearInputs();
void addTouchInput(TouchInput input);
void updateOrAddTouchInput(TouchInput input);
void removeTouchInput(TouchInput input);
std::unique_ptr<TuioEar> _ear;
TouchInteraction _touch;
TouchMarker _markers;
std::vector<TouchInputHolder> _touchPoints;
std::vector<TouchInput> _deferredRemovals;
std::vector<TouchInput> _lastTouchInputs;
properties::BoolProperty _touchActive;
// contains an id and the Point that was processed last frame
glm::ivec2 _webPositionCallback = glm::ivec2(0,0);
#ifdef WIN32
std::unique_ptr<Win32TouchHook> _win32TouchHook;
#endif //WIN32
bool _tap = false;
};
} // namespace openspace

View File

@@ -27,6 +27,7 @@
#include <openspace/util/keys.h>
#include <openspace/util/mouse.h>
#include <openspace/util/touch.h>
#include <ghoul/glm.h>
#include <chrono>
@@ -60,10 +61,6 @@ public:
void setBrowserInstance(BrowserInstance* browserInstance);
void resetBrowserInstance();
void touchPressCallback(const double x, const double y);
void touchReleaseCallback(const double x, const double y);
bool hasContentCallback(const double, const double);
private:
bool mouseButtonCallback(MouseButton button, MouseAction action, KeyModifier mods);
bool mousePositionCallback(double x, double y);
@@ -107,6 +104,9 @@ private:
MouseButtonState _leftButton;
MouseButtonState _rightButton;
//This vector assumes first element to be the active one:
std::vector<TouchInput> _validTouchStates;
/**
* determines if a click should be sent as a double click or not
* @return

View File

@@ -29,7 +29,6 @@
#include <openspace/engine/globals.h>
#include <openspace/engine/windowdelegate.h>
#include <openspace/interaction/interactionmonitor.h>
#include <ghoul/logging/logmanager.h>
#include <fmt/format.h>
@@ -37,12 +36,12 @@ namespace {
constexpr const char* _loggerCat = "WebBrowser:EventHandler";
/**
* Map from GLFW key codes to windows key codes, supported by JS and CEF.
* See http://keycode.info/ for lookup
*
* \param key
* \return the key code, if mapped or the GLFW key code
*/
* Map from GLFW key codes to windows key codes, supported by JS and CEF.
* See http://keycode.info/ for lookup
*
* \param key
* \return the key code, if mapped or the GLFW key code
*/
int mapFromGlfwToWindows(openspace::Key key) {
switch (key) {
case openspace::Key::BackSpace: return 8;
@@ -192,34 +191,114 @@ void EventHandler::initialize() {
return false;
}
);
global::callback::touchDetected.emplace_back(
[&](TouchInput input) -> bool {
if (!_browserInstance) {
return false;
}
const glm::vec2 windowPos = input.currentWindowCoordinates();
const bool hasContent = _browserInstance->hasContent(
static_cast<int>(windowPos.x),
static_cast<int>(windowPos.y)
);
if (!hasContent) {
return false;
}
if (_validTouchStates.empty()) {
_mousePosition.x = windowPos.x;
_mousePosition.y = windowPos.y;
_leftButton.down = true;
_browserInstance->sendMouseClickEvent(
mouseEvent(),
MBT_LEFT,
false,
BrowserInstance::SingleClick
);
_validTouchStates.emplace_back(input);
}
else {
_validTouchStates.emplace_back(input);
}
return true;
}
);
global::callback::touchUpdated.emplace_back(
[&](TouchInput input) -> bool {
if (!_browserInstance) {
return false;
}
if (_validTouchStates.empty()) {
return false;
}
auto it = std::find_if(
_validTouchStates.cbegin(),
_validTouchStates.cend(),
[&](const TouchInput& state){
return state.fingerId == input.fingerId &&
state.touchDeviceId == input.touchDeviceId;
}
);
if (it == _validTouchStates.cbegin()) {
glm::vec2 windowPos = input.currentWindowCoordinates();
_mousePosition.x = windowPos.x;
_mousePosition.y = windowPos.y;
_leftButton.down = true;
_browserInstance->sendMouseMoveEvent(mouseEvent());
return true;
}
else if (it != _validTouchStates.cend()){
return true;
}
return false;
}
);
global::callback::touchExit.emplace_back(
[&](TouchInput input) {
if (!_browserInstance) {
return;
}
if (_validTouchStates.empty()) {
return;
}
const auto found = std::find_if(
_validTouchStates.cbegin(),
_validTouchStates.cend(),
[&](const TouchInput& state){
return state.fingerId == input.fingerId &&
state.touchDeviceId == input.touchDeviceId;
}
);
if (found == _validTouchStates.cend()) {
return;
}
_validTouchStates.erase(found);
if (_validTouchStates.empty()) {
glm::vec2 windowPos = input.currentWindowCoordinates();
_mousePosition.x = windowPos.x;
_mousePosition.y = windowPos.y;
_leftButton.down = false;
_browserInstance->sendMouseClickEvent(
mouseEvent(),
MBT_LEFT,
true,
BrowserInstance::SingleClick
);
}
}
);
}
void EventHandler::touchPressCallback(const double x, const double y) {
if (_browserInstance) {
_mousePosition.x = static_cast<float>(x);
_mousePosition.y = static_cast<float>(y);
int clickCount = BrowserInstance::SingleClick;
_browserInstance->sendMouseClickEvent(mouseEvent(), MBT_LEFT, false, clickCount);
}
}
void EventHandler::touchReleaseCallback(const double x, const double y) {
if (_browserInstance) {
_mousePosition.x = static_cast<float>(x);
_mousePosition.y = static_cast<float>(y);
int clickCount = BrowserInstance::SingleClick;
_browserInstance->sendMouseClickEvent(mouseEvent(), MBT_LEFT, true, clickCount);
}
}
bool EventHandler::hasContentCallback(const double x, const double y) {
return _browserInstance->hasContent(static_cast<int>(x), static_cast<int>(y));
}
bool EventHandler::mouseButtonCallback(MouseButton button,
MouseAction action,
bool EventHandler::mouseButtonCallback(MouseButton button, MouseAction action,
KeyModifier mods)
{
if (button != MouseButton::Left && button != MouseButton::Right) {
@@ -234,10 +313,12 @@ bool EventHandler::mouseButtonCallback(MouseButton button,
// click or release?
if (action == MouseAction::Release) {
state.down = false;
} else {
}
else {
if (isDoubleClick(state)) {
++clickCount;
} else {
}
else {
state.lastClickTime = std::chrono::high_resolution_clock::now();
}
@@ -256,6 +337,7 @@ bool EventHandler::mouseButtonCallback(MouseButton button,
bool EventHandler::isDoubleClick(const MouseButtonState& button) const {
// check time
using namespace std::chrono;
auto now = high_resolution_clock::now();
milliseconds maxTimeDifference(doubleClickTime());
auto requiredTime = button.lastClickTime + maxTimeDifference;
@@ -336,11 +418,7 @@ bool EventHandler::specialKeyEvent(Key key, KeyModifier mod, KeyAction) {
}
cef_key_event_type_t EventHandler::keyEventType(KeyAction action) {
if (action == KeyAction::Release) {
return KEYEVENT_KEYUP;
} else {
return KEYEVENT_KEYDOWN;
}
return action == KeyAction::Release ? KEYEVENT_KEYUP : KEYEVENT_KEYDOWN;
}
CefMouseEvent EventHandler::mouseEvent(KeyModifier mods) {

View File

@@ -195,6 +195,7 @@ set(OPENSPACE_SOURCE
${OPENSPACE_BASE_DIR}/src/util/timemanager.cpp
${OPENSPACE_BASE_DIR}/src/util/time_lua.inl
${OPENSPACE_BASE_DIR}/src/util/timerange.cpp
${OPENSPACE_BASE_DIR}/src/util/touch.cpp
${OPENSPACE_BASE_DIR}/src/util/transformationmanager.cpp
${OPENSPACE_BASE_DIR}/src/util/versionchecker.cpp
)
@@ -389,6 +390,7 @@ set(OPENSPACE_HEADER
${OPENSPACE_BASE_DIR}/include/openspace/util/timeline.inl
${OPENSPACE_BASE_DIR}/include/openspace/util/timemanager.h
${OPENSPACE_BASE_DIR}/include/openspace/util/timerange.h
${OPENSPACE_BASE_DIR}/include/openspace/util/touch.h
${OPENSPACE_BASE_DIR}/include/openspace/util/updatestructures.h
${OPENSPACE_BASE_DIR}/include/openspace/util/versionchecker.h
${OPENSPACE_BASE_DIR}/include/openspace/util/transformationmanager.h

View File

@@ -187,8 +187,12 @@ std::shared_ptr<DownloadManager::FileFuture> DownloadManager::downloadFile(
std::chrono::system_clock::now(),
&progressCb
};
#if LIBCURL_VERSION_NUM >= 0x072000
// xferinfo was introduced in 7.32.0, if a lower curl version is used the progress
// will not be shown for downloads on the splash screen
curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, xferinfo); // NOLINT
curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &p); // NOLINT
#endif
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); // NOLINT
CURLcode res = curl_easy_perform(curl);

View File

@@ -96,6 +96,21 @@ std::vector<std::function<bool(double, double)>>& gMouseScrollWheel() {
return g;
}
std::vector<std::function<bool(TouchInput)>>& gTouchDetected() {
static std::vector<std::function<bool(TouchInput)>> g;
return g;
}
std::vector<std::function<bool(TouchInput)>>& gTouchUpdated() {
static std::vector<std::function<bool(TouchInput)>> g;
return g;
}
std::vector<std::function<void(TouchInput)>>& gTouchExit() {
static std::vector<std::function<void(TouchInput)>> g;
return g;
}
} // namespace openspace::global::detail
namespace openspace::global::callback {

View File

@@ -1295,6 +1295,34 @@ void OpenSpaceEngine::mouseScrollWheelCallback(double posX, double posY) {
global::interactionMonitor.markInteraction();
}
void OpenSpaceEngine::touchDetectionCallback(TouchInput input) {
using F = std::function<bool (TouchInput)>;
for (const F& func : global::callback::touchDetected) {
bool isConsumed = func(input);
if (isConsumed) {
return;
}
}
}
void OpenSpaceEngine::touchUpdateCallback(TouchInput input) {
using F = std::function<bool(TouchInput)>;
for (const F& func : global::callback::touchUpdated) {
bool isConsumed = func(input);
if (isConsumed) {
return;
}
}
}
void OpenSpaceEngine::touchExitCallback(TouchInput input) {
using F = std::function<void(TouchInput)>;
for (const F& func : global::callback::touchExit) {
func(input);
}
}
std::vector<char> OpenSpaceEngine::encode() {
std::vector<char> buffer = global::syncEngine.encodeSyncables();
return buffer;

View File

@@ -1020,9 +1020,6 @@ void FramebufferRenderer::updateHDRAndFiltering() {
absPath("${SHADERS}/framebuffer/hdrAndFiltering.vert"),
absPath("${SHADERS}/framebuffer/hdrAndFiltering.frag")
);
using IgnoreError = ghoul::opengl::ProgramObject::IgnoreError;
//_hdrFilteringProgram->setIgnoreSubroutineUniformLocationError(IgnoreError::Yes);
//_hdrFilteringProgram->setIgnoreUniformLocationError(IgnoreError::Yes);
}
void FramebufferRenderer::updateFXAA() {
@@ -1031,9 +1028,6 @@ void FramebufferRenderer::updateFXAA() {
absPath("${SHADERS}/framebuffer/fxaa.vert"),
absPath("${SHADERS}/framebuffer/fxaa.frag")
);
using IgnoreError = ghoul::opengl::ProgramObject::IgnoreError;
//_fxaaProgram->setIgnoreSubroutineUniformLocationError(IgnoreError::Yes);
//_fxaaProgram->setIgnoreUniformLocationError(IgnoreError::Yes);
}
void FramebufferRenderer::updateDownscaledVolume() {
@@ -1042,9 +1036,6 @@ void FramebufferRenderer::updateDownscaledVolume() {
absPath("${SHADERS}/framebuffer/mergeDownscaledVolume.vert"),
absPath("${SHADERS}/framebuffer/mergeDownscaledVolume.frag")
);
using IgnoreError = ghoul::opengl::ProgramObject::IgnoreError;
//_downscaledVolumeProgram->setIgnoreSubroutineUniformLocationError(IgnoreError::Yes);
//_downscaledVolumeProgram->setIgnoreUniformLocationError(IgnoreError::Yes);
}
void FramebufferRenderer::render(Scene* scene, Camera* camera, float blackoutFactor) {

View File

@@ -335,7 +335,7 @@ RenderEngine::RenderEngine()
_hue.onChange([this]() {
if (_renderer) {
const float h = _hue / 360.0;
const float h = _hue / 360.f;
_renderer->setHue(h);
}
});

View File

@@ -124,9 +124,13 @@ void HttpRequest::perform(RequestOptions opt) {
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlfunctions::writeCallback);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); // NOLINT
#if LIBCURL_VERSION_NUM >= 0x072000
// xferinfo was introduced in 7.32.0, if a lower curl version is used the progress
// will not be shown for downloads on the splash screen
curl_easy_setopt(curl, CURLOPT_XFERINFODATA, this); // NOLINT
// NOLINTNEXTLINE
curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, curlfunctions::progressCallback);
#endif
if (opt.requestTimeoutSeconds > 0) {
curl_easy_setopt(curl, CURLOPT_TIMEOUT, opt.requestTimeoutSeconds); // NOLINT

185
src/util/touch.cpp Normal file
View File

@@ -0,0 +1,185 @@
/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2020 *
* *
* 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 <openspace/util/touch.h>
#include <openspace/engine/globals.h>
#include <openspace/engine/windowdelegate.h>
#include <cmath>
namespace openspace {
TouchInput::TouchInput(size_t touchDeviceId, size_t fingerId, float x, float y,
double timestamp)
: touchDeviceId(touchDeviceId)
, fingerId(fingerId)
, x(x)
, y(y)
, timestamp(timestamp)
{}
glm::vec2 TouchInput::screenCoordinates(glm::vec2 resolution) const {
return { std::floor(x * resolution.x + 0.5f), std::floor(y * resolution.y + 0.5f) };
}
glm::vec2 TouchInput::currentWindowCoordinates() const {
glm::vec2 res = global::windowDelegate.currentWindowSize();
return { std::floor(x * res.x + 0.5f), std::floor(y * res.y + 0.5f) };
}
bool TouchInput::isMoving() const {
return dx != 0.f || dy != 0.f;
}
float TouchInput::distanceToPos(float otherX, float otherY) const {
const float distX = x - otherX;
const float distY = y - otherY;
return std::sqrt(distX*distX + distY*distY);
}
float TouchInput::angleToPos(float otherX, float otherY) const {
const float side = x - otherX;
const float height = y - otherY;
const float distance = distanceToPos(otherX, otherY);
float angle = glm::half_pi<float>() + std::asin(side / distance);
if (height < 0.f) {
angle = 2.f * glm::pi<float>() - angle;
}
return angle;
}
TouchInputHolder::TouchInputHolder(TouchInput input)
: _inputs{ input }
, _touchDeviceId(input.touchDeviceId)
, _fingerId(input.fingerId)
{}
bool TouchInputHolder::tryAddInput(TouchInput input) {
constexpr const double ONE_MS = 0.001;
const TouchInput& lastInput = latestInput();
input.dx = input.x - lastInput.x;
input.dy = input.y - lastInput.y;
const bool sameTimeAsLastInput = (input.timestamp - lastInput.timestamp) > ONE_MS;
bool wasInserted = false;
if (isMoving()) {
_inputs.emplace_front(input);
wasInserted = true;
}
else if (sameTimeAsLastInput && input.isMoving()) {
_inputs.emplace_front(input);
wasInserted = true;
}
constexpr const int MaxInputs = 128;
if (_inputs.size() > MaxInputs) {
_inputs.pop_back();
}
return wasInserted;
}
void TouchInputHolder::clearInputs() {
_inputs.clear();
}
bool TouchInputHolder::holdsInput(const TouchInput &input) const {
return input.fingerId == _fingerId && input.touchDeviceId == _touchDeviceId;
}
size_t TouchInputHolder::touchDeviceId() const {
return _touchDeviceId;
}
size_t TouchInputHolder::fingerId() const {
return _fingerId;
}
float TouchInputHolder::speedX() const {
if (_inputs.size() <= 1) {
return 0.f;
}
const TouchInput& currentInput = _inputs[0];
const TouchInput& previousInput = _inputs[1];
const float dt = static_cast<float>(currentInput.timestamp - previousInput.timestamp);
return currentInput.dx / dt;
}
float TouchInputHolder::speedY() const {
if(_inputs.size() <= 1) {
return 0.f;
}
const TouchInput& currentInput = _inputs[0];
const TouchInput& previousInput = _inputs[1];
const float dt = static_cast<float>(currentInput.timestamp - previousInput.timestamp);
return currentInput.dy / dt;
}
bool TouchInputHolder::isMoving() const {
if (_inputs.size() <= 1) {
return false;
}
const TouchInput& currentInput = _inputs[0];
return currentInput.dx != 0.f || currentInput.dy != 0.f;
}
float TouchInputHolder::gestureDistance() const {
if (_inputs.size() <= 1) {
return 0.f;
}
float distX = 0.f;
float distY = 0.f;
const float startX = _inputs.front().x;
const float startY = _inputs.front().y;
for (const TouchInput& input : _inputs) {
distX += std::abs(input.x - startX);
distY += std::abs(input.y - startY);
}
return std::sqrt(distX*distX + distY*distY);
}
double TouchInputHolder::gestureTime() const {
if (_inputs.size() <= 1) {
return 0.0;
}
const double before = _inputs.back().timestamp;
const double after = _inputs.front().timestamp;
return after - before;
}
size_t TouchInputHolder::numInputs() const {
return _inputs.size();
}
const TouchInput& TouchInputHolder::latestInput() const {
return _inputs.front();
}
const std::deque<TouchInput>& TouchInputHolder::peekInputs() const {
return _inputs;
}
} // namespace openspace