diff --git a/interface/resources/qml/controls-uit/Key.qml b/interface/resources/qml/controls-uit/Key.qml index e54250c872..3bba1e5f07 100644 --- a/interface/resources/qml/controls-uit/Key.qml +++ b/interface/resources/qml/controls-uit/Key.qml @@ -64,11 +64,12 @@ Item { keyItem.state = "mouseOver"; var globalPosition = keyItem.mapToGlobal(mouseArea1.mouseX, mouseArea1.mouseY); - var deviceId = Web3DOverlay.deviceIdByTouchPoint(globalPosition.x, globalPosition.y); - var hand = deviceId - 1; // based on touchEventUtils.js, deviceId is 'hand + 1', so 'hand' is 'deviceId' - 1 + var pointerID = Web3DOverlay.deviceIdByTouchPoint(globalPosition.x, globalPosition.y); - if (hand == leftHand || hand == rightHand) { - Controller.triggerHapticPulse(_HAPTIC_STRENGTH, _HAPTIC_DURATION, hand); + if (Pointers.isLeftHand(pointerID)) { + Controller.triggerHapticPulse(_HAPTIC_STRENGTH, _HAPTIC_DURATION, leftHand); + } else if (Pointers.isRightHand(pointerID)) { + Controller.triggerHapticPulse(_HAPTIC_STRENGTH, _HAPTIC_DURATION, rightHand); } } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 420c4e7ce6..a77f066cdb 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1812,11 +1812,17 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(&_myCamera, &Camera::modeUpdated, this, &Application::cameraModeChanged); DependencyManager::get()->setShouldPickHUDOperator([&]() { return DependencyManager::get()->isHMDMode(); }); + DependencyManager::get()->setCalculatePos2DFromHUDOperator([&](const glm::vec3& intersection) { + const glm::vec2 MARGIN(25.0f); + glm::vec2 maxPos = _controllerScriptingInterface->getViewportDimensions() - MARGIN; + glm::vec2 pos2D = DependencyManager::get()->overlayFromWorldPoint(intersection); + return glm::max(MARGIN, glm::min(pos2D, maxPos)); + }); // Setup the mouse ray pick and related operators DependencyManager::get()->setMouseRayPickID(DependencyManager::get()->addPick(PickQuery::Ray, std::make_shared( PickFilter(PickScriptingInterface::PICK_ENTITIES() | PickScriptingInterface::PICK_INCLUDE_NONCOLLIDABLE()), 0.0f, true))); - DependencyManager::get()->setMouseRayPickResultOperator([&](QUuid rayPickID) { + DependencyManager::get()->setMouseRayPickResultOperator([&](unsigned int rayPickID) { RayToEntityIntersectionResult entityResult; entityResult.intersects = false; auto pickResult = DependencyManager::get()->getPrevPickResultTyped(rayPickID); @@ -1832,7 +1838,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo } return entityResult; }); - DependencyManager::get()->setSetPrecisionPickingOperator([&](QUuid rayPickID, bool value) { + DependencyManager::get()->setSetPrecisionPickingOperator([&](unsigned int rayPickID, bool value) { DependencyManager::get()->setPrecisionPicking(rayPickID, value); }); @@ -3290,7 +3296,7 @@ void Application::mouseMoveEvent(QMouseEvent* event) { auto offscreenUi = DependencyManager::get(); auto eventPosition = compositor.getMouseEventPosition(event); - QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition, _glWidget); + QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition); auto button = event->button(); auto buttons = event->buttons(); // Determine if the ReticleClick Action is 1 and if so, fake include the LeftMouseButton @@ -3336,7 +3342,7 @@ void Application::mousePressEvent(QMouseEvent* event) { offscreenUi->unfocusWindows(); auto eventPosition = getApplicationCompositor().getMouseEventPosition(event); - QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition, _glWidget); + QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition); QMouseEvent mappedEvent(event->type(), transformedPos, event->screenPos(), event->button(), @@ -3366,7 +3372,7 @@ void Application::mousePressEvent(QMouseEvent* event) { void Application::mouseDoublePressEvent(QMouseEvent* event) { auto offscreenUi = DependencyManager::get(); auto eventPosition = getApplicationCompositor().getMouseEventPosition(event); - QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition, _glWidget); + QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition); QMouseEvent mappedEvent(event->type(), transformedPos, event->screenPos(), event->button(), @@ -3392,7 +3398,7 @@ void Application::mouseReleaseEvent(QMouseEvent* event) { auto offscreenUi = DependencyManager::get(); auto eventPosition = getApplicationCompositor().getMouseEventPosition(event); - QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition, _glWidget); + QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition); QMouseEvent mappedEvent(event->type(), transformedPos, event->screenPos(), event->button(), diff --git a/interface/src/raypick/JointRayPick.h b/interface/src/raypick/JointRayPick.h index 0ef39d6336..947eb81ba8 100644 --- a/interface/src/raypick/JointRayPick.h +++ b/interface/src/raypick/JointRayPick.h @@ -20,6 +20,9 @@ public: PickRay getMathematicalPick() const override; + bool isLeftHand() const override { return (_jointName == "_CONTROLLER_LEFTHAND") || (_jointName == "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND"); } + bool isRightHand() const override { return (_jointName == "_CONTROLLER_RIGHTHAND") || (_jointName == "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND"); } + private: std::string _jointName; glm::vec3 _posOffset; diff --git a/interface/src/raypick/LaserPointer.cpp b/interface/src/raypick/LaserPointer.cpp index bf1847e3f6..464de83565 100644 --- a/interface/src/raypick/LaserPointer.cpp +++ b/interface/src/raypick/LaserPointer.cpp @@ -290,7 +290,6 @@ RenderState LaserPointer::buildRenderState(const QVariantMap& propMap) { } PointerEvent LaserPointer::buildPointerEvent(const PickedObject& target, const PickResultPointer& pickResult) const { - uint32_t id = 0; QUuid pickedID; glm::vec3 intersection, surfaceNormal, direction, origin; if (target.type != NONE) { @@ -302,7 +301,7 @@ PointerEvent LaserPointer::buildPointerEvent(const PickedObject& target, const P origin = vec3FromVariant(searchRay["origin"]); pickedID = rayPickResult->objectID;; } - + glm::vec2 pos2D; if (pickedID != target.objectID) { if (target.type == ENTITY) { @@ -315,8 +314,10 @@ PointerEvent LaserPointer::buildPointerEvent(const PickedObject& target, const P pos2D = projectOntoEntityXYPlane(target.objectID, intersection); } else if (target.type == OVERLAY) { pos2D = projectOntoOverlayXYPlane(target.objectID, intersection); + } else if (target.type == HUD) { + pos2D = DependencyManager::get()->calculatePos2DFromHUD(intersection); } - return PointerEvent(PointerEvent::Move, id, pos2D, intersection, surfaceNormal, direction, PointerEvent::NoButtons); + return PointerEvent(pos2D, intersection, surfaceNormal, direction); } glm::vec3 LaserPointer::intersectRayWithXYPlane(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& point, const glm::quat rotation, const glm::vec3& registration) const { diff --git a/interface/src/raypick/LaserPointer.h b/interface/src/raypick/LaserPointer.h index 9fbbadb475..c93404e14d 100644 --- a/interface/src/raypick/LaserPointer.h +++ b/interface/src/raypick/LaserPointer.h @@ -67,14 +67,17 @@ public: void updateVisuals(const PickResultPointer& prevRayPickResult) override; - PickedObject getHoveredObject(const PickResultPointer& pickResult) override; - Pointer::Buttons getPressedButtons() override; - static RenderState buildRenderState(const QVariantMap& propMap); protected: PointerEvent buildPointerEvent(const PickedObject& target, const PickResultPointer& pickResult) const override; + PickedObject getHoveredObject(const PickResultPointer& pickResult) override; + Pointer::Buttons getPressedButtons() override; + + bool shouldHover() override { return _currentRenderState != ""; } + bool shouldTrigger() override { return _currentRenderState != ""; } + private: PointerTriggers _triggers; float _laserLength { 0.0f }; diff --git a/interface/src/raypick/LaserPointerScriptingInterface.cpp b/interface/src/raypick/LaserPointerScriptingInterface.cpp index 92ad837e7a..25720b5f9d 100644 --- a/interface/src/raypick/LaserPointerScriptingInterface.cpp +++ b/interface/src/raypick/LaserPointerScriptingInterface.cpp @@ -14,21 +14,22 @@ #include "RegisteredMetaTypes.h" #include "PointerScriptingInterface.h" -void LaserPointerScriptingInterface::setIgnoreItems(const QUuid& uid, const QScriptValue& ignoreItems) const { +void LaserPointerScriptingInterface::setIgnoreItems(unsigned int uid, const QScriptValue& ignoreItems) const { DependencyManager::get()->setIgnoreItems(uid, qVectorQUuidFromScriptValue(ignoreItems)); } -void LaserPointerScriptingInterface::setIncludeItems(const QUuid& uid, const QScriptValue& includeItems) const { + +void LaserPointerScriptingInterface::setIncludeItems(unsigned int uid, const QScriptValue& includeItems) const { DependencyManager::get()->setIncludeItems(uid, qVectorQUuidFromScriptValue(includeItems)); } -QUuid LaserPointerScriptingInterface::createLaserPointer(const QVariant& properties) const { +unsigned int LaserPointerScriptingInterface::createLaserPointer(const QVariant& properties) const { return DependencyManager::get()->createLaserPointer(properties); } -void LaserPointerScriptingInterface::editRenderState(const QUuid& uid, const QString& renderState, const QVariant& properties) const { +void LaserPointerScriptingInterface::editRenderState(unsigned int uid, const QString& renderState, const QVariant& properties) const { DependencyManager::get()->editRenderState(uid, renderState, properties); } -QVariantMap LaserPointerScriptingInterface::getPrevRayPickResult(const QUuid& uid) const { +QVariantMap LaserPointerScriptingInterface::getPrevRayPickResult(unsigned int uid) const { return DependencyManager::get()->getPrevPickResult(uid); } diff --git a/interface/src/raypick/LaserPointerScriptingInterface.h b/interface/src/raypick/LaserPointerScriptingInterface.h index d1dd5499f4..f87f4f4631 100644 --- a/interface/src/raypick/LaserPointerScriptingInterface.h +++ b/interface/src/raypick/LaserPointerScriptingInterface.h @@ -20,21 +20,25 @@ class LaserPointerScriptingInterface : public QObject, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY -public slots: - Q_INVOKABLE QUuid createLaserPointer(const QVariant& properties) const; - Q_INVOKABLE void enableLaserPointer(const QUuid& uid) const { DependencyManager::get()->enablePointer(uid); } - Q_INVOKABLE void disableLaserPointer(const QUuid& uid) const { DependencyManager::get()->disablePointer(uid); } - Q_INVOKABLE void removeLaserPointer(const QUuid& uid) const { DependencyManager::get()->removePointer(uid); } - Q_INVOKABLE void editRenderState(const QUuid& uid, const QString& renderState, const QVariant& properties) const; - Q_INVOKABLE void setRenderState(const QUuid& uid, const QString& renderState) const { DependencyManager::get()->setRenderState(uid, renderState.toStdString()); } - Q_INVOKABLE QVariantMap getPrevRayPickResult(const QUuid& uid) const; +public: + Q_INVOKABLE unsigned int createLaserPointer(const QVariant& properties) const; + Q_INVOKABLE void enableLaserPointer(unsigned int uid) const { DependencyManager::get()->enablePointer(uid); } + Q_INVOKABLE void disableLaserPointer(unsigned int uid) const { DependencyManager::get()->disablePointer(uid); } + Q_INVOKABLE void removeLaserPointer(unsigned int uid) const { DependencyManager::get()->removePointer(uid); } + Q_INVOKABLE void editRenderState(unsigned int uid, const QString& renderState, const QVariant& properties) const; + Q_INVOKABLE void setRenderState(unsigned int uid, const QString& renderState) const { DependencyManager::get()->setRenderState(uid, renderState.toStdString()); } + Q_INVOKABLE QVariantMap getPrevRayPickResult(unsigned int uid) const; - Q_INVOKABLE void setPrecisionPicking(const QUuid& uid, bool precisionPicking) const { DependencyManager::get()->setPrecisionPicking(uid, precisionPicking); } - Q_INVOKABLE void setLaserLength(const QUuid& uid, float laserLength) const { DependencyManager::get()->setLength(uid, laserLength); } - Q_INVOKABLE void setIgnoreItems(const QUuid& uid, const QScriptValue& ignoreEntities) const; - Q_INVOKABLE void setIncludeItems(const QUuid& uid, const QScriptValue& includeEntities) const; + Q_INVOKABLE void setPrecisionPicking(unsigned int uid, bool precisionPicking) const { DependencyManager::get()->setPrecisionPicking(uid, precisionPicking); } + Q_INVOKABLE void setLaserLength(unsigned int uid, float laserLength) const { DependencyManager::get()->setLength(uid, laserLength); } + Q_INVOKABLE void setIgnoreItems(unsigned int uid, const QScriptValue& ignoreEntities) const; + Q_INVOKABLE void setIncludeItems(unsigned int uid, const QScriptValue& includeEntities) const; - Q_INVOKABLE void setLockEndUUID(const QUuid& uid, const QUuid& objectID, bool isOverlay) const { DependencyManager::get()->setLockEndUUID(uid, objectID, isOverlay); } + Q_INVOKABLE void setLockEndUUID(unsigned int uid, const QUuid& objectID, bool isOverlay) const { DependencyManager::get()->setLockEndUUID(uid, objectID, isOverlay); } + + Q_INVOKABLE bool isLeftHand(unsigned int uid) { return DependencyManager::get()->isLeftHand(uid); } + Q_INVOKABLE bool isRightHand(unsigned int uid) { return DependencyManager::get()->isRightHand(uid); } + Q_INVOKABLE bool isMouse(unsigned int uid) { return DependencyManager::get()->isMouse(uid); } }; diff --git a/interface/src/raypick/MouseRayPick.h b/interface/src/raypick/MouseRayPick.h index 81561d7459..79e94ed777 100644 --- a/interface/src/raypick/MouseRayPick.h +++ b/interface/src/raypick/MouseRayPick.h @@ -19,6 +19,8 @@ public: MouseRayPick(const PickFilter& filter, const float maxDistance = 0.0f, const bool enabled = false); PickRay getMathematicalPick() const override; + + bool isMouse() const override { return true; } }; #endif // hifi_MouseRayPick_h diff --git a/interface/src/raypick/PickScriptingInterface.cpp b/interface/src/raypick/PickScriptingInterface.cpp index ac81bcf72f..137c3fa6ae 100644 --- a/interface/src/raypick/PickScriptingInterface.cpp +++ b/interface/src/raypick/PickScriptingInterface.cpp @@ -20,16 +20,16 @@ #include #include -QUuid PickScriptingInterface::createPick(const PickQuery::PickType type, const QVariant& properties) { +unsigned int PickScriptingInterface::createPick(const PickQuery::PickType type, const QVariant& properties) { switch (type) { case PickQuery::PickType::Ray: return createRayPick(properties); default: - return QUuid(); + return PickManager::INVALID_PICK_ID; } } -QUuid PickScriptingInterface::createRayPick(const QVariant& properties) { +unsigned int PickScriptingInterface::createRayPick(const QVariant& properties) { QVariantMap propMap = properties.toMap(); bool enabled = false; @@ -78,22 +78,22 @@ QUuid PickScriptingInterface::createRayPick(const QVariant& properties) { return DependencyManager::get()->addPick(PickQuery::Ray, std::make_shared(position, direction, filter, maxDistance, enabled)); } - return QUuid(); + return PickManager::INVALID_PICK_ID; } -void PickScriptingInterface::enablePick(const QUuid& uid) { +void PickScriptingInterface::enablePick(unsigned int uid) { DependencyManager::get()->enablePick(uid); } -void PickScriptingInterface::disablePick(const QUuid& uid) { +void PickScriptingInterface::disablePick(unsigned int uid) { DependencyManager::get()->disablePick(uid); } -void PickScriptingInterface::removePick(const QUuid& uid) { +void PickScriptingInterface::removePick(unsigned int uid) { DependencyManager::get()->removePick(uid); } -QVariantMap PickScriptingInterface::getPrevPickResult(const QUuid& uid) { +QVariantMap PickScriptingInterface::getPrevPickResult(unsigned int uid) { QVariantMap result; auto pickResult = DependencyManager::get()->getPrevPickResult(uid); if (pickResult) { @@ -102,18 +102,30 @@ QVariantMap PickScriptingInterface::getPrevPickResult(const QUuid& uid) { return result; } -void PickScriptingInterface::setPrecisionPicking(const QUuid& uid, const bool precisionPicking) { +void PickScriptingInterface::setPrecisionPicking(unsigned int uid, bool precisionPicking) { DependencyManager::get()->setPrecisionPicking(uid, precisionPicking); } -void PickScriptingInterface::setIgnoreItems(const QUuid& uid, const QScriptValue& ignoreItems) { +void PickScriptingInterface::setIgnoreItems(unsigned int uid, const QScriptValue& ignoreItems) { DependencyManager::get()->setIgnoreItems(uid, qVectorQUuidFromScriptValue(ignoreItems)); } -void PickScriptingInterface::setIncludeItems(const QUuid& uid, const QScriptValue& includeItems) { +void PickScriptingInterface::setIncludeItems(unsigned int uid, const QScriptValue& includeItems) { DependencyManager::get()->setIncludeItems(uid, qVectorQUuidFromScriptValue(includeItems)); } +bool PickScriptingInterface::isLeftHand(unsigned int uid) { + return DependencyManager::get()->isLeftHand(uid); +} + +bool PickScriptingInterface::isRightHand(unsigned int uid) { + return DependencyManager::get()->isRightHand(uid); +} + +bool PickScriptingInterface::isMouse(unsigned int uid) { + return DependencyManager::get()->isMouse(uid); +} + QScriptValue pickTypesToScriptValue(QScriptEngine* engine, const PickQuery::PickType& pickType) { return pickType; } diff --git a/interface/src/raypick/PickScriptingInterface.h b/interface/src/raypick/PickScriptingInterface.h index 7c2eeb1ffc..613686be9b 100644 --- a/interface/src/raypick/PickScriptingInterface.h +++ b/interface/src/raypick/PickScriptingInterface.h @@ -33,21 +33,25 @@ class PickScriptingInterface : public QObject, public Dependency { SINGLETON_DEPENDENCY public: - QUuid createRayPick(const QVariant& properties); + unsigned int createRayPick(const QVariant& properties); void registerMetaTypes(QScriptEngine* engine); + Q_INVOKABLE unsigned int createPick(const PickQuery::PickType type, const QVariant& properties); + Q_INVOKABLE void enablePick(unsigned int uid); + Q_INVOKABLE void disablePick(unsigned int uid); + Q_INVOKABLE void removePick(unsigned int uid); + Q_INVOKABLE QVariantMap getPrevPickResult(unsigned int uid); + + Q_INVOKABLE void setPrecisionPicking(unsigned int uid, bool precisionPicking); + Q_INVOKABLE void setIgnoreItems(unsigned int uid, const QScriptValue& ignoreEntities); + Q_INVOKABLE void setIncludeItems(unsigned int uid, const QScriptValue& includeEntities); + + Q_INVOKABLE bool isLeftHand(unsigned int uid); + Q_INVOKABLE bool isRightHand(unsigned int uid); + Q_INVOKABLE bool isMouse(unsigned int uid); + public slots: - Q_INVOKABLE QUuid createPick(const PickQuery::PickType type, const QVariant& properties); - Q_INVOKABLE void enablePick(const QUuid& uid); - Q_INVOKABLE void disablePick(const QUuid& uid); - Q_INVOKABLE void removePick(const QUuid& uid); - Q_INVOKABLE QVariantMap getPrevPickResult(const QUuid& uid); - - Q_INVOKABLE void setPrecisionPicking(const QUuid& uid, const bool precisionPicking); - Q_INVOKABLE void setIgnoreItems(const QUuid& uid, const QScriptValue& ignoreEntities); - Q_INVOKABLE void setIncludeItems(const QUuid& uid, const QScriptValue& includeEntities); - static constexpr unsigned int PICK_NOTHING() { return 0; } static constexpr unsigned int PICK_ENTITIES() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_ENTITIES); } static constexpr unsigned int PICK_OVERLAYS() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_OVERLAYS); } diff --git a/interface/src/raypick/PointerScriptingInterface.cpp b/interface/src/raypick/PointerScriptingInterface.cpp index 74f207f793..0572434c56 100644 --- a/interface/src/raypick/PointerScriptingInterface.cpp +++ b/interface/src/raypick/PointerScriptingInterface.cpp @@ -16,18 +16,19 @@ #include "LaserPointer.h" #include "StylusPointer.h" -void PointerScriptingInterface::setIgnoreItems(const QUuid& uid, const QScriptValue& ignoreItems) const { +void PointerScriptingInterface::setIgnoreItems(unsigned int uid, const QScriptValue& ignoreItems) const { DependencyManager::get()->setIgnoreItems(uid, qVectorQUuidFromScriptValue(ignoreItems)); } -void PointerScriptingInterface::setIncludeItems(const QUuid& uid, const QScriptValue& includeItems) const { + +void PointerScriptingInterface::setIncludeItems(unsigned int uid, const QScriptValue& includeItems) const { DependencyManager::get()->setIncludeItems(uid, qVectorQUuidFromScriptValue(includeItems)); } -QUuid PointerScriptingInterface::createPointer(const PickQuery::PickType& type, const QVariant& properties) { - // Interaction with managers should always happen ont he main thread +unsigned int PointerScriptingInterface::createPointer(const PickQuery::PickType& type, const QVariant& properties) { + // Interaction with managers should always happen on the main thread if (QThread::currentThread() != qApp->thread()) { - QUuid result; - BLOCKING_INVOKE_METHOD(this, "createPointer", Q_RETURN_ARG(QUuid, result), Q_ARG(PickQuery::PickType, type), Q_ARG(QVariant, properties)); + unsigned int result; + BLOCKING_INVOKE_METHOD(this, "createPointer", Q_RETURN_ARG(unsigned int, result), Q_ARG(PickQuery::PickType, type), Q_ARG(QVariant, properties)); return result; } @@ -37,11 +38,11 @@ QUuid PointerScriptingInterface::createPointer(const PickQuery::PickType& type, case PickQuery::PickType::Stylus: return createStylus(properties); default: - return QUuid(); + return PointerEvent::INVALID_POINTER_ID; } } -QUuid PointerScriptingInterface::createStylus(const QVariant& properties) const { +unsigned int PointerScriptingInterface::createStylus(const QVariant& properties) const { bilateral::Side side = bilateral::Side::Invalid; { QVariant handVar = properties.toMap()["hand"]; @@ -51,13 +52,13 @@ QUuid PointerScriptingInterface::createStylus(const QVariant& properties) const } if (bilateral::Side::Invalid == side) { - return QUuid(); + return PointerEvent::INVALID_POINTER_ID; } return DependencyManager::get()->addPointer(std::make_shared(side)); } -QUuid PointerScriptingInterface::createLaserPointer(const QVariant& properties) const { +unsigned int PointerScriptingInterface::createLaserPointer(const QVariant& properties) const { QVariantMap propertyMap = properties.toMap(); bool faceAvatar = false; @@ -141,7 +142,7 @@ QUuid PointerScriptingInterface::createLaserPointer(const QVariant& properties) faceAvatar, centerEndY, lockEnd, distanceScaleEnd, enabled)); } -void PointerScriptingInterface::editRenderState(const QUuid& uid, const QString& renderState, const QVariant& properties) const { +void PointerScriptingInterface::editRenderState(unsigned int uid, const QString& renderState, const QVariant& properties) const { QVariantMap propMap = properties.toMap(); QVariant startProps; @@ -162,12 +163,11 @@ void PointerScriptingInterface::editRenderState(const QUuid& uid, const QString& DependencyManager::get()->editRenderState(uid, renderState.toStdString(), startProps, pathProps, endProps); } -QVariantMap PointerScriptingInterface::getPrevPickResult(const QUuid& uid) const { +QVariantMap PointerScriptingInterface::getPrevPickResult(unsigned int uid) const { QVariantMap result; auto pickResult = DependencyManager::get()->getPrevPickResult(uid); if (pickResult) { result = pickResult->toVariantMap(); } return result; -} - +} \ No newline at end of file diff --git a/interface/src/raypick/PointerScriptingInterface.h b/interface/src/raypick/PointerScriptingInterface.h index 0cba7e2a6d..25b2c4b4b1 100644 --- a/interface/src/raypick/PointerScriptingInterface.h +++ b/interface/src/raypick/PointerScriptingInterface.h @@ -19,24 +19,27 @@ class PointerScriptingInterface : public QObject, public Dependency { SINGLETON_DEPENDENCY public: - QUuid createLaserPointer(const QVariant& properties) const; - QUuid createStylus(const QVariant& properties) const; + unsigned int createLaserPointer(const QVariant& properties) const; + unsigned int createStylus(const QVariant& properties) const; -public slots: - Q_INVOKABLE QUuid createPointer(const PickQuery::PickType& type, const QVariant& properties); - Q_INVOKABLE void enablePointer(const QUuid& uid) const { DependencyManager::get()->enablePointer(uid); } - Q_INVOKABLE void disablePointer(const QUuid& uid) const { DependencyManager::get()->disablePointer(uid); } - Q_INVOKABLE void removePointer(const QUuid& uid) const { DependencyManager::get()->removePointer(uid); } - Q_INVOKABLE void editRenderState(const QUuid& uid, const QString& renderState, const QVariant& properties) const; - Q_INVOKABLE void setRenderState(const QUuid& uid, const QString& renderState) const { DependencyManager::get()->setRenderState(uid, renderState.toStdString()); } - Q_INVOKABLE QVariantMap getPrevPickResult(const QUuid& uid) const; + Q_INVOKABLE unsigned int createPointer(const PickQuery::PickType& type, const QVariant& properties); + Q_INVOKABLE void enablePointer(unsigned int uid) const { DependencyManager::get()->enablePointer(uid); } + Q_INVOKABLE void disablePointer(unsigned int uid) const { DependencyManager::get()->disablePointer(uid); } + Q_INVOKABLE void removePointer(unsigned int uid) const { DependencyManager::get()->removePointer(uid); } + Q_INVOKABLE void editRenderState(unsigned int uid, const QString& renderState, const QVariant& properties) const; + Q_INVOKABLE void setRenderState(unsigned int uid, const QString& renderState) const { DependencyManager::get()->setRenderState(uid, renderState.toStdString()); } + Q_INVOKABLE QVariantMap getPrevPickResult(unsigned int uid) const; - Q_INVOKABLE void setPrecisionPicking(const QUuid& uid, bool precisionPicking) const { DependencyManager::get()->setPrecisionPicking(uid, precisionPicking); } - Q_INVOKABLE void setLaserLength(const QUuid& uid, float laserLength) const { DependencyManager::get()->setLength(uid, laserLength); } - Q_INVOKABLE void setIgnoreItems(const QUuid& uid, const QScriptValue& ignoreEntities) const; - Q_INVOKABLE void setIncludeItems(const QUuid& uid, const QScriptValue& includeEntities) const; + Q_INVOKABLE void setPrecisionPicking(unsigned int uid, bool precisionPicking) const { DependencyManager::get()->setPrecisionPicking(uid, precisionPicking); } + Q_INVOKABLE void setLaserLength(unsigned int uid, float laserLength) const { DependencyManager::get()->setLength(uid, laserLength); } + Q_INVOKABLE void setIgnoreItems(unsigned int uid, const QScriptValue& ignoreEntities) const; + Q_INVOKABLE void setIncludeItems(unsigned int uid, const QScriptValue& includeEntities) const; - Q_INVOKABLE void setLockEndUUID(const QUuid& uid, const QUuid& objectID, bool isOverlay) const { DependencyManager::get()->setLockEndUUID(uid, objectID, isOverlay); } + Q_INVOKABLE void setLockEndUUID(unsigned int uid, const QUuid& objectID, bool isOverlay) const { DependencyManager::get()->setLockEndUUID(uid, objectID, isOverlay); } + + Q_INVOKABLE bool isLeftHand(unsigned int uid) { return DependencyManager::get()->isLeftHand(uid); } + Q_INVOKABLE bool isRightHand(unsigned int uid) { return DependencyManager::get()->isRightHand(uid); } + Q_INVOKABLE bool isMouse(unsigned int uid) { return DependencyManager::get()->isMouse(uid); } signals: void triggerBegin(const QUuid& id, const PointerEvent& pointerEvent); diff --git a/interface/src/raypick/RayPickScriptingInterface.cpp b/interface/src/raypick/RayPickScriptingInterface.cpp index 8cee02270d..ba1cc0a57b 100644 --- a/interface/src/raypick/RayPickScriptingInterface.cpp +++ b/interface/src/raypick/RayPickScriptingInterface.cpp @@ -20,23 +20,23 @@ #include "JointRayPick.h" #include "MouseRayPick.h" -QUuid RayPickScriptingInterface::createRayPick(const QVariant& properties) { +unsigned int RayPickScriptingInterface::createRayPick(const QVariant& properties) { return DependencyManager::get()->createRayPick(properties); } -void RayPickScriptingInterface::enableRayPick(const QUuid& uid) { +void RayPickScriptingInterface::enableRayPick(unsigned int uid) { DependencyManager::get()->enablePick(uid); } -void RayPickScriptingInterface::disableRayPick(const QUuid& uid) { +void RayPickScriptingInterface::disableRayPick(unsigned int uid) { DependencyManager::get()->disablePick(uid); } -void RayPickScriptingInterface::removeRayPick(const QUuid& uid) { +void RayPickScriptingInterface::removeRayPick(unsigned int uid) { DependencyManager::get()->removePick(uid); } -QVariantMap RayPickScriptingInterface::getPrevRayPickResult(const QUuid& uid) { +QVariantMap RayPickScriptingInterface::getPrevRayPickResult(unsigned int uid) { QVariantMap result; auto pickResult = DependencyManager::get()->getPrevPickResult(uid); if (pickResult) { @@ -45,14 +45,26 @@ QVariantMap RayPickScriptingInterface::getPrevRayPickResult(const QUuid& uid) { return result; } -void RayPickScriptingInterface::setPrecisionPicking(const QUuid& uid, const bool precisionPicking) { +void RayPickScriptingInterface::setPrecisionPicking(unsigned int uid, bool precisionPicking) { DependencyManager::get()->setPrecisionPicking(uid, precisionPicking); } -void RayPickScriptingInterface::setIgnoreItems(const QUuid& uid, const QScriptValue& ignoreItems) { +void RayPickScriptingInterface::setIgnoreItems(unsigned int uid, const QScriptValue& ignoreItems) { DependencyManager::get()->setIgnoreItems(uid, qVectorQUuidFromScriptValue(ignoreItems)); } -void RayPickScriptingInterface::setIncludeItems(const QUuid& uid, const QScriptValue& includeItems) { +void RayPickScriptingInterface::setIncludeItems(unsigned int uid, const QScriptValue& includeItems) { DependencyManager::get()->setIncludeItems(uid, qVectorQUuidFromScriptValue(includeItems)); } + +bool RayPickScriptingInterface::isLeftHand(unsigned int uid) { + return DependencyManager::get()->isLeftHand(uid); +} + +bool RayPickScriptingInterface::isRightHand(unsigned int uid) { + return DependencyManager::get()->isRightHand(uid); +} + +bool RayPickScriptingInterface::isMouse(unsigned int uid) { + return DependencyManager::get()->isMouse(uid); +} \ No newline at end of file diff --git a/interface/src/raypick/RayPickScriptingInterface.h b/interface/src/raypick/RayPickScriptingInterface.h index b0d36f482c..3795f191b3 100644 --- a/interface/src/raypick/RayPickScriptingInterface.h +++ b/interface/src/raypick/RayPickScriptingInterface.h @@ -36,17 +36,22 @@ class RayPickScriptingInterface : public QObject, public Dependency { Q_PROPERTY(unsigned int INTERSECTED_HUD READ INTERSECTED_HUD CONSTANT) SINGLETON_DEPENDENCY +public: + Q_INVOKABLE unsigned int createRayPick(const QVariant& properties); + Q_INVOKABLE void enableRayPick(unsigned int uid); + Q_INVOKABLE void disableRayPick(unsigned int uid); + Q_INVOKABLE void removeRayPick(unsigned int uid); + Q_INVOKABLE QVariantMap getPrevRayPickResult(unsigned int uid); + + Q_INVOKABLE void setPrecisionPicking(unsigned int uid, bool precisionPicking); + Q_INVOKABLE void setIgnoreItems(unsigned int uid, const QScriptValue& ignoreEntities); + Q_INVOKABLE void setIncludeItems(unsigned int uid, const QScriptValue& includeEntities); + + Q_INVOKABLE bool isLeftHand(unsigned int uid); + Q_INVOKABLE bool isRightHand(unsigned int uid); + Q_INVOKABLE bool isMouse(unsigned int uid); + public slots: - Q_INVOKABLE QUuid createRayPick(const QVariant& properties); - Q_INVOKABLE void enableRayPick(const QUuid& uid); - Q_INVOKABLE void disableRayPick(const QUuid& uid); - Q_INVOKABLE void removeRayPick(const QUuid& uid); - Q_INVOKABLE QVariantMap getPrevRayPickResult(const QUuid& uid); - - Q_INVOKABLE void setPrecisionPicking(const QUuid& uid, const bool precisionPicking); - Q_INVOKABLE void setIgnoreItems(const QUuid& uid, const QScriptValue& ignoreEntities); - Q_INVOKABLE void setIncludeItems(const QUuid& uid, const QScriptValue& includeEntities); - static unsigned int PICK_NOTHING() { return PickScriptingInterface::PICK_NOTHING(); } static unsigned int PICK_ENTITIES() { return PickScriptingInterface::PICK_ENTITIES(); } static unsigned int PICK_OVERLAYS() { return PickScriptingInterface::PICK_OVERLAYS(); } diff --git a/interface/src/raypick/StylusPointer.cpp b/interface/src/raypick/StylusPointer.cpp index 4cc34f36d9..6da755a797 100644 --- a/interface/src/raypick/StylusPointer.cpp +++ b/interface/src/raypick/StylusPointer.cpp @@ -448,7 +448,7 @@ void StylusPointer::updateStylusTarget() { } } -void StylusPointer::update(float deltaTime) { +void StylusPointer::update(unsigned int pointerID, float deltaTime) { // This only needs to be a read lock because update won't change any of the properties that can be modified from scripts withReadLock([&] { auto myAvatar = DependencyManager::get()->getMyAvatar(); diff --git a/interface/src/raypick/StylusPointer.h b/interface/src/raypick/StylusPointer.h index 7e67a7398e..5308315187 100644 --- a/interface/src/raypick/StylusPointer.h +++ b/interface/src/raypick/StylusPointer.h @@ -89,7 +89,7 @@ public: void enable() override; void disable() override; - void update(float deltaTime) override; + void update(unsigned int pointerID, float deltaTime) override; private: @@ -97,6 +97,8 @@ private: virtual void editRenderState(const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) override {} virtual PickedObject getHoveredObject(const PickResultPointer& pickResult) override { return PickedObject(); } virtual Buttons getPressedButtons() override { return {}; } + bool shouldHover() override { return true; } + bool shouldTrigger() override { return true; } virtual PointerEvent buildPointerEvent(const PickedObject& target, const PickResultPointer& pickResult) const override { return PointerEvent(); } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index b5af529f2b..fa02d4107c 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -19,6 +19,8 @@ #include #include +#include + #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif @@ -79,8 +81,6 @@ ContextOverlayInterface::ContextOverlayInterface() { _challengeOwnershipTimeoutTimer.setSingleShot(true); } -static const uint32_t MOUSE_HW_ID = 0; -static const uint32_t LEFT_HAND_HW_ID = 1; static const xColor CONTEXT_OVERLAY_COLOR = { 255, 255, 255 }; static const float CONTEXT_OVERLAY_INSIDE_DISTANCE = 1.0f; // in meters static const float CONTEXT_OVERLAY_SIZE = 0.09f; // in meters, same x and y dims @@ -100,7 +100,7 @@ void ContextOverlayInterface::setEnabled(bool enabled) { bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { if (_enabled && event.getButton() == PointerEvent::SecondaryButton) { if (contextOverlayFilterPassed(entityItemID)) { - if (event.getID() == MOUSE_HW_ID) { + if (event.getID() == PointerManager::MOUSE_POINTER_ID || DependencyManager::get()->isMouse(event.getID())) { enableEntityHighlight(entityItemID); } @@ -151,7 +151,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& glm::vec3 normal; boundingBox.findRayIntersection(cameraPosition, direction, distance, face, normal); float offsetAngle = -CONTEXT_OVERLAY_OFFSET_ANGLE; - if (event.getID() == LEFT_HAND_HW_ID) { + if (DependencyManager::get()->isLeftHand(event.getID())) { offsetAngle *= -1.0f; } contextOverlayPosition = cameraPosition + @@ -253,13 +253,15 @@ void ContextOverlayInterface::contextOverlays_hoverLeaveOverlay(const OverlayID& } void ContextOverlayInterface::contextOverlays_hoverEnterEntity(const EntityItemID& entityID, const PointerEvent& event) { - if (contextOverlayFilterPassed(entityID) && _enabled && event.getID() != MOUSE_HW_ID) { + bool isMouse = event.getID() == PointerManager::MOUSE_POINTER_ID || DependencyManager::get()->isMouse(event.getID()); + if (contextOverlayFilterPassed(entityID) && _enabled && !isMouse) { enableEntityHighlight(entityID); } } void ContextOverlayInterface::contextOverlays_hoverLeaveEntity(const EntityItemID& entityID, const PointerEvent& event) { - if (_currentEntityWithContextOverlay != entityID && _enabled && event.getID() != MOUSE_HW_ID) { + bool isMouse = event.getID() == PointerManager::MOUSE_POINTER_ID || DependencyManager::get()->isMouse(event.getID()); + if (_currentEntityWithContextOverlay != entityID && _enabled && !isMouse) { disableEntityHighlight(entityID); } } diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index b37a376de3..05ff3e289b 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -46,7 +46,7 @@ extern void initOverlay3DPipelines(render::ShapePlumber& plumber, bool depthTest Overlays::Overlays() { auto pointerManager = DependencyManager::get(); connect(pointerManager.data(), &PointerManager::hoverBeginOverlay, this, &Overlays::hoverEnterOverlay); - connect(pointerManager.data(), &PointerManager::hoverContinueOverlay, this, &Overlays::hoverOverOverlay); + connect(pointerManager.data(), &PointerManager::hoverContinueOverlay, this, &Overlays::hoverOverPointerEvent); connect(pointerManager.data(), &PointerManager::hoverEndOverlay, this, &Overlays::hoverLeavePointerEvent); connect(pointerManager.data(), &PointerManager::triggerBeginOverlay, this, &Overlays::mousePressPointerEvent); connect(pointerManager.data(), &PointerManager::triggerContinueOverlay, this, &Overlays::mouseMovePointerEvent); @@ -745,7 +745,7 @@ void Overlays::sendHoverEnterOverlay(const OverlayID& overlayID, const PointerEv } void Overlays::sendHoverOverOverlay(const OverlayID& overlayID, const PointerEvent& event) { - emit hoverOverOverlay(overlayID, event); + hoverOverPointerEvent(overlayID, event); } void Overlays::sendHoverLeaveOverlay(const OverlayID& overlayID, const PointerEvent& event) { @@ -784,8 +784,6 @@ float Overlays::height() { return offscreenUi->getWindow()->size().height(); } -static const uint32_t MOUSE_POINTER_ID = 0; - static glm::vec2 projectOntoOverlayXYPlane(glm::vec3 position, glm::quat rotation, glm::vec2 dimensions, const PickRay& pickRay, const RayToOverlayIntersectionResult& rayPickResult) { @@ -846,7 +844,7 @@ PointerEvent Overlays::calculateOverlayPointerEvent(OverlayID overlayID, PickRay glm::vec2 pos2D = projectOntoOverlayXYPlane(position, rotation, dimensions, ray, rayPickResult); - PointerEvent pointerEvent(eventType, MOUSE_POINTER_ID, pos2D, rayPickResult.intersection, rayPickResult.surfaceNormal, + PointerEvent pointerEvent(eventType, PointerManager::MOUSE_POINTER_ID, pos2D, rayPickResult.intersection, rayPickResult.surfaceNormal, ray.direction, toPointerButton(*event), toPointerButtons(*event), event->modifiers()); return pointerEvent; @@ -940,6 +938,21 @@ bool Overlays::mouseDoublePressEvent(QMouseEvent* event) { return false; } +void Overlays::hoverOverPointerEvent(const OverlayID& overlayID, const PointerEvent& event) { + // TODO: generalize this to allow any overlay to recieve events + std::shared_ptr thisOverlay; + if (getOverlayType(overlayID) == "web3d") { + thisOverlay = std::static_pointer_cast(getOverlay(overlayID)); + } + if (thisOverlay) { + // Send to web overlay + QMetaObject::invokeMethod(thisOverlay.get(), "handlePointerEvent", Q_ARG(PointerEvent, event)); + } + + // emit to scripts + emit hoverOverOverlay(overlayID, event); +} + void Overlays::hoverLeavePointerEvent(const OverlayID& overlayID, const PointerEvent& event) { // TODO: generalize this to allow any overlay to recieve events std::shared_ptr thisOverlay; @@ -1005,7 +1018,7 @@ bool Overlays::mouseMoveEvent(QMouseEvent* event) { } // Hover over current overlay. - emit hoverOverOverlay(rayPickResult.overlayID, pointerEvent); + hoverOverPointerEvent(rayPickResult.overlayID, pointerEvent); _currentHoverOverOverlayID = rayPickResult.overlayID; } else { diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 8bcfa99aba..a5ad013246 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -308,6 +308,7 @@ public slots: void mousePressPointerEvent(const OverlayID& overlayID, const PointerEvent& event); void mouseMovePointerEvent(const OverlayID& overlayID, const PointerEvent& event); void mouseReleasePointerEvent(const OverlayID& overlayID, const PointerEvent& event); + void hoverOverPointerEvent(const OverlayID& overlayID, const PointerEvent& event); void hoverLeavePointerEvent(const OverlayID& overlayID, const PointerEvent& event); signals: diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index 5339deba2b..658b4edab8 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -56,6 +56,8 @@ #include "ui/Snapshot.h" #include "SoundCache.h" +#include "raypick/PointerScriptingInterface.h" + static const float DPI = 30.47f; static const float INCHES_TO_METERS = 1.0f / 39.3701f; static const float METERS_TO_INCHES = 39.3701f; @@ -160,14 +162,6 @@ void Web3DOverlay::buildWebSurface() { QObject::connect(_webSurface.data(), &OffscreenQmlSurface::webEventReceived, this, &Web3DOverlay::webEventReceived); } -void Web3DOverlay::hoverLeaveOverlay(const PointerEvent& event) { - if ((_pressed || (!_activeTouchPoints.empty() && _touchBeginAccepted))) { - PointerEvent endEvent(PointerEvent::Release, event.getID(), event.getPos2D(), event.getPos3D(), event.getNormal(), event.getDirection(), - event.getButton(), event.getButtons(), event.getKeyboardModifiers()); - handlePointerEvent(endEvent); - } -} - void Web3DOverlay::update(float deltatime) { if (_webSurface) { // update globalPosition @@ -224,6 +218,7 @@ void Web3DOverlay::setupQmlSurface() { _webSurface->getSurfaceContext()->setContextProperty("Settings", SettingsScriptingInterface::getInstance()); _webSurface->getSurfaceContext()->setContextProperty("Render", AbstractViewStateInterface::instance()->getRenderEngine()->getConfiguration().get()); _webSurface->getSurfaceContext()->setContextProperty("Controller", DependencyManager::get().data()); + _webSurface->getSurfaceContext()->setContextProperty("Pointers", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("Web3DOverlay", this); _webSurface->getSurfaceContext()->setContextProperty("pathToFonts", "../../"); @@ -253,18 +248,12 @@ void Web3DOverlay::onResizeWebSurface() { _webSurface->resize(QSize(_resolution.x, _resolution.y)); } -const int INVALID_DEVICE_ID = -1; - -Q_INVOKABLE int Web3DOverlay::deviceIdByTouchPoint(qreal x, qreal y) { - auto mapped = _webSurface->getRootItem()->mapFromGlobal(QPoint(x, y)); - - for (auto pair : _activeTouchPoints) { - if (mapped.x() == (int)pair.second.pos().x() && mapped.y() == (int)pair.second.pos().y()) { - return pair.first; - } +unsigned int Web3DOverlay::deviceIdByTouchPoint(qreal x, qreal y) { + if (_webSurface) { + return _webSurface->deviceIdByTouchPoint(x, y); + } else { + return PointerEvent::INVALID_POINTER_ID; } - - return INVALID_DEVICE_ID; } void Web3DOverlay::render(RenderArgs* args) { @@ -348,6 +337,19 @@ void Web3DOverlay::setProxyWindow(QWindow* proxyWindow) { _webSurface->setProxyWindow(proxyWindow); } +void Web3DOverlay::hoverLeaveOverlay(const PointerEvent& event) { + if (_inputMode == Mouse) { + PointerEvent endEvent(PointerEvent::Release, event.getID(), event.getPos2D(), event.getPos3D(), event.getNormal(), event.getDirection(), + event.getButton(), event.getButtons(), event.getKeyboardModifiers()); + handlePointerEvent(endEvent); + // QML onReleased is only triggered if a click has happened first. We need to send this "fake" mouse move event to properly trigger an onExited. + PointerEvent endMoveEvent(PointerEvent::Move, event.getID()); + handlePointerEvent(endMoveEvent); + } else if (_webSurface) { + _webSurface->hoverEndEvent(event, _touchDevice); + } +} + void Web3DOverlay::handlePointerEvent(const PointerEvent& event) { if (event.getType() == PointerEvent::Press) { _pressed = true; @@ -363,93 +365,11 @@ void Web3DOverlay::handlePointerEvent(const PointerEvent& event) { } void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) { - if (!_webSurface) { - return; + if (_webSurface) { + PointerEvent webEvent = event; + webEvent.setPos2D(event.getPos2D() * (METERS_TO_INCHES * _dpi)); + _webSurface->handlePointerEvent(webEvent, _touchDevice); } - - glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _dpi); - QPointF windowPoint(windowPos.x, windowPos.y); - - Qt::TouchPointState state = Qt::TouchPointStationary; - if (event.getType() == PointerEvent::Press && event.getButton() == PointerEvent::PrimaryButton) { - state = Qt::TouchPointPressed; - } else if (event.getType() == PointerEvent::Release) { - state = Qt::TouchPointReleased; - } else if (_activeTouchPoints.count(event.getID()) && windowPoint != _activeTouchPoints[event.getID()].pos()) { - state = Qt::TouchPointMoved; - } - - QEvent::Type touchType = QEvent::TouchUpdate; - if (_activeTouchPoints.empty()) { - // If the first active touch point is being created, send a begin - touchType = QEvent::TouchBegin; - } if (state == Qt::TouchPointReleased && _activeTouchPoints.size() == 1 && _activeTouchPoints.count(event.getID())) { - // If the last active touch point is being released, send an end - touchType = QEvent::TouchEnd; - } - - { - QTouchEvent::TouchPoint point; - point.setId(event.getID()); - point.setState(state); - point.setPos(windowPoint); - point.setScreenPos(windowPoint); - _activeTouchPoints[event.getID()] = point; - } - - QTouchEvent touchEvent(touchType, &_touchDevice, event.getKeyboardModifiers()); - { - QList touchPoints; - Qt::TouchPointStates touchPointStates; - for (const auto& entry : _activeTouchPoints) { - touchPointStates |= entry.second.state(); - touchPoints.push_back(entry.second); - } - - touchEvent.setWindow(_webSurface->getWindow()); - touchEvent.setDevice(&_touchDevice); - touchEvent.setTarget(_webSurface->getRootItem()); - touchEvent.setTouchPoints(touchPoints); - touchEvent.setTouchPointStates(touchPointStates); - } - - // Send mouse events to the Web surface so that HTML dialog elements work with mouse press and hover. - // - // In Qt 5.9 mouse events must be sent before touch events to make sure some QtQuick components will - // receive mouse events - Qt::MouseButton button = Qt::NoButton; - Qt::MouseButtons buttons = Qt::NoButton; - if (event.getButton() == PointerEvent::PrimaryButton) { - button = Qt::LeftButton; - } - if (event.getButtons() & PointerEvent::PrimaryButton) { - buttons |= Qt::LeftButton; - } - -#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) - if (event.getType() == PointerEvent::Move) { - QMouseEvent mouseEvent(QEvent::MouseMove, windowPoint, windowPoint, windowPoint, button, buttons, Qt::NoModifier); - QCoreApplication::sendEvent(_webSurface->getWindow(), &mouseEvent); - } -#endif - - if (touchType == QEvent::TouchBegin) { - _touchBeginAccepted = QCoreApplication::sendEvent(_webSurface->getWindow(), &touchEvent); - } else if (_touchBeginAccepted) { - QCoreApplication::sendEvent(_webSurface->getWindow(), &touchEvent); - } - - // If this was a release event, remove the point from the active touch points - if (state == Qt::TouchPointReleased) { - _activeTouchPoints.erase(event.getID()); - } - -#if QT_VERSION < QT_VERSION_CHECK(5, 9, 0) - if (event.getType() == PointerEvent::Move) { - QMouseEvent mouseEvent(QEvent::MouseMove, windowPoint, windowPoint, windowPoint, button, buttons, Qt::NoModifier); - QCoreApplication::sendEvent(_webSurface->getWindow(), &mouseEvent); - } -#endif } void Web3DOverlay::handlePointerEventAsMouse(const PointerEvent& event) { @@ -485,7 +405,7 @@ void Web3DOverlay::handlePointerEventAsMouse(const PointerEvent& event) { return; } - QMouseEvent mouseEvent(type, windowPoint, windowPoint, windowPoint, button, buttons, Qt::NoModifier); + QMouseEvent mouseEvent(type, windowPoint, windowPoint, windowPoint, button, buttons, event.getKeyboardModifiers()); QCoreApplication::sendEvent(_webSurface->getWindow(), &mouseEvent); } diff --git a/interface/src/ui/overlays/Web3DOverlay.h b/interface/src/ui/overlays/Web3DOverlay.h index 2fc63df76a..d2da1f7310 100644 --- a/interface/src/ui/overlays/Web3DOverlay.h +++ b/interface/src/ui/overlays/Web3DOverlay.h @@ -67,7 +67,7 @@ public: void destroyWebSurface(); void onResizeWebSurface(); - Q_INVOKABLE int deviceIdByTouchPoint(qreal x, qreal y); + Q_INVOKABLE unsigned int deviceIdByTouchPoint(qreal x, qreal y); public slots: void emitScriptEvent(const QVariant& scriptMessage); @@ -98,8 +98,6 @@ private: bool _showKeyboardFocusHighlight{ true }; bool _pressed{ false }; - bool _touchBeginAccepted { false }; - std::map _activeTouchPoints; QTouchDevice _touchDevice; uint8_t _desiredMaxFPS { 10 }; diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 06504a0589..8493d73db3 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -51,11 +51,11 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf _displayModelBounds(false), _layeredZones(this) { - setMouseRayPickResultOperator([](QUuid rayPickID) { + setMouseRayPickResultOperator([](unsigned int rayPickID) { RayToEntityIntersectionResult entityResult; return entityResult; }); - setSetPrecisionPickingOperator([](QUuid rayPickID, bool value) {}); + setSetPrecisionPickingOperator([](unsigned int rayPickID, bool value) {}); EntityRenderer::initEntityRenderers(); _currentHoverOverEntityID = UNKNOWN_ENTITY_ID; _currentClickingOnEntityID = UNKNOWN_ENTITY_ID; @@ -83,6 +83,7 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf connect(entityScriptingInterface.data(), &EntityScriptingInterface::mousePressOnEntity, this, handlePointerEvent); connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseReleaseOnEntity, this, handlePointerEvent); connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseMoveOnEntity, this, handlePointerEvent); + connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverOverEntity, this, handlePointerEvent); connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity, this, [&](const EntityItemID& entityID, const PointerEvent& event) { std::shared_ptr thisEntity; auto entity = getEntity(entityID); @@ -570,8 +571,6 @@ static PointerEvent::Button toPointerButton(const QMouseEvent& event) { } } -static const uint32_t MOUSE_POINTER_ID = 0; - void EntityTreeRenderer::mousePressEvent(QMouseEvent* event) { // If we don't have a tree, or we're in the process of shutting down, then don't // process these events. @@ -592,7 +591,7 @@ void EntityTreeRenderer::mousePressEvent(QMouseEvent* event) { } glm::vec2 pos2D = projectOntoEntityXYPlane(rayPickResult.entity, ray, rayPickResult); - PointerEvent pointerEvent(PointerEvent::Press, MOUSE_POINTER_ID, + PointerEvent pointerEvent(PointerEvent::Press, PointerManager::MOUSE_POINTER_ID, pos2D, rayPickResult.intersection, rayPickResult.surfaceNormal, ray.direction, toPointerButton(*event), toPointerButtons(*event), @@ -624,7 +623,7 @@ void EntityTreeRenderer::mouseDoublePressEvent(QMouseEvent* event) { RayToEntityIntersectionResult rayPickResult = _getPrevRayPickResultOperator(_mouseRayPickID); if (rayPickResult.intersects && rayPickResult.entity) { glm::vec2 pos2D = projectOntoEntityXYPlane(rayPickResult.entity, ray, rayPickResult); - PointerEvent pointerEvent(PointerEvent::Press, MOUSE_POINTER_ID, + PointerEvent pointerEvent(PointerEvent::Press, PointerManager::MOUSE_POINTER_ID, pos2D, rayPickResult.intersection, rayPickResult.surfaceNormal, ray.direction, toPointerButton(*event), toPointerButtons(*event), Qt::NoModifier); @@ -656,7 +655,7 @@ void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event) { //qCDebug(entitiesrenderer) << "mouseReleaseEvent over entity:" << rayPickResult.entityID; glm::vec2 pos2D = projectOntoEntityXYPlane(rayPickResult.entity, ray, rayPickResult); - PointerEvent pointerEvent(PointerEvent::Release, MOUSE_POINTER_ID, + PointerEvent pointerEvent(PointerEvent::Release, PointerManager::MOUSE_POINTER_ID, pos2D, rayPickResult.intersection, rayPickResult.surfaceNormal, ray.direction, toPointerButton(*event), toPointerButtons(*event), @@ -672,7 +671,7 @@ void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event) { // we're releasing the button, then this is considered a clickReleaseOn event if (!_currentClickingOnEntityID.isInvalidID()) { glm::vec2 pos2D = projectOntoEntityXYPlane(rayPickResult.entity, ray, rayPickResult); - PointerEvent pointerEvent(PointerEvent::Release, MOUSE_POINTER_ID, + PointerEvent pointerEvent(PointerEvent::Release, PointerManager::MOUSE_POINTER_ID, pos2D, rayPickResult.intersection, rayPickResult.surfaceNormal, ray.direction, toPointerButton(*event), toPointerButtons(*event), @@ -698,7 +697,7 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) { RayToEntityIntersectionResult rayPickResult = _getPrevRayPickResultOperator(_mouseRayPickID); if (rayPickResult.intersects && rayPickResult.entity) { glm::vec2 pos2D = projectOntoEntityXYPlane(rayPickResult.entity, ray, rayPickResult); - PointerEvent pointerEvent(PointerEvent::Move, MOUSE_POINTER_ID, + PointerEvent pointerEvent(PointerEvent::Move, PointerManager::MOUSE_POINTER_ID, pos2D, rayPickResult.intersection, rayPickResult.surfaceNormal, ray.direction, toPointerButton(*event), toPointerButtons(*event), @@ -712,7 +711,7 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) { // then we need to send the hover leave. if (!_currentHoverOverEntityID.isInvalidID() && rayPickResult.entityID != _currentHoverOverEntityID) { glm::vec2 pos2D = projectOntoEntityXYPlane(rayPickResult.entity, ray, rayPickResult); - PointerEvent pointerEvent(PointerEvent::Move, MOUSE_POINTER_ID, + PointerEvent pointerEvent(PointerEvent::Move, PointerManager::MOUSE_POINTER_ID, pos2D, rayPickResult.intersection, rayPickResult.surfaceNormal, ray.direction, toPointerButton(*event), toPointerButtons(*event), @@ -743,7 +742,7 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) { // send the hover leave for our previous entity if (!_currentHoverOverEntityID.isInvalidID()) { glm::vec2 pos2D = projectOntoEntityXYPlane(rayPickResult.entity, ray, rayPickResult); - PointerEvent pointerEvent(PointerEvent::Move, MOUSE_POINTER_ID, + PointerEvent pointerEvent(PointerEvent::Move, PointerManager::MOUSE_POINTER_ID, pos2D, rayPickResult.intersection, rayPickResult.surfaceNormal, ray.direction, toPointerButton(*event), toPointerButtons(*event), diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 1eb44f996a..52876fa90e 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -74,9 +74,9 @@ public: static float getEntityLoadingPriority(const EntityItem& item) { return _calculateEntityLoadingPriorityFunc(item); } static void setEntityLoadingPriorityFunction(CalculateEntityLoadingPriority fn) { _calculateEntityLoadingPriorityFunc = fn; } - void setMouseRayPickID(QUuid rayPickID) { _mouseRayPickID = rayPickID; } - void setMouseRayPickResultOperator(std::function getPrevRayPickResultOperator) { _getPrevRayPickResultOperator = getPrevRayPickResultOperator; } - void setSetPrecisionPickingOperator(std::function setPrecisionPickingOperator) { _setPrecisionPickingOperator = setPrecisionPickingOperator; } + void setMouseRayPickID(unsigned int rayPickID) { _mouseRayPickID = rayPickID; } + void setMouseRayPickResultOperator(std::function getPrevRayPickResultOperator) { _getPrevRayPickResultOperator = getPrevRayPickResultOperator; } + void setSetPrecisionPickingOperator(std::function setPrecisionPickingOperator) { _setPrecisionPickingOperator = setPrecisionPickingOperator; } void shutdown(); void update(bool simulate); @@ -182,9 +182,9 @@ private: QMultiMap _waitingOnPreload; - QUuid _mouseRayPickID; - std::function _getPrevRayPickResultOperator; - std::function _setPrecisionPickingOperator; + unsigned int _mouseRayPickID; + std::function _getPrevRayPickResultOperator; + std::function _setPrecisionPickingOperator; class LayeredZone { public: diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index ad4230c55b..469e1c4b6f 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -315,109 +315,18 @@ void WebEntityRenderer::loadSourceURL() { } void WebEntityRenderer::hoverLeaveEntity(const PointerEvent& event) { - if (!_lastLocked && _webSurface && _pressed) { - // If the user mouses off the entity while the button is down, simulate a touch end. - PointerEvent endEvent(PointerEvent::Release, event.getID(), event.getPos2D(), event.getPos3D(), event.getNormal(), event.getDirection(), - event.getButton(), event.getButtons(), event.getKeyboardModifiers()); - handlePointerEvent(endEvent); + if (!_lastLocked && _webSurface) { + _webSurface->hoverEndEvent(event, _touchDevice); } } void WebEntityRenderer::handlePointerEvent(const PointerEvent& event) { // Ignore mouse interaction if we're locked - if (_lastLocked || !_webSurface) { - return; + if (!_lastLocked && _webSurface) { + PointerEvent webEvent = event; + webEvent.setPos2D(event.getPos2D() * (METERS_TO_INCHES * _lastDPI)); + _webSurface->handlePointerEvent(webEvent, _touchDevice); } - - if (event.getType() == PointerEvent::Press) { - _pressed = true; - } else if (event.getType() == PointerEvent::Release) { - _pressed = false; - } - - glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _lastDPI); - QPointF windowPoint(windowPos.x, windowPos.y); - - Qt::TouchPointState state = Qt::TouchPointStationary; - if (event.getType() == PointerEvent::Press && event.getButton() == PointerEvent::PrimaryButton) { - state = Qt::TouchPointPressed; - } else if (event.getType() == PointerEvent::Release) { - state = Qt::TouchPointReleased; - } else if (_activeTouchPoints.count(event.getID()) && windowPoint != _activeTouchPoints[event.getID()].pos()) { - state = Qt::TouchPointMoved; - } - - QEvent::Type touchType = QEvent::TouchUpdate; - if (_activeTouchPoints.empty()) { - // If the first active touch point is being created, send a begin - touchType = QEvent::TouchBegin; - } if (state == Qt::TouchPointReleased && _activeTouchPoints.size() == 1 && _activeTouchPoints.count(event.getID())) { - // If the last active touch point is being released, send an end - touchType = QEvent::TouchEnd; - } - - { - QTouchEvent::TouchPoint point; - point.setId(event.getID()); - point.setState(state); - point.setPos(windowPoint); - point.setScreenPos(windowPoint); - _activeTouchPoints[event.getID()] = point; - } - - QTouchEvent touchEvent(touchType, &_touchDevice, event.getKeyboardModifiers()); - { - QList touchPoints; - Qt::TouchPointStates touchPointStates; - for (const auto& entry : _activeTouchPoints) { - touchPointStates |= entry.second.state(); - touchPoints.push_back(entry.second); - } - - touchEvent.setWindow(_webSurface->getWindow()); - touchEvent.setDevice(&_touchDevice); - touchEvent.setTarget(_webSurface->getRootItem()); - touchEvent.setTouchPoints(touchPoints); - touchEvent.setTouchPointStates(touchPointStates); - } - - // Send mouse events to the Web surface so that HTML dialog elements work with mouse press and hover. - // - // In Qt 5.9 mouse events must be sent before touch events to make sure some QtQuick components will - // receive mouse events - Qt::MouseButton button = Qt::NoButton; - Qt::MouseButtons buttons = Qt::NoButton; - if (event.getButton() == PointerEvent::PrimaryButton) { - button = Qt::LeftButton; - } - if (event.getButtons() & PointerEvent::PrimaryButton) { - buttons |= Qt::LeftButton; - } - -#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) - if (event.getType() == PointerEvent::Move) { - QMouseEvent mouseEvent(QEvent::MouseMove, windowPoint, windowPoint, windowPoint, button, buttons, Qt::NoModifier); - QCoreApplication::sendEvent(_webSurface->getWindow(), &mouseEvent); - } -#endif - - if (touchType == QEvent::TouchBegin) { - _touchBeginAccepted = QCoreApplication::sendEvent(_webSurface->getWindow(), &touchEvent); - } else if (_touchBeginAccepted) { - QCoreApplication::sendEvent(_webSurface->getWindow(), &touchEvent); - } - - // If this was a release event, remove the point from the active touch points - if (state == Qt::TouchPointReleased) { - _activeTouchPoints.erase(event.getID()); - } - -#if QT_VERSION < QT_VERSION_CHECK(5, 9, 0) - if (event.getType() == PointerEvent::Move) { - QMouseEvent mouseEvent(QEvent::MouseMove, windowPoint, windowPoint, windowPoint, button, buttons, Qt::NoModifier); - QCoreApplication::sendEvent(_webSurface->getWindow(), &mouseEvent); - } -#endif } void WebEntityRenderer::setProxyWindow(QWindow* proxyWindow) { diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h index 8adbc17a75..28c024cdbb 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -12,8 +12,6 @@ #include #include "RenderableEntityItem.h" -#include - class OffscreenQmlSurface; class PointerEvent; @@ -66,10 +64,6 @@ private: QTimer _timer; uint64_t _lastRenderTime { 0 }; Transform _renderTransform; - - bool _pressed{ false }; - bool _touchBeginAccepted{ false }; - std::map _activeTouchPoints; }; } } // namespace diff --git a/libraries/pointers/src/pointers/Pick.h b/libraries/pointers/src/pointers/Pick.h index 791238d601..a4afbdbf8f 100644 --- a/libraries/pointers/src/pointers/Pick.h +++ b/libraries/pointers/src/pointers/Pick.h @@ -182,6 +182,10 @@ public: void setIgnoreItems(const QVector& items); void setIncludeItems(const QVector& items); + virtual bool isLeftHand() const { return false; } + virtual bool isRightHand() const { return false; } + virtual bool isMouse() const { return false; } + private: PickFilter _filter; const float _maxDistance; @@ -206,4 +210,13 @@ public: virtual PickResultPointer getHUDIntersection(const T& pick) = 0; }; +namespace std { + template <> + struct hash { + size_t operator()(const PickQuery::PickType& a) const { + return a; + } + }; +} + #endif // hifi_Pick_h diff --git a/libraries/pointers/src/pointers/PickCacheOptimizer.h b/libraries/pointers/src/pointers/PickCacheOptimizer.h index 58c3ac9098..10c9d6cf84 100644 --- a/libraries/pointers/src/pointers/PickCacheOptimizer.h +++ b/libraries/pointers/src/pointers/PickCacheOptimizer.h @@ -37,7 +37,7 @@ template class PickCacheOptimizer { public: - void update(QHash>& picks, bool shouldPickHUD); + void update(std::unordered_map>& picks, bool shouldPickHUD); protected: typedef std::unordered_map> PickCache; @@ -67,22 +67,19 @@ void PickCacheOptimizer::cacheResult(const bool intersects, const PickResultP } template -void PickCacheOptimizer::update(QHash>& picks, bool shouldPickHUD) { +void PickCacheOptimizer::update(std::unordered_map>& picks, bool shouldPickHUD) { PickCache results; - for (const auto& uid : picks.keys()) { - std::shared_ptr> pick = std::static_pointer_cast>(picks[uid]); - if (!pick->isEnabled() || pick->getFilter().doesPickNothing() || pick->getMaxDistance() < 0.0f) { - continue; - } + for (const auto& pickPair : picks) { + std::shared_ptr> pick = std::static_pointer_cast>(pickPair.second); T mathematicalPick = pick->getMathematicalPick(); + PickResultPointer res = pick->getDefaultResult(mathematicalPick.toVariantMap()); - if (!mathematicalPick) { + if (!pick->isEnabled() || pick->getFilter().doesPickNothing() || pick->getMaxDistance() < 0.0f || !mathematicalPick) { + pick->setPickResult(res); continue; } - PickResultPointer res = pick->getDefaultResult(mathematicalPick.toVariantMap()); - if (pick->getFilter().doesPickEntities()) { PickCacheKey entityKey = { pick->getFilter().getEntityFlags(), pick->getIncludeItems(), pick->getIgnoreItems() }; if (!checkAndCompareCachedResults(mathematicalPick, results, res, entityKey)) { diff --git a/libraries/pointers/src/pointers/PickManager.cpp b/libraries/pointers/src/pointers/PickManager.cpp index 385115d732..92fec014da 100644 --- a/libraries/pointers/src/pointers/PickManager.cpp +++ b/libraries/pointers/src/pointers/PickManager.cpp @@ -9,38 +9,43 @@ PickManager::PickManager() { setShouldPickHUDOperator([]() { return false; }); + setCalculatePos2DFromHUDOperator([](const glm::vec3& intersection) { return glm::vec2(NAN); }); } -QUuid PickManager::addPick(PickQuery::PickType type, const std::shared_ptr pick) { - QUuid id = QUuid::createUuid(); +unsigned int PickManager::addPick(PickQuery::PickType type, const std::shared_ptr pick) { + unsigned int id = INVALID_PICK_ID; withWriteLock([&] { - _picks[type][id] = pick; - _typeMap[id] = type; + // Don't let the pick IDs overflow + if (_nextPickID < UINT32_MAX) { + id = _nextPickID++; + _picks[type][id] = pick; + _typeMap[id] = type; + } }); return id; } -std::shared_ptr PickManager::findPick(const QUuid& uid) const { +std::shared_ptr PickManager::findPick(unsigned int uid) const { return resultWithReadLock>([&] { auto type = _typeMap.find(uid); if (type != _typeMap.end()) { - return _picks[type.value()][uid]; + return _picks.find(type->second)->second.find(uid)->second; } return std::shared_ptr(); }); } -void PickManager::removePick(const QUuid& uid) { +void PickManager::removePick(unsigned int uid) { withWriteLock([&] { auto type = _typeMap.find(uid); if (type != _typeMap.end()) { - _picks[type.value()].remove(uid); - _typeMap.remove(uid); + _picks[type->second].erase(uid); + _typeMap.erase(uid); } }); } -PickResultPointer PickManager::getPrevPickResult(const QUuid& uid) const { +PickResultPointer PickManager::getPrevPickResult(unsigned int uid) const { auto pick = findPick(uid); if (pick) { return pick->getPrevPickResult(); @@ -48,35 +53,35 @@ PickResultPointer PickManager::getPrevPickResult(const QUuid& uid) const { return PickResultPointer(); } -void PickManager::enablePick(const QUuid& uid) const { +void PickManager::enablePick(unsigned int uid) const { auto pick = findPick(uid); if (pick) { pick->enable(); } } -void PickManager::disablePick(const QUuid& uid) const { +void PickManager::disablePick(unsigned int uid) const { auto pick = findPick(uid); if (pick) { pick->disable(); } } -void PickManager::setPrecisionPicking(const QUuid& uid, bool precisionPicking) const { +void PickManager::setPrecisionPicking(unsigned int uid, bool precisionPicking) const { auto pick = findPick(uid); if (pick) { pick->setPrecisionPicking(precisionPicking); } } -void PickManager::setIgnoreItems(const QUuid& uid, const QVector& ignore) const { +void PickManager::setIgnoreItems(unsigned int uid, const QVector& ignore) const { auto pick = findPick(uid); if (pick) { pick->setIgnoreItems(ignore); } } -void PickManager::setIncludeItems(const QUuid& uid, const QVector& include) const { +void PickManager::setIncludeItems(unsigned int uid, const QVector& include) const { auto pick = findPick(uid); if (pick) { pick->setIncludeItems(include); @@ -84,7 +89,7 @@ void PickManager::setIncludeItems(const QUuid& uid, const QVector& includ } void PickManager::update() { - QHash>> cachedPicks; + std::unordered_map>> cachedPicks; withReadLock([&] { cachedPicks = _picks; }); @@ -92,4 +97,28 @@ void PickManager::update() { bool shouldPickHUD = _shouldPickHUDOperator(); _rayPickCacheOptimizer.update(cachedPicks[PickQuery::Ray], shouldPickHUD); _stylusPickCacheOptimizer.update(cachedPicks[PickQuery::Stylus], false); +} + +bool PickManager::isLeftHand(unsigned int uid) { + auto pick = findPick(uid); + if (pick) { + return pick->isLeftHand(); + } + return false; +} + +bool PickManager::isRightHand(unsigned int uid) { + auto pick = findPick(uid); + if (pick) { + return pick->isRightHand(); + } + return false; +} + +bool PickManager::isMouse(unsigned int uid) { + auto pick = findPick(uid); + if (pick) { + return pick->isMouse(); + } + return false; } \ No newline at end of file diff --git a/libraries/pointers/src/pointers/PickManager.h b/libraries/pointers/src/pointers/PickManager.h index 5c3ed6ef25..5b069879a8 100644 --- a/libraries/pointers/src/pointers/PickManager.h +++ b/libraries/pointers/src/pointers/PickManager.h @@ -22,30 +22,40 @@ public: void update(); - QUuid addPick(PickQuery::PickType type, const std::shared_ptr pick); - void removePick(const QUuid& uid); - void enablePick(const QUuid& uid) const; - void disablePick(const QUuid& uid) const; + unsigned int addPick(PickQuery::PickType type, const std::shared_ptr pick); + void removePick(unsigned int uid); + void enablePick(unsigned int uid) const; + void disablePick(unsigned int uid) const; - PickResultPointer getPrevPickResult(const QUuid& uid) const; + PickResultPointer getPrevPickResult(unsigned int uid) const; template - std::shared_ptr getPrevPickResultTyped(const QUuid& uid) const { + std::shared_ptr getPrevPickResultTyped(unsigned int uid) const { return std::static_pointer_cast(getPrevPickResult(uid)); } - void setPrecisionPicking(const QUuid& uid, bool precisionPicking) const; - void setIgnoreItems(const QUuid& uid, const QVector& ignore) const; - void setIncludeItems(const QUuid& uid, const QVector& include) const; + void setPrecisionPicking(unsigned int uid, bool precisionPicking) const; + void setIgnoreItems(unsigned int uid, const QVector& ignore) const; + void setIncludeItems(unsigned int uid, const QVector& include) const; + + bool isLeftHand(unsigned int uid); + bool isRightHand(unsigned int uid); + bool isMouse(unsigned int uid); void setShouldPickHUDOperator(std::function shouldPickHUDOperator) { _shouldPickHUDOperator = shouldPickHUDOperator; } + void setCalculatePos2DFromHUDOperator(std::function calculatePos2DFromHUDOperator) { _calculatePos2DFromHUDOperator = calculatePos2DFromHUDOperator; } + glm::vec2 calculatePos2DFromHUD(const glm::vec3& intersection) { return _calculatePos2DFromHUDOperator(intersection); } + + static const unsigned int INVALID_PICK_ID { 0 }; protected: std::function _shouldPickHUDOperator; + std::function _calculatePos2DFromHUDOperator; - std::shared_ptr findPick(const QUuid& uid) const; - QHash>> _picks; - QHash _typeMap; + std::shared_ptr findPick(unsigned int uid) const; + std::unordered_map>> _picks; + std::unordered_map _typeMap; + unsigned int _nextPickID { INVALID_PICK_ID + 1 }; PickCacheOptimizer _rayPickCacheOptimizer; PickCacheOptimizer _stylusPickCacheOptimizer; diff --git a/libraries/pointers/src/pointers/Pointer.cpp b/libraries/pointers/src/pointers/Pointer.cpp index 8551f29dd9..767721ba7d 100644 --- a/libraries/pointers/src/pointers/Pointer.cpp +++ b/libraries/pointers/src/pointers/Pointer.cpp @@ -46,32 +46,78 @@ void Pointer::setIncludeItems(const QVector& includeItems) const { DependencyManager::get()->setIncludeItems(_pickUID, includeItems); } -void Pointer::update(float deltaTime) { +bool Pointer::isLeftHand() const { + return DependencyManager::get()->isLeftHand(_pickUID); +} + +bool Pointer::isRightHand() const { + return DependencyManager::get()->isRightHand(_pickUID); +} + +bool Pointer::isMouse() const { + return DependencyManager::get()->isMouse(_pickUID); +} + +void Pointer::update(unsigned int pointerID, float deltaTime) { // This only needs to be a read lock because update won't change any of the properties that can be modified from scripts withReadLock([&] { auto pickResult = getPrevPickResult(); updateVisuals(pickResult); - generatePointerEvents(pickResult); + generatePointerEvents(pointerID, pickResult); }); } -void Pointer::generatePointerEvents(const PickResultPointer& pickResult) { +void Pointer::generatePointerEvents(unsigned int pointerID, const PickResultPointer& pickResult) { // TODO: avatars/HUD? auto pointerManager = DependencyManager::get(); + // NOTE: After this loop: _prevButtons = buttons that were removed + // If switching to disabled or should stop triggering, release all buttons + Buttons buttons; + Buttons newButtons; + Buttons sameButtons; + const std::string PRIMARY_BUTTON = "Primary"; + bool primaryPressed = false; + if (_enabled && shouldTrigger()) { + buttons = getPressedButtons(); + primaryPressed = buttons.find(PRIMARY_BUTTON) != buttons.end(); + for (const std::string& button : buttons) { + if (_prevButtons.find(button) == _prevButtons.end()) { + newButtons.insert(button); + } else { + sameButtons.insert(button); + _prevButtons.erase(button); + } + } + } + // Hover events + bool doHover = shouldHover(); Pointer::PickedObject hoveredObject = getHoveredObject(pickResult); PointerEvent hoveredEvent = buildPointerEvent(hoveredObject, pickResult); hoveredEvent.setType(PointerEvent::Move); - // TODO: set buttons on hover events - hoveredEvent.setButton(PointerEvent::NoButtons); - if (_enabled && _hover) { + hoveredEvent.setID(pointerID); + bool releaseOnHoverLeave = !primaryPressed || (!_enabled && _prevEnabled) || (!doHover && _prevDoHover); + hoveredEvent.setReleaseOnHoverLeave(releaseOnHoverLeave); + + // if shouldHover && !_prevDoHover, only send hoverBegin + if (_enabled && _hover && doHover && !_prevDoHover) { + if (hoveredObject.type == ENTITY) { + emit pointerManager->hoverBeginEntity(hoveredObject.objectID, hoveredEvent); + } else if (hoveredObject.type == OVERLAY) { + emit pointerManager->hoverBeginOverlay(hoveredObject.objectID, hoveredEvent); + } else if (hoveredObject.type == HUD) { + emit pointerManager->hoverBeginHUD(hoveredEvent); + } + } else if (_enabled && _hover && doHover) { if (hoveredObject.type == OVERLAY) { if (_prevHoveredObject.type == OVERLAY) { if (hoveredObject.objectID == _prevHoveredObject.objectID) { emit pointerManager->hoverContinueOverlay(hoveredObject.objectID, hoveredEvent); } else { PointerEvent prevHoveredEvent = buildPointerEvent(_prevHoveredObject, pickResult); + prevHoveredEvent.setID(pointerID); + prevHoveredEvent.setReleaseOnHoverLeave(releaseOnHoverLeave); emit pointerManager->hoverEndOverlay(_prevHoveredObject.objectID, prevHoveredEvent); emit pointerManager->hoverBeginOverlay(hoveredObject.objectID, hoveredEvent); } @@ -79,6 +125,8 @@ void Pointer::generatePointerEvents(const PickResultPointer& pickResult) { emit pointerManager->hoverBeginOverlay(hoveredObject.objectID, hoveredEvent); if (_prevHoveredObject.type == ENTITY) { emit pointerManager->hoverEndEntity(_prevHoveredObject.objectID, hoveredEvent); + } else if (_prevHoveredObject.type == HUD) { + emit pointerManager->hoverEndHUD(hoveredEvent); } } } @@ -90,6 +138,8 @@ void Pointer::generatePointerEvents(const PickResultPointer& pickResult) { emit pointerManager->hoverContinueEntity(hoveredObject.objectID, hoveredEvent); } else { PointerEvent prevHoveredEvent = buildPointerEvent(_prevHoveredObject, pickResult); + prevHoveredEvent.setID(pointerID); + prevHoveredEvent.setReleaseOnHoverLeave(releaseOnHoverLeave); emit pointerManager->hoverEndEntity(_prevHoveredObject.objectID, prevHoveredEvent); emit pointerManager->hoverBeginEntity(hoveredObject.objectID, hoveredEvent); } @@ -97,25 +147,36 @@ void Pointer::generatePointerEvents(const PickResultPointer& pickResult) { emit pointerManager->hoverBeginEntity(hoveredObject.objectID, hoveredEvent); if (_prevHoveredObject.type == OVERLAY) { emit pointerManager->hoverEndOverlay(_prevHoveredObject.objectID, hoveredEvent); + } else if (_prevHoveredObject.type == HUD) { + emit pointerManager->hoverEndHUD(hoveredEvent); + } + } + } + + if (hoveredObject.type == HUD) { + if (_prevHoveredObject.type == HUD) { + // There's only one HUD + emit pointerManager->hoverContinueHUD(hoveredEvent); + } else { + emit pointerManager->hoverBeginHUD(hoveredEvent); + if (_prevHoveredObject.type == ENTITY) { + emit pointerManager->hoverEndEntity(_prevHoveredObject.objectID, hoveredEvent); + } else if (_prevHoveredObject.type == OVERLAY) { + emit pointerManager->hoverEndOverlay(_prevHoveredObject.objectID, hoveredEvent); } } } } - // Trigger events - Buttons buttons; - Buttons newButtons; - Buttons sameButtons; - // NOTE: After this loop: _prevButtons = buttons that were removed - // If !_enabled, release all buttons - if (_enabled) { - buttons = getPressedButtons(); - for (const std::string& button : buttons) { - if (_prevButtons.find(button) == _prevButtons.end()) { - newButtons.insert(button); - } else { - sameButtons.insert(button); - _prevButtons.erase(button); + if (_hover) { + // send hoverEnd events if we disable the pointer, disable hovering, or actually stop hovering over an object + if ((!_enabled && _prevEnabled) || (!doHover && _prevDoHover) || (hoveredObject.type == NONE && _prevHoveredObject.type != NONE)) { + if (_prevHoveredObject.type == ENTITY) { + emit pointerManager->hoverEndEntity(_prevHoveredObject.objectID, hoveredEvent); + } else if (_prevHoveredObject.type == OVERLAY) { + emit pointerManager->hoverEndOverlay(_prevHoveredObject.objectID, hoveredEvent); + } else if (_prevHoveredObject.type == HUD) { + emit pointerManager->hoverEndHUD(hoveredEvent); } } } @@ -130,6 +191,8 @@ void Pointer::generatePointerEvents(const PickResultPointer& pickResult) { emit pointerManager->triggerBeginEntity(hoveredObject.objectID, hoveredEvent); } else if (hoveredObject.type == OVERLAY) { emit pointerManager->triggerBeginOverlay(hoveredObject.objectID, hoveredEvent); + } else if (hoveredObject.type == HUD) { + emit pointerManager->triggerBeginHUD(hoveredEvent); } _triggeredObjects[button] = hoveredObject; } @@ -137,30 +200,38 @@ void Pointer::generatePointerEvents(const PickResultPointer& pickResult) { // Trigger continue for (const std::string& button : sameButtons) { PointerEvent triggeredEvent = buildPointerEvent(_triggeredObjects[button], pickResult); + triggeredEvent.setID(pointerID); triggeredEvent.setType(PointerEvent::Move); - hoveredEvent.setButton(chooseButton(button)); + triggeredEvent.setButton(chooseButton(button)); if (_triggeredObjects[button].type == ENTITY) { emit pointerManager->triggerContinueEntity(_triggeredObjects[button].objectID, triggeredEvent); } else if (_triggeredObjects[button].type == OVERLAY) { emit pointerManager->triggerContinueOverlay(_triggeredObjects[button].objectID, triggeredEvent); + } else if (_triggeredObjects[button].type == HUD) { + emit pointerManager->triggerContinueHUD(triggeredEvent); } } // Trigger end for (const std::string& button : _prevButtons) { PointerEvent triggeredEvent = buildPointerEvent(_triggeredObjects[button], pickResult); + triggeredEvent.setID(pointerID); triggeredEvent.setType(PointerEvent::Release); - hoveredEvent.setButton(chooseButton(button)); + triggeredEvent.setButton(chooseButton(button)); if (_triggeredObjects[button].type == ENTITY) { emit pointerManager->triggerEndEntity(_triggeredObjects[button].objectID, triggeredEvent); } else if (_triggeredObjects[button].type == OVERLAY) { emit pointerManager->triggerEndOverlay(_triggeredObjects[button].objectID, triggeredEvent); + } else if (_triggeredObjects[button].type == HUD) { + emit pointerManager->triggerEndHUD(triggeredEvent); } _triggeredObjects.erase(button); } _prevHoveredObject = hoveredObject; _prevButtons = buttons; + _prevEnabled = _enabled; + _prevDoHover = doHover; } PointerEvent::Button Pointer::chooseButton(const std::string& button) { diff --git a/libraries/pointers/src/pointers/Pointer.h b/libraries/pointers/src/pointers/Pointer.h index fa09442c83..9eeeb5ea7e 100644 --- a/libraries/pointers/src/pointers/Pointer.h +++ b/libraries/pointers/src/pointers/Pointer.h @@ -39,7 +39,7 @@ using PointerTriggers = std::vector; class Pointer : protected ReadWriteLockable { public: - Pointer(const QUuid& uid, bool enabled, bool hover) : _pickUID(uid), _enabled(enabled), _hover(hover) {} + Pointer(unsigned int uid, bool enabled, bool hover) : _pickUID(uid), _enabled(enabled), _hover(hover) {} virtual ~Pointer(); @@ -54,13 +54,17 @@ public: virtual void setIgnoreItems(const QVector& ignoreItems) const; virtual void setIncludeItems(const QVector& includeItems) const; + bool isLeftHand() const; + bool isRightHand() const; + bool isMouse() const; + // Pointers can choose to implement these virtual void setLength(float length) {} virtual void setLockEndUUID(const QUuid& objectID, bool isOverlay) {} - virtual void update(float deltaTime); + virtual void update(unsigned int pointerID, float deltaTime); virtual void updateVisuals(const PickResultPointer& pickResult) {} - void generatePointerEvents(const PickResultPointer& pickResult); + void generatePointerEvents(unsigned int pointerID, const PickResultPointer& pickResult); struct PickedObject { PickedObject(const QUuid& objectID = QUuid(), IntersectionType type = IntersectionType::NONE) : objectID(objectID), type(type) {} @@ -71,21 +75,26 @@ public: using Buttons = std::unordered_set; - virtual PickedObject getHoveredObject(const PickResultPointer& pickResult) = 0; - virtual Buttons getPressedButtons() = 0; - - QUuid getRayUID() { return _pickUID; } + unsigned int getRayUID() { return _pickUID; } protected: - const QUuid _pickUID; + const unsigned int _pickUID; bool _enabled; bool _hover; virtual PointerEvent buildPointerEvent(const PickedObject& target, const PickResultPointer& pickResult) const = 0; + virtual PickedObject getHoveredObject(const PickResultPointer& pickResult) = 0; + virtual Buttons getPressedButtons() = 0; + + virtual bool shouldHover() = 0; + virtual bool shouldTrigger() = 0; + private: PickedObject _prevHoveredObject; Buttons _prevButtons; + bool _prevEnabled { false }; + bool _prevDoHover { false }; std::unordered_map _triggeredObjects; PointerEvent::Button chooseButton(const std::string& button); diff --git a/libraries/pointers/src/pointers/PointerManager.cpp b/libraries/pointers/src/pointers/PointerManager.cpp index cd1273900d..cd59b7cddc 100644 --- a/libraries/pointers/src/pointers/PointerManager.cpp +++ b/libraries/pointers/src/pointers/PointerManager.cpp @@ -8,60 +8,67 @@ #include "PointerManager.h" -std::shared_ptr PointerManager::find(const QUuid& uid) const { +#include "PickManager.h" + +std::shared_ptr PointerManager::find(unsigned int uid) const { return resultWithReadLock>([&] { auto itr = _pointers.find(uid); if (itr != _pointers.end()) { - return *itr; + return itr->second; } return std::shared_ptr(); }); } -QUuid PointerManager::addPointer(std::shared_ptr pointer) { - QUuid result; - if (!pointer->getRayUID().isNull()) { - result = QUuid::createUuid(); - withWriteLock([&] { _pointers[result] = pointer; }); +unsigned int PointerManager::addPointer(std::shared_ptr pointer) { + unsigned int result = PointerEvent::INVALID_POINTER_ID; + if (pointer->getRayUID() != PickManager::INVALID_PICK_ID) { + withWriteLock([&] { + // Don't let the pointer IDs overflow + if (_nextPointerID < UINT32_MAX) { + result = _nextPointerID++; + _pointers[result] = pointer; + } + }); } return result; } -void PointerManager::removePointer(const QUuid& uid) { +void PointerManager::removePointer(unsigned int uid) { withWriteLock([&] { - _pointers.remove(uid); + _pointers.erase(uid); }); } -void PointerManager::enablePointer(const QUuid& uid) const { +void PointerManager::enablePointer(unsigned int uid) const { auto pointer = find(uid); if (pointer) { pointer->enable(); } } -void PointerManager::disablePointer(const QUuid& uid) const { +void PointerManager::disablePointer(unsigned int uid) const { auto pointer = find(uid); if (pointer) { pointer->disable(); } } -void PointerManager::setRenderState(const QUuid& uid, const std::string& renderState) const { +void PointerManager::setRenderState(unsigned int uid, const std::string& renderState) const { auto pointer = find(uid); if (pointer) { pointer->setRenderState(renderState); } } -void PointerManager::editRenderState(const QUuid& uid, const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) const { +void PointerManager::editRenderState(unsigned int uid, const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) const { auto pointer = find(uid); if (pointer) { pointer->editRenderState(state, startProps, pathProps, endProps); } } -PickResultPointer PointerManager::getPrevPickResult(const QUuid& uid) const { +PickResultPointer PointerManager::getPrevPickResult(unsigned int uid) const { PickResultPointer result; auto pointer = find(uid); if (pointer) { @@ -71,46 +78,70 @@ PickResultPointer PointerManager::getPrevPickResult(const QUuid& uid) const { } void PointerManager::update(float deltaTime) { - auto cachedPointers = resultWithReadLock>>([&] { - return _pointers.values(); + auto cachedPointers = resultWithReadLock>>([&] { + return _pointers; }); - for (const auto& pointer : cachedPointers) { - pointer->update(deltaTime); + for (const auto& pointerPair : cachedPointers) { + pointerPair.second->update(pointerPair.first, deltaTime); } } -void PointerManager::setPrecisionPicking(const QUuid& uid, bool precisionPicking) const { +void PointerManager::setPrecisionPicking(unsigned int uid, bool precisionPicking) const { auto pointer = find(uid); if (pointer) { pointer->setPrecisionPicking(precisionPicking); } } -void PointerManager::setIgnoreItems(const QUuid& uid, const QVector& ignoreEntities) const { +void PointerManager::setIgnoreItems(unsigned int uid, const QVector& ignoreEntities) const { auto pointer = find(uid); if (pointer) { pointer->setIgnoreItems(ignoreEntities); } } -void PointerManager::setIncludeItems(const QUuid& uid, const QVector& includeEntities) const { +void PointerManager::setIncludeItems(unsigned int uid, const QVector& includeEntities) const { auto pointer = find(uid); if (pointer) { pointer->setIncludeItems(includeEntities); } } -void PointerManager::setLength(const QUuid& uid, float length) const { +void PointerManager::setLength(unsigned int uid, float length) const { auto pointer = find(uid); if (pointer) { pointer->setLength(length); } } -void PointerManager::setLockEndUUID(const QUuid& uid, const QUuid& objectID, bool isOverlay) const { +void PointerManager::setLockEndUUID(unsigned int uid, const QUuid& objectID, bool isOverlay) const { auto pointer = find(uid); if (pointer) { pointer->setLockEndUUID(objectID, isOverlay); } } + +bool PointerManager::isLeftHand(unsigned int uid) { + auto pointer = find(uid); + if (pointer) { + return pointer->isLeftHand(); + } + return false; +} + +bool PointerManager::isRightHand(unsigned int uid) { + auto pointer = find(uid); + if (pointer) { + return pointer->isRightHand(); + } + return false; +} + +bool PointerManager::isMouse(unsigned int uid) { + auto pointer = find(uid); + if (pointer) { + return pointer->isMouse(); + } + return false; +} \ No newline at end of file diff --git a/libraries/pointers/src/pointers/PointerManager.h b/libraries/pointers/src/pointers/PointerManager.h index a9c0a47b17..7be3b8d437 100644 --- a/libraries/pointers/src/pointers/PointerManager.h +++ b/libraries/pointers/src/pointers/PointerManager.h @@ -23,26 +23,33 @@ class PointerManager : public QObject, public Dependency, protected ReadWriteLoc public: PointerManager() {} - QUuid addPointer(std::shared_ptr pointer); - void removePointer(const QUuid& uid); - void enablePointer(const QUuid& uid) const; - void disablePointer(const QUuid& uid) const; - void setRenderState(const QUuid& uid, const std::string& renderState) const; - void editRenderState(const QUuid& uid, const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) const; - PickResultPointer getPrevPickResult(const QUuid& uid) const; + unsigned int addPointer(std::shared_ptr pointer); + void removePointer(unsigned int uid); + void enablePointer(unsigned int uid) const; + void disablePointer(unsigned int uid) const; + void setRenderState(unsigned int uid, const std::string& renderState) const; + void editRenderState(unsigned int uid, const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) const; + PickResultPointer getPrevPickResult(unsigned int uid) const; - void setPrecisionPicking(const QUuid& uid, bool precisionPicking) const; - void setIgnoreItems(const QUuid& uid, const QVector& ignoreEntities) const; - void setIncludeItems(const QUuid& uid, const QVector& includeEntities) const; + void setPrecisionPicking(unsigned int uid, bool precisionPicking) const; + void setIgnoreItems(unsigned int uid, const QVector& ignoreEntities) const; + void setIncludeItems(unsigned int uid, const QVector& includeEntities) const; - void setLength(const QUuid& uid, float length) const; - void setLockEndUUID(const QUuid& uid, const QUuid& objectID, bool isOverlay) const; + void setLength(unsigned int uid, float length) const; + void setLockEndUUID(unsigned int uid, const QUuid& objectID, bool isOverlay) const; void update(float deltaTime); + bool isLeftHand(unsigned int uid); + bool isRightHand(unsigned int uid); + bool isMouse(unsigned int uid); + + static const unsigned int MOUSE_POINTER_ID { PointerEvent::INVALID_POINTER_ID + 1 }; + private: - std::shared_ptr find(const QUuid& uid) const; - QHash> _pointers; + std::shared_ptr find(unsigned int uid) const; + std::unordered_map> _pointers; + unsigned int _nextPointerID { MOUSE_POINTER_ID + 1 }; signals: void triggerBeginOverlay(const QUuid& id, const PointerEvent& pointerEvent); @@ -58,6 +65,13 @@ signals: void hoverBeginEntity(const QUuid& id, const PointerEvent& pointerEvent); void hoverContinueEntity(const QUuid& id, const PointerEvent& pointerEvent); void hoverEndEntity(const QUuid& id, const PointerEvent& pointerEvent); + + void triggerBeginHUD(const PointerEvent& pointerEvent); + void triggerContinueHUD(const PointerEvent& pointerEvent); + void triggerEndHUD(const PointerEvent& pointerEvent); + void hoverBeginHUD(const PointerEvent& pointerEvent); + void hoverContinueHUD(const PointerEvent& pointerEvent); + void hoverEndHUD(const PointerEvent& pointerEvent); }; #endif // hifi_pointers_PointerManager_h diff --git a/libraries/shared/src/PointerEvent.cpp b/libraries/shared/src/PointerEvent.cpp index e35832391d..f0f868655c 100644 --- a/libraries/shared/src/PointerEvent.cpp +++ b/libraries/shared/src/PointerEvent.cpp @@ -20,8 +20,28 @@ static bool areFlagsSet(uint32_t flags, uint32_t mask) { return (flags & mask) != 0; } -PointerEvent::PointerEvent() { - ; +PointerEvent::PointerEvent(EventType type, uint32_t id) : + _type(type), + _id(id) +{ +} + +PointerEvent::PointerEvent(EventType type, uint32_t id, const glm::vec2& pos2D, Button button, uint32_t buttons, Qt::KeyboardModifiers keyboardModifiers) : + _type(type), + _id(id), + _pos2D(pos2D), + _button(button), + _buttons(buttons), + _keyboardModifiers(keyboardModifiers) +{ +} + +PointerEvent::PointerEvent(const glm::vec2& pos2D, const glm::vec3& pos3D, const glm::vec3& normal, const glm::vec3& direction) : + _pos2D(pos2D), + _pos3D(pos3D), + _normal(normal), + _direction(direction) +{ } PointerEvent::PointerEvent(EventType type, uint32_t id, @@ -38,7 +58,11 @@ PointerEvent::PointerEvent(EventType type, uint32_t id, _buttons(buttons), _keyboardModifiers(keyboardModifiers) { - ; +} + +void PointerEvent::setButton(Button button) { + _button = button; + _buttons |= button; } QScriptValue PointerEvent::toScriptValue(QScriptEngine* engine, const PointerEvent& event) { diff --git a/libraries/shared/src/PointerEvent.h b/libraries/shared/src/PointerEvent.h index 73e37c7509..d0cba0a1a2 100644 --- a/libraries/shared/src/PointerEvent.h +++ b/libraries/shared/src/PointerEvent.h @@ -35,11 +35,14 @@ public: NumEventTypes }; - PointerEvent(); + PointerEvent() {} + PointerEvent(EventType type, uint32_t id); + PointerEvent(EventType type, uint32_t id, const glm::vec2& pos2D, Button button, uint32_t buttons, Qt::KeyboardModifiers keyboardModifiers); + PointerEvent(const glm::vec2& pos2D, const glm::vec3& pos3D, const glm::vec3& normal, const glm::vec3& direction); PointerEvent(EventType type, uint32_t id, - const glm::vec2& pos2D = glm::vec2(), const glm::vec3& pos3D = glm::vec3(), - const glm::vec3& normal = glm::vec3(), const glm::vec3& direction = glm::vec3(), - Button button = NoButtons, uint32_t buttons = NoButtons, Qt::KeyboardModifiers keyboardModifiers = Qt::KeyboardModifier::NoModifier); + const glm::vec2& pos2D, const glm::vec3& pos3D, + const glm::vec3& normal, const glm::vec3& direction, + Button button = NoButtons, uint32_t buttons = NoButtons, Qt::KeyboardModifiers keyboardModifiers = Qt::NoModifier); static QScriptValue toScriptValue(QScriptEngine* engine, const PointerEvent& event); static void fromScriptValue(const QScriptValue& object, PointerEvent& event); @@ -56,24 +59,31 @@ public: uint32_t getButtons() const { return _buttons; } Qt::KeyboardModifiers getKeyboardModifiers() const { return _keyboardModifiers; } bool shouldFocus() const { return _shouldFocus; } + bool sendReleaseOnHoverLeave() const { return _releaseOnHoverLeave; } + void setID(uint32_t id) { _id = id; } void setType(EventType type) { _type = type; } - void setButton(Button button) { _button = button; } + void setButton(Button button); void setShouldFocus(bool focus) { _shouldFocus = focus; } + void setReleaseOnHoverLeave(bool releaseOnHoverLeave) { _releaseOnHoverLeave = releaseOnHoverLeave; } + void setPos2D(const glm::vec2& pos2D) { _pos2D = pos2D; } + + static const unsigned int INVALID_POINTER_ID { 0 }; private: EventType _type; - uint32_t _id; // used to identify the pointer. (left vs right hand, for example) - glm::vec2 _pos2D; // (in meters) projected onto the xy plane of entities dimension box, (0, 0) is upper right hand corner - glm::vec3 _pos3D; // surface location in world coordinates (in meters) - glm::vec3 _normal; // surface normal - glm::vec3 _direction; // incoming direction of pointer ray. + uint32_t _id { INVALID_POINTER_ID }; // used to identify the pointer. (left vs right hand, for example) + glm::vec2 _pos2D { glm::vec2(NAN) }; // (in meters) projected onto the xy plane of entities dimension box, (0, 0) is upper right hand corner + glm::vec3 _pos3D { glm::vec3(NAN) }; // surface location in world coordinates (in meters) + glm::vec3 _normal { glm::vec3(NAN) }; // surface normal + glm::vec3 _direction { glm::vec3(NAN) }; // incoming direction of pointer ray. Button _button { NoButtons }; // button associated with this event, (if type is Press, this will be the button that is pressed) uint32_t _buttons { NoButtons }; // the current state of all the buttons. - Qt::KeyboardModifiers _keyboardModifiers; // set of keys held when event was generated + Qt::KeyboardModifiers _keyboardModifiers { Qt::KeyboardModifier::NoModifier }; // set of keys held when event was generated bool _shouldFocus { true }; + bool _releaseOnHoverLeave { true }; }; QDebug& operator<<(QDebug& dbg, const PointerEvent& p); diff --git a/libraries/ui/CMakeLists.txt b/libraries/ui/CMakeLists.txt index f28157ff97..334cce97e5 100644 --- a/libraries/ui/CMakeLists.txt +++ b/libraries/ui/CMakeLists.txt @@ -1,6 +1,7 @@ set(TARGET_NAME ui) setup_hifi_library(OpenGL Network Qml Quick Script WebChannel WebEngine WebSockets XmlPatterns) -link_hifi_libraries(shared networking gl audio) +link_hifi_libraries(shared networking gl audio pointers) +include_hifi_library_headers(controllers) # Required for some low level GL interaction in the OffscreenQMLSurface target_opengl() diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 297ed9ca50..c41ef9d4a4 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -28,6 +28,7 @@ #include "ui/Logging.h" +#include // Needs to match the constants in resources/qml/Global.js class OffscreenFlags : public QObject { @@ -84,7 +85,31 @@ bool OffscreenUi::shouldSwallowShortcut(QEvent* event) { return false; } +static QTouchDevice _touchDevice; OffscreenUi::OffscreenUi() { + static std::once_flag once; + std::call_once(once, [&] { + _touchDevice.setCapabilities(QTouchDevice::Position); + _touchDevice.setType(QTouchDevice::TouchScreen); + _touchDevice.setName("OffscreenUiTouchDevice"); + _touchDevice.setMaximumTouchPoints(4); + }); + + auto pointerManager = DependencyManager::get(); + connect(pointerManager.data(), &PointerManager::hoverBeginHUD, this, &OffscreenUi::handlePointerEvent); + connect(pointerManager.data(), &PointerManager::hoverContinueHUD, this, &OffscreenUi::handlePointerEvent); + connect(pointerManager.data(), &PointerManager::hoverEndHUD, this, &OffscreenUi::hoverEndEvent); + connect(pointerManager.data(), &PointerManager::triggerBeginHUD, this, &OffscreenUi::handlePointerEvent); + connect(pointerManager.data(), &PointerManager::triggerContinueHUD, this, &OffscreenUi::handlePointerEvent); + connect(pointerManager.data(), &PointerManager::triggerEndHUD, this, &OffscreenUi::handlePointerEvent); +} + +void OffscreenUi::hoverEndEvent(const PointerEvent& event) { + OffscreenQmlSurface::hoverEndEvent(event, _touchDevice); +} + +void OffscreenUi::handlePointerEvent(const PointerEvent& event) { + OffscreenQmlSurface::handlePointerEvent(event, _touchDevice); } QObject* OffscreenUi::getFlags() { @@ -1072,6 +1097,23 @@ bool OffscreenUi::eventFilter(QObject* originalDestination, QEvent* event) { // let the parent class do it's work bool result = OffscreenQmlSurface::eventFilter(originalDestination, event); + switch (event->type()) { + // Fall through + case QEvent::MouseButtonDblClick: + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseMove: { + QMouseEvent* mouseEvent = static_cast(event); + QPointF transformedPos = mapToVirtualScreen(mouseEvent->localPos()); + PointerEvent pointerEvent(choosePointerEventType(mouseEvent->type()), PointerManager::MOUSE_POINTER_ID, glm::vec2(transformedPos.x(), transformedPos.y()), + PointerEvent::Button(mouseEvent->button()), mouseEvent->buttons(), mouseEvent->modifiers()); + result = OffscreenQmlSurface::handlePointerEvent(pointerEvent, _touchDevice); + break; + } + default: + break; + } + // Check if this is a key press/release event that might need special attention auto type = event->type(); diff --git a/libraries/ui/src/OffscreenUi.h b/libraries/ui/src/OffscreenUi.h index 93c55d06f7..0261591295 100644 --- a/libraries/ui/src/OffscreenUi.h +++ b/libraries/ui/src/OffscreenUi.h @@ -249,6 +249,10 @@ signals: public slots: void removeModalDialog(QObject* modal); +private slots: + void hoverEndEvent(const PointerEvent& event); + void handlePointerEvent(const PointerEvent& event); + private: QString fileDialog(const QVariantMap& properties); ModalDialogListener *fileDialogAsync(const QVariantMap &properties); diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index ecd07a5874..41c3da2a4f 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -855,7 +855,7 @@ QPointF OffscreenQmlSurface::mapWindowToUi(const QPointF& sourcePosition, QObjec return QPointF(offscreenPosition.x, offscreenPosition.y); } -QPointF OffscreenQmlSurface::mapToVirtualScreen(const QPointF& originalPoint, QObject* originalWidget) { +QPointF OffscreenQmlSurface::mapToVirtualScreen(const QPointF& originalPoint) { return _mouseTranslator(originalPoint); } @@ -909,7 +909,7 @@ bool OffscreenQmlSurface::eventFilter(QObject* originalDestination, QEvent* even case QEvent::Wheel: { QWheelEvent* wheelEvent = static_cast(event); - QPointF transformedPos = mapToVirtualScreen(wheelEvent->pos(), originalDestination); + QPointF transformedPos = mapToVirtualScreen(wheelEvent->pos()); QWheelEvent mappedEvent( transformedPos, wheelEvent->delta(), wheelEvent->buttons(), @@ -920,30 +920,6 @@ bool OffscreenQmlSurface::eventFilter(QObject* originalDestination, QEvent* even } break; } - - // Fall through - case QEvent::MouseButtonDblClick: - case QEvent::MouseButtonPress: - case QEvent::MouseButtonRelease: - case QEvent::MouseMove: { - QMouseEvent* mouseEvent = static_cast(event); - QPointF transformedPos = mapToVirtualScreen(mouseEvent->localPos(), originalDestination); - QMouseEvent mappedEvent(mouseEvent->type(), - transformedPos, - mouseEvent->screenPos(), mouseEvent->button(), - mouseEvent->buttons(), mouseEvent->modifiers()); - if (event->type() == QEvent::MouseMove) { - // TODO - this line necessary for the QML Tooltop to work (which is not currently being used), but it causes interface to crash on launch on a fresh install - // need to investigate into why this crash is happening. - //_qmlContext->setContextProperty("lastMousePosition", transformedPos); - } - mappedEvent.ignore(); - if (QCoreApplication::sendEvent(_quickWindow, &mappedEvent)) { - return mappedEvent.isAccepted(); - } - break; - } - default: break; } @@ -951,6 +927,153 @@ bool OffscreenQmlSurface::eventFilter(QObject* originalDestination, QEvent* even return false; } +unsigned int OffscreenQmlSurface::deviceIdByTouchPoint(qreal x, qreal y) { + auto mapped = _rootItem->mapFromGlobal(QPoint(x, y)); + + for (auto pair : _activeTouchPoints) { + if (mapped.x() == (int)pair.second.pos().x() && mapped.y() == (int)pair.second.pos().y()) { + return pair.first; + } + } + + return PointerEvent::INVALID_POINTER_ID; +} + +PointerEvent::EventType OffscreenQmlSurface::choosePointerEventType(QEvent::Type type) { + switch (type) { + case QEvent::MouseButtonDblClick: + return PointerEvent::DoublePress; + case QEvent::MouseButtonPress: + return PointerEvent::Press; + case QEvent::MouseButtonRelease: + return PointerEvent::Release; + case QEvent::MouseMove: + return PointerEvent::Move; + default: + return PointerEvent::Move; + } +} + +void OffscreenQmlSurface::hoverEndEvent(const PointerEvent& event, class QTouchDevice& device) { + if (!_paused && _quickWindow && _pressed && event.sendReleaseOnHoverLeave()) { + PointerEvent endEvent(PointerEvent::Release, event.getID(), event.getPos2D(), event.getPos3D(), event.getNormal(), event.getDirection(), + event.getButton(), event.getButtons(), event.getKeyboardModifiers()); + handlePointerEvent(endEvent, device); + // QML onReleased is only triggered if a click has happened first. We need to send this "fake" mouse move event to properly trigger an onExited. + PointerEvent endMoveEvent(PointerEvent::Move, event.getID()); + handlePointerEvent(endMoveEvent, device); + } +} + +bool OffscreenQmlSurface::handlePointerEvent(const PointerEvent& event, class QTouchDevice& device) { + // Ignore mouse interaction if we're paused + if (_paused || !_quickWindow) { + return false; + } + + if (event.getType() == PointerEvent::Press) { + _pressed = true; + } else if (event.getType() == PointerEvent::Release) { + _pressed = false; + } + + QPointF windowPoint(event.getPos2D().x, event.getPos2D().y); + + Qt::TouchPointState state = Qt::TouchPointStationary; + if (event.getType() == PointerEvent::Press && event.getButton() == PointerEvent::PrimaryButton) { + state = Qt::TouchPointPressed; + } else if (event.getType() == PointerEvent::Release) { + state = Qt::TouchPointReleased; + } else if (_activeTouchPoints.count(event.getID()) && windowPoint != _activeTouchPoints[event.getID()].pos()) { + state = Qt::TouchPointMoved; + } + + QEvent::Type touchType = QEvent::TouchUpdate; + if (_activeTouchPoints.empty()) { + // If the first active touch point is being created, send a begin + touchType = QEvent::TouchBegin; + } if (state == Qt::TouchPointReleased && _activeTouchPoints.size() == 1 && _activeTouchPoints.count(event.getID())) { + // If the last active touch point is being released, send an end + touchType = QEvent::TouchEnd; + } + + { + QTouchEvent::TouchPoint point; + point.setId(event.getID()); + point.setState(state); + point.setPos(windowPoint); + point.setScreenPos(windowPoint); + _activeTouchPoints[event.getID()] = point; + } + + QTouchEvent touchEvent(touchType, &device, event.getKeyboardModifiers()); + { + QList touchPoints; + Qt::TouchPointStates touchPointStates; + for (const auto& entry : _activeTouchPoints) { + touchPointStates |= entry.second.state(); + touchPoints.push_back(entry.second); + } + + touchEvent.setWindow(_quickWindow); + touchEvent.setDevice(&device); + touchEvent.setTarget(_rootItem); + touchEvent.setTouchPoints(touchPoints); + touchEvent.setTouchPointStates(touchPointStates); + } + + // Send mouse events to the surface so that HTML dialog elements work with mouse press and hover. + // + // In Qt 5.9 mouse events must be sent before touch events to make sure some QtQuick components will + // receive mouse events + Qt::MouseButton button = Qt::NoButton; + Qt::MouseButtons buttons = Qt::NoButton; + if (event.getButton() == PointerEvent::PrimaryButton) { + button = Qt::LeftButton; + } + if (event.getButtons() & PointerEvent::PrimaryButton) { + buttons |= Qt::LeftButton; + } + + bool eventsAccepted = false; + +#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) + if (event.getType() == PointerEvent::Move) { + QMouseEvent mouseEvent(QEvent::MouseMove, windowPoint, windowPoint, windowPoint, button, buttons, event.getKeyboardModifiers()); + // TODO - this line necessary for the QML Tooltop to work (which is not currently being used), but it causes interface to crash on launch on a fresh install + // need to investigate into why this crash is happening. + //_qmlContext->setContextProperty("lastMousePosition", windowPoint); + QCoreApplication::sendEvent(_quickWindow, &mouseEvent); + eventsAccepted &= mouseEvent.isAccepted(); + } +#endif + + if (touchType == QEvent::TouchBegin) { + _touchBeginAccepted = QCoreApplication::sendEvent(_quickWindow, &touchEvent); + } else if (_touchBeginAccepted) { + QCoreApplication::sendEvent(_quickWindow, &touchEvent); + } + eventsAccepted &= touchEvent.isAccepted(); + + // If this was a release event, remove the point from the active touch points + if (state == Qt::TouchPointReleased) { + _activeTouchPoints.erase(event.getID()); + } + +#if QT_VERSION < QT_VERSION_CHECK(5, 9, 0) + if (event.getType() == PointerEvent::Move) { + // TODO - this line necessary for the QML Tooltop to work (which is not currently being used), but it causes interface to crash on launch on a fresh install + // need to investigate into why this crash is happening. + //_qmlContext->setContextProperty("lastMousePosition", windowPoint); + QMouseEvent mouseEvent(QEvent::MouseMove, windowPoint, windowPoint, windowPoint, button, buttons, event.getKeyboardModifiers()); + QCoreApplication::sendEvent(_webSurface->getWindow(), &mouseEvent); + eventsAccepted &= mouseEvent.isAccepted(); + } +#endif + + return eventsAccepted; +} + void OffscreenQmlSurface::pause() { _paused = true; } diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.h b/libraries/ui/src/ui/OffscreenQmlSurface.h index 12ee9e59a1..8ddfbfe8cc 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.h +++ b/libraries/ui/src/ui/OffscreenQmlSurface.h @@ -21,6 +21,9 @@ #include #include +#include +#include "PointerEvent.h" + class QWindow; class QMyQuickRenderControl; class OffscreenGLCanvas; @@ -79,7 +82,7 @@ public: QObject* getEventHandler(); QQmlContext* getSurfaceContext(); - QPointF mapToVirtualScreen(const QPointF& originalPoint, QObject* originalWidget); + QPointF mapToVirtualScreen(const QPointF& originalPoint); bool eventFilter(QObject* originalDestination, QEvent* event) override; void setKeyboardRaised(QObject* object, bool raised, bool numeric = false, bool passwordField = false); @@ -95,6 +98,10 @@ public: static std::function getDiscardLambda(); static size_t getUsedTextureMemory(); + PointerEvent::EventType choosePointerEventType(QEvent::Type type); + + unsigned int deviceIdByTouchPoint(qreal x, qreal y); + signals: void focusObjectChanged(QObject* newFocus); void focusTextChanged(bool focusText); @@ -136,6 +143,10 @@ private slots: void updateQuick(); void onFocusObjectChanged(QObject* newFocus); +public slots: + void hoverEndEvent(const PointerEvent& event, class QTouchDevice& device); + bool handlePointerEvent(const PointerEvent& event, class QTouchDevice& device); + private: QQuickWindow* _quickWindow { nullptr }; QMyQuickRenderControl* _renderControl{ nullptr }; @@ -161,6 +172,10 @@ private: QWindow* _proxyWindow { nullptr }; QQuickItem* _currentFocusItem { nullptr }; + + bool _pressed { false }; + bool _touchBeginAccepted { false }; + std::map _activeTouchPoints; }; #endif diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index 61e520af21..1243ed28e2 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -32,7 +32,7 @@ var DEFAULT_SCRIPTS_COMBINED = [ "system/tablet-ui/tabletUI.js" ]; var DEFAULT_SCRIPTS_SEPARATE = [ - "system/controllers/controllerScripts.js" + //"system/controllers/controllerScripts.js" // "system/chat.js" ];