mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 03:24:00 +02:00
Merge remote-tracking branch 'upstream/master' into particle_explorer
This commit is contained in:
commit
55178cdc91
10 changed files with 130 additions and 102 deletions
BIN
examples/tests/dot.png
Normal file
BIN
examples/tests/dot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.9 KiB |
46
examples/tests/overlayMouseTrackingTest.js
Normal file
46
examples/tests/overlayMouseTrackingTest.js
Normal file
|
@ -0,0 +1,46 @@
|
|||
MouseTracker = function() {
|
||||
this.WIDTH = 60;
|
||||
this.HEIGHT = 60;
|
||||
|
||||
this.overlay = Overlays.addOverlay("image", {
|
||||
imageURL: Script.resolvePath("dot.png"),
|
||||
width: this.HEIGHT,
|
||||
height: this.WIDTH,
|
||||
x: 100,
|
||||
y: 100,
|
||||
visible: true
|
||||
});
|
||||
|
||||
var that = this;
|
||||
Script.scriptEnding.connect(function() {
|
||||
that.onCleanup();
|
||||
});
|
||||
|
||||
Controller.mousePressEvent.connect(function(event) {
|
||||
that.onMousePress(event);
|
||||
});
|
||||
|
||||
Controller.mouseMoveEvent.connect(function(event) {
|
||||
that.onMouseMove(event);
|
||||
});
|
||||
}
|
||||
|
||||
MouseTracker.prototype.onCleanup = function() {
|
||||
Overlays.deleteOverlay(this.overlay);
|
||||
}
|
||||
|
||||
MouseTracker.prototype.onMousePress = function(event) {
|
||||
}
|
||||
|
||||
MouseTracker.prototype.onMouseMove = function(event) {
|
||||
var width = Overlays.width();
|
||||
var height = Overlays.height();
|
||||
var x = Math.max(event.x, 0);
|
||||
x = Math.min(x, width);
|
||||
var y = Math.max(event.y, 0);
|
||||
y = Math.min(y, height);
|
||||
Overlays.editOverlay(this.overlay, {x: x - this.WIDTH / 2.0, y: y - this.HEIGHT / 2.0});
|
||||
}
|
||||
|
||||
|
||||
new MouseTracker();
|
|
@ -1849,8 +1849,16 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
|
|||
|
||||
|
||||
_entities.mouseMoveEvent(event, deviceID);
|
||||
{
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
QPointF transformedPos = offscreenUi->mapToVirtualScreen(event->localPos(), _glWidget);
|
||||
QMouseEvent mappedEvent(event->type(),
|
||||
transformedPos,
|
||||
event->screenPos(), event->button(),
|
||||
event->buttons(), event->modifiers());
|
||||
_controllerScriptingInterface.emitMouseMoveEvent(&mappedEvent, deviceID); // send events to any registered scripts
|
||||
}
|
||||
|
||||
_controllerScriptingInterface.emitMouseMoveEvent(event, deviceID); // send events to any registered scripts
|
||||
// if one of our scripts have asked to capture this event, then stop processing it
|
||||
if (_controllerScriptingInterface.isMouseCaptured()) {
|
||||
return;
|
||||
|
@ -1865,12 +1873,19 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
|
|||
void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) {
|
||||
// Inhibit the menu if the user is using alt-mouse dragging
|
||||
_altPressed = false;
|
||||
|
||||
if (!_aboutToQuit) {
|
||||
_entities.mousePressEvent(event, deviceID);
|
||||
}
|
||||
|
||||
_controllerScriptingInterface.emitMousePressEvent(event); // send events to any registered scripts
|
||||
{
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
QPointF transformedPos = offscreenUi->mapToVirtualScreen(event->localPos(), _glWidget);
|
||||
QMouseEvent mappedEvent(event->type(),
|
||||
transformedPos,
|
||||
event->screenPos(), event->button(),
|
||||
event->buttons(), event->modifiers());
|
||||
_controllerScriptingInterface.emitMousePressEvent(&mappedEvent); // send events to any registered scripts
|
||||
}
|
||||
|
||||
// if one of our scripts have asked to capture this event, then stop processing it
|
||||
if (_controllerScriptingInterface.isMouseCaptured()) {
|
||||
|
@ -1921,7 +1936,15 @@ void Application::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {
|
|||
_entities.mouseReleaseEvent(event, deviceID);
|
||||
}
|
||||
|
||||
_controllerScriptingInterface.emitMouseReleaseEvent(event); // send events to any registered scripts
|
||||
{
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
QPointF transformedPos = offscreenUi->mapToVirtualScreen(event->localPos(), _glWidget);
|
||||
QMouseEvent mappedEvent(event->type(),
|
||||
transformedPos,
|
||||
event->screenPos(), event->button(),
|
||||
event->buttons(), event->modifiers());
|
||||
_controllerScriptingInterface.emitMouseReleaseEvent(&mappedEvent); // send events to any registered scripts
|
||||
}
|
||||
|
||||
// if one of our scripts have asked to capture this event, then stop processing it
|
||||
if (_controllerScriptingInterface.isMouseCaptured()) {
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
#include "Overlays.h"
|
||||
|
||||
#include <QScriptValueIterator>
|
||||
#include <QtScript/QScriptValueIterator>
|
||||
|
||||
#include <limits>
|
||||
|
||||
|
@ -31,6 +31,7 @@
|
|||
#include "TextOverlay.h"
|
||||
#include "Text3DOverlay.h"
|
||||
#include "Web3DOverlay.h"
|
||||
#include <QtQuick/QQuickWindow>
|
||||
|
||||
|
||||
Overlays::Overlays() : _nextOverlayID(1) {
|
||||
|
@ -331,10 +332,6 @@ void Overlays::setParentPanel(unsigned int childId, unsigned int panelId) {
|
|||
|
||||
unsigned int Overlays::getOverlayAtPoint(const glm::vec2& point) {
|
||||
glm::vec2 pointCopy = point;
|
||||
if (qApp->isHMDMode()) {
|
||||
pointCopy = qApp->getApplicationCompositor().screenToOverlay(point);
|
||||
}
|
||||
|
||||
QReadLocker lock(&_lock);
|
||||
if (!_enabled) {
|
||||
return 0;
|
||||
|
@ -607,3 +604,13 @@ void Overlays::deletePanel(unsigned int panelId) {
|
|||
bool Overlays::isAddedOverlay(unsigned int id) {
|
||||
return _overlaysHUD.contains(id) || _overlaysWorld.contains(id);
|
||||
}
|
||||
|
||||
float Overlays::width() const {
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
return offscreenUi->getWindow()->size().width();
|
||||
}
|
||||
|
||||
float Overlays::height() const {
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
return offscreenUi->getWindow()->size().height();
|
||||
}
|
|
@ -113,6 +113,10 @@ public slots:
|
|||
/// overlay; in meters if it is a 3D text overlay
|
||||
QSizeF textSize(unsigned int id, const QString& text) const;
|
||||
|
||||
// Return the size of the virtual screen
|
||||
float width() const;
|
||||
float height() const;
|
||||
|
||||
|
||||
/// adds a panel that has already been created
|
||||
unsigned int addPanel(OverlayPanel::Pointer panel);
|
||||
|
|
|
@ -13,11 +13,12 @@
|
|||
|
||||
#include <QCoreApplication>
|
||||
|
||||
#include <PerfStat.h>
|
||||
#include <GLMHelpers.h>
|
||||
#include <NumericalConstants.h>
|
||||
#include <PerfStat.h>
|
||||
#include <plugins/PluginContainer.h>
|
||||
|
||||
#include "NumericalConstants.h"
|
||||
#include <plugins/PluginContainer.h>
|
||||
#include "SixenseManager.h"
|
||||
#include "UserActivityLogger.h"
|
||||
|
||||
|
@ -38,14 +39,9 @@ const unsigned int RIGHT_MASK = 1U << 1;
|
|||
|
||||
const int CALIBRATION_STATE_IDLE = 0;
|
||||
const int CALIBRATION_STATE_X = 1;
|
||||
const int CALIBRATION_STATE_Y = 2;
|
||||
const int CALIBRATION_STATE_Z = 3;
|
||||
const int CALIBRATION_STATE_COMPLETE = 4;
|
||||
const int CALIBRATION_STATE_COMPLETE = 2;
|
||||
|
||||
// default (expected) location of neck in sixense space
|
||||
const float NECK_X = 0.25f; // meters
|
||||
const float NECK_Y = 0.3f; // meters
|
||||
const float NECK_Z = 0.3f; // meters
|
||||
const glm::vec3 DEFAULT_AVATAR_POSITION(-0.25f, -0.35f, -0.3f); // in hydra frame
|
||||
|
||||
const float CONTROLLER_THRESHOLD = 0.35f;
|
||||
|
||||
|
@ -92,9 +88,7 @@ bool SixenseManager::isSupported() const {
|
|||
void SixenseManager::activate() {
|
||||
#ifdef HAVE_SIXENSE
|
||||
_calibrationState = CALIBRATION_STATE_IDLE;
|
||||
// By default we assume the _neckBase (in orb frame) is as high above the orb
|
||||
// as the "torso" is below it.
|
||||
_neckBase = glm::vec3(NECK_X, -NECK_Y, NECK_Z);
|
||||
_avatarPosition = DEFAULT_AVATAR_POSITION;
|
||||
|
||||
CONTAINER->addMenu(MENU_PATH);
|
||||
CONTAINER->addMenuItem(MENU_PATH, TOGGLE_SMOOTH,
|
||||
|
@ -258,11 +252,13 @@ void SixenseManager::update(float deltaTime, bool jointsCaptured) {
|
|||
#ifdef HAVE_SIXENSE
|
||||
|
||||
// the calibration sequence is:
|
||||
// (1) press BUTTON_FWD on both hands
|
||||
// (2) reach arm straight out to the side (X)
|
||||
// (3) lift arms staight up above head (Y)
|
||||
// (4) move arms a bit forward (Z)
|
||||
// (5) release BUTTON_FWD on both hands
|
||||
// (1) reach arm straight out to the sides (xAxis is to the left)
|
||||
// (2) press BUTTON_FWD on both hands and hold for one second
|
||||
// (3) release both BUTTON_FWDs
|
||||
//
|
||||
// The code will:
|
||||
// (4) assume that the orb is on a flat surface (yAxis is UP)
|
||||
// (5) compute the forward direction (zAxis = xAxis cross yAxis)
|
||||
|
||||
const float MINIMUM_ARM_REACH = 0.3f; // meters
|
||||
const float MAXIMUM_NOISE_LEVEL = 0.05f; // meters
|
||||
|
@ -279,21 +275,16 @@ void SixenseManager::updateCalibration(void* controllersX) {
|
|||
return;
|
||||
}
|
||||
switch (_calibrationState) {
|
||||
case CALIBRATION_STATE_Y:
|
||||
case CALIBRATION_STATE_Z:
|
||||
case CALIBRATION_STATE_COMPLETE:
|
||||
{
|
||||
// compute calibration results
|
||||
// ATM we only handle the case where the XAxis has been measured, and we assume the rest
|
||||
// (i.e. that the orb is on a level surface)
|
||||
// TODO: handle COMPLETE state where all three axes have been defined. This would allow us
|
||||
// to also handle the case where left and right controllers have been reversed.
|
||||
_neckBase = 0.5f * (_reachLeft + _reachRight); // neck is midway between right and left reaches
|
||||
_avatarPosition = - 0.5f * (_reachLeft + _reachRight); // neck is midway between right and left hands
|
||||
glm::vec3 xAxis = glm::normalize(_reachRight - _reachLeft);
|
||||
glm::vec3 yAxis(0.0f, 1.0f, 0.0f);
|
||||
glm::vec3 zAxis = glm::normalize(glm::cross(xAxis, yAxis));
|
||||
xAxis = glm::normalize(glm::cross(yAxis, zAxis));
|
||||
_orbRotation = glm::inverse(glm::quat_cast(glm::mat3(xAxis, yAxis, zAxis)));
|
||||
glm::vec3 zAxis = glm::normalize(glm::cross(xAxis, Vectors::UNIT_Y));
|
||||
xAxis = glm::normalize(glm::cross(Vectors::UNIT_Y, zAxis));
|
||||
_avatarRotation = glm::inverse(glm::quat_cast(glm::mat3(xAxis, Vectors::UNIT_Y, zAxis)));
|
||||
const float Y_OFFSET_CALIBRATED_HANDS_TO_AVATAR = -0.3f;
|
||||
_avatarPosition.y += Y_OFFSET_CALIBRATED_HANDS_TO_AVATAR;
|
||||
qCDebug(inputplugins, "succeess: sixense calibration");
|
||||
}
|
||||
break;
|
||||
|
@ -349,54 +340,10 @@ void SixenseManager::updateCalibration(void* controllersX) {
|
|||
_lockExpiry = now + LOCK_DURATION;
|
||||
_lastDistance = 0.0f;
|
||||
_reachUp = 0.5f * (_reachLeft + _reachRight);
|
||||
_calibrationState = CALIBRATION_STATE_Y;
|
||||
_calibrationState = CALIBRATION_STATE_COMPLETE;
|
||||
qCDebug(inputplugins, "success: sixense calibration: left");
|
||||
}
|
||||
}
|
||||
else if (_calibrationState == CALIBRATION_STATE_Y) {
|
||||
glm::vec3 torso = 0.5f * (_reachLeft + _reachRight);
|
||||
glm::vec3 averagePosition = 0.5f * (_averageLeft + _averageRight);
|
||||
float distance = (averagePosition - torso).y;
|
||||
if (fabsf(distance) > fabsf(_lastDistance) + MAXIMUM_NOISE_LEVEL) {
|
||||
// distance is increasing so acquire the data and push the expiry out
|
||||
_reachUp = averagePosition;
|
||||
_lastDistance = distance;
|
||||
_lockExpiry = now + LOCK_DURATION;
|
||||
} else if (now > _lockExpiry) {
|
||||
if (_lastDistance > MINIMUM_ARM_REACH) {
|
||||
// lock has expired so clamp the data and move on
|
||||
_reachForward = _reachUp;
|
||||
_lastDistance = 0.0f;
|
||||
_lockExpiry = now + LOCK_DURATION;
|
||||
_calibrationState = CALIBRATION_STATE_Z;
|
||||
qCDebug(inputplugins, "success: sixense calibration: up");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (_calibrationState == CALIBRATION_STATE_Z) {
|
||||
glm::vec3 xAxis = glm::normalize(_reachRight - _reachLeft);
|
||||
glm::vec3 torso = 0.5f * (_reachLeft + _reachRight);
|
||||
//glm::vec3 yAxis = glm::normalize(_reachUp - torso);
|
||||
glm::vec3 yAxis(0.0f, 1.0f, 0.0f);
|
||||
glm::vec3 zAxis = glm::normalize(glm::cross(xAxis, yAxis));
|
||||
|
||||
glm::vec3 averagePosition = 0.5f * (_averageLeft + _averageRight);
|
||||
float distance = glm::dot((averagePosition - torso), zAxis);
|
||||
if (fabs(distance) > fabs(_lastDistance)) {
|
||||
// distance is increasing so acquire the data and push the expiry out
|
||||
_reachForward = averagePosition;
|
||||
_lastDistance = distance;
|
||||
_lockExpiry = now + LOCK_DURATION;
|
||||
} else if (now > _lockExpiry) {
|
||||
if (fabsf(_lastDistance) > 0.05f * MINIMUM_ARM_REACH) {
|
||||
// lock has expired so clamp the data and move on
|
||||
_calibrationState = CALIBRATION_STATE_COMPLETE;
|
||||
qCDebug(inputplugins, "success: sixense calibration: forward");
|
||||
// TODO: it is theoretically possible to detect that the controllers have been
|
||||
// accidentally switched (left hand is holding right controller) and to swap the order.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // HAVE_SIXENSE
|
||||
|
@ -456,12 +403,9 @@ void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, int
|
|||
// z
|
||||
|
||||
// Transform the measured position into body frame.
|
||||
glm::vec3 neck = _neckBase;
|
||||
// Set y component of the "neck" to raise the measured position a little bit.
|
||||
neck.y = 0.5f;
|
||||
position = _orbRotation * (position - neck);
|
||||
position = _avatarRotation * (position + _avatarPosition);
|
||||
|
||||
// From ABOVE the hand canonical axes looks like this:
|
||||
// From ABOVE the hand canonical axes look like this:
|
||||
//
|
||||
// | | | | y | | | |
|
||||
// | | | | | | | | |
|
||||
|
@ -480,28 +424,25 @@ void SixenseManager::handlePoseEvent(glm::vec3 position, glm::quat rotation, int
|
|||
//
|
||||
// Qsh = angleAxis(PI, zAxis) * angleAxis(-PI/2, xAxis)
|
||||
//
|
||||
const glm::vec3 xAxis = glm::vec3(1.0f, 0.0f, 0.0f);
|
||||
const glm::vec3 yAxis = glm::vec3(0.0f, 1.0f, 0.0f);
|
||||
const glm::vec3 zAxis = glm::vec3(0.0f, 0.0f, 1.0f);
|
||||
const glm::quat sixenseToHand = glm::angleAxis(PI, zAxis) * glm::angleAxis(-PI/2.0f, xAxis);
|
||||
const glm::quat sixenseToHand = glm::angleAxis(PI, Vectors::UNIT_Z) * glm::angleAxis(-PI/2.0f, Vectors::UNIT_X);
|
||||
|
||||
// In addition to Qsh each hand has pre-offset introduced by the shape of the sixense controllers
|
||||
// and how they fit into the hand in their relaxed state. This offset is a quarter turn about
|
||||
// the sixense's z-axis, with its direction different for the two hands:
|
||||
float sign = (index == 0) ? 1.0f : -1.0f;
|
||||
const glm::quat preOffset = glm::angleAxis(sign * PI / 2.0f, zAxis);
|
||||
const glm::quat preOffset = glm::angleAxis(sign * PI / 2.0f, Vectors::UNIT_Z);
|
||||
|
||||
// Finally, there is a post-offset (same for both hands) to get the hand's rest orientation
|
||||
// (fingers forward, palm down) aligned properly in the avatar's model-frame,
|
||||
// and then a flip about the yAxis to get into model-frame.
|
||||
const glm::quat postOffset = glm::angleAxis(PI, yAxis) * glm::angleAxis(PI / 2.0f, xAxis);
|
||||
const glm::quat postOffset = glm::angleAxis(PI, Vectors::UNIT_Y) * glm::angleAxis(PI / 2.0f, Vectors::UNIT_X);
|
||||
|
||||
// The total rotation of the hand uses the formula:
|
||||
//
|
||||
// rotation = postOffset * Qsh^ * (measuredRotation * preOffset) * Qsh
|
||||
//
|
||||
// TODO: find a shortcut with fewer rotations.
|
||||
rotation = postOffset * glm::inverse(sixenseToHand) * rotation * preOffset * sixenseToHand;
|
||||
rotation = _avatarRotation * postOffset * glm::inverse(sixenseToHand) * rotation * preOffset * sixenseToHand;
|
||||
|
||||
_poseStateMap[makeInput(JointChannel(index)).getChannel()] = UserInputMapper::PoseValue(position, rotation);
|
||||
#endif // HAVE_SIXENSE
|
||||
|
|
|
@ -97,8 +97,8 @@ private:
|
|||
int _calibrationState;
|
||||
|
||||
// these are calibration results
|
||||
glm::vec3 _neckBase; // midpoint between controllers during X-axis calibration
|
||||
glm::quat _orbRotation; // rotates from orb frame into body frame
|
||||
glm::vec3 _avatarPosition; // in hydra-frame
|
||||
glm::quat _avatarRotation; // in hydra-frame
|
||||
float _armLength;
|
||||
|
||||
// these are measured values used to compute the calibration results
|
||||
|
|
|
@ -496,6 +496,13 @@ QPointF OffscreenQmlSurface::mapWindowToUi(const QPointF& sourcePosition, QObjec
|
|||
return QPointF(offscreenPosition.x, offscreenPosition.y);
|
||||
}
|
||||
|
||||
QPointF OffscreenQmlSurface::mapToVirtualScreen(const QPointF& originalPoint, QObject* originalWidget) {
|
||||
QPointF transformedPos = _mouseTranslator(originalPoint);
|
||||
transformedPos = mapWindowToUi(transformedPos, originalWidget);
|
||||
return transformedPos;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
//
|
||||
// Event handling customization
|
||||
|
@ -541,8 +548,9 @@ bool OffscreenQmlSurface::eventFilter(QObject* originalDestination, QEvent* even
|
|||
|
||||
case QEvent::Wheel: {
|
||||
QWheelEvent* wheelEvent = static_cast<QWheelEvent*>(event);
|
||||
QPointF transformedPos = mapToVirtualScreen(wheelEvent->pos(), originalDestination);
|
||||
QWheelEvent mappedEvent(
|
||||
mapWindowToUi(wheelEvent->pos(), originalDestination),
|
||||
transformedPos,
|
||||
wheelEvent->delta(), wheelEvent->buttons(),
|
||||
wheelEvent->modifiers(), wheelEvent->orientation());
|
||||
mappedEvent.ignore();
|
||||
|
@ -558,9 +566,7 @@ bool OffscreenQmlSurface::eventFilter(QObject* originalDestination, QEvent* even
|
|||
case QEvent::MouseButtonRelease:
|
||||
case QEvent::MouseMove: {
|
||||
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
|
||||
QPointF originalPos = mouseEvent->localPos();
|
||||
QPointF transformedPos = _mouseTranslator(originalPos);
|
||||
transformedPos = mapWindowToUi(transformedPos, originalDestination);
|
||||
QPointF transformedPos = mapToVirtualScreen(mouseEvent->localPos(), originalDestination);
|
||||
QMouseEvent mappedEvent(mouseEvent->type(),
|
||||
transformedPos,
|
||||
mouseEvent->screenPos(), mouseEvent->button(),
|
||||
|
|
|
@ -61,6 +61,7 @@ public:
|
|||
QQuickWindow* getWindow();
|
||||
QObject* getEventHandler();
|
||||
|
||||
QPointF mapToVirtualScreen(const QPointF& originalPoint, QObject* originalWidget);
|
||||
virtual bool eventFilter(QObject* originalDestination, QEvent* event);
|
||||
|
||||
signals:
|
||||
|
|
|
@ -23,12 +23,12 @@ float Interpolate::bezierInterpolate(float y1, float y2, float y3, float u) {
|
|||
float Interpolate::interpolate3Points(float y1, float y2, float y3, float u) {
|
||||
assert(0.0f <= u && u <= 1.0f);
|
||||
|
||||
if (u <= 0.5f && y1 == y2 || u >= 0.5f && y2 == y3) {
|
||||
if ((u <= 0.5f && y1 == y2) || (u >= 0.5f && y2 == y3)) {
|
||||
// Flat line.
|
||||
return y2;
|
||||
}
|
||||
|
||||
if (y2 >= y1 && y2 >= y3 || y2 <= y1 && y2 <= y3) {
|
||||
if ((y2 >= y1 && y2 >= y3) || (y2 <= y1 && y2 <= y3)) {
|
||||
// U or inverted-U shape.
|
||||
// Make the slope at y2 = 0, which means that the control points half way between the value points have the value y2.
|
||||
if (u <= 0.5f) {
|
||||
|
|
Loading…
Reference in a new issue