From e9a5acda06373ac98a8a7c70c53f67df29b222c0 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 27 Oct 2017 10:39:37 -0700 Subject: [PATCH] control hovering, preset buttons for clicking, constness, scroll off surface --- interface/src/raypick/LaserPointer.cpp | 56 ++++++++++++++----- interface/src/raypick/LaserPointer.h | 18 +++--- .../src/raypick/PointerScriptingInterface.cpp | 7 ++- libraries/pointers/src/pointers/Pointer.cpp | 36 ++++++++---- libraries/pointers/src/pointers/Pointer.h | 14 +++-- .../pointers/src/pointers/PointerManager.cpp | 6 +- .../pointers/src/pointers/PointerManager.h | 6 +- 7 files changed, 100 insertions(+), 43 deletions(-) diff --git a/interface/src/raypick/LaserPointer.cpp b/interface/src/raypick/LaserPointer.cpp index b8122a2388..92a404e765 100644 --- a/interface/src/raypick/LaserPointer.cpp +++ b/interface/src/raypick/LaserPointer.cpp @@ -17,9 +17,9 @@ #include #include "PickScriptingInterface.h" -LaserPointer::LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, const PointerTriggers& triggers, - const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool enabled) : - Pointer(DependencyManager::get()->createRayPick(rayProps), enabled), +LaserPointer::LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, bool hover, + const PointerTriggers& triggers, bool faceAvatar, bool centerEndY, bool lockEnd, bool distanceScaleEnd, bool enabled) : + Pointer(DependencyManager::get()->createRayPick(rayProps), enabled, hover), _triggers(triggers), _renderStates(renderStates), _defaultRenderStates(defaultRenderStates), @@ -83,7 +83,7 @@ void LaserPointer::updateRenderStateOverlay(const OverlayID& id, const QVariant& } } -void LaserPointer::updateRenderState(const RenderState& renderState, const IntersectionType type, const float distance, const QUuid& objectID, const PickRay& pickRay, const bool defaultState) { +void LaserPointer::updateRenderState(const RenderState& renderState, const IntersectionType type, float distance, const QUuid& objectID, const PickRay& pickRay, bool defaultState) { if (!renderState.getStartID().isNull()) { QVariantMap startProps; startProps.insert("position", vec3toVariant(pickRay.origin)); @@ -210,13 +210,13 @@ Pointer::Buttons LaserPointer::getPressedButtons() { return toReturn; } -void LaserPointer::setLength(const float length) { +void LaserPointer::setLength(float length) { withWriteLock([&] { _laserLength = length; }); } -void LaserPointer::setLockEndUUID(QUuid objectID, const bool isOverlay) { +void LaserPointer::setLockEndUUID(const QUuid& objectID, bool isOverlay) { withWriteLock([&] { _objectLockEnd = std::pair(objectID, isOverlay); }); @@ -281,21 +281,49 @@ RenderState LaserPointer::buildRenderState(const QVariantMap& propMap) { return RenderState(startID, pathID, endID); } -PointerEvent LaserPointer::buildPointerEvent(const QUuid& uid, const QVariantMap& pickResult) const { +PointerEvent LaserPointer::buildPointerEvent(const PickedObject& target, const QVariantMap& pickResult) const { uint32_t id = 0; glm::vec3 intersection = vec3FromVariant(pickResult["intersection"]); glm::vec3 surfaceNormal = vec3FromVariant(pickResult["surfaceNormal"]); glm::vec3 direction = -surfaceNormal; - IntersectionType type = IntersectionType(pickResult["type"].toUInt()); + QUuid pickedID = pickResult["objectID"].toUuid(); glm::vec2 pos2D; - if (type == ENTITY) { - pos2D = projectOntoEntityXYPlane(uid, intersection); - } else if (type == OVERLAY) { - pos2D = projectOntoOverlayXYPlane(uid, intersection); + if (pickedID != target.objectID) { + QVariantMap searchRay = pickResult["searchRay"].toMap(); + glm::vec3 origin = vec3FromVariant(searchRay["origin"]); + glm::vec3 direction = vec3FromVariant(searchRay["direction"]); + if (target.type == ENTITY) { + intersection = intersectRayWithEntityXYPlane(target.objectID, origin, direction); + } else if (target.type == OVERLAY) { + intersection = intersectRayWithOverlayXYPlane(target.objectID, origin, direction); + } + } + if (target.type == ENTITY) { + pos2D = projectOntoEntityXYPlane(target.objectID, intersection); + } else if (target.type == OVERLAY) { + pos2D = projectOntoOverlayXYPlane(target.objectID, intersection); } return PointerEvent(PointerEvent::Move, id, pos2D, intersection, surfaceNormal, direction, PointerEvent::NoButtons); } +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 { + glm::vec3 n = rotation * Vectors::FRONT; + float t = glm::dot(n, point - origin) / glm::dot(n, direction); + return origin + t * direction; +} + +glm::vec3 LaserPointer::intersectRayWithOverlayXYPlane(const QUuid& overlayID, const glm::vec3& origin, const glm::vec3& direction) const { + glm::vec3 position = vec3FromVariant(qApp->getOverlays().getProperty(overlayID, "position").value); + glm::quat rotation = quatFromVariant(qApp->getOverlays().getProperty(overlayID, "rotation").value); + const glm::vec3 DEFAULT_REGISTRATION_POINT = glm::vec3(0.5f); + return intersectRayWithXYPlane(origin, direction, position, rotation, DEFAULT_REGISTRATION_POINT); +} + +glm::vec3 LaserPointer::intersectRayWithEntityXYPlane(const QUuid& entityID, const glm::vec3& origin, const glm::vec3& direction) const { + auto props = DependencyManager::get()->getEntityProperties(entityID); + return intersectRayWithXYPlane(origin, direction, props.getPosition(), props.getRotation(), props.getRegistrationPoint()); +} + glm::vec2 LaserPointer::projectOntoXYPlane(const glm::vec3& worldPos, const glm::vec3& position, const glm::quat& rotation, const glm::vec3& dimensions, const glm::vec3& registrationPoint) const { glm::quat invRot = glm::inverse(rotation); glm::vec3 localPos = invRot * (worldPos - position); @@ -325,7 +353,7 @@ glm::vec2 LaserPointer::projectOntoOverlayXYPlane(const QUuid& overlayID, const return projectOntoXYPlane(worldPos, position, rotation, dimensions, DEFAULT_REGISTRATION_POINT); } -glm::vec2 LaserPointer::projectOntoEntityXYPlane(const QUuid& entity, const glm::vec3& worldPos) const { - auto props = DependencyManager::get()->getEntityProperties(entity); +glm::vec2 LaserPointer::projectOntoEntityXYPlane(const QUuid& entityID, const glm::vec3& worldPos) const { + auto props = DependencyManager::get()->getEntityProperties(entityID); return projectOntoXYPlane(worldPos, props.getPosition(), props.getRotation(), props.getDimensions(), props.getRegistrationPoint()); } \ No newline at end of file diff --git a/interface/src/raypick/LaserPointer.h b/interface/src/raypick/LaserPointer.h index 167de88385..bdd3f2ffa0 100644 --- a/interface/src/raypick/LaserPointer.h +++ b/interface/src/raypick/LaserPointer.h @@ -54,16 +54,16 @@ public: typedef std::unordered_map RenderStateMap; typedef std::unordered_map> DefaultRenderStateMap; - LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, const PointerTriggers& triggers, - const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool enabled); + LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, bool hover, const PointerTriggers& triggers, + bool faceAvatar, bool centerEndY, bool lockEnd, bool distanceScaleEnd, bool enabled); ~LaserPointer(); void setRenderState(const std::string& state) override; // You cannot use editRenderState to change the overlay type of any part of the laser pointer. You can only edit the properties of the existing overlays. void editRenderState(const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) override; - void setLength(const float length) override; - void setLockEndUUID(QUuid objectID, const bool isOverlay) override; + void setLength(float length) override; + void setLockEndUUID(const QUuid& objectID, bool isOverlay) override; void updateVisuals(const QVariantMap& prevRayPickResult) override; @@ -73,7 +73,7 @@ public: static RenderState buildRenderState(const QVariantMap& propMap); protected: - PointerEvent buildPointerEvent(const QUuid& uid, const QVariantMap& pickResult) const override; + PointerEvent buildPointerEvent(const PickedObject& target, const QVariantMap& pickResult) const override; private: PointerTriggers _triggers; @@ -88,10 +88,14 @@ private: std::pair _objectLockEnd { std::pair(QUuid(), false)}; void updateRenderStateOverlay(const OverlayID& id, const QVariant& props); - void updateRenderState(const RenderState& renderState, const IntersectionType type, const float distance, const QUuid& objectID, const PickRay& pickRay, const bool defaultState); + void updateRenderState(const RenderState& renderState, const IntersectionType type, float distance, const QUuid& objectID, const PickRay& pickRay, bool defaultState); void disableRenderState(const RenderState& renderState); - glm::vec2 projectOntoEntityXYPlane(const QUuid& entity, const glm::vec3& worldPos) const; + + glm::vec3 intersectRayWithEntityXYPlane(const QUuid& entityID, const glm::vec3& origin, const glm::vec3& direction) const; + glm::vec3 intersectRayWithOverlayXYPlane(const QUuid& overlayID, const glm::vec3& origin, const glm::vec3& direction) const; + glm::vec3 intersectRayWithXYPlane(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& point, const glm::quat rotation, const glm::vec3& registration) const; + glm::vec2 projectOntoEntityXYPlane(const QUuid& entityID, const glm::vec3& worldPos) const; glm::vec2 projectOntoOverlayXYPlane(const QUuid& overlayID, const glm::vec3& worldPos) const; glm::vec2 projectOntoXYPlane(const glm::vec3& worldPos, const glm::vec3& position, const glm::quat& rotation, const glm::vec3& dimensions, const glm::vec3& registrationPoint) const; diff --git a/interface/src/raypick/PointerScriptingInterface.cpp b/interface/src/raypick/PointerScriptingInterface.cpp index 5ac209d295..c3a4ac164a 100644 --- a/interface/src/raypick/PointerScriptingInterface.cpp +++ b/interface/src/raypick/PointerScriptingInterface.cpp @@ -87,6 +87,11 @@ QUuid PointerScriptingInterface::createLaserPointer(const QVariant& properties) } } + bool hover = false; + if (propertyMap["hover"].isValid()) { + hover = propertyMap["hover"].toBool(); + } + PointerTriggers triggers; auto userInputMapper = DependencyManager::get(); if (propertyMap["triggers"].isValid()) { @@ -105,7 +110,7 @@ QUuid PointerScriptingInterface::createLaserPointer(const QVariant& properties) } } - return DependencyManager::get()->addPointer(std::make_shared(properties, renderStates, defaultRenderStates, triggers, + return DependencyManager::get()->addPointer(std::make_shared(properties, renderStates, defaultRenderStates, hover, triggers, faceAvatar, centerEndY, lockEnd, distanceScaleEnd, enabled)); } diff --git a/libraries/pointers/src/pointers/Pointer.cpp b/libraries/pointers/src/pointers/Pointer.cpp index 0075762f02..af560a45ab 100644 --- a/libraries/pointers/src/pointers/Pointer.cpp +++ b/libraries/pointers/src/pointers/Pointer.cpp @@ -33,7 +33,7 @@ const QVariantMap Pointer::getPrevPickResult() { return DependencyManager::get()->getPrevPickResult(_pickUID); } -void Pointer::setPrecisionPicking(const bool precisionPicking) { +void Pointer::setPrecisionPicking(bool precisionPicking) { DependencyManager::get()->setPrecisionPicking(_pickUID, precisionPicking); } @@ -60,16 +60,17 @@ void Pointer::generatePointerEvents(const QVariantMap& pickResult) { // Hover events Pointer::PickedObject hoveredObject = getHoveredObject(pickResult); - PointerEvent hoveredEvent = buildPointerEvent(hoveredObject.objectID, pickResult); + PointerEvent hoveredEvent = buildPointerEvent(hoveredObject, pickResult); hoveredEvent.setType(PointerEvent::Move); + // TODO: set buttons on hover events hoveredEvent.setButton(PointerEvent::NoButtons); - if (_enabled) { + if (_enabled && _hover) { if (hoveredObject.type == OVERLAY) { if (_prevHoveredObject.type == OVERLAY) { if (hoveredObject.objectID == _prevHoveredObject.objectID) { emit pointerManager->hoverContinueOverlay(hoveredObject.objectID, hoveredEvent); } else { - PointerEvent prevHoveredEvent = buildPointerEvent(_prevHoveredObject.objectID, pickResult); + PointerEvent prevHoveredEvent = buildPointerEvent(_prevHoveredObject, pickResult); emit pointerManager->hoverEndOverlay(_prevHoveredObject.objectID, prevHoveredEvent); emit pointerManager->hoverBeginOverlay(hoveredObject.objectID, hoveredEvent); } @@ -87,7 +88,7 @@ void Pointer::generatePointerEvents(const QVariantMap& pickResult) { if (hoveredObject.objectID == _prevHoveredObject.objectID) { emit pointerManager->hoverContinueEntity(hoveredObject.objectID, hoveredEvent); } else { - PointerEvent prevHoveredEvent = buildPointerEvent(_prevHoveredObject.objectID, pickResult); + PointerEvent prevHoveredEvent = buildPointerEvent(_prevHoveredObject, pickResult); emit pointerManager->hoverEndEntity(_prevHoveredObject.objectID, prevHoveredEvent); emit pointerManager->hoverBeginEntity(hoveredObject.objectID, hoveredEvent); } @@ -122,7 +123,7 @@ void Pointer::generatePointerEvents(const QVariantMap& pickResult) { const std::string SHOULD_FOCUS_BUTTON = "Focus"; for (const std::string& button : newButtons) { hoveredEvent.setType(PointerEvent::Press); - hoveredEvent.setButton(PointerEvent::PrimaryButton); + hoveredEvent.setButton(chooseButton(button)); hoveredEvent.setShouldFocus(button == SHOULD_FOCUS_BUTTON); if (hoveredObject.type == ENTITY) { emit pointerManager->triggerBeginEntity(hoveredObject.objectID, hoveredEvent); @@ -134,9 +135,9 @@ void Pointer::generatePointerEvents(const QVariantMap& pickResult) { // Trigger continue for (const std::string& button : sameButtons) { - PointerEvent triggeredEvent = buildPointerEvent(_triggeredObjects[button].objectID, pickResult); + PointerEvent triggeredEvent = buildPointerEvent(_triggeredObjects[button], pickResult); triggeredEvent.setType(PointerEvent::Move); - triggeredEvent.setButton(PointerEvent::PrimaryButton); + hoveredEvent.setButton(chooseButton(button)); if (_triggeredObjects[button].type == ENTITY) { emit pointerManager->triggerContinueEntity(_triggeredObjects[button].objectID, triggeredEvent); } else if (_triggeredObjects[button].type == OVERLAY) { @@ -146,9 +147,9 @@ void Pointer::generatePointerEvents(const QVariantMap& pickResult) { // Trigger end for (const std::string& button : _prevButtons) { - PointerEvent triggeredEvent = buildPointerEvent(_triggeredObjects[button].objectID, pickResult); + PointerEvent triggeredEvent = buildPointerEvent(_triggeredObjects[button], pickResult); triggeredEvent.setType(PointerEvent::Release); - triggeredEvent.setButton(PointerEvent::PrimaryButton); + hoveredEvent.setButton(chooseButton(button)); if (_triggeredObjects[button].type == ENTITY) { emit pointerManager->triggerEndEntity(_triggeredObjects[button].objectID, triggeredEvent); } else if (_triggeredObjects[button].type == OVERLAY) { @@ -159,4 +160,19 @@ void Pointer::generatePointerEvents(const QVariantMap& pickResult) { _prevHoveredObject = hoveredObject; _prevButtons = buttons; +} + +PointerEvent::Button Pointer::chooseButton(const std::string& button) { + const std::string PRIMARY_BUTTON = "Primary"; + const std::string SECONDARY_BUTTON = "Secondary"; + const std::string TERTIARY_BUTTON = "Tertiary"; + if (button == PRIMARY_BUTTON) { + return PointerEvent::PrimaryButton; + } else if (button == SECONDARY_BUTTON) { + return PointerEvent::SecondaryButton; + } else if (button == TERTIARY_BUTTON) { + return PointerEvent::TertiaryButton; + } else { + return PointerEvent::NoButtons; + } } \ No newline at end of file diff --git a/libraries/pointers/src/pointers/Pointer.h b/libraries/pointers/src/pointers/Pointer.h index 595daebc33..ca35c38c7a 100644 --- a/libraries/pointers/src/pointers/Pointer.h +++ b/libraries/pointers/src/pointers/Pointer.h @@ -38,7 +38,7 @@ using PointerTriggers = std::vector; class Pointer : protected ReadWriteLockable { public: - Pointer(const QUuid& uid, bool enabled) : _pickUID(uid), _enabled(enabled) {} + Pointer(const QUuid& uid, bool enabled, bool hover) : _pickUID(uid), _enabled(enabled), _hover(hover) {} virtual ~Pointer(); @@ -49,13 +49,13 @@ public: virtual void setRenderState(const std::string& state) = 0; virtual void editRenderState(const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) = 0; - virtual void setPrecisionPicking(const bool precisionPicking); + virtual void setPrecisionPicking(bool precisionPicking); virtual void setIgnoreItems(const QVector& ignoreItems) const; virtual void setIncludeItems(const QVector& includeItems) const; // Pointers can choose to implement these - virtual void setLength(const float length) {} - virtual void setLockEndUUID(QUuid objectID, const bool isOverlay) {} + virtual void setLength(float length) {} + virtual void setLockEndUUID(const QUuid& objectID, bool isOverlay) {} void update(); virtual void updateVisuals(const QVariantMap& pickResult) = 0; @@ -79,13 +79,17 @@ public: protected: const QUuid _pickUID; bool _enabled; + bool _hover; - virtual PointerEvent buildPointerEvent(const QUuid& uid, const QVariantMap& pickResult) const = 0; + virtual PointerEvent buildPointerEvent(const PickedObject& target, const QVariantMap& pickResult) const = 0; private: PickedObject _prevHoveredObject; Buttons _prevButtons; std::unordered_map _triggeredObjects; + + PointerEvent::Button chooseButton(const std::string& button); + }; #endif // hifi_Pick_h diff --git a/libraries/pointers/src/pointers/PointerManager.cpp b/libraries/pointers/src/pointers/PointerManager.cpp index a475ba4d83..2d41543b6b 100644 --- a/libraries/pointers/src/pointers/PointerManager.cpp +++ b/libraries/pointers/src/pointers/PointerManager.cpp @@ -79,7 +79,7 @@ void PointerManager::update() { } } -void PointerManager::setPrecisionPicking(const QUuid& uid, const bool precisionPicking) const { +void PointerManager::setPrecisionPicking(const QUuid& uid, bool precisionPicking) const { auto pointer = find(uid); if (pointer) { pointer->setPrecisionPicking(precisionPicking); @@ -100,14 +100,14 @@ void PointerManager::setIncludeItems(const QUuid& uid, const QVector& inc } } -void PointerManager::setLength(const QUuid& uid, const float length) const { +void PointerManager::setLength(const QUuid& uid, float length) const { auto pointer = find(uid); if (pointer) { pointer->setLength(length); } } -void PointerManager::setLockEndUUID(const QUuid& uid, const QUuid& objectID, const bool isOverlay) const { +void PointerManager::setLockEndUUID(const QUuid& uid, const QUuid& objectID, bool isOverlay) const { auto pointer = find(uid); if (pointer) { pointer->setLockEndUUID(objectID, isOverlay); diff --git a/libraries/pointers/src/pointers/PointerManager.h b/libraries/pointers/src/pointers/PointerManager.h index 6e13c09851..9f477d9eb2 100644 --- a/libraries/pointers/src/pointers/PointerManager.h +++ b/libraries/pointers/src/pointers/PointerManager.h @@ -31,12 +31,12 @@ public: void editRenderState(const QUuid& uid, const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) const; const QVariantMap getPrevPickResult(const QUuid& uid) const; - void setPrecisionPicking(const QUuid& uid, const bool precisionPicking) 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 setLength(const QUuid& uid, const float length) const; - void setLockEndUUID(const QUuid& uid, const QUuid& objectID, const bool isOverlay) const; + void setLength(const QUuid& uid, float length) const; + void setLockEndUUID(const QUuid& uid, const QUuid& objectID, bool isOverlay) const; void update();