mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-01-07 20:21:24 -06:00
Merge branch 'master' into feature/AALines2
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
88
include/openspace/util/touch.h
Normal file
88
include/openspace/util/touch.h
Normal 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__
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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__
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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
185
src/util/touch.cpp
Normal 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
|
||||
Reference in New Issue
Block a user