From cf7450beb72b74fe9b8bd871425b693fd224d64a Mon Sep 17 00:00:00 2001 From: Anthony Thibault Date: Fri, 29 Jul 2016 17:42:41 -0700 Subject: [PATCH 01/20] handControllerGrab and web entity input integration --- interface/src/Application.cpp | 178 ++++++++++------ interface/src/Application.h | 13 +- .../src/display-plugins/CompositorHelper.cpp | 22 ++ .../src/display-plugins/CompositorHelper.h | 8 + .../src/RenderableWebEntityItem.cpp | 142 ++++++------- .../src/RenderableWebEntityItem.h | 5 +- .../system/controllers/handControllerGrab.js | 191 ++++++++++-------- 7 files changed, 340 insertions(+), 219 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5d50a1c9fe..aefbeff1c3 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1045,70 +1045,24 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : // If the user clicks an an entity, we will check that it's an unlocked web entity, and if so, set the focus to it auto entityScriptingInterface = DependencyManager::get(); connect(entityScriptingInterface.data(), &EntityScriptingInterface::clickDownOnEntity, - [this, entityScriptingInterface](const EntityItemID& entityItemID, const MouseEvent& event) { - if (_keyboardFocusedItem != entityItemID) { - _keyboardFocusedItem = UNKNOWN_ENTITY_ID; - auto properties = entityScriptingInterface->getEntityProperties(entityItemID); - if (EntityTypes::Web == properties.getType() && !properties.getLocked() && properties.getVisible()) { - auto entity = entityScriptingInterface->getEntityTree()->findEntityByID(entityItemID); - RenderableWebEntityItem* webEntity = dynamic_cast(entity.get()); - if (webEntity) { - webEntity->setProxyWindow(_window->windowHandle()); - if (_keyboardMouseDevice->isActive()) { - _keyboardMouseDevice->pluginFocusOutEvent(); - } - _keyboardFocusedItem = entityItemID; - _lastAcceptedKeyPress = usecTimestampNow(); - if (_keyboardFocusHighlightID < 0 || !getOverlays().isAddedOverlay(_keyboardFocusHighlightID)) { - _keyboardFocusHighlight = new Cube3DOverlay(); - _keyboardFocusHighlight->setAlpha(1.0f); - _keyboardFocusHighlight->setBorderSize(1.0f); - _keyboardFocusHighlight->setColor({ 0xFF, 0xEF, 0x00 }); - _keyboardFocusHighlight->setIsSolid(false); - _keyboardFocusHighlight->setPulseMin(0.5); - _keyboardFocusHighlight->setPulseMax(1.0); - _keyboardFocusHighlight->setColorPulse(1.0); - _keyboardFocusHighlight->setIgnoreRayIntersection(true); - _keyboardFocusHighlight->setDrawInFront(true); - } - _keyboardFocusHighlight->setRotation(webEntity->getRotation()); - _keyboardFocusHighlight->setPosition(webEntity->getPosition()); - _keyboardFocusHighlight->setDimensions(webEntity->getDimensions() * 1.05f); - _keyboardFocusHighlight->setVisible(true); - _keyboardFocusHighlightID = getOverlays().addOverlay(_keyboardFocusHighlight); - } - } - if (_keyboardFocusedItem == UNKNOWN_ENTITY_ID && _keyboardFocusHighlight) { - _keyboardFocusHighlight->setVisible(false); - } - - } + [this](const EntityItemID& entityItemID, const MouseEvent& event) { + setKeyboardFocusEntity(entityItemID); }); - connect(entityScriptingInterface.data(), &EntityScriptingInterface::deletingEntity, - [=](const EntityItemID& entityItemID) { - if (entityItemID == _keyboardFocusedItem) { - _keyboardFocusedItem = UNKNOWN_ENTITY_ID; - if (_keyboardFocusHighlight) { - _keyboardFocusHighlight->setVisible(false); - } + connect(entityScriptingInterface.data(), &EntityScriptingInterface::deletingEntity, [=](const EntityItemID& entityItemID) { + if (entityItemID == _keyboardFocusedItem.get()) { + setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); } }); // If the user clicks somewhere where there is NO entity at all, we will release focus connect(getEntities(), &EntityTreeRenderer::mousePressOffEntity, [=](const RayToEntityIntersectionResult& entityItemID, const QMouseEvent* event) { - _keyboardFocusedItem = UNKNOWN_ENTITY_ID; - if (_keyboardFocusHighlight) { - _keyboardFocusHighlight->setVisible(false); - } + setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); }); connect(this, &Application::aboutToQuit, [=]() { - _keyboardFocusedItem = UNKNOWN_ENTITY_ID; - if (_keyboardFocusHighlight) { - _keyboardFocusHighlight->setVisible(false); - } + setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); }); // Add periodic checks to send user activity data @@ -1334,11 +1288,13 @@ void Application::cleanupBeforeQuit() { // FIXME: once we move to shared pointer for the INputDevice we shoud remove this naked delete: _applicationStateDevice.reset(); - if (_keyboardFocusHighlightID > 0) { - getOverlays().deleteOverlay(_keyboardFocusHighlightID); - _keyboardFocusHighlightID = -1; + { + if (_keyboardFocusHighlightID > 0) { + getOverlays().deleteOverlay(_keyboardFocusHighlightID); + _keyboardFocusHighlightID = -1; + } + _keyboardFocusHighlight = nullptr; } - _keyboardFocusHighlight = nullptr; auto nodeList = DependencyManager::get(); @@ -2063,12 +2019,13 @@ bool Application::event(QEvent* event) { return true; } - if (!_keyboardFocusedItem.isInvalidID()) { - switch (event->type()) { + { + if (!_keyboardFocusedItem.get().isInvalidID()) { + switch (event->type()) { case QEvent::KeyPress: case QEvent::KeyRelease: { auto entityScriptingInterface = DependencyManager::get(); - auto entity = entityScriptingInterface->getEntityTree()->findEntityByID(_keyboardFocusedItem); + auto entity = entityScriptingInterface->getEntityTree()->findEntityByID(_keyboardFocusedItem.get()); RenderableWebEntityItem* webEntity = dynamic_cast(entity.get()); if (webEntity && webEntity->getEventHandler()) { event->setAccepted(false); @@ -2083,6 +2040,7 @@ bool Application::event(QEvent* event) { default: break; + } } } @@ -2920,12 +2878,24 @@ void Application::idle(float nsecsElapsed) { _simCounter.increment(); PerformanceTimer perfTimer("idle"); + // Drop focus from _keyboardFocusedItem if no keyboard messages for 30 seconds - if (!_keyboardFocusedItem.isInvalidID()) { - const quint64 LOSE_FOCUS_AFTER_ELAPSED_TIME = 30 * USECS_PER_SECOND; // if idle for 30 seconds, drop focus - quint64 elapsedSinceAcceptedKeyPress = usecTimestampNow() - _lastAcceptedKeyPress; - if (elapsedSinceAcceptedKeyPress > LOSE_FOCUS_AFTER_ELAPSED_TIME) { - _keyboardFocusedItem = UNKNOWN_ENTITY_ID; + { + if (!_keyboardFocusedItem.get().isInvalidID()) { + const quint64 LOSE_FOCUS_AFTER_ELAPSED_TIME = 30 * USECS_PER_SECOND; // if idle for 30 seconds, drop focus + quint64 elapsedSinceAcceptedKeyPress = usecTimestampNow() - _lastAcceptedKeyPress; + if (elapsedSinceAcceptedKeyPress > LOSE_FOCUS_AFTER_ELAPSED_TIME) { + setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); + } else { + // update position of highlight overlay + auto entityScriptingInterface = DependencyManager::get(); + auto entity = entityScriptingInterface->getEntityTree()->findEntityByID(_keyboardFocusedItem.get()); + RenderableWebEntityItem* webEntity = dynamic_cast(entity.get()); + if (webEntity && _keyboardFocusHighlight) { + _keyboardFocusHighlight->setRotation(webEntity->getRotation()); + _keyboardFocusHighlight->setPosition(webEntity->getPosition()); + } + } } } @@ -3505,6 +3475,84 @@ void Application::rotationModeChanged() const { } } +QUuid Application::getKeyboardFocusEntity() const { + return _keyboardFocusedItem.get(); +} + +void Application::setKeyboardFocusEntity(QUuid id) { + EntityItemID entityItemID(id); + setKeyboardFocusEntity(entityItemID); +} + +void Application::setKeyboardFocusEntity(EntityItemID entityItemID) { + auto entityScriptingInterface = DependencyManager::get(); + if (_keyboardFocusedItem.get() != entityItemID) { + _keyboardFocusedItem.set(UNKNOWN_ENTITY_ID); + auto properties = entityScriptingInterface->getEntityProperties(entityItemID); + if (EntityTypes::Web == properties.getType() && !properties.getLocked() && properties.getVisible()) { + auto entity = entityScriptingInterface->getEntityTree()->findEntityByID(entityItemID); + RenderableWebEntityItem* webEntity = dynamic_cast(entity.get()); + if (webEntity) { + webEntity->setProxyWindow(_window->windowHandle()); + if (_keyboardMouseDevice->isActive()) { + _keyboardMouseDevice->pluginFocusOutEvent(); + } + _keyboardFocusedItem.set(entityItemID); + _lastAcceptedKeyPress = usecTimestampNow(); + if (_keyboardFocusHighlightID < 0 || !getOverlays().isAddedOverlay(_keyboardFocusHighlightID)) { + _keyboardFocusHighlight = new Cube3DOverlay(); + _keyboardFocusHighlight->setAlpha(1.0f); + _keyboardFocusHighlight->setBorderSize(1.0f); + _keyboardFocusHighlight->setColor({ 0xFF, 0xEF, 0x00 }); + _keyboardFocusHighlight->setIsSolid(false); + _keyboardFocusHighlight->setPulseMin(0.5); + _keyboardFocusHighlight->setPulseMax(1.0); + _keyboardFocusHighlight->setColorPulse(1.0); + _keyboardFocusHighlight->setIgnoreRayIntersection(true); + _keyboardFocusHighlight->setDrawInFront(true); + } + _keyboardFocusHighlight->setRotation(webEntity->getRotation()); + _keyboardFocusHighlight->setPosition(webEntity->getPosition()); + _keyboardFocusHighlight->setDimensions(webEntity->getDimensions() * 1.05f); + _keyboardFocusHighlight->setVisible(true); + _keyboardFocusHighlightID = getOverlays().addOverlay(_keyboardFocusHighlight); + } + } + if (_keyboardFocusedItem.get() == UNKNOWN_ENTITY_ID && _keyboardFocusHighlight) { + _keyboardFocusHighlight->setVisible(false); + } + } +} + +void Application::sendEntityMouseMoveEvent(QUuid id, glm::vec3 intersectionPoint) { + QMouseEvent mouseEvent(QEvent::MouseMove, QPoint(0, 0), QPoint(0, 0), + Qt::NoButton, Qt::NoButton, Qt::NoModifier); + sendEntityMouseEvent(id, mouseEvent, intersectionPoint); +} + +void Application::sendEntityLeftMouseDownEvent(QUuid id, glm::vec3 intersectionPoint) { + QMouseEvent mouseEvent(QEvent::MouseButtonPress, QPoint(0, 0), QPoint(0, 0), + Qt::LeftButton, Qt::NoButton, Qt::NoModifier); + sendEntityMouseEvent(id, mouseEvent, intersectionPoint); +} + +void Application::sendEntityLeftMouseUpEvent(QUuid id, glm::vec3 intersectionPoint) { + QMouseEvent mouseEvent(QEvent::MouseButtonRelease, QPoint(0, 0), QPoint(0, 0), + Qt::LeftButton, Qt::NoButton, Qt::NoModifier); + sendEntityMouseEvent(id, mouseEvent, intersectionPoint); +} + +void Application::sendEntityMouseEvent(const QUuid& id, const QMouseEvent& mouseEvent, const glm::vec3& intersectionPoint) { + auto entityScriptingInterface = DependencyManager::get(); + EntityItemID entityItemID(id); + auto properties = entityScriptingInterface->getEntityProperties(entityItemID); + if (EntityTypes::Web == properties.getType() && !properties.getLocked() && properties.getVisible()) { + auto entity = entityScriptingInterface->getEntityTree()->findEntityByID(entityItemID); + RenderableWebEntityItem* webEntity = dynamic_cast(entity.get()); + webEntity->handleMouseEvent(mouseEvent, intersectionPoint); + } +} + void Application::updateDialogs(float deltaTime) const { PerformanceTimer perfTimer("updateDialogs"); bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); diff --git a/interface/src/Application.h b/interface/src/Application.h index 0af65f665f..139efb4ba7 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -43,6 +43,7 @@ #include #include #include +#include #include "avatar/MyAvatar.h" #include "Bookmarks.h" @@ -313,6 +314,16 @@ public slots: static void runTests(); + QUuid getKeyboardFocusEntity() const; // thread-safe + void setKeyboardFocusEntity(QUuid id); + void setKeyboardFocusEntity(EntityItemID entityItemID); + void sendEntityMouseMoveEvent(QUuid id, glm::vec3 intersectionPoint); + void sendEntityLeftMouseDownEvent(QUuid id, glm::vec3 intersectionPoint); + void sendEntityLeftMouseUpEvent(QUuid id, glm::vec3 intersectionPoint); + +private: + void sendEntityMouseEvent(const QUuid& id, const QMouseEvent& mouseEvent, const glm::vec3& intersectionPoint); + private slots: void showDesktop(); void clearDomainOctreeDetails(); @@ -526,7 +537,7 @@ private: DialogsManagerScriptingInterface* _dialogsManagerScriptingInterface = new DialogsManagerScriptingInterface(); - EntityItemID _keyboardFocusedItem; + ThreadSafeValueCache _keyboardFocusedItem; quint64 _lastAcceptedKeyPress = 0; bool _isForeground = true; // starts out assumed to be in foreground bool _inPaint = false; diff --git a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp index 56be8e1cf9..e460fa6648 100644 --- a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp +++ b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp @@ -451,3 +451,25 @@ QVariant ReticleInterface::getPosition() const { void ReticleInterface::setPosition(QVariant position) { _compositor->setReticlePosition(vec2FromVariant(position)); } + +QUuid ReticleInterface::getKeyboardFocusEntity() const { + QUuid result; + QMetaObject::invokeMethod(qApp, "getKeyboardFocusEntity", Qt::DirectConnection, Q_RETURN_ARG(QUuid, result)); + return result; +} + +void ReticleInterface::setKeyboardFocusEntity(QUuid id) { + QMetaObject::invokeMethod(qApp, "setKeyboardFocusEntity", Qt::QueuedConnection, Q_ARG(QUuid, id)); +} + +void ReticleInterface::sendEntityMouseMoveEvent(QUuid id, glm::vec3 intersectionPoint) { + QMetaObject::invokeMethod(qApp, "sendEntityMouseMoveEvent", Qt::QueuedConnection, Q_ARG(QUuid, id), Q_ARG(glm::vec3, intersectionPoint)); +} + +void ReticleInterface::sendEntityLeftMouseDownEvent(QUuid id, glm::vec3 intersectionPoint) { + QMetaObject::invokeMethod(qApp, "sendEntityLeftMouseDownEvent", Qt::QueuedConnection, Q_ARG(QUuid, id), Q_ARG(glm::vec3, intersectionPoint)); +} + +void ReticleInterface::sendEntityLeftMouseUpEvent(QUuid id, glm::vec3 intersectionPoint) { + QMetaObject::invokeMethod(qApp, "sendEntityLeftMouseUpEvent", Qt::QueuedConnection, Q_ARG(QUuid, id), Q_ARG(glm::vec3, intersectionPoint)); +} diff --git a/libraries/display-plugins/src/display-plugins/CompositorHelper.h b/libraries/display-plugins/src/display-plugins/CompositorHelper.h index b0b96d86be..b93ffeb67b 100644 --- a/libraries/display-plugins/src/display-plugins/CompositorHelper.h +++ b/libraries/display-plugins/src/display-plugins/CompositorHelper.h @@ -181,6 +181,7 @@ class ReticleInterface : public QObject { Q_PROPERTY(bool mouseCaptured READ isMouseCaptured) Q_PROPERTY(bool allowMouseCapture READ getAllowMouseCapture WRITE setAllowMouseCapture) Q_PROPERTY(bool pointingAtSystemOverlay READ isPointingAtSystemOverlay) + Q_PROPERTY(QUuid keyboardFocusEntity READ getKeyboardFocusEntity WRITE setKeyboardFocusEntity) public: ReticleInterface(CompositorHelper* outer) : QObject(outer), _compositor(outer) {} @@ -203,6 +204,13 @@ public: Q_INVOKABLE glm::vec2 getMaximumPosition() { return _compositor->getReticleMaximumPosition(); } + Q_INVOKABLE QUuid getKeyboardFocusEntity() const; + Q_INVOKABLE void setKeyboardFocusEntity(QUuid id); + Q_INVOKABLE void sendEntityMouseMoveEvent(QUuid id, glm::vec3 intersectionPoint); + Q_INVOKABLE void sendEntityLeftMouseDownEvent(QUuid id, glm::vec3 intersectionPoint); + Q_INVOKABLE void sendEntityLeftMouseUpEvent(QUuid id, glm::vec3 intersectionPoint); + // TODO: right mouse + double click + private: CompositorHelper* _compositor; }; diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 82c142db37..2b01de9beb 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -74,77 +74,8 @@ bool RenderableWebEntityItem::buildWebSurface(EntityTreeRenderer* renderer) { currentContext->makeCurrent(currentSurface); auto forwardMouseEvent = [=](const RayToEntityIntersectionResult& intersection, const QMouseEvent* event) { - // Ignore mouse interaction if we're locked - if (this->getLocked()) { - return; - } - - if (event->button() == Qt::MouseButton::RightButton) { - if (event->type() == QEvent::MouseButtonPress) { - const QMouseEvent* mouseEvent = static_cast(event); - _lastPress = toGlm(mouseEvent->pos()); - } - } - if (intersection.entityID == getID()) { - if (event->button() == Qt::MouseButton::RightButton) { - if (event->type() == QEvent::MouseButtonRelease) { - const QMouseEvent* mouseEvent = static_cast(event); - ivec2 dist = glm::abs(toGlm(mouseEvent->pos()) - _lastPress); - if (!glm::any(glm::greaterThan(dist, ivec2(1)))) { - AbstractViewStateInterface::instance()->postLambdaEvent([this] { - QMetaObject::invokeMethod(_webSurface->getRootItem(), "goBack"); - }); - } - _lastPress = ivec2(INT_MIN); - } - return; - } - - // FIXME doesn't work... double click events not received - if (event->type() == QEvent::MouseButtonDblClick) { - AbstractViewStateInterface::instance()->postLambdaEvent([this] { - _webSurface->getRootItem()->setProperty("url", _sourceUrl); - }); - } - - if (event->button() == Qt::MouseButton::MiddleButton) { - if (event->type() == QEvent::MouseButtonRelease) { - AbstractViewStateInterface::instance()->postLambdaEvent([this] { - _webSurface->getRootItem()->setProperty("url", _sourceUrl); - }); - } - return; - } - - // Map the intersection point to an actual offscreen pixel - glm::vec3 point = intersection.intersection; - glm::vec3 dimensions = getDimensions(); - point -= getPosition(); - point = glm::inverse(getRotation()) * point; - point /= dimensions; - point += 0.5f; - point.y = 1.0f - point.y; - point *= dimensions * (METERS_TO_INCHES * DPI); - - if (event->button() == Qt::MouseButton::LeftButton) { - if (event->type() == QEvent::MouseButtonPress) { - this->_pressed = true; - this->_lastMove = ivec2((int)point.x, (int)point.y); - } else if (event->type() == QEvent::MouseButtonRelease) { - this->_pressed = false; - } - } - if (event->type() == QEvent::MouseMove) { - this->_lastMove = ivec2((int)point.x, (int)point.y); - } - - // Forward the mouse event. - QMouseEvent mappedEvent(event->type(), - QPoint((int)point.x, (int)point.y), - event->screenPos(), event->button(), - event->buttons(), event->modifiers()); - QCoreApplication::sendEvent(_webSurface->getWindow(), &mappedEvent); + handleMouseEvent(*event, intersection.intersection); } }; _mousePressConnection = QObject::connect(renderer, &EntityTreeRenderer::mousePressOnEntity, forwardMouseEvent); @@ -236,6 +167,77 @@ QObject* RenderableWebEntityItem::getEventHandler() { return _webSurface->getEventHandler(); } +void RenderableWebEntityItem::handleMouseEvent(QMouseEvent event, glm::vec3 intersectionPoint) { + // Ignore mouse interaction if we're locked + if (getLocked()) { + return; + } + + if (event.button() == Qt::MouseButton::RightButton) { + if (event.type() == QEvent::MouseButtonPress) { + _lastPress = toGlm(event.pos()); + } + } + + if (event.button() == Qt::MouseButton::RightButton) { + if (event.type() == QEvent::MouseButtonRelease) { + ivec2 dist = glm::abs(toGlm(event.pos()) - _lastPress); + if (!glm::any(glm::greaterThan(dist, ivec2(1)))) { + AbstractViewStateInterface::instance()->postLambdaEvent([this] { + QMetaObject::invokeMethod(_webSurface->getRootItem(), "goBack"); + }); + } + _lastPress = ivec2(INT_MIN); + } + return; + } + + // FIXME doesn't work... double click events not received + if (event.type() == QEvent::MouseButtonDblClick) { + AbstractViewStateInterface::instance()->postLambdaEvent([this] { + _webSurface->getRootItem()->setProperty("url", _sourceUrl); + }); + } + + if (event.button() == Qt::MouseButton::MiddleButton) { + if (event.type() == QEvent::MouseButtonRelease) { + AbstractViewStateInterface::instance()->postLambdaEvent([this] { + _webSurface->getRootItem()->setProperty("url", _sourceUrl); + }); + } + return; + } + + // Map the intersection point to an actual offscreen pixel + glm::vec3 point = intersectionPoint; + glm::vec3 dimensions = getDimensions(); + point -= getPosition(); + point = glm::inverse(getRotation()) * point; + point /= dimensions; + point += 0.5f; + point.y = 1.0f - point.y; + point *= dimensions * (METERS_TO_INCHES * DPI); + + if (event.button() == Qt::MouseButton::LeftButton) { + if (event.type() == QEvent::MouseButtonPress) { + _pressed = true; + _lastMove = ivec2((int)point.x, (int)point.y); + } else if (event.type() == QEvent::MouseButtonRelease) { + _pressed = false; + } + } + if (event.type() == QEvent::MouseMove) { + _lastMove = ivec2((int)point.x, (int)point.y); + } + + // Forward the mouse event. + QMouseEvent mappedEvent(event.type(), + QPoint((int)point.x, (int)point.y), + event.screenPos(), event.button(), + event.buttons(), event.modifiers()); + QCoreApplication::sendEvent(_webSurface->getWindow(), &mappedEvent); +} + void RenderableWebEntityItem::destroyWebSurface() { if (_webSurface) { --_currentWebCount; diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h index 4125be61dd..b20a9af928 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -10,6 +10,7 @@ #define hifi_RenderableWebEntityItem_h #include +#include #include @@ -28,10 +29,12 @@ public: virtual void render(RenderArgs* args) override; virtual void setSourceUrl(const QString& value) override; - + void setProxyWindow(QWindow* proxyWindow); QObject* getEventHandler(); + void handleMouseEvent(QMouseEvent event, glm::vec3 intersectionPoint); + void update(const quint64& now) override; bool needsToCallUpdate() const override { return _webSurface != nullptr; } diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index c023278a3b..3d4c9ac8b0 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1019,7 +1019,8 @@ function MyController(hand) { entityID: intersection.entityID, overlayID: intersection.overlayID, searchRay: pickRay, - distance: Vec3.distance(pickRay.origin, intersection.intersection) + distance: Vec3.distance(pickRay.origin, intersection.intersection), + intersection: intersection.intersection }; } else { return result; @@ -1259,106 +1260,132 @@ function MyController(hand) { var handPosition = this.getHandPosition(); - var candidateEntities = Entities.findEntities(handPosition, NEAR_GRAB_RADIUS); - entityPropertiesCache.addEntities(candidateEntities); + var rayPickInfo = this.calcRayPickInfo(this.hand); - var potentialEquipHotspot = this.chooseBestEquipHotspot(candidateEntities); - if (potentialEquipHotspot) { - if (this.triggerSmoothedGrab()) { - this.grabbedHotspot = potentialEquipHotspot; - this.grabbedEntity = potentialEquipHotspot.entityID; - this.setState(STATE_HOLD, "eqipping '" + entityPropertiesCache.getProps(this.grabbedEntity).name + "'"); - return; - } + if (rayPickInfo.entityID) { + entityPropertiesCache.addEntity(rayPickInfo.entityID); } - var grabbableEntities = candidateEntities.filter(function(entity) { - return _this.entityIsNearGrabbable(entity, handPosition, NEAR_GRAB_MAX_DISTANCE); - }); + // if the line probe hits a non-grabbable web entity or a web entity that is grabbed by the other hand. + // route simulated mouse events to that entity. + if (rayPickInfo.entityID && entityPropertiesCache.getProps(rayPickInfo.entityID).type === "Web" && + (!this.entityIsGrabbable(rayPickInfo.entityID) || this.getOtherHandController().grabbedEntity == rayPickInfo.entityID)) { - var rayPickInfo = this.calcRayPickInfo(this.hand); - if (rayPickInfo.entityID) { - this.intersectionDistance = rayPickInfo.distance; - entityPropertiesCache.addEntity(rayPickInfo.entityID); - if (this.entityIsGrabbable(rayPickInfo.entityID) && rayPickInfo.distance < NEAR_GRAB_PICK_RADIUS) { - grabbableEntities.push(rayPickInfo.entityID); + if (Reticle.keyboardFocusEntity != rayPickInfo.entityID) { + Reticle.keyboardFocusEntity = rayPickInfo.entityID; } - } else if (rayPickInfo.overlayID) { + Reticle.sendEntityMouseMoveEvent(rayPickInfo.entityID, rayPickInfo.intersection); + if (this.triggerSmoothedGrab() && !this.lastTriggerSmoothedGrab) { + print("AJT: mouse down"); + Reticle.sendEntityLeftMouseDownEvent(rayPickInfo.entityID, rayPickInfo.intersection); + } + if (!this.triggerSmoothedGrab() && this.lastTriggerSmoothedGrab) { + print("AJT: mouse up"); + Reticle.sendEntityLeftMouseUpEvent(rayPickInfo.entityID, rayPickInfo.intersection); + } + this.lastTriggerSmoothedGrab = this.triggerSmoothedGrab(); + equipHotspotBuddy.updateHotspots([], timestamp); this.intersectionDistance = rayPickInfo.distance; } else { - this.intersectionDistance = 0; - } - var entity; - if (grabbableEntities.length > 0) { - // sort by distance - grabbableEntities.sort(function(a, b) { - var aDistance = Vec3.distance(entityPropertiesCache.getProps(a).position, handPosition); - var bDistance = Vec3.distance(entityPropertiesCache.getProps(b).position, handPosition); - return aDistance - bDistance; + var candidateEntities = Entities.findEntities(handPosition, NEAR_GRAB_RADIUS); + entityPropertiesCache.addEntities(candidateEntities); + + var potentialEquipHotspot = this.chooseBestEquipHotspot(candidateEntities); + if (potentialEquipHotspot) { + if (this.triggerSmoothedGrab()) { + this.grabbedHotspot = potentialEquipHotspot; + this.grabbedEntity = potentialEquipHotspot.entityID; + this.setState(STATE_HOLD, "eqipping '" + entityPropertiesCache.getProps(this.grabbedEntity).name + "'"); + return; + } + } + + var grabbableEntities = candidateEntities.filter(function(entity) { + return _this.entityIsNearGrabbable(entity, handPosition, NEAR_GRAB_MAX_DISTANCE); }); - entity = grabbableEntities[0]; - name = entityPropertiesCache.getProps(entity).name; - this.grabbedEntity = entity; - if (this.entityWantsTrigger(entity)) { - if (this.triggerSmoothedGrab()) { - this.setState(STATE_NEAR_TRIGGER, "near trigger '" + name + "'"); - return; - } else { - // potentialNearTriggerEntity = entity; + + if (rayPickInfo.entityID) { + this.intersectionDistance = rayPickInfo.distance; + if (this.entityIsGrabbable(rayPickInfo.entityID) && rayPickInfo.distance < NEAR_GRAB_PICK_RADIUS) { + grabbableEntities.push(rayPickInfo.entityID); } + } else if (rayPickInfo.overlayID) { + this.intersectionDistance = rayPickInfo.distance; } else { - if (this.triggerSmoothedGrab()) { - var props = entityPropertiesCache.getProps(entity); - var grabProps = entityPropertiesCache.getGrabProps(entity); - var refCount = grabProps.refCount ? grabProps.refCount : 0; - if (refCount >= 1) { - // if another person is holding the object, remember to restore the - // parent info, when we are finished grabbing it. - this.shouldResetParentOnRelease = true; - this.previousParentID = props.parentID; - this.previousParentJointIndex = props.parentJointIndex; + this.intersectionDistance = 0; + } + + var entity; + if (grabbableEntities.length > 0) { + // sort by distance + grabbableEntities.sort(function(a, b) { + var aDistance = Vec3.distance(entityPropertiesCache.getProps(a).position, handPosition); + var bDistance = Vec3.distance(entityPropertiesCache.getProps(b).position, handPosition); + return aDistance - bDistance; + }); + entity = grabbableEntities[0]; + name = entityPropertiesCache.getProps(entity).name; + this.grabbedEntity = entity; + if (this.entityWantsTrigger(entity)) { + if (this.triggerSmoothedGrab()) { + this.setState(STATE_NEAR_TRIGGER, "near trigger '" + name + "'"); + return; + } else { + // potentialNearTriggerEntity = entity; } - - this.setState(STATE_NEAR_GRABBING, "near grab '" + name + "'"); - return; } else { - // potentialNearGrabEntity = entity; + if (this.triggerSmoothedGrab()) { + var props = entityPropertiesCache.getProps(entity); + var grabProps = entityPropertiesCache.getGrabProps(entity); + var refCount = grabProps.refCount ? grabProps.refCount : 0; + if (refCount >= 1) { + // if another person is holding the object, remember to restore the + // parent info, when we are finished grabbing it. + this.shouldResetParentOnRelease = true; + this.previousParentID = props.parentID; + this.previousParentJointIndex = props.parentJointIndex; + } + + this.setState(STATE_NEAR_GRABBING, "near grab '" + name + "'"); + return; + } else { + // potentialNearGrabEntity = entity; + } } } - } - if (rayPickInfo.entityID) { - entity = rayPickInfo.entityID; - name = entityPropertiesCache.getProps(entity).name; - if (this.entityWantsTrigger(entity)) { - if (this.triggerSmoothedGrab()) { - this.grabbedEntity = entity; - this.setState(STATE_FAR_TRIGGER, "far trigger '" + name + "'"); - return; - } else { - // potentialFarTriggerEntity = entity; - } - } else if (this.entityIsDistanceGrabbable(rayPickInfo.entityID, handPosition)) { - if (this.triggerSmoothedGrab() && !isEditing()) { - this.grabbedEntity = entity; - this.setState(STATE_DISTANCE_HOLDING, "distance hold '" + name + "'"); - return; - } else { - // potentialFarGrabEntity = entity; + if (rayPickInfo.entityID) { + entity = rayPickInfo.entityID; + name = entityPropertiesCache.getProps(entity).name; + if (this.entityWantsTrigger(entity)) { + if (this.triggerSmoothedGrab()) { + this.grabbedEntity = entity; + this.setState(STATE_FAR_TRIGGER, "far trigger '" + name + "'"); + return; + } else { + // potentialFarTriggerEntity = entity; + } + } else if (this.entityIsDistanceGrabbable(rayPickInfo.entityID, handPosition)) { + if (this.triggerSmoothedGrab() && !isEditing()) { + this.grabbedEntity = entity; + this.setState(STATE_DISTANCE_HOLDING, "distance hold '" + name + "'"); + return; + } else { + // potentialFarGrabEntity = entity; + } } } + + this.updateEquipHaptics(potentialEquipHotspot, handPosition); + + var nearEquipHotspots = this.chooseNearEquipHotspots(candidateEntities, EQUIP_HOTSPOT_RENDER_RADIUS); + equipHotspotBuddy.updateHotspots(nearEquipHotspots, timestamp); + if (potentialEquipHotspot) { + equipHotspotBuddy.highlightHotspot(potentialEquipHotspot); + } } - this.updateEquipHaptics(potentialEquipHotspot, handPosition); - - var nearEquipHotspots = this.chooseNearEquipHotspots(candidateEntities, EQUIP_HOTSPOT_RENDER_RADIUS); - equipHotspotBuddy.updateHotspots(nearEquipHotspots, timestamp); - if (potentialEquipHotspot) { - equipHotspotBuddy.highlightHotspot(potentialEquipHotspot); - } - - this.searchIndicatorOn(rayPickInfo.searchRay); Reticle.setVisible(false); }; @@ -2395,4 +2422,4 @@ function cleanup() { } Script.scriptEnding.connect(cleanup); -Script.update.connect(update); \ No newline at end of file +Script.update.connect(update); From 6d768d832723f5894d10c85b978e7796f9946d2d Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 2 Aug 2016 15:23:46 -0700 Subject: [PATCH 02/20] hand controllers send touch events instead of mouse events to web entities This gives a much better experience when scrolling web content. --- interface/src/Application.cpp | 41 +++++++++++ interface/src/Application.h | 5 ++ .../src/display-plugins/CompositorHelper.cpp | 12 ++++ .../src/display-plugins/CompositorHelper.h | 5 +- .../src/RenderableWebEntityItem.cpp | 32 +++++++++ .../src/RenderableWebEntityItem.h | 1 + .../system/controllers/handControllerGrab.js | 68 +++++++++++++++---- 7 files changed, 150 insertions(+), 14 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index aefbeff1c3..3d1b28ada8 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3553,6 +3553,47 @@ void Application::sendEntityMouseEvent(const QUuid& id, const QMouseEvent& mouse } } +void Application::sendEntityTouchUpdateEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint) { + QTouchEvent::TouchPoint point; + point.setId(fingerID); + point.setState(Qt::TouchPointMoved); + QList touchPoints; + touchPoints.push_back(point); + QTouchEvent touchEvent(QEvent::TouchUpdate, nullptr, Qt::NoModifier, Qt::TouchPointMoved, touchPoints); + sendEntityTouchEvent(entityID, touchEvent, intersectionPoint); +} + +void Application::sendEntityTouchBeginEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint) { + QTouchEvent::TouchPoint point; + point.setId(fingerID); + point.setState(Qt::TouchPointPressed); + QList touchPoints; + touchPoints.push_back(point); + QTouchEvent touchEvent(QEvent::TouchBegin, nullptr, Qt::NoModifier, Qt::TouchPointPressed, touchPoints); + sendEntityTouchEvent(entityID, touchEvent, intersectionPoint); +} + +void Application::sendEntityTouchEndEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint) { + QTouchEvent::TouchPoint point; + point.setId(fingerID); + point.setState(Qt::TouchPointReleased); + QList touchPoints; + touchPoints.push_back(point); + QTouchEvent touchEvent(QEvent::TouchEnd, nullptr, Qt::NoModifier, Qt::TouchPointReleased, touchPoints); + sendEntityTouchEvent(entityID, touchEvent, intersectionPoint); +} + +void Application::sendEntityTouchEvent(const QUuid& id, const QTouchEvent& touchEvent, const glm::vec3& intersectionPoint) { + auto entityScriptingInterface = DependencyManager::get(); + EntityItemID entityItemID(id); + auto properties = entityScriptingInterface->getEntityProperties(entityItemID); + if (EntityTypes::Web == properties.getType() && !properties.getLocked() && properties.getVisible()) { + auto entity = entityScriptingInterface->getEntityTree()->findEntityByID(entityItemID); + RenderableWebEntityItem* webEntity = dynamic_cast(entity.get()); + webEntity->handleTouchEvent(touchEvent, intersectionPoint); + } +} + void Application::updateDialogs(float deltaTime) const { PerformanceTimer perfTimer("updateDialogs"); bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); diff --git a/interface/src/Application.h b/interface/src/Application.h index 139efb4ba7..bfa0856103 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -321,7 +321,12 @@ public slots: void sendEntityLeftMouseDownEvent(QUuid id, glm::vec3 intersectionPoint); void sendEntityLeftMouseUpEvent(QUuid id, glm::vec3 intersectionPoint); + void sendEntityTouchUpdateEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint); + void sendEntityTouchBeginEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint); + void sendEntityTouchEndEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint); + private: + void sendEntityTouchEvent(const QUuid& id, const QTouchEvent& touchEvent, const glm::vec3& intersectionPoint); void sendEntityMouseEvent(const QUuid& id, const QMouseEvent& mouseEvent, const glm::vec3& intersectionPoint); private slots: diff --git a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp index e460fa6648..3a154af462 100644 --- a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp +++ b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp @@ -473,3 +473,15 @@ void ReticleInterface::sendEntityLeftMouseDownEvent(QUuid id, glm::vec3 intersec void ReticleInterface::sendEntityLeftMouseUpEvent(QUuid id, glm::vec3 intersectionPoint) { QMetaObject::invokeMethod(qApp, "sendEntityLeftMouseUpEvent", Qt::QueuedConnection, Q_ARG(QUuid, id), Q_ARG(glm::vec3, intersectionPoint)); } + +void ReticleInterface::sendEntityTouchUpdateEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint) { + QMetaObject::invokeMethod(qApp, "sendEntityTouchUpdateEvent", Qt::QueuedConnection, Q_ARG(QUuid, entityID), Q_ARG(int, fingerID), Q_ARG(glm::vec3, intersectionPoint)); +} + +void ReticleInterface::sendEntityTouchBeginEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint) { + QMetaObject::invokeMethod(qApp, "sendEntityTouchBeginEvent", Qt::QueuedConnection, Q_ARG(QUuid, entityID), Q_ARG(int, fingerID), Q_ARG(glm::vec3, intersectionPoint)); +} + +void ReticleInterface::sendEntityTouchEndEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint) { + QMetaObject::invokeMethod(qApp, "sendEntityTouchEndEvent", Qt::QueuedConnection, Q_ARG(QUuid, entityID), Q_ARG(int, fingerID), Q_ARG(glm::vec3, intersectionPoint)); +} diff --git a/libraries/display-plugins/src/display-plugins/CompositorHelper.h b/libraries/display-plugins/src/display-plugins/CompositorHelper.h index b93ffeb67b..78d4a10bf0 100644 --- a/libraries/display-plugins/src/display-plugins/CompositorHelper.h +++ b/libraries/display-plugins/src/display-plugins/CompositorHelper.h @@ -209,7 +209,10 @@ public: Q_INVOKABLE void sendEntityMouseMoveEvent(QUuid id, glm::vec3 intersectionPoint); Q_INVOKABLE void sendEntityLeftMouseDownEvent(QUuid id, glm::vec3 intersectionPoint); Q_INVOKABLE void sendEntityLeftMouseUpEvent(QUuid id, glm::vec3 intersectionPoint); - // TODO: right mouse + double click + + Q_INVOKABLE void sendEntityTouchUpdateEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint); + Q_INVOKABLE void sendEntityTouchBeginEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint); + Q_INVOKABLE void sendEntityTouchEndEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint); private: CompositorHelper* _compositor; diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 2b01de9beb..20e921cd16 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -238,6 +238,38 @@ void RenderableWebEntityItem::handleMouseEvent(QMouseEvent event, glm::vec3 inte QCoreApplication::sendEvent(_webSurface->getWindow(), &mappedEvent); } +void RenderableWebEntityItem::handleTouchEvent(QTouchEvent event, glm::vec3 intersectionPoint) { + // Ignore mouse interaction if we're locked + if (getLocked()) { + return; + } + + // Map the intersection point to an actual offscreen pixel + glm::vec3 point = intersectionPoint; + glm::vec3 dimensions = getDimensions(); + point -= getPosition(); + point = glm::inverse(getRotation()) * point; + point /= dimensions; + point += 0.5f; + point.y = 1.0f - point.y; + point *= dimensions * (METERS_TO_INCHES * DPI); + + QList touchPoints = event.touchPoints(); + for (auto& touchPoint : touchPoints) { + touchPoint.setPos(QPointF(point.x, point.y)); + touchPoint.setScreenPos(QPointF(point.x, point.y)); + } + + // Forward the touch event. + QTouchEvent mappedEvent(event.type(), + event.device(), + event.modifiers(), + event.touchPointStates(), + touchPoints); + QCoreApplication::sendEvent(_webSurface->getWindow(), &mappedEvent); +} + + void RenderableWebEntityItem::destroyWebSurface() { if (_webSurface) { --_currentWebCount; diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h index b20a9af928..c6afacdf5f 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -34,6 +34,7 @@ public: QObject* getEventHandler(); void handleMouseEvent(QMouseEvent event, glm::vec3 intersectionPoint); + void handleTouchEvent(QTouchEvent event, glm::vec3 intersectionPoint); void update(const quint64& now) override; bool needsToCallUpdate() const override { return _webSurface != nullptr; } diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 3d4c9ac8b0..6a3f0ca949 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -192,6 +192,7 @@ CONTROLLER_STATE_MACHINE[STATE_OFF] = { }; CONTROLLER_STATE_MACHINE[STATE_SEARCHING] = { name: "searching", + enterMethod: "searchEnter", updateMethod: "search" }; CONTROLLER_STATE_MACHINE[STATE_DISTANCE_HOLDING] = { @@ -220,6 +221,18 @@ CONTROLLER_STATE_MACHINE[STATE_FAR_TRIGGER] = { updateMethod: "farTrigger" }; +function rayIntersectPlane(planePosition, planeNormal, rayStart, rayDirection) { + var rayDirectionDotPlaneNormal = Vec3.dot(rayDirection, planeNormal); + if (rayDirectionDotPlaneNormal > 0.00001 || rayDirectionDotPlaneNormal < -0.00001) { + var rayStartDotPlaneNormal = Vec3.dot(Vec3.subtract(planePosition, rayStart), planeNormal); + var distance = rayStartDotPlaneNormal / rayDirectionDotPlaneNormal; + return {hit: true, distance: distance}; + } else { + // ray is parallel to the plane + return {hit: false, distance: 0}; + } +} + function stateToName(state) { return CONTROLLER_STATE_MACHINE[state] ? CONTROLLER_STATE_MACHINE[state].name : "???"; } @@ -965,7 +978,7 @@ function MyController(hand) { } else if (potentialEquipHotspot && Vec3.distance(this.lastHapticPulseLocation, currentLocation) > HAPTIC_TEXTURE_DISTANCE) { Controller.triggerHapticPulse(HAPTIC_TEXTURE_STRENGTH, HAPTIC_TEXTURE_DURATION, this.hand); this.lastHapticPulseLocation = currentLocation; - } + } this.prevPotentialEquipHotspot = potentialEquipHotspot; }; @@ -1243,6 +1256,10 @@ function MyController(hand) { } }; + this.searchEnter = function() { + this.capturedWebEntity = null; + }; + this.search = function(deltaTime, timestamp) { var _this = this; var name; @@ -1266,26 +1283,51 @@ function MyController(hand) { entityPropertiesCache.addEntity(rayPickInfo.entityID); } - // if the line probe hits a non-grabbable web entity or a web entity that is grabbed by the other hand. - // route simulated mouse events to that entity. - if (rayPickInfo.entityID && entityPropertiesCache.getProps(rayPickInfo.entityID).type === "Web" && - (!this.entityIsGrabbable(rayPickInfo.entityID) || this.getOtherHandController().grabbedEntity == rayPickInfo.entityID)) { + // route simulated touch events to a webEntity. + if (this.capturedWebEntity || + (rayPickInfo.entityID && entityPropertiesCache.getProps(rayPickInfo.entityID).type === "Web" && + (!this.entityIsGrabbable(rayPickInfo.entityID) || this.getOtherHandController().grabbedEntity == rayPickInfo.entityID))) { - if (Reticle.keyboardFocusEntity != rayPickInfo.entityID) { - Reticle.keyboardFocusEntity = rayPickInfo.entityID; + var standardControllerValue = (hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; + var pose = Controller.getPoseValue(standardControllerValue); + var worldHandPosition = Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, pose.translation), MyAvatar.position); + var worldHandRotation = Quat.multiply(MyAvatar.orientation, pose.rotation); + + var focusedEntity = this.capturedWebEntity || rayPickInfo.entityID; + entityPropertiesCache.addEntity(focusedEntity); + + var props = entityPropertiesCache.getProps(focusedEntity); + var planePosition = props.position + var planeNormal = Vec3.multiplyQbyV(props.rotation, {x: 0, y: 0, z: 1.0}); + var rayStart = worldHandPosition; + var rayDirection = Quat.getUp(worldHandRotation); + var intersectionInfo = rayIntersectPlane(planePosition, planeNormal, rayStart, rayDirection); + + var intersectionPoint = planePosition; + if (intersectionInfo.hit && intersectionInfo.distance > 0) { + intersectionPoint = Vec3.sum(rayStart, Vec3.multiply(intersectionInfo.distance, rayDirection)); + } else { + intersectionPoint = planePosition; } - Reticle.sendEntityMouseMoveEvent(rayPickInfo.entityID, rayPickInfo.intersection); + + if (Reticle.keyboardFocusEntity != focusedEntity) { + Reticle.keyboardFocusEntity = focusedEntity; + } + + Reticle.sendEntityTouchUpdateEvent(focusedEntity, this.hand, intersectionPoint); + if (this.triggerSmoothedGrab() && !this.lastTriggerSmoothedGrab) { - print("AJT: mouse down"); - Reticle.sendEntityLeftMouseDownEvent(rayPickInfo.entityID, rayPickInfo.intersection); + Reticle.sendEntityTouchBeginEvent(focusedEntity, this.hand, intersectionPoint); + this.capturedWebEntity = focusedEntity; } if (!this.triggerSmoothedGrab() && this.lastTriggerSmoothedGrab) { - print("AJT: mouse up"); - Reticle.sendEntityLeftMouseUpEvent(rayPickInfo.entityID, rayPickInfo.intersection); + Reticle.sendEntityTouchEndEvent(focusedEntity, this.hand, intersectionPoint); + this.capturedWebEntity = null; } this.lastTriggerSmoothedGrab = this.triggerSmoothedGrab(); + equipHotspotBuddy.updateHotspots([], timestamp); - this.intersectionDistance = rayPickInfo.distance; + this.intersectionDistance = intersectionInfo.distance; } else { var candidateEntities = Entities.findEntities(handPosition, NEAR_GRAB_RADIUS); From 0ab15f0dcdc82757367136e9b44e8f1b6abcf0a3 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 2 Aug 2016 15:35:59 -0700 Subject: [PATCH 03/20] fixed one frame lag on webEntity keyboard highlight overlay. Also, made overlay not draw in front. --- interface/src/Application.cpp | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 3d1b28ada8..7a2487b366 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2879,6 +2879,20 @@ void Application::idle(float nsecsElapsed) { PerformanceTimer perfTimer("idle"); + // Normally we check PipelineWarnings, but since idle will often take more than 10ms we only show these idle timing + // details if we're in ExtraDebugging mode. However, the ::update() and its subcomponents will show their timing + // details normally. + bool showWarnings = getLogger()->extraDebugging(); + PerformanceWarning warn(showWarnings, "idle()"); + + { + PerformanceTimer perfTimer("update"); + PerformanceWarning warn(showWarnings, "Application::idle()... update()"); + static const float BIGGEST_DELTA_TIME_SECS = 0.25f; + update(glm::clamp(secondsSinceLastUpdate, 0.0f, BIGGEST_DELTA_TIME_SECS)); + } + + // Drop focus from _keyboardFocusedItem if no keyboard messages for 30 seconds { if (!_keyboardFocusedItem.get().isInvalidID()) { @@ -2899,18 +2913,6 @@ void Application::idle(float nsecsElapsed) { } } - // Normally we check PipelineWarnings, but since idle will often take more than 10ms we only show these idle timing - // details if we're in ExtraDebugging mode. However, the ::update() and its subcomponents will show their timing - // details normally. - bool showWarnings = getLogger()->extraDebugging(); - PerformanceWarning warn(showWarnings, "idle()"); - - { - PerformanceTimer perfTimer("update"); - PerformanceWarning warn(showWarnings, "Application::idle()... update()"); - static const float BIGGEST_DELTA_TIME_SECS = 0.25f; - update(glm::clamp(secondsSinceLastUpdate, 0.0f, BIGGEST_DELTA_TIME_SECS)); - } { PerformanceTimer perfTimer("pluginIdle"); PerformanceWarning warn(showWarnings, "Application::idle()... pluginIdle()"); @@ -3509,7 +3511,7 @@ void Application::setKeyboardFocusEntity(EntityItemID entityItemID) { _keyboardFocusHighlight->setPulseMax(1.0); _keyboardFocusHighlight->setColorPulse(1.0); _keyboardFocusHighlight->setIgnoreRayIntersection(true); - _keyboardFocusHighlight->setDrawInFront(true); + _keyboardFocusHighlight->setDrawInFront(false); } _keyboardFocusHighlight->setRotation(webEntity->getRotation()); _keyboardFocusHighlight->setPosition(webEntity->getPosition()); From 75b88b0794e0aac2a468930092ea6bd8b8d8dc49 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 3 Aug 2016 10:17:51 -0700 Subject: [PATCH 04/20] WebEntities now support custom hifi url handling WebEntities now use qml/controls/WebView.qml instead of qml/WebEntity.qml. marketplace urls and hifi urls should work properly in web entities. --- interface/resources/qml/WebEntity.qml | 10 ----- interface/src/ui/overlays/Web3DOverlay.cpp | 4 +- .../src/RenderableWebEntityItem.cpp | 4 +- libraries/gl/src/gl/OffscreenQmlSurface.cpp | 40 +++++++++++++++++++ libraries/ui/src/OffscreenUi.cpp | 34 ---------------- 5 files changed, 44 insertions(+), 48 deletions(-) delete mode 100644 interface/resources/qml/WebEntity.qml diff --git a/interface/resources/qml/WebEntity.qml b/interface/resources/qml/WebEntity.qml deleted file mode 100644 index ae94105672..0000000000 --- a/interface/resources/qml/WebEntity.qml +++ /dev/null @@ -1,10 +0,0 @@ -import QtQuick 2.3 -import QtQuick.Controls 1.2 -import QtWebEngine 1.1 - -WebEngineView { - id: root - anchors.fill: parent - objectName: "webview" - url: "about:blank" -} diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index 1c84e71fa7..4b9267a31b 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -70,8 +70,8 @@ void Web3DOverlay::render(RenderArgs* args) { if (!_webSurface) { _webSurface = new OffscreenQmlSurface(); _webSurface->create(currentContext); - _webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/")); - _webSurface->load("WebEntity.qml"); + _webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/controls/")); + _webSurface->load("WebView.qml"); _webSurface->resume(); _webSurface->getRootItem()->setProperty("url", _url); _webSurface->resize(QSize(_resolution.x, _resolution.y)); diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 20e921cd16..0c40020051 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -63,8 +63,8 @@ bool RenderableWebEntityItem::buildWebSurface(EntityTreeRenderer* renderer) { QSurface * currentSurface = currentContext->surface(); _webSurface = new OffscreenQmlSurface(); _webSurface->create(currentContext); - _webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/")); - _webSurface->load("WebEntity.qml"); + _webSurface->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/controls/")); + _webSurface->load("WebView.qml"); _webSurface->resume(); _webSurface->getRootItem()->setProperty("url", _sourceUrl); _connection = QObject::connect(_webSurface, &OffscreenQmlSurface::textureUpdated, [&](GLuint textureId) { diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp index 8c167fafdc..3c2bbc2469 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp +++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp @@ -25,12 +25,47 @@ #include #include #include +#include +#include #include "OffscreenGLCanvas.h" #include "GLEscrow.h" #include "GLHelpers.h" +QString fixupHifiUrl(const QString& urlString) { + static const QString ACCESS_TOKEN_PARAMETER = "access_token"; + static const QString ALLOWED_HOST = "metaverse.highfidelity.com"; + QUrl url(urlString); + QUrlQuery query(url); + if (url.host() == ALLOWED_HOST && query.allQueryItemValues(ACCESS_TOKEN_PARAMETER).empty()) { + auto accountManager = DependencyManager::get(); + query.addQueryItem(ACCESS_TOKEN_PARAMETER, accountManager->getAccountInfo().getAccessToken().token); + url.setQuery(query.query()); + return url.toString(); + } + return urlString; +} + +class UrlHandler : public QObject { + Q_OBJECT +public: + Q_INVOKABLE bool canHandleUrl(const QString& url) { + static auto handler = dynamic_cast(qApp); + return handler->canAcceptURL(url); + } + + Q_INVOKABLE bool handleUrl(const QString& url) { + static auto handler = dynamic_cast(qApp); + return handler->acceptURL(url); + } + + // FIXME hack for authentication, remove when we migrate to Qt 5.6 + Q_INVOKABLE QString fixupUrl(const QString& originalUrl) { + return fixupHifiUrl(originalUrl); + } +}; + // Time between receiving a request to render the offscreen UI actually triggering // the render. Could possibly be increased depending on the framerate we expect to // achieve. @@ -419,6 +454,9 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) { QObject::connect(qApp, &QCoreApplication::aboutToQuit, this, &OffscreenQmlSurface::onAboutToQuit); _updateTimer.setInterval(MIN_TIMER_MS); _updateTimer.start(); + + auto rootContext = getRootContext(); + rootContext->setContextProperty("urlHandler", new UrlHandler()); } void OffscreenQmlSurface::resize(const QSize& newSize_, bool forceResize) { @@ -767,3 +805,5 @@ void OffscreenQmlSurface::setFocusText(bool newFocusText) { emit focusTextChanged(_focusText); } } + +#include "OffscreenQmlSurface.moc" diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 06ef456006..8cae137f67 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -60,39 +60,6 @@ private: bool _navigationFocusDisabled{ false }; }; -QString fixupHifiUrl(const QString& urlString) { - static const QString ACCESS_TOKEN_PARAMETER = "access_token"; - static const QString ALLOWED_HOST = "metaverse.highfidelity.com"; - QUrl url(urlString); - QUrlQuery query(url); - if (url.host() == ALLOWED_HOST && query.allQueryItemValues(ACCESS_TOKEN_PARAMETER).empty()) { - auto accountManager = DependencyManager::get(); - query.addQueryItem(ACCESS_TOKEN_PARAMETER, accountManager->getAccountInfo().getAccessToken().token); - url.setQuery(query.query()); - return url.toString(); - } - return urlString; -} - -class UrlHandler : public QObject { - Q_OBJECT -public: - Q_INVOKABLE bool canHandleUrl(const QString& url) { - static auto handler = dynamic_cast(qApp); - return handler->canAcceptURL(url); - } - - Q_INVOKABLE bool handleUrl(const QString& url) { - static auto handler = dynamic_cast(qApp); - return handler->acceptURL(url); - } - - // FIXME hack for authentication, remove when we migrate to Qt 5.6 - Q_INVOKABLE QString fixupUrl(const QString& originalUrl) { - return fixupHifiUrl(originalUrl); - } -}; - static OffscreenFlags* offscreenFlags { nullptr }; // This hack allows the QML UI to work with keys that are also bound as @@ -126,7 +93,6 @@ void OffscreenUi::create(QOpenGLContext* context) { rootContext->setContextProperty("OffscreenUi", this); rootContext->setContextProperty("offscreenFlags", offscreenFlags = new OffscreenFlags()); - rootContext->setContextProperty("urlHandler", new UrlHandler()); rootContext->setContextProperty("fileDialogHelper", new FileDialogHelper()); } From 1dd276c1a59b6b5b729dabc71f258d371349d9a4 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 5 Aug 2016 11:32:50 -0700 Subject: [PATCH 05/20] Improved support for button rollover and hover Send a combination of mouse and touch events to the web browser entity. * MouseMove events for when the trigger is not depressed. This will trigger mouse over and hover events within the browser * Touch Begin, End, Update events when the trigger is squeezed. This will give us the iPad like scrolling behavior. --- scripts/system/controllers/handControllerGrab.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 40392f1d8b..7421dc03f7 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1314,7 +1314,13 @@ function MyController(hand) { Reticle.keyboardFocusEntity = focusedEntity; } - Reticle.sendEntityTouchUpdateEvent(focusedEntity, this.hand, intersectionPoint); + if (!this.triggerSmoothedGrab()) { + // send mouse events for button highlights and tooltips + Reticle.sendEntityMouseMoveEvent(focusedEntity, intersectionPoint); + } else { + // but send touch updates when grab is pressed. + Reticle.sendEntityTouchUpdateEvent(focusedEntity, this.hand, intersectionPoint); + } if (this.triggerSmoothedGrab() && !this.lastTriggerSmoothedGrab) { Reticle.sendEntityTouchBeginEvent(focusedEntity, this.hand, intersectionPoint); From 0ba34c5635c6ee0aad9a4cd23f8c93a153b28fd4 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 5 Aug 2016 15:25:54 -0700 Subject: [PATCH 06/20] Remove input restrictions on grabbable web entities. You no longer have to be grabbing a grabbable web entity to interact with it. --- .../system/controllers/handControllerGrab.js | 316 ++++++++++-------- 1 file changed, 177 insertions(+), 139 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 7421dc03f7..84b4bc592d 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -170,6 +170,7 @@ var STATE_NEAR_GRABBING = 3; var STATE_NEAR_TRIGGER = 4; var STATE_FAR_TRIGGER = 5; var STATE_HOLD = 6; +var STATE_ENTITY_TOUCHING = 7; // "collidesWith" is specified by comma-separated list of group names // the possible group names are: static, dynamic, kinematic, myAvatar, otherAvatar @@ -185,6 +186,8 @@ var delayedDeactivateEntityID; var CONTROLLER_STATE_MACHINE = {}; +var mostRecentSearchingHand = RIGHT_HAND; + CONTROLLER_STATE_MACHINE[STATE_OFF] = { name: "off", enterMethod: "offEnter", @@ -220,6 +223,40 @@ CONTROLLER_STATE_MACHINE[STATE_FAR_TRIGGER] = { enterMethod: "farTriggerEnter", updateMethod: "farTrigger" }; +CONTROLLER_STATE_MACHINE[STATE_ENTITY_TOUCHING] = { + name: "entityTouching", + enterMethod: "entityTouchingEnter", + exitMethod: "entityTouchingExit", + updateMethod: "entityTouching" +}; + +function handLaserIntersectWebEntity(entityID, hand) { + var standardControllerValue = (hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; + var pose = Controller.getPoseValue(standardControllerValue); + var worldHandPosition = Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, pose.translation), MyAvatar.position); + var worldHandRotation = Quat.multiply(MyAvatar.orientation, pose.rotation); + + var props = entityPropertiesCache.getProps(entityID); + var planePosition = props.position; + var planeNormal = Vec3.multiplyQbyV(props.rotation, {x: 0, y: 0, z: 1.0}); + var rayStart = worldHandPosition; + var rayDirection = Quat.getUp(worldHandRotation); + var intersectionInfo = rayIntersectPlane(planePosition, planeNormal, rayStart, rayDirection); + + var intersectionPoint = planePosition; + if (intersectionInfo.hit && intersectionInfo.distance > 0) { + intersectionPoint = Vec3.sum(rayStart, Vec3.multiply(intersectionInfo.distance, rayDirection)); + } else { + intersectionPoint = planePosition; + } + intersectionInfo.point = intersectionPoint; + intersectionInfo.searchRay = { + origin: rayStart, + direction: rayDirection, + length: PICK_MAX_DISTANCE + }; + return intersectionInfo; +} function rayIntersectPlane(planePosition, planeNormal, rayStart, rayDirection) { var rayDirectionDotPlaneNormal = Vec3.dot(rayDirection, planeNormal); @@ -743,8 +780,6 @@ function MyController(hand) { } }; - - this.searchSphereOn = function(location, size, color) { var rotation = Quat.lookAt(location, Camera.getPosition(), Vec3.UP); @@ -1257,7 +1292,7 @@ function MyController(hand) { }; this.searchEnter = function() { - this.capturedWebEntity = null; + mostRecentSearchingHand = this.hand; }; this.search = function(deltaTime, timestamp) { @@ -1283,155 +1318,124 @@ function MyController(hand) { entityPropertiesCache.addEntity(rayPickInfo.entityID); } - // route simulated touch events to a webEntity. - if (this.capturedWebEntity || - (rayPickInfo.entityID && entityPropertiesCache.getProps(rayPickInfo.entityID).type === "Web" && - (!this.entityIsGrabbable(rayPickInfo.entityID) || this.getOtherHandController().grabbedEntity == rayPickInfo.entityID))) { + var candidateEntities = Entities.findEntities(handPosition, NEAR_GRAB_RADIUS); + entityPropertiesCache.addEntities(candidateEntities); - var standardControllerValue = (hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; - var pose = Controller.getPoseValue(standardControllerValue); - var worldHandPosition = Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, pose.translation), MyAvatar.position); - var worldHandRotation = Quat.multiply(MyAvatar.orientation, pose.rotation); - - var focusedEntity = this.capturedWebEntity || rayPickInfo.entityID; - entityPropertiesCache.addEntity(focusedEntity); - - var props = entityPropertiesCache.getProps(focusedEntity); - var planePosition = props.position - var planeNormal = Vec3.multiplyQbyV(props.rotation, {x: 0, y: 0, z: 1.0}); - var rayStart = worldHandPosition; - var rayDirection = Quat.getUp(worldHandRotation); - var intersectionInfo = rayIntersectPlane(planePosition, planeNormal, rayStart, rayDirection); - - var intersectionPoint = planePosition; - if (intersectionInfo.hit && intersectionInfo.distance > 0) { - intersectionPoint = Vec3.sum(rayStart, Vec3.multiply(intersectionInfo.distance, rayDirection)); - } else { - intersectionPoint = planePosition; + var potentialEquipHotspot = this.chooseBestEquipHotspot(candidateEntities); + if (potentialEquipHotspot) { + if (this.triggerSmoothedGrab()) { + this.grabbedHotspot = potentialEquipHotspot; + this.grabbedEntity = potentialEquipHotspot.entityID; + this.setState(STATE_HOLD, "equipping '" + entityPropertiesCache.getProps(this.grabbedEntity).name + "'"); + return; } + } - if (Reticle.keyboardFocusEntity != focusedEntity) { - Reticle.keyboardFocusEntity = focusedEntity; - } + var grabbableEntities = candidateEntities.filter(function(entity) { + return _this.entityIsNearGrabbable(entity, handPosition, NEAR_GRAB_MAX_DISTANCE); + }); - if (!this.triggerSmoothedGrab()) { - // send mouse events for button highlights and tooltips - Reticle.sendEntityMouseMoveEvent(focusedEntity, intersectionPoint); - } else { - // but send touch updates when grab is pressed. - Reticle.sendEntityTouchUpdateEvent(focusedEntity, this.hand, intersectionPoint); + if (rayPickInfo.entityID) { + this.intersectionDistance = rayPickInfo.distance; + if (this.entityIsGrabbable(rayPickInfo.entityID) && rayPickInfo.distance < NEAR_GRAB_PICK_RADIUS) { + grabbableEntities.push(rayPickInfo.entityID); } - - if (this.triggerSmoothedGrab() && !this.lastTriggerSmoothedGrab) { - Reticle.sendEntityTouchBeginEvent(focusedEntity, this.hand, intersectionPoint); - this.capturedWebEntity = focusedEntity; - } - if (!this.triggerSmoothedGrab() && this.lastTriggerSmoothedGrab) { - Reticle.sendEntityTouchEndEvent(focusedEntity, this.hand, intersectionPoint); - this.capturedWebEntity = null; - } - this.lastTriggerSmoothedGrab = this.triggerSmoothedGrab(); - - equipHotspotBuddy.updateHotspots([], timestamp); - this.intersectionDistance = intersectionInfo.distance; + } else if (rayPickInfo.overlayID) { + this.intersectionDistance = rayPickInfo.distance; } else { + this.intersectionDistance = 0; + } - var candidateEntities = Entities.findEntities(handPosition, NEAR_GRAB_RADIUS); - entityPropertiesCache.addEntities(candidateEntities); - - var potentialEquipHotspot = this.chooseBestEquipHotspot(candidateEntities); - if (potentialEquipHotspot) { - if (this.triggerSmoothedGrab()) { - this.grabbedHotspot = potentialEquipHotspot; - this.grabbedEntity = potentialEquipHotspot.entityID; - this.setState(STATE_HOLD, "equipping '" + entityPropertiesCache.getProps(this.grabbedEntity).name + "'"); - return; - } - } - - var grabbableEntities = candidateEntities.filter(function(entity) { - return _this.entityIsNearGrabbable(entity, handPosition, NEAR_GRAB_MAX_DISTANCE); + var entity; + if (grabbableEntities.length > 0) { + // sort by distance + grabbableEntities.sort(function(a, b) { + var aDistance = Vec3.distance(entityPropertiesCache.getProps(a).position, handPosition); + var bDistance = Vec3.distance(entityPropertiesCache.getProps(b).position, handPosition); + return aDistance - bDistance; }); - - if (rayPickInfo.entityID) { - this.intersectionDistance = rayPickInfo.distance; - if (this.entityIsGrabbable(rayPickInfo.entityID) && rayPickInfo.distance < NEAR_GRAB_PICK_RADIUS) { - grabbableEntities.push(rayPickInfo.entityID); - } - } else if (rayPickInfo.overlayID) { - this.intersectionDistance = rayPickInfo.distance; - } else { - this.intersectionDistance = 0; - } - - var entity; - if (grabbableEntities.length > 0) { - // sort by distance - grabbableEntities.sort(function(a, b) { - var aDistance = Vec3.distance(entityPropertiesCache.getProps(a).position, handPosition); - var bDistance = Vec3.distance(entityPropertiesCache.getProps(b).position, handPosition); - return aDistance - bDistance; - }); - entity = grabbableEntities[0]; - name = entityPropertiesCache.getProps(entity).name; - this.grabbedEntity = entity; - if (this.entityWantsTrigger(entity)) { - if (this.triggerSmoothedGrab()) { - this.setState(STATE_NEAR_TRIGGER, "near trigger '" + name + "'"); - return; - } else { - // potentialNearTriggerEntity = entity; - } + entity = grabbableEntities[0]; + name = entityPropertiesCache.getProps(entity).name; + this.grabbedEntity = entity; + if (this.entityWantsTrigger(entity)) { + if (this.triggerSmoothedGrab()) { + this.setState(STATE_NEAR_TRIGGER, "near trigger '" + name + "'"); + return; } else { - if (this.triggerSmoothedGrab()) { - var props = entityPropertiesCache.getProps(entity); - var grabProps = entityPropertiesCache.getGrabProps(entity); - var refCount = grabProps.refCount ? grabProps.refCount : 0; - if (refCount >= 1) { - // if another person is holding the object, remember to restore the - // parent info, when we are finished grabbing it. - this.shouldResetParentOnRelease = true; - this.previousParentID = props.parentID; - this.previousParentJointIndex = props.parentJointIndex; - } - - this.setState(STATE_NEAR_GRABBING, "near grab '" + name + "'"); - return; - } else { - // potentialNearGrabEntity = entity; + // potentialNearTriggerEntity = entity; + } + } else { + if (this.triggerSmoothedGrab()) { + var props = entityPropertiesCache.getProps(entity); + var grabProps = entityPropertiesCache.getGrabProps(entity); + var refCount = grabProps.refCount ? grabProps.refCount : 0; + if (refCount >= 1) { + // if another person is holding the object, remember to restore the + // parent info, when we are finished grabbing it. + this.shouldResetParentOnRelease = true; + this.previousParentID = props.parentID; + this.previousParentJointIndex = props.parentJointIndex; } + + this.setState(STATE_NEAR_GRABBING, "near grab '" + name + "'"); + return; + } else { + // potentialNearGrabEntity = entity; } } + } - if (rayPickInfo.entityID) { - entity = rayPickInfo.entityID; - name = entityPropertiesCache.getProps(entity).name; - if (this.entityWantsTrigger(entity)) { - if (this.triggerSmoothedGrab()) { - this.grabbedEntity = entity; - this.setState(STATE_FAR_TRIGGER, "far trigger '" + name + "'"); - return; - } else { - // potentialFarTriggerEntity = entity; - } - } else if (this.entityIsDistanceGrabbable(rayPickInfo.entityID, handPosition)) { - if (this.triggerSmoothedGrab() && !isEditing()) { - this.grabbedEntity = entity; - this.setState(STATE_DISTANCE_HOLDING, "distance hold '" + name + "'"); - return; - } else { - // potentialFarGrabEntity = entity; - } + if (rayPickInfo.entityID && entityPropertiesCache.getProps(rayPickInfo.entityID).type === "Web") { + entity = rayPickInfo.entityID; + name = entityPropertiesCache.getProps(entity).name; + + if (Reticle.keyboardFocusEntity != entity) { + Reticle.keyboardFocusEntity = entity; + } + + // send mouse events for button highlights and tooltips. + if (this.hand == mostRecentSearchingHand || (this.hand !== mostRecentSearchingHand && + this.getOtherHandController().state !== STATE_SEARCHING && + this.getOtherHandController().state !== STATE_ENTITY_TOUCHING)) { + // most recently searching hand has priority over other hand, for the purposes of button highlighting. + Reticle.sendEntityMouseMoveEvent(entity, rayPickInfo.intersection); + } + + if (this.triggerSmoothedGrab() && !isEditing()) { + this.grabbedEntity = entity; + this.setState(STATE_ENTITY_TOUCHING, "begin touching entity '" + name + "'"); + return; + } + } + + if (rayPickInfo.entityID) { + entity = rayPickInfo.entityID; + name = entityPropertiesCache.getProps(entity).name; + if (this.entityWantsTrigger(entity)) { + if (this.triggerSmoothedGrab()) { + this.grabbedEntity = entity; + this.setState(STATE_FAR_TRIGGER, "far trigger '" + name + "'"); + return; + } else { + // potentialFarTriggerEntity = entity; + } + } else if (this.entityIsDistanceGrabbable(rayPickInfo.entityID, handPosition)) { + if (this.triggerSmoothedGrab() && !isEditing()) { + this.grabbedEntity = entity; + this.setState(STATE_DISTANCE_HOLDING, "distance hold '" + name + "'"); + return; + } else { + // potentialFarGrabEntity = entity; } } + } - this.updateEquipHaptics(potentialEquipHotspot, handPosition); + this.updateEquipHaptics(potentialEquipHotspot, handPosition); - var nearEquipHotspots = this.chooseNearEquipHotspots(candidateEntities, EQUIP_HOTSPOT_RENDER_RADIUS); - equipHotspotBuddy.updateHotspots(nearEquipHotspots, timestamp); - if (potentialEquipHotspot) { - equipHotspotBuddy.highlightHotspot(potentialEquipHotspot); - } + var nearEquipHotspots = this.chooseNearEquipHotspots(candidateEntities, EQUIP_HOTSPOT_RENDER_RADIUS); + equipHotspotBuddy.updateHotspots(nearEquipHotspots, timestamp); + if (potentialEquipHotspot) { + equipHotspotBuddy.highlightHotspot(potentialEquipHotspot); } this.searchIndicatorOn(rayPickInfo.searchRay); @@ -1518,8 +1522,8 @@ function MyController(hand) { }; this.distanceHolding = function(deltaTime, timestamp) { - - if (!this.triggerClicked) { + + if (!this.triggerClicked) { this.callEntityMethodOnGrabbed("releaseGrab"); this.setState(STATE_OFF, "trigger released"); return; @@ -1609,9 +1613,9 @@ function MyController(hand) { // visualizations - var rayPickInfo = this.calcRayPickInfo(this.hand); + var rayPickInfo = this.calcRayPickInfo(this.hand); - this.overlayLineOn(rayPickInfo.searchRay.origin, grabbedProperties.position, COLORS_GRAB_DISTANCE_HOLD); + this.overlayLineOn(rayPickInfo.searchRay.origin, grabbedProperties.position, COLORS_GRAB_DISTANCE_HOLD); var distanceToObject = Vec3.length(Vec3.subtract(MyAvatar.position, this.currentObjectPosition)); var success = Entities.updateAction(this.grabbedEntity, this.actionID, { @@ -2044,6 +2048,40 @@ function MyController(hand) { this.release(); }; + this.entityTouchingEnter = function() { + // test for intersection between controller laser and web entity plane. + var intersectInfo = handLaserIntersectWebEntity(this.grabbedEntity, this.hand); + Reticle.sendEntityTouchBeginEvent(this.grabbedEntity, this.hand, intersectInfo.point); + }; + + this.entityTouchingExit = function() { + // test for intersection between controller laser and web entity plane. + var intersectInfo = handLaserIntersectWebEntity(this.grabbedEntity, this.hand); + Reticle.sendEntityTouchEndEvent(this.grabbedEntity, this.hand, intersectInfo.point); + }; + + this.entityTouching = function() { + entityPropertiesCache.addEntity(this.grabbedEntity); + + if (!this.triggerSmoothedGrab()) { + this.setState(STATE_OFF, "released trigger"); + return; + } + + // test for intersection between controller laser and web entity plane. + var intersectInfo = handLaserIntersectWebEntity(this.grabbedEntity, this.hand); + + if (Reticle.keyboardFocusEntity != this.grabbedEntity) { + Reticle.keyboardFocusEntity = this.grabbedEntity; + } + + Reticle.sendEntityTouchUpdateEvent(this.grabbedEntity, this.hand, intersectInfo.point); + + this.intersectionDistance = intersectInfo.distance; + this.searchIndicatorOn(intersectInfo.searchRay); + Reticle.setVisible(false); + }; + this.release = function() { Messages.sendLocalMessage('Hifi-Teleport-Disabler','none'); this.turnOffVisualizations(); From 1ddbd7022a0b3229dff32fb44b6e7766c93063ab Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 11 Aug 2016 11:52:04 -0700 Subject: [PATCH 07/20] Moved keyboardFocus, mouse/touch event methods from Reticle to Entities --- .../src/display-plugins/CompositorHelper.cpp | 34 ------------------- .../src/display-plugins/CompositorHelper.h | 11 ------ .../entities/src/EntityScriptingInterface.cpp | 34 +++++++++++++++++++ .../entities/src/EntityScriptingInterface.h | 16 ++++++++- .../system/controllers/handControllerGrab.js | 16 ++++----- 5 files changed, 57 insertions(+), 54 deletions(-) diff --git a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp index 3a154af462..56be8e1cf9 100644 --- a/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp +++ b/libraries/display-plugins/src/display-plugins/CompositorHelper.cpp @@ -451,37 +451,3 @@ QVariant ReticleInterface::getPosition() const { void ReticleInterface::setPosition(QVariant position) { _compositor->setReticlePosition(vec2FromVariant(position)); } - -QUuid ReticleInterface::getKeyboardFocusEntity() const { - QUuid result; - QMetaObject::invokeMethod(qApp, "getKeyboardFocusEntity", Qt::DirectConnection, Q_RETURN_ARG(QUuid, result)); - return result; -} - -void ReticleInterface::setKeyboardFocusEntity(QUuid id) { - QMetaObject::invokeMethod(qApp, "setKeyboardFocusEntity", Qt::QueuedConnection, Q_ARG(QUuid, id)); -} - -void ReticleInterface::sendEntityMouseMoveEvent(QUuid id, glm::vec3 intersectionPoint) { - QMetaObject::invokeMethod(qApp, "sendEntityMouseMoveEvent", Qt::QueuedConnection, Q_ARG(QUuid, id), Q_ARG(glm::vec3, intersectionPoint)); -} - -void ReticleInterface::sendEntityLeftMouseDownEvent(QUuid id, glm::vec3 intersectionPoint) { - QMetaObject::invokeMethod(qApp, "sendEntityLeftMouseDownEvent", Qt::QueuedConnection, Q_ARG(QUuid, id), Q_ARG(glm::vec3, intersectionPoint)); -} - -void ReticleInterface::sendEntityLeftMouseUpEvent(QUuid id, glm::vec3 intersectionPoint) { - QMetaObject::invokeMethod(qApp, "sendEntityLeftMouseUpEvent", Qt::QueuedConnection, Q_ARG(QUuid, id), Q_ARG(glm::vec3, intersectionPoint)); -} - -void ReticleInterface::sendEntityTouchUpdateEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint) { - QMetaObject::invokeMethod(qApp, "sendEntityTouchUpdateEvent", Qt::QueuedConnection, Q_ARG(QUuid, entityID), Q_ARG(int, fingerID), Q_ARG(glm::vec3, intersectionPoint)); -} - -void ReticleInterface::sendEntityTouchBeginEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint) { - QMetaObject::invokeMethod(qApp, "sendEntityTouchBeginEvent", Qt::QueuedConnection, Q_ARG(QUuid, entityID), Q_ARG(int, fingerID), Q_ARG(glm::vec3, intersectionPoint)); -} - -void ReticleInterface::sendEntityTouchEndEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint) { - QMetaObject::invokeMethod(qApp, "sendEntityTouchEndEvent", Qt::QueuedConnection, Q_ARG(QUuid, entityID), Q_ARG(int, fingerID), Q_ARG(glm::vec3, intersectionPoint)); -} diff --git a/libraries/display-plugins/src/display-plugins/CompositorHelper.h b/libraries/display-plugins/src/display-plugins/CompositorHelper.h index 78d4a10bf0..b0b96d86be 100644 --- a/libraries/display-plugins/src/display-plugins/CompositorHelper.h +++ b/libraries/display-plugins/src/display-plugins/CompositorHelper.h @@ -181,7 +181,6 @@ class ReticleInterface : public QObject { Q_PROPERTY(bool mouseCaptured READ isMouseCaptured) Q_PROPERTY(bool allowMouseCapture READ getAllowMouseCapture WRITE setAllowMouseCapture) Q_PROPERTY(bool pointingAtSystemOverlay READ isPointingAtSystemOverlay) - Q_PROPERTY(QUuid keyboardFocusEntity READ getKeyboardFocusEntity WRITE setKeyboardFocusEntity) public: ReticleInterface(CompositorHelper* outer) : QObject(outer), _compositor(outer) {} @@ -204,16 +203,6 @@ public: Q_INVOKABLE glm::vec2 getMaximumPosition() { return _compositor->getReticleMaximumPosition(); } - Q_INVOKABLE QUuid getKeyboardFocusEntity() const; - Q_INVOKABLE void setKeyboardFocusEntity(QUuid id); - Q_INVOKABLE void sendEntityMouseMoveEvent(QUuid id, glm::vec3 intersectionPoint); - Q_INVOKABLE void sendEntityLeftMouseDownEvent(QUuid id, glm::vec3 intersectionPoint); - Q_INVOKABLE void sendEntityLeftMouseUpEvent(QUuid id, glm::vec3 intersectionPoint); - - Q_INVOKABLE void sendEntityTouchUpdateEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint); - Q_INVOKABLE void sendEntityTouchBeginEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint); - Q_INVOKABLE void sendEntityTouchEndEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint); - private: CompositorHelper* _compositor; }; diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 653b37c3bb..35852e5acb 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -1184,6 +1184,40 @@ QVector EntityScriptingInterface::getChildrenIDsOfJoint(const QUuid& pare return result; } +QUuid EntityScriptingInterface::getKeyboardFocusEntity() const { + QUuid result; + QMetaObject::invokeMethod(qApp, "getKeyboardFocusEntity", Qt::DirectConnection, Q_RETURN_ARG(QUuid, result)); + return result; +} + +void EntityScriptingInterface::setKeyboardFocusEntity(QUuid id) { + QMetaObject::invokeMethod(qApp, "setKeyboardFocusEntity", Qt::QueuedConnection, Q_ARG(QUuid, id)); +} + +void EntityScriptingInterface::sendEntityMouseMoveEvent(QUuid id, glm::vec3 intersectionPoint) { + QMetaObject::invokeMethod(qApp, "sendEntityMouseMoveEvent", Qt::QueuedConnection, Q_ARG(QUuid, id), Q_ARG(glm::vec3, intersectionPoint)); +} + +void EntityScriptingInterface::sendEntityLeftMouseDownEvent(QUuid id, glm::vec3 intersectionPoint) { + QMetaObject::invokeMethod(qApp, "sendEntityLeftMouseDownEvent", Qt::QueuedConnection, Q_ARG(QUuid, id), Q_ARG(glm::vec3, intersectionPoint)); +} + +void EntityScriptingInterface::sendEntityLeftMouseUpEvent(QUuid id, glm::vec3 intersectionPoint) { + QMetaObject::invokeMethod(qApp, "sendEntityLeftMouseUpEvent", Qt::QueuedConnection, Q_ARG(QUuid, id), Q_ARG(glm::vec3, intersectionPoint)); +} + +void EntityScriptingInterface::sendEntityTouchUpdateEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint) { + QMetaObject::invokeMethod(qApp, "sendEntityTouchUpdateEvent", Qt::QueuedConnection, Q_ARG(QUuid, entityID), Q_ARG(int, fingerID), Q_ARG(glm::vec3, intersectionPoint)); +} + +void EntityScriptingInterface::sendEntityTouchBeginEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint) { + QMetaObject::invokeMethod(qApp, "sendEntityTouchBeginEvent", Qt::QueuedConnection, Q_ARG(QUuid, entityID), Q_ARG(int, fingerID), Q_ARG(glm::vec3, intersectionPoint)); +} + +void EntityScriptingInterface::sendEntityTouchEndEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint) { + QMetaObject::invokeMethod(qApp, "sendEntityTouchEndEvent", Qt::QueuedConnection, Q_ARG(QUuid, entityID), Q_ARG(int, fingerID), Q_ARG(glm::vec3, intersectionPoint)); +} + float EntityScriptingInterface::calculateCost(float mass, float oldVelocity, float newVelocity) { return std::abs(mass * (newVelocity - oldVelocity)); } diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 5aa0f5907e..b98d872f74 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -59,9 +59,11 @@ void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, Ra /// handles scripting of Entity commands from JS passed to assigned clients class EntityScriptingInterface : public OctreeScriptingInterface, public Dependency { Q_OBJECT - + Q_PROPERTY(float currentAvatarEnergy READ getCurrentAvatarEnergy WRITE setCurrentAvatarEnergy) Q_PROPERTY(float costMultiplier READ getCostMultiplier WRITE setCostMultiplier) + Q_PROPERTY(QUuid keyboardFocusEntity READ getKeyboardFocusEntity WRITE setKeyboardFocusEntity) + public: EntityScriptingInterface(bool bidOnSimulationOwnership); @@ -176,6 +178,18 @@ public slots: Q_INVOKABLE QVector getChildrenIDs(const QUuid& parentID); Q_INVOKABLE QVector getChildrenIDsOfJoint(const QUuid& parentID, int jointIndex); + Q_INVOKABLE QUuid getKeyboardFocusEntity() const; + Q_INVOKABLE void setKeyboardFocusEntity(QUuid id); + + Q_INVOKABLE void sendEntityMouseMoveEvent(QUuid id, glm::vec3 intersectionPoint); + Q_INVOKABLE void sendEntityLeftMouseDownEvent(QUuid id, glm::vec3 intersectionPoint); + Q_INVOKABLE void sendEntityLeftMouseUpEvent(QUuid id, glm::vec3 intersectionPoint); + + Q_INVOKABLE void sendEntityTouchUpdateEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint); + Q_INVOKABLE void sendEntityTouchBeginEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint); + Q_INVOKABLE void sendEntityTouchEndEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint); + + signals: void collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision); diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 72c26c5ef1..96482c509d 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1392,8 +1392,8 @@ function MyController(hand) { entity = rayPickInfo.entityID; name = entityPropertiesCache.getProps(entity).name; - if (Reticle.keyboardFocusEntity != entity) { - Reticle.keyboardFocusEntity = entity; + if (Entities.keyboardFocusEntity != entity) { + Entities.keyboardFocusEntity = entity; } // send mouse events for button highlights and tooltips. @@ -1401,7 +1401,7 @@ function MyController(hand) { this.getOtherHandController().state !== STATE_SEARCHING && this.getOtherHandController().state !== STATE_ENTITY_TOUCHING)) { // most recently searching hand has priority over other hand, for the purposes of button highlighting. - Reticle.sendEntityMouseMoveEvent(entity, rayPickInfo.intersection); + Entities.sendEntityMouseMoveEvent(entity, rayPickInfo.intersection); } if (this.triggerSmoothedGrab() && !isEditing()) { @@ -2054,13 +2054,13 @@ function MyController(hand) { this.entityTouchingEnter = function() { // test for intersection between controller laser and web entity plane. var intersectInfo = handLaserIntersectWebEntity(this.grabbedEntity, this.hand); - Reticle.sendEntityTouchBeginEvent(this.grabbedEntity, this.hand, intersectInfo.point); + Entities.sendEntityTouchBeginEvent(this.grabbedEntity, this.hand, intersectInfo.point); }; this.entityTouchingExit = function() { // test for intersection between controller laser and web entity plane. var intersectInfo = handLaserIntersectWebEntity(this.grabbedEntity, this.hand); - Reticle.sendEntityTouchEndEvent(this.grabbedEntity, this.hand, intersectInfo.point); + Entities.sendEntityTouchEndEvent(this.grabbedEntity, this.hand, intersectInfo.point); }; this.entityTouching = function() { @@ -2074,11 +2074,11 @@ function MyController(hand) { // test for intersection between controller laser and web entity plane. var intersectInfo = handLaserIntersectWebEntity(this.grabbedEntity, this.hand); - if (Reticle.keyboardFocusEntity != this.grabbedEntity) { - Reticle.keyboardFocusEntity = this.grabbedEntity; + if (Entities.keyboardFocusEntity != this.grabbedEntity) { + Entities.keyboardFocusEntity = this.grabbedEntity; } - Reticle.sendEntityTouchUpdateEvent(this.grabbedEntity, this.hand, intersectInfo.point); + Entities.sendEntityTouchUpdateEvent(this.grabbedEntity, this.hand, intersectInfo.point); this.intersectionDistance = intersectInfo.distance; this.searchIndicatorOn(intersectInfo.searchRay); From 1be434342be369646f1409f407031e478321c52f Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 12 Aug 2016 17:11:59 -0700 Subject: [PATCH 08/20] Work In Progress snapshot * Added new PointerEvent type. * Mouse events are still sent from Application to EntityTreeRenderer, however, EntityTreeRenderer converts them to PointerEvents * All outgoing signals from EntityTreeRenderer use PointerEvents instead of MouseEvents * Associated JavaScript entity methods will receive PointerEvents instead of MouseEvents * Events from handControllerGrab.js to entities are currently broken. --- interface/src/Application.cpp | 77 +------ interface/src/Application.h | 11 - .../src/EntityTreeRenderer.cpp | 195 +++++++++++++----- .../src/EntityTreeRenderer.h | 27 +-- .../src/RenderableWebEntityItem.cpp | 177 +++++++--------- .../src/RenderableWebEntityItem.h | 7 +- .../entities/src/EntityScriptingInterface.cpp | 18 +- .../entities/src/EntityScriptingInterface.h | 22 +- libraries/script-engine/src/EventTypes.cpp | 2 + libraries/script-engine/src/PointerEvent.cpp | 110 ++++++++++ libraries/script-engine/src/PointerEvent.h | 68 ++++++ libraries/script-engine/src/ScriptEngine.cpp | 30 +-- libraries/script-engine/src/ScriptEngine.h | 4 +- libraries/shared/src/GeometryUtil.h | 14 ++ 14 files changed, 476 insertions(+), 286 deletions(-) create mode 100644 libraries/script-engine/src/PointerEvent.cpp create mode 100644 libraries/script-engine/src/PointerEvent.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5a68e11c02..1c62b690b8 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1074,7 +1074,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : // If the user clicks an an entity, we will check that it's an unlocked web entity, and if so, set the focus to it auto entityScriptingInterface = DependencyManager::get(); connect(entityScriptingInterface.data(), &EntityScriptingInterface::clickDownOnEntity, - [this](const EntityItemID& entityItemID, const MouseEvent& event) { + [this](const EntityItemID& entityItemID, const PointerEvent& event) { setKeyboardFocusEntity(entityItemID); }); @@ -1085,9 +1085,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : }); // If the user clicks somewhere where there is NO entity at all, we will release focus - connect(getEntities(), &EntityTreeRenderer::mousePressOffEntity, - [=](const RayToEntityIntersectionResult& entityItemID, const QMouseEvent* event) { - setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); + connect(getEntities(), &EntityTreeRenderer::mousePressOffEntity, [=]() { + setKeyboardFocusEntity(UNKNOWN_ENTITY_ID); }); connect(this, &Application::aboutToQuit, [=]() { @@ -3578,76 +3577,6 @@ void Application::setKeyboardFocusEntity(EntityItemID entityItemID) { } } -void Application::sendEntityMouseMoveEvent(QUuid id, glm::vec3 intersectionPoint) { - QMouseEvent mouseEvent(QEvent::MouseMove, QPoint(0, 0), QPoint(0, 0), - Qt::NoButton, Qt::NoButton, Qt::NoModifier); - sendEntityMouseEvent(id, mouseEvent, intersectionPoint); -} - -void Application::sendEntityLeftMouseDownEvent(QUuid id, glm::vec3 intersectionPoint) { - QMouseEvent mouseEvent(QEvent::MouseButtonPress, QPoint(0, 0), QPoint(0, 0), - Qt::LeftButton, Qt::NoButton, Qt::NoModifier); - sendEntityMouseEvent(id, mouseEvent, intersectionPoint); -} - -void Application::sendEntityLeftMouseUpEvent(QUuid id, glm::vec3 intersectionPoint) { - QMouseEvent mouseEvent(QEvent::MouseButtonRelease, QPoint(0, 0), QPoint(0, 0), - Qt::LeftButton, Qt::NoButton, Qt::NoModifier); - sendEntityMouseEvent(id, mouseEvent, intersectionPoint); -} - -void Application::sendEntityMouseEvent(const QUuid& id, const QMouseEvent& mouseEvent, const glm::vec3& intersectionPoint) { - auto entityScriptingInterface = DependencyManager::get(); - EntityItemID entityItemID(id); - auto properties = entityScriptingInterface->getEntityProperties(entityItemID); - if (EntityTypes::Web == properties.getType() && !properties.getLocked() && properties.getVisible()) { - auto entity = entityScriptingInterface->getEntityTree()->findEntityByID(entityItemID); - RenderableWebEntityItem* webEntity = dynamic_cast(entity.get()); - webEntity->handleMouseEvent(mouseEvent, intersectionPoint); - } -} - -void Application::sendEntityTouchUpdateEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint) { - QTouchEvent::TouchPoint point; - point.setId(fingerID); - point.setState(Qt::TouchPointMoved); - QList touchPoints; - touchPoints.push_back(point); - QTouchEvent touchEvent(QEvent::TouchUpdate, nullptr, Qt::NoModifier, Qt::TouchPointMoved, touchPoints); - sendEntityTouchEvent(entityID, touchEvent, intersectionPoint); -} - -void Application::sendEntityTouchBeginEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint) { - QTouchEvent::TouchPoint point; - point.setId(fingerID); - point.setState(Qt::TouchPointPressed); - QList touchPoints; - touchPoints.push_back(point); - QTouchEvent touchEvent(QEvent::TouchBegin, nullptr, Qt::NoModifier, Qt::TouchPointPressed, touchPoints); - sendEntityTouchEvent(entityID, touchEvent, intersectionPoint); -} - -void Application::sendEntityTouchEndEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint) { - QTouchEvent::TouchPoint point; - point.setId(fingerID); - point.setState(Qt::TouchPointReleased); - QList touchPoints; - touchPoints.push_back(point); - QTouchEvent touchEvent(QEvent::TouchEnd, nullptr, Qt::NoModifier, Qt::TouchPointReleased, touchPoints); - sendEntityTouchEvent(entityID, touchEvent, intersectionPoint); -} - -void Application::sendEntityTouchEvent(const QUuid& id, const QTouchEvent& touchEvent, const glm::vec3& intersectionPoint) { - auto entityScriptingInterface = DependencyManager::get(); - EntityItemID entityItemID(id); - auto properties = entityScriptingInterface->getEntityProperties(entityItemID); - if (EntityTypes::Web == properties.getType() && !properties.getLocked() && properties.getVisible()) { - auto entity = entityScriptingInterface->getEntityTree()->findEntityByID(entityItemID); - RenderableWebEntityItem* webEntity = dynamic_cast(entity.get()); - webEntity->handleTouchEvent(touchEvent, intersectionPoint); - } -} - void Application::updateDialogs(float deltaTime) const { PerformanceTimer perfTimer("updateDialogs"); bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); diff --git a/interface/src/Application.h b/interface/src/Application.h index 16917244ff..a5035793ff 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -325,17 +325,6 @@ public slots: QUuid getKeyboardFocusEntity() const; // thread-safe void setKeyboardFocusEntity(QUuid id); void setKeyboardFocusEntity(EntityItemID entityItemID); - void sendEntityMouseMoveEvent(QUuid id, glm::vec3 intersectionPoint); - void sendEntityLeftMouseDownEvent(QUuid id, glm::vec3 intersectionPoint); - void sendEntityLeftMouseUpEvent(QUuid id, glm::vec3 intersectionPoint); - - void sendEntityTouchUpdateEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint); - void sendEntityTouchBeginEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint); - void sendEntityTouchEndEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint); - -private: - void sendEntityTouchEvent(const QUuid& id, const QTouchEvent& touchEvent, const glm::vec3& intersectionPoint); - void sendEntityMouseEvent(const QUuid& id, const QMouseEvent& mouseEvent, const glm::vec3& intersectionPoint); private slots: void showDesktop(); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 9eadd6d0b9..f80da5a208 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -48,7 +48,7 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf OctreeRenderer(), _wantScripts(wantScripts), _entitiesScriptEngine(NULL), - _lastMouseEventValid(false), + _lastPointerEventValid(false), _viewState(viewState), _scriptingServices(scriptingServices), _displayModelBounds(false), @@ -190,9 +190,9 @@ void EntityTreeRenderer::update() { // Even if we're not moving the mouse, if we started clicking on an entity and we have // not yet released the hold then this is still considered a holdingClickOnEntity event // and we want to simulate this message here as well as in mouse move - if (_lastMouseEventValid && !_currentClickingOnEntityID.isInvalidID()) { - emit holdingClickOnEntity(_currentClickingOnEntityID, _lastMouseEvent); - _entitiesScriptEngine->callEntityScriptMethod(_currentClickingOnEntityID, "holdingClickOnEntity", _lastMouseEvent); + if (_lastPointerEventValid && !_currentClickingOnEntityID.isInvalidID()) { + emit holdingClickOnEntity(_currentClickingOnEntityID, _lastPointerEvent); + _entitiesScriptEngine->callEntityScriptMethod(_currentClickingOnEntityID, "holdingClickOnEntity", _lastPointerEvent); } } @@ -608,18 +608,10 @@ RayToEntityIntersectionResult EntityTreeRenderer::findRayIntersectionWorker(cons } void EntityTreeRenderer::connectSignalsToSlots(EntityScriptingInterface* entityScriptingInterface) { - connect(this, &EntityTreeRenderer::mousePressOnEntity, entityScriptingInterface, - [=](const RayToEntityIntersectionResult& intersection, const QMouseEvent* event){ - entityScriptingInterface->mousePressOnEntity(intersection.entityID, MouseEvent(*event)); - }); - connect(this, &EntityTreeRenderer::mouseMoveOnEntity, entityScriptingInterface, - [=](const RayToEntityIntersectionResult& intersection, const QMouseEvent* event) { - entityScriptingInterface->mouseMoveOnEntity(intersection.entityID, MouseEvent(*event)); - }); - connect(this, &EntityTreeRenderer::mouseReleaseOnEntity, entityScriptingInterface, - [=](const RayToEntityIntersectionResult& intersection, const QMouseEvent* event) { - entityScriptingInterface->mouseReleaseOnEntity(intersection.entityID, MouseEvent(*event)); - }); + + connect(this, &EntityTreeRenderer::mousePressOnEntity, entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity); + connect(this, &EntityTreeRenderer::mouseMoveOnEntity, entityScriptingInterface, &EntityScriptingInterface::mouseMoveOnEntity); + connect(this, &EntityTreeRenderer::mouseReleaseOnEntity, entityScriptingInterface, &EntityScriptingInterface::mouseReleaseOnEntity); connect(this, &EntityTreeRenderer::clickDownOnEntity, entityScriptingInterface, &EntityScriptingInterface::clickDownOnEntity); connect(this, &EntityTreeRenderer::holdingClickOnEntity, entityScriptingInterface, &EntityScriptingInterface::holdingClickOnEntity); @@ -636,6 +628,59 @@ void EntityTreeRenderer::connectSignalsToSlots(EntityScriptingInterface* entityS connect(DependencyManager::get().data(), &SceneScriptingInterface::shouldRenderEntitiesChanged, this, &EntityTreeRenderer::updateEntityRenderStatus, Qt::QueuedConnection); } +static glm::vec2 projectOntoEntityXYPlane(EntityItemPointer entity, const PickRay& pickRay, const RayToEntityIntersectionResult& rayPickResult) { + + if (entity) { + + glm::vec3 entityPosition = entity->getPosition(); + glm::quat entityRotation = entity->getRotation(); + glm::vec3 entityDimensions = entity->getDimensions(); + glm::vec3 entityRegistrationPoint = entity->getRegistrationPoint(); + + // project the intersection point onto the local xy plane of the object. + float distance; + glm::vec3 planePosition = entityPosition; + glm::vec3 planeNormal = entityRotation * Vectors::UNIT_Z; + glm::vec3 rayDirection = pickRay.direction; + glm::vec3 rayStart = pickRay.origin; + glm::vec3 p; + if (rayPlaneIntersection(planePosition, planeNormal, rayStart, rayDirection, distance)) { + p = rayStart + rayDirection * distance; + } else { + p = rayPickResult.intersection; + } + glm::vec3 localP = glm::inverse(entityRotation) * (p - entityPosition); + glm::vec3 normalizedP = (localP / entityDimensions) + entityRegistrationPoint; + return glm::vec2(normalizedP.x * entityDimensions.x, + (1.0f - normalizedP.y) * entityDimensions.y); // flip y-axis + } else { + return glm::vec2(); + } +} + +static uint32_t toPointerButtons(const QMouseEvent& event) { + uint32_t buttons = 0; + buttons |= event.buttons().testFlag(Qt::LeftButton) ? PointerEvent::PrimaryButton : 0; + buttons |= event.buttons().testFlag(Qt::RightButton) ? PointerEvent::SecondaryButton : 0; + buttons |= event.buttons().testFlag(Qt::MiddleButton) ? PointerEvent::TertiaryButton : 0; + return buttons; +} + +static PointerEvent::Button toPointerButton(const QMouseEvent& event) { + switch (event.button()) { + case Qt::LeftButton: + return PointerEvent::PrimaryButton; + case Qt::RightButton: + return PointerEvent::SecondaryButton; + case Qt::MiddleButton: + return PointerEvent::TertiaryButton; + default: + return PointerEvent::NoButtons; + } +} + +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. @@ -654,24 +699,32 @@ void EntityTreeRenderer::mousePressEvent(QMouseEvent* event) { QUrl url = QUrl(urlString, QUrl::StrictMode); if (url.isValid() && !url.isEmpty()){ DependencyManager::get()->handleLookupString(urlString); - } - emit mousePressOnEntity(rayPickResult, event); + glm::vec2 pos2D = projectOntoEntityXYPlane(rayPickResult.entity, ray, rayPickResult); + PointerEvent pointerEvent(PointerEvent::Press, MOUSE_POINTER_ID, + pos2D, rayPickResult.intersection, + rayPickResult.surfaceNormal, ray.direction, + toPointerButton(*event), toPointerButtons(*event)); + + emit mousePressOnEntity(rayPickResult.entityID, pointerEvent); + if (_entitiesScriptEngine) { - _entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mousePressOnEntity", MouseEvent(*event)); + _entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mousePressOnEntity", pointerEvent); } - + _currentClickingOnEntityID = rayPickResult.entityID; - emit clickDownOnEntity(_currentClickingOnEntityID, MouseEvent(*event)); + emit clickDownOnEntity(_currentClickingOnEntityID, pointerEvent); if (_entitiesScriptEngine) { - _entitiesScriptEngine->callEntityScriptMethod(_currentClickingOnEntityID, "clickDownOnEntity", MouseEvent(*event)); + _entitiesScriptEngine->callEntityScriptMethod(_currentClickingOnEntityID, "clickDownOnEntity", pointerEvent); } + + _lastPointerEvent = pointerEvent; + _lastPointerEventValid = true; + } else { - emit mousePressOffEntity(rayPickResult, event); + emit mousePressOffEntity(); } - _lastMouseEvent = MouseEvent(*event); - _lastMouseEventValid = true; } void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event) { @@ -680,31 +733,48 @@ void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event) { if (!_tree || _shuttingDown) { return; } + PerformanceTimer perfTimer("EntityTreeRenderer::mouseReleaseEvent"); PickRay ray = _viewState->computePickRay(event->x(), event->y()); bool precisionPicking = !_dontDoPrecisionPicking; RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::Lock, precisionPicking); if (rayPickResult.intersects) { //qCDebug(entitiesrenderer) << "mouseReleaseEvent over entity:" << rayPickResult.entityID; - emit mouseReleaseOnEntity(rayPickResult, event); + + glm::vec2 pos2D = projectOntoEntityXYPlane(rayPickResult.entity, ray, rayPickResult); + PointerEvent pointerEvent(PointerEvent::Release, MOUSE_POINTER_ID, + pos2D, rayPickResult.intersection, + rayPickResult.surfaceNormal, ray.direction, + toPointerButton(*event), toPointerButtons(*event)); + + emit mouseReleaseOnEntity(rayPickResult.entityID, pointerEvent); if (_entitiesScriptEngine) { - _entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mouseReleaseOnEntity", MouseEvent(*event)); + _entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mouseReleaseOnEntity", pointerEvent); } + + _lastPointerEvent = pointerEvent; + _lastPointerEventValid = true; } // Even if we're no longer intersecting with an entity, if we started clicking on it, and now // we're releasing the button, then this is considered a clickOn event if (!_currentClickingOnEntityID.isInvalidID()) { - emit clickReleaseOnEntity(_currentClickingOnEntityID, MouseEvent(*event)); + + auto entity = getTree()->findEntityByID(_currentClickingOnEntityID); + glm::vec2 pos2D = projectOntoEntityXYPlane(entity, ray, rayPickResult); + PointerEvent pointerEvent(PointerEvent::Release, MOUSE_POINTER_ID, + pos2D, rayPickResult.intersection, + rayPickResult.surfaceNormal, ray.direction, + toPointerButton(*event), toPointerButtons(*event)); + + emit clickReleaseOnEntity(_currentClickingOnEntityID, pointerEvent); if (_entitiesScriptEngine) { - _entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "clickReleaseOnEntity", MouseEvent(*event)); + _entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "clickReleaseOnEntity", pointerEvent); } } // makes it the unknown ID, we just released so we can't be clicking on anything _currentClickingOnEntityID = UNKNOWN_ENTITY_ID; - _lastMouseEvent = MouseEvent(*event); - _lastMouseEventValid = true; } void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) { @@ -721,19 +791,35 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) { RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking); if (rayPickResult.intersects) { + glm::vec2 pos2D = projectOntoEntityXYPlane(rayPickResult.entity, ray, rayPickResult); + PointerEvent pointerEvent(PointerEvent::Move, MOUSE_POINTER_ID, + pos2D, rayPickResult.intersection, + rayPickResult.surfaceNormal, ray.direction, + toPointerButton(*event), toPointerButtons(*event)); + + emit mouseMoveOnEntity(rayPickResult.entityID, pointerEvent); + if (_entitiesScriptEngine) { - _entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mouseMoveEvent", MouseEvent(*event)); - _entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mouseMoveOnEntity", MouseEvent(*event)); + _entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mouseMoveEvent", pointerEvent); + _entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mouseMoveOnEntity", pointerEvent); } - + // handle the hover logic... - + // if we were previously hovering over an entity, and this new entity is not the same as our previous entity // then we need to send the hover leave. if (!_currentHoverOverEntityID.isInvalidID() && rayPickResult.entityID != _currentHoverOverEntityID) { - emit hoverLeaveEntity(_currentHoverOverEntityID, MouseEvent(*event)); + + auto entity = getTree()->findEntityByID(_currentHoverOverEntityID); + glm::vec2 pos2D = projectOntoEntityXYPlane(entity, ray, rayPickResult); + PointerEvent pointerEvent(PointerEvent::Move, MOUSE_POINTER_ID, + pos2D, rayPickResult.intersection, + rayPickResult.surfaceNormal, ray.direction, + toPointerButton(*event), toPointerButtons(*event)); + + emit hoverLeaveEntity(_currentHoverOverEntityID, pointerEvent); if (_entitiesScriptEngine) { - _entitiesScriptEngine->callEntityScriptMethod(_currentHoverOverEntityID, "hoverLeaveEntity", MouseEvent(*event)); + _entitiesScriptEngine->callEntityScriptMethod(_currentHoverOverEntityID, "hoverLeaveEntity", pointerEvent); } } @@ -741,28 +827,39 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) { // this is true if the _currentHoverOverEntityID is known or unknown if (rayPickResult.entityID != _currentHoverOverEntityID) { if (_entitiesScriptEngine) { - _entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "hoverEnterEntity", MouseEvent(*event)); + _entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "hoverEnterEntity", pointerEvent); } } // and finally, no matter what, if we're intersecting an entity then we're definitely hovering over it, and // we should send our hover over event - emit hoverOverEntity(rayPickResult.entityID, MouseEvent(*event)); + emit hoverOverEntity(rayPickResult.entityID, pointerEvent); if (_entitiesScriptEngine) { - _entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "hoverOverEntity", MouseEvent(*event)); + _entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "hoverOverEntity", pointerEvent); } // remember what we're hovering over _currentHoverOverEntityID = rayPickResult.entityID; + _lastPointerEvent = pointerEvent; + _lastPointerEventValid = true; + } else { // handle the hover logic... // if we were previously hovering over an entity, and we're no longer hovering over any entity then we need to // send the hover leave for our previous entity if (!_currentHoverOverEntityID.isInvalidID()) { - emit hoverLeaveEntity(_currentHoverOverEntityID, MouseEvent(*event)); + + auto entity = getTree()->findEntityByID(_currentHoverOverEntityID); + glm::vec2 pos2D = projectOntoEntityXYPlane(entity, ray, rayPickResult); + PointerEvent pointerEvent(PointerEvent::Move, MOUSE_POINTER_ID, + pos2D, rayPickResult.intersection, + rayPickResult.surfaceNormal, ray.direction, + toPointerButton(*event), toPointerButtons(*event)); + + emit hoverLeaveEntity(_currentHoverOverEntityID, pointerEvent); if (_entitiesScriptEngine) { - _entitiesScriptEngine->callEntityScriptMethod(_currentHoverOverEntityID, "hoverLeaveEntity", MouseEvent(*event)); + _entitiesScriptEngine->callEntityScriptMethod(_currentHoverOverEntityID, "hoverLeaveEntity", pointerEvent); } _currentHoverOverEntityID = UNKNOWN_ENTITY_ID; // makes it the unknown ID } @@ -771,13 +868,19 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event) { // Even if we're no longer intersecting with an entity, if we started clicking on an entity and we have // not yet released the hold then this is still considered a holdingClickOnEntity event if (!_currentClickingOnEntityID.isInvalidID()) { - emit holdingClickOnEntity(_currentClickingOnEntityID, MouseEvent(*event)); + + auto entity = getTree()->findEntityByID(_currentClickingOnEntityID); + glm::vec2 pos2D = projectOntoEntityXYPlane(entity, ray, rayPickResult); + PointerEvent pointerEvent(PointerEvent::Move, MOUSE_POINTER_ID, + pos2D, rayPickResult.intersection, + rayPickResult.surfaceNormal, ray.direction, + toPointerButton(*event), toPointerButtons(*event)); + + emit holdingClickOnEntity(_currentClickingOnEntityID, pointerEvent); if (_entitiesScriptEngine) { - _entitiesScriptEngine->callEntityScriptMethod(_currentClickingOnEntityID, "holdingClickOnEntity", MouseEvent(*event)); + _entitiesScriptEngine->callEntityScriptMethod(_currentClickingOnEntityID, "holdingClickOnEntity", pointerEvent); } } - _lastMouseEvent = MouseEvent(*event); - _lastMouseEventValid = true; } void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) { diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 99c62ab5f6..b9f32c1e48 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -18,7 +18,8 @@ #include #include // for RayToEntityIntersectionResult #include -#include +#include +#include #include #include #include @@ -98,18 +99,18 @@ public: std::shared_ptr myAvatarZone() { return _bestZone; } signals: - void mousePressOnEntity(const RayToEntityIntersectionResult& intersection, const QMouseEvent* event); - void mousePressOffEntity(const RayToEntityIntersectionResult& intersection, const QMouseEvent* event); - void mouseMoveOnEntity(const RayToEntityIntersectionResult& intersection, const QMouseEvent* event); - void mouseReleaseOnEntity(const RayToEntityIntersectionResult& intersection, const QMouseEvent* event); + void mousePressOnEntity(const EntityItemID& entityItemID, const PointerEvent& event); + void mouseMoveOnEntity(const EntityItemID& entityItemID, const PointerEvent& event); + void mouseReleaseOnEntity(const EntityItemID& entityItemID, const PointerEvent& event); + void mousePressOffEntity(); - void clickDownOnEntity(const EntityItemID& entityItemID, const MouseEvent& event); - void holdingClickOnEntity(const EntityItemID& entityItemID, const MouseEvent& event); - void clickReleaseOnEntity(const EntityItemID& entityItemID, const MouseEvent& event); + void clickDownOnEntity(const EntityItemID& entityItemID, const PointerEvent& event); + void holdingClickOnEntity(const EntityItemID& entityItemID, const PointerEvent& event); + void clickReleaseOnEntity(const EntityItemID& entityItemID, const PointerEvent& event); - void hoverEnterEntity(const EntityItemID& entityItemID, const MouseEvent& event); - void hoverOverEntity(const EntityItemID& entityItemID, const MouseEvent& event); - void hoverLeaveEntity(const EntityItemID& entityItemID, const MouseEvent& event); + void hoverEnterEntity(const EntityItemID& entityItemID, const PointerEvent& event); + void hoverOverEntity(const EntityItemID& entityItemID, const PointerEvent& event); + void hoverLeaveEntity(const EntityItemID& entityItemID, const PointerEvent& event); void enterEntity(const EntityItemID& entityItemID); void leaveEntity(const EntityItemID& entityItemID); @@ -174,8 +175,8 @@ private: void playEntityCollisionSound(const QUuid& myNodeID, EntityTreePointer entityTree, const EntityItemID& id, const Collision& collision); - bool _lastMouseEventValid; - MouseEvent _lastMouseEvent; + bool _lastPointerEventValid; + PointerEvent _lastPointerEvent; AbstractViewStateInterface* _viewState; AbstractScriptingServicesInterface* _scriptingServices; bool _displayModelBounds; diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 734b3c8f3b..5e29a3a3da 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -73,22 +73,27 @@ bool RenderableWebEntityItem::buildWebSurface(EntityTreeRenderer* renderer) { // Restore the original GL context currentContext->makeCurrent(currentSurface); - auto forwardMouseEvent = [=](const RayToEntityIntersectionResult& intersection, const QMouseEvent* event) { - if (intersection.entityID == getID()) { - handleMouseEvent(*event, intersection.intersection); + auto forwardPointerEvent = [=](const EntityItemID& entityItemID, const PointerEvent& event) { + if (entityItemID == getID()) { + handlePointerEvent(event); } }; - _mousePressConnection = QObject::connect(renderer, &EntityTreeRenderer::mousePressOnEntity, forwardMouseEvent); - _mouseReleaseConnection = QObject::connect(renderer, &EntityTreeRenderer::mouseReleaseOnEntity, forwardMouseEvent); - _mouseMoveConnection = QObject::connect(renderer, &EntityTreeRenderer::mouseMoveOnEntity, forwardMouseEvent); - _hoverLeaveConnection = QObject::connect(renderer, &EntityTreeRenderer::hoverLeaveEntity, [=](const EntityItemID& entityItemID, const MouseEvent& event) { + _mousePressConnection = QObject::connect(renderer, &EntityTreeRenderer::mousePressOnEntity, forwardPointerEvent); + _mouseReleaseConnection = QObject::connect(renderer, &EntityTreeRenderer::mouseReleaseOnEntity, forwardPointerEvent); + _mouseMoveConnection = QObject::connect(renderer, &EntityTreeRenderer::mouseMoveOnEntity, forwardPointerEvent); + _hoverLeaveConnection = QObject::connect(renderer, &EntityTreeRenderer::hoverLeaveEntity, [=](const EntityItemID& entityItemID, const PointerEvent& event) { if (this->_pressed && this->getID() == entityItemID) { - // If the user mouses off the entity while the button is down, simulate a mouse release - QMouseEvent mappedEvent(QEvent::MouseButtonRelease, - QPoint(_lastMove.x, _lastMove.y), - Qt::MouseButton::LeftButton, - Qt::MouseButtons(), Qt::KeyboardModifiers()); - QCoreApplication::sendEvent(_webSurface->getWindow(), &mappedEvent); + // If the user mouses off the entity while the button is down, simulate a touch end. + QTouchEvent::TouchPoint point; + point.setId(event.getID()); + point.setState(Qt::TouchPointReleased); + glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * DPI); + QPointF windowPoint(windowPos.x, windowPos.y); + point.setPos(windowPoint); + QList touchPoints; + touchPoints.push_back(point); + QTouchEvent touchEvent = QTouchEvent(QEvent::TouchEnd, nullptr, Qt::NoModifier, Qt::TouchPointReleased, touchPoints); + QCoreApplication::sendEvent(_webSurface->getWindow(), &touchEvent); } }); return true; @@ -173,111 +178,73 @@ QObject* RenderableWebEntityItem::getEventHandler() { return _webSurface->getEventHandler(); } -void RenderableWebEntityItem::handleMouseEvent(QMouseEvent event, glm::vec3 intersectionPoint) { +void RenderableWebEntityItem::handlePointerEvent(const PointerEvent& event) { + // Ignore mouse interaction if we're locked - if (getLocked()) { + if (getLocked() || !_webSurface) { return; } - if (event.button() == Qt::MouseButton::RightButton) { - if (event.type() == QEvent::MouseButtonPress) { - _lastPress = toGlm(event.pos()); + glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * DPI); + QPointF windowPoint(windowPos.x, windowPos.y); + + /* + qDebug() << "AJT: RenderableWebEntityItem::handlePointerEvent!"; + qDebug() << "AJT: type = " << (int)event.getType(); + qDebug() << "AJT: id = " << event.getID(); + qDebug() << "AJT: pos2D = " << event.getPos2D(); + qDebug() << "AJT: pos3D = " << event.getPos3D(); + qDebug() << "AJT: normal = " << event.getNormal(); + qDebug() << "AJT: direction = " << event.getDirection(); + qDebug() << "AJT: button = " << (int)event.getButton(); + qDebug() << "AJT: buttons = " << event.getButtons(); + qDebug() << "AJT: windowPos = " << windowPos; + */ + + if (event.getType() == PointerEvent::Move && event.getButtons() == PointerEvent::NoButtons) { + // Forward a mouse move event to webSurface + QMouseEvent mouseEvent(QEvent::MouseMove, windowPoint, Qt::NoButton, Qt::NoButton, Qt::NoModifier); + QCoreApplication::sendEvent(_webSurface->getWindow(), &mouseEvent); + } else { + // Forward a touch update event to webSurface + + QEvent::Type type; + Qt::TouchPointState touchPointState; + switch (event.getType()) { + case PointerEvent::Press: + type = QEvent::TouchBegin; + touchPointState = Qt::TouchPointPressed; + break; + case PointerEvent::Release: + type = QEvent::TouchEnd; + touchPointState = Qt::TouchPointReleased; + break; + case PointerEvent::Move: + default: + type = QEvent::TouchUpdate; + touchPointState = Qt::TouchPointMoved; + break; } - } - if (event.button() == Qt::MouseButton::RightButton) { - if (event.type() == QEvent::MouseButtonRelease) { - ivec2 dist = glm::abs(toGlm(event.pos()) - _lastPress); - if (!glm::any(glm::greaterThan(dist, ivec2(1)))) { - AbstractViewStateInterface::instance()->postLambdaEvent([this] { - QMetaObject::invokeMethod(_webSurface->getRootItem(), "goBack"); - }); - } - _lastPress = ivec2(INT_MIN); - } - return; - } + QTouchEvent::TouchPoint point; + point.setId(event.getID()); + point.setState(touchPointState); + point.setPos(windowPoint); + point.setScreenPos(windowPoint); + QList touchPoints; + touchPoints.push_back(point); - // FIXME doesn't work... double click events not received - if (event.type() == QEvent::MouseButtonDblClick) { - AbstractViewStateInterface::instance()->postLambdaEvent([this] { - _webSurface->getRootItem()->setProperty("url", _sourceUrl); - }); - } + QTouchEvent touchEvent(type, nullptr, Qt::NoModifier, touchPointState, touchPoints); + QCoreApplication::sendEvent(_webSurface->getWindow(), &touchEvent); - if (event.button() == Qt::MouseButton::MiddleButton) { - if (event.type() == QEvent::MouseButtonRelease) { - AbstractViewStateInterface::instance()->postLambdaEvent([this] { - _webSurface->getRootItem()->setProperty("url", _sourceUrl); - }); - } - return; + _lastTouchEvent = touchEvent; } - - // Map the intersection point to an actual offscreen pixel - glm::vec3 point = intersectionPoint; - glm::vec3 dimensions = getDimensions(); - point -= getPosition(); - point = glm::inverse(getRotation()) * point; - point /= dimensions; - point += 0.5f; - point.y = 1.0f - point.y; - point *= dimensions * (METERS_TO_INCHES * DPI); - - if (event.button() == Qt::MouseButton::LeftButton) { - if (event.type() == QEvent::MouseButtonPress) { - _pressed = true; - _lastMove = ivec2((int)point.x, (int)point.y); - } else if (event.type() == QEvent::MouseButtonRelease) { - _pressed = false; - } - } - if (event.type() == QEvent::MouseMove) { - _lastMove = ivec2((int)point.x, (int)point.y); - } - - // Forward the mouse event. - QMouseEvent mappedEvent(event.type(), - QPoint((int)point.x, (int)point.y), - event.screenPos(), event.button(), - event.buttons(), event.modifiers()); - QCoreApplication::sendEvent(_webSurface->getWindow(), &mappedEvent); } -void RenderableWebEntityItem::handleTouchEvent(QTouchEvent event, glm::vec3 intersectionPoint) { - // Ignore mouse interaction if we're locked - if (getLocked()) { - return; - } - - // Map the intersection point to an actual offscreen pixel - glm::vec3 point = intersectionPoint; - glm::vec3 dimensions = getDimensions(); - point -= getPosition(); - point = glm::inverse(getRotation()) * point; - point /= dimensions; - point += 0.5f; - point.y = 1.0f - point.y; - point *= dimensions * (METERS_TO_INCHES * DPI); - - QList touchPoints = event.touchPoints(); - for (auto& touchPoint : touchPoints) { - touchPoint.setPos(QPointF(point.x, point.y)); - touchPoint.setScreenPos(QPointF(point.x, point.y)); - } - - // Forward the touch event. - QTouchEvent mappedEvent(event.type(), - event.device(), - event.modifiers(), - event.touchPointStates(), - touchPoints); - QCoreApplication::sendEvent(_webSurface->getWindow(), &mappedEvent); -} - - void RenderableWebEntityItem::destroyWebSurface() { if (_webSurface) { + qDebug() << "AJT: destroyWebSurface!"; + --_currentWebCount; _webSurface->pause(); _webSurface->disconnect(_connection); diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h index c6afacdf5f..578bd2f3b1 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -11,6 +11,8 @@ #include #include +#include +#include #include @@ -33,8 +35,7 @@ public: void setProxyWindow(QWindow* proxyWindow); QObject* getEventHandler(); - void handleMouseEvent(QMouseEvent event, glm::vec3 intersectionPoint); - void handleTouchEvent(QTouchEvent event, glm::vec3 intersectionPoint); + void handlePointerEvent(const PointerEvent& event); void update(const quint64& now) override; bool needsToCallUpdate() const override { return _webSurface != nullptr; } @@ -50,7 +51,7 @@ private: uint32_t _texture{ 0 }; ivec2 _lastPress{ INT_MIN }; bool _pressed{ false }; - ivec2 _lastMove{ INT_MIN }; + QTouchEvent _lastTouchEvent { QEvent::TouchUpdate }; uint64_t _lastRenderTime{ 0 }; QMetaObject::Connection _mousePressConnection; diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 35852e5acb..988ebbfefe 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -1195,27 +1195,33 @@ void EntityScriptingInterface::setKeyboardFocusEntity(QUuid id) { } void EntityScriptingInterface::sendEntityMouseMoveEvent(QUuid id, glm::vec3 intersectionPoint) { - QMetaObject::invokeMethod(qApp, "sendEntityMouseMoveEvent", Qt::QueuedConnection, Q_ARG(QUuid, id), Q_ARG(glm::vec3, intersectionPoint)); + // AJT: TODO CURRENTLY BROKEN + //QMetaObject::invokeMethod(qApp, "sendEntityMouseMoveEvent", Qt::QueuedConnection, Q_ARG(QUuid, id), Q_ARG(glm::vec3, intersectionPoint)); } void EntityScriptingInterface::sendEntityLeftMouseDownEvent(QUuid id, glm::vec3 intersectionPoint) { - QMetaObject::invokeMethod(qApp, "sendEntityLeftMouseDownEvent", Qt::QueuedConnection, Q_ARG(QUuid, id), Q_ARG(glm::vec3, intersectionPoint)); + // AJT: TODO CURRENTLY BROKEN + //QMetaObject::invokeMethod(qApp, "sendEntityLeftMouseDownEvent", Qt::QueuedConnection, Q_ARG(QUuid, id), Q_ARG(glm::vec3, intersectionPoint)); } void EntityScriptingInterface::sendEntityLeftMouseUpEvent(QUuid id, glm::vec3 intersectionPoint) { - QMetaObject::invokeMethod(qApp, "sendEntityLeftMouseUpEvent", Qt::QueuedConnection, Q_ARG(QUuid, id), Q_ARG(glm::vec3, intersectionPoint)); + // AJT: TODO CURRENTLY BROKEN + //QMetaObject::invokeMethod(qApp, "sendEntityLeftMouseUpEvent", Qt::QueuedConnection, Q_ARG(QUuid, id), Q_ARG(glm::vec3, intersectionPoint)); } void EntityScriptingInterface::sendEntityTouchUpdateEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint) { - QMetaObject::invokeMethod(qApp, "sendEntityTouchUpdateEvent", Qt::QueuedConnection, Q_ARG(QUuid, entityID), Q_ARG(int, fingerID), Q_ARG(glm::vec3, intersectionPoint)); + // AJT: TODO CURRENTLY BROKEN + //QMetaObject::invokeMethod(qApp, "sendEntityTouchUpdateEvent", Qt::QueuedConnection, Q_ARG(QUuid, entityID), Q_ARG(int, fingerID), Q_ARG(glm::vec3, intersectionPoint)); } void EntityScriptingInterface::sendEntityTouchBeginEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint) { - QMetaObject::invokeMethod(qApp, "sendEntityTouchBeginEvent", Qt::QueuedConnection, Q_ARG(QUuid, entityID), Q_ARG(int, fingerID), Q_ARG(glm::vec3, intersectionPoint)); + // AJT: TODO CURRENTLY BROKEN + //QMetaObject::invokeMethod(qApp, "sendEntityTouchBeginEvent", Qt::QueuedConnection, Q_ARG(QUuid, entityID), Q_ARG(int, fingerID), Q_ARG(glm::vec3, intersectionPoint)); } void EntityScriptingInterface::sendEntityTouchEndEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint) { - QMetaObject::invokeMethod(qApp, "sendEntityTouchEndEvent", Qt::QueuedConnection, Q_ARG(QUuid, entityID), Q_ARG(int, fingerID), Q_ARG(glm::vec3, intersectionPoint)); + // AJT: TODO CURRENTLY BROKEN + //QMetaObject::invokeMethod(qApp, "sendEntityTouchEndEvent", Qt::QueuedConnection, Q_ARG(QUuid, entityID), Q_ARG(int, fingerID), Q_ARG(glm::vec3, intersectionPoint)); } float EntityScriptingInterface::calculateCost(float mass, float oldVelocity, float newVelocity) { diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index b98d872f74..8737209d8d 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -34,7 +34,7 @@ #include "EntityItemProperties.h" class EntityTree; -class MouseEvent; +class PointerEvent; class RayToEntityIntersectionResult { public: @@ -181,10 +181,10 @@ public slots: Q_INVOKABLE QUuid getKeyboardFocusEntity() const; Q_INVOKABLE void setKeyboardFocusEntity(QUuid id); + // AJT: TODO CURRENTLY BROKEN Q_INVOKABLE void sendEntityMouseMoveEvent(QUuid id, glm::vec3 intersectionPoint); Q_INVOKABLE void sendEntityLeftMouseDownEvent(QUuid id, glm::vec3 intersectionPoint); Q_INVOKABLE void sendEntityLeftMouseUpEvent(QUuid id, glm::vec3 intersectionPoint); - Q_INVOKABLE void sendEntityTouchUpdateEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint); Q_INVOKABLE void sendEntityTouchBeginEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint); Q_INVOKABLE void sendEntityTouchEndEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint); @@ -197,17 +197,17 @@ signals: void canRezChanged(bool canRez); void canRezTmpChanged(bool canRez); - void mousePressOnEntity(const EntityItemID& entityItemID, const MouseEvent& event); - void mouseMoveOnEntity(const EntityItemID& entityItemID, const MouseEvent& event); - void mouseReleaseOnEntity(const EntityItemID& entityItemID, const MouseEvent& event); + void mousePressOnEntity(const EntityItemID& entityItemID, const PointerEvent& event); + void mouseMoveOnEntity(const EntityItemID& entityItemID, const PointerEvent& event); + void mouseReleaseOnEntity(const EntityItemID& entityItemID, const PointerEvent& event); - void clickDownOnEntity(const EntityItemID& entityItemID, const MouseEvent& event); - void holdingClickOnEntity(const EntityItemID& entityItemID, const MouseEvent& event); - void clickReleaseOnEntity(const EntityItemID& entityItemID, const MouseEvent& event); + void clickDownOnEntity(const EntityItemID& entityItemID, const PointerEvent& event); + void holdingClickOnEntity(const EntityItemID& entityItemID, const PointerEvent& event); + void clickReleaseOnEntity(const EntityItemID& entityItemID, const PointerEvent& event); - void hoverEnterEntity(const EntityItemID& entityItemID, const MouseEvent& event); - void hoverOverEntity(const EntityItemID& entityItemID, const MouseEvent& event); - void hoverLeaveEntity(const EntityItemID& entityItemID, const MouseEvent& event); + void hoverEnterEntity(const EntityItemID& entityItemID, const PointerEvent& event); + void hoverOverEntity(const EntityItemID& entityItemID, const PointerEvent& event); + void hoverLeaveEntity(const EntityItemID& entityItemID, const PointerEvent& event); void enterEntity(const EntityItemID& entityItemID); void leaveEntity(const EntityItemID& entityItemID); diff --git a/libraries/script-engine/src/EventTypes.cpp b/libraries/script-engine/src/EventTypes.cpp index ce3c82e121..1bdf0f5034 100644 --- a/libraries/script-engine/src/EventTypes.cpp +++ b/libraries/script-engine/src/EventTypes.cpp @@ -13,6 +13,7 @@ #include "KeyEvent.h" #include "MouseEvent.h" #include "SpatialEvent.h" +#include "PointerEvent.h" #include "TouchEvent.h" #include "WheelEvent.h" @@ -22,6 +23,7 @@ void registerEventTypes(QScriptEngine* engine) { qScriptRegisterMetaType(engine, HFActionEvent::toScriptValue, HFActionEvent::fromScriptValue); qScriptRegisterMetaType(engine, KeyEvent::toScriptValue, KeyEvent::fromScriptValue); qScriptRegisterMetaType(engine, MouseEvent::toScriptValue, MouseEvent::fromScriptValue); + qScriptRegisterMetaType(engine, PointerEvent::toScriptValue, PointerEvent::fromScriptValue); qScriptRegisterMetaType(engine, TouchEvent::toScriptValue, TouchEvent::fromScriptValue); qScriptRegisterMetaType(engine, WheelEvent::toScriptValue, WheelEvent::fromScriptValue); qScriptRegisterMetaType(engine, SpatialEvent::toScriptValue, SpatialEvent::fromScriptValue); diff --git a/libraries/script-engine/src/PointerEvent.cpp b/libraries/script-engine/src/PointerEvent.cpp new file mode 100644 index 0000000000..1dcf9fdc43 --- /dev/null +++ b/libraries/script-engine/src/PointerEvent.cpp @@ -0,0 +1,110 @@ +// +// PointerEvent.cpp +// script-engine/src +// +// Created by Anthony Thibault on 2016-8-11. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include + +#include "PointerEvent.h" + +static bool areFlagsSet(uint32_t flags, uint32_t mask) { + return (flags & mask) != 0; +} + +PointerEvent::PointerEvent() { + ; +} + +PointerEvent::PointerEvent(EventType type, uint32_t id, + const glm::vec2& pos2D, const glm::vec3& pos3D, + const glm::vec3& normal, const glm::vec3& direction, + Button button, uint32_t buttons) : + _type(type), + _id(id), + _pos2D(pos2D), + _pos3D(pos3D), + _normal(normal), + _direction(direction), + _button(button), + _buttons(buttons) +{ + ; +} + +QScriptValue PointerEvent::toScriptValue(QScriptEngine* engine, const PointerEvent& event) { + QScriptValue obj = engine->newObject(); + + switch (event._type) { + case Press: + obj.setProperty("type", "Press"); + break; + case Release: + obj.setProperty("type", "Release"); + break; + default: + case Move: + obj.setProperty("type", "Move"); + break; + }; + + obj.setProperty("id", event._id); + + QScriptValue pos2D = engine->newObject(); + pos2D.setProperty("x", event._pos2D.x); + pos2D.setProperty("y", event._pos2D.y); + obj.setProperty("pos2D", pos2D); + + QScriptValue pos3D = engine->newObject(); + pos3D.setProperty("x", event._pos3D.x); + pos3D.setProperty("y", event._pos3D.y); + pos3D.setProperty("z", event._pos3D.z); + obj.setProperty("pos3D", pos3D); + + QScriptValue normal = engine->newObject(); + normal.setProperty("x", event._normal.x); + normal.setProperty("y", event._normal.y); + normal.setProperty("z", event._normal.z); + obj.setProperty("pos3D", normal); + + QScriptValue direction = engine->newObject(); + direction.setProperty("x", event._direction.x); + direction.setProperty("y", event._direction.y); + direction.setProperty("z", event._direction.z); + obj.setProperty("pos3D", direction); + + switch (event._button) { + case NoButtons: + obj.setProperty("button", "None"); + break; + case PrimaryButton: + obj.setProperty("button", "Primary"); + break; + case SecondaryButton: + obj.setProperty("button", "Secondary"); + break; + case TertiaryButton: + obj.setProperty("button", "Tertiary"); + break; + } + + obj.setProperty("isLeftButton", areFlagsSet(event._buttons, PrimaryButton)); + obj.setProperty("isRightButton", areFlagsSet(event._buttons, SecondaryButton)); + obj.setProperty("isMiddleButton", areFlagsSet(event._buttons, TertiaryButton)); + + obj.setProperty("isPrimaryButton", areFlagsSet(event._buttons, PrimaryButton)); + obj.setProperty("isSecondaryButton", areFlagsSet(event._buttons, SecondaryButton)); + obj.setProperty("isTertiaryButton", areFlagsSet(event._buttons, TertiaryButton)); + + return obj; +} + +void PointerEvent::fromScriptValue(const QScriptValue& object, PointerEvent& event) { + // nothing for now... +} diff --git a/libraries/script-engine/src/PointerEvent.h b/libraries/script-engine/src/PointerEvent.h new file mode 100644 index 0000000000..054835c4fc --- /dev/null +++ b/libraries/script-engine/src/PointerEvent.h @@ -0,0 +1,68 @@ +// +// PointerEvent.h +// script-engine/src +// +// Created by Anthony Thibault on 2016-8-11. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_PointerEvent_h +#define hifi_PointerEvent_h + +#include +#include +#include + +class PointerEvent { +public: + enum Button { + NoButtons = 0x0, + PrimaryButton = 0x1, + SecondaryButton = 0x2, + TertiaryButton = 0x4 + }; + + enum EventType { + Press, // A button has just been pressed + Release, // A button has just been released + Move // The pointer has just moved + }; + + PointerEvent(); + PointerEvent(EventType type, uint32_t id, + const glm::vec2& pos2D, const glm::vec3& pos3D, + const glm::vec3& normal, const glm::vec3& direction, + Button button, uint32_t buttons); + + static QScriptValue toScriptValue(QScriptEngine* engine, const PointerEvent& event); + static void fromScriptValue(const QScriptValue& object, PointerEvent& event); + + QScriptValue toScriptValue(QScriptEngine* engine) const { return PointerEvent::toScriptValue(engine, *this); } + + EventType getType() const { return _type; } + uint32_t getID() const { return _id; } + const glm::vec2& getPos2D() const { return _pos2D; } + const glm::vec3& getPos3D() const { return _pos3D; } + const glm::vec3& getNormal() const { return _normal; } + const glm::vec3& getDirection() const { return _direction; } + Button getButton() const { return _button; } + uint32_t getButtons() const { return _buttons; } + +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. + + Button _button { NoButtons }; // button assosiated 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. +}; + +Q_DECLARE_METATYPE(PointerEvent) + +#endif // hifi_PointerEvent_h diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index f98b07478b..2fccd81c42 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -723,9 +723,9 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& }; }; - using MouseHandler = std::function; - auto makeMouseHandler = [this](QString eventName) -> MouseHandler { - return [this, eventName](const EntityItemID& entityItemID, const MouseEvent& event) { + using PointerHandler = std::function; + auto makePointerHandler = [this](QString eventName) -> PointerHandler { + return [this, eventName](const EntityItemID& entityItemID, const PointerEvent& event) { forwardHandlerCall(entityItemID, eventName, { entityItemID.toScriptValue(this), event.toScriptValue(this) }); }; }; @@ -741,17 +741,17 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& connect(entities.data(), &EntityScriptingInterface::enterEntity, this, makeSingleEntityHandler("enterEntity")); connect(entities.data(), &EntityScriptingInterface::leaveEntity, this, makeSingleEntityHandler("leaveEntity")); - connect(entities.data(), &EntityScriptingInterface::mousePressOnEntity, this, makeMouseHandler("mousePressOnEntity")); - connect(entities.data(), &EntityScriptingInterface::mouseMoveOnEntity, this, makeMouseHandler("mouseMoveOnEntity")); - connect(entities.data(), &EntityScriptingInterface::mouseReleaseOnEntity, this, makeMouseHandler("mouseReleaseOnEntity")); + connect(entities.data(), &EntityScriptingInterface::mousePressOnEntity, this, makePointerHandler("mousePressOnEntity")); + connect(entities.data(), &EntityScriptingInterface::mouseMoveOnEntity, this, makePointerHandler("mouseMoveOnEntity")); + connect(entities.data(), &EntityScriptingInterface::mouseReleaseOnEntity, this, makePointerHandler("mouseReleaseOnEntity")); - connect(entities.data(), &EntityScriptingInterface::clickDownOnEntity, this, makeMouseHandler("clickDownOnEntity")); - connect(entities.data(), &EntityScriptingInterface::holdingClickOnEntity, this, makeMouseHandler("holdingClickOnEntity")); - connect(entities.data(), &EntityScriptingInterface::clickReleaseOnEntity, this, makeMouseHandler("clickReleaseOnEntity")); + connect(entities.data(), &EntityScriptingInterface::clickDownOnEntity, this, makePointerHandler("clickDownOnEntity")); + connect(entities.data(), &EntityScriptingInterface::holdingClickOnEntity, this, makePointerHandler("holdingClickOnEntity")); + connect(entities.data(), &EntityScriptingInterface::clickReleaseOnEntity, this, makePointerHandler("clickReleaseOnEntity")); - connect(entities.data(), &EntityScriptingInterface::hoverEnterEntity, this, makeMouseHandler("hoverEnterEntity")); - connect(entities.data(), &EntityScriptingInterface::hoverOverEntity, this, makeMouseHandler("hoverOverEntity")); - connect(entities.data(), &EntityScriptingInterface::hoverLeaveEntity, this, makeMouseHandler("hoverLeaveEntity")); + connect(entities.data(), &EntityScriptingInterface::hoverEnterEntity, this, makePointerHandler("hoverEnterEntity")); + connect(entities.data(), &EntityScriptingInterface::hoverOverEntity, this, makePointerHandler("hoverOverEntity")); + connect(entities.data(), &EntityScriptingInterface::hoverLeaveEntity, this, makePointerHandler("hoverLeaveEntity")); connect(entities.data(), &EntityScriptingInterface::collisionWithEntity, this, makeCollisionHandler("collisionWithEntity")); } @@ -1546,7 +1546,7 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS } } -void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const MouseEvent& event) { +void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const PointerEvent& event) { if (QThread::currentThread() != thread()) { #ifdef THREAD_DEBUGGING qDebug() << "*** WARNING *** ScriptEngine::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " @@ -1556,12 +1556,12 @@ void ScriptEngine::callEntityScriptMethod(const EntityItemID& entityID, const QS QMetaObject::invokeMethod(this, "callEntityScriptMethod", Q_ARG(const EntityItemID&, entityID), Q_ARG(const QString&, methodName), - Q_ARG(const MouseEvent&, event)); + Q_ARG(const PointerEvent&, event)); return; } #ifdef THREAD_DEBUGGING qDebug() << "ScriptEngine::callEntityScriptMethod() called on correct thread [" << thread() << "] " - "entityID:" << entityID << "methodName:" << methodName << "event: mouseEvent"; + "entityID:" << entityID << "methodName:" << methodName << "event: pointerEvent"; #endif refreshFileScript(entityID); diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index e093f0393b..be4ced2490 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -30,7 +30,7 @@ #include #include -#include "MouseEvent.h" +#include "PointerEvent.h" #include "ArrayBufferClass.h" #include "AssetScriptingInterface.h" #include "AudioScriptingInterface.h" @@ -139,7 +139,7 @@ public: Q_INVOKABLE void unloadEntityScript(const EntityItemID& entityID); // will call unload method Q_INVOKABLE void unloadAllEntityScripts(); Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const QStringList& params = QStringList()); - Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const MouseEvent& event); + Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const PointerEvent& event); Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const EntityItemID& otherID, const Collision& collision); Q_INVOKABLE void requestGarbageCollection() { collectGarbage(); } diff --git a/libraries/shared/src/GeometryUtil.h b/libraries/shared/src/GeometryUtil.h index a66b16b0fd..1c951ca09a 100644 --- a/libraries/shared/src/GeometryUtil.h +++ b/libraries/shared/src/GeometryUtil.h @@ -113,6 +113,20 @@ int computeDirection(float xi, float yi, float xj, float yj, float xk, float yk) // calculate the angle between a point on a sphere that is closest to the cone. float coneSphereAngle(const glm::vec3& coneCenter, const glm::vec3& coneDirection, const glm::vec3& sphereCenter, float sphereRadius); +inline bool rayPlaneIntersection(const glm::vec3& planePosition, const glm::vec3& planeNormal, + const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distanceOut) { + float rayDirectionDotPlaneNormal = glm::dot(rayDirection, planeNormal); + const float PARALLEL_THRESHOLD = 0.0001f; + if (fabsf(rayDirectionDotPlaneNormal) > PARALLEL_THRESHOLD) { + float rayStartDotPlaneNormal = glm::dot(planePosition - rayStart, planeNormal); + distanceOut = rayStartDotPlaneNormal / rayDirectionDotPlaneNormal; + return true; + } else { + // ray is parallel to the plane + return false; + } +} + typedef glm::vec2 LineSegment2[2]; // Polygon Clipping routines inspired by, pseudo code found here: http://www.cs.rit.edu/~icss571/clipTrans/PolyClipBack.html From c407818d6330d2539b4b0b88dc32d8603ff26983 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 16 Aug 2016 15:26:14 -0700 Subject: [PATCH 09/20] send pointer events from handControllerGrab.js to webEntities --- interface/src/Application.cpp | 49 ++++++++- interface/src/Application.h | 12 +++ .../src/RenderableWebEntityItem.cpp | 46 ++++---- .../entities/src/EntityScriptingInterface.cpp | 42 ++++---- .../entities/src/EntityScriptingInterface.h | 19 ++-- libraries/entities/src/WebEntityItem.cpp | 23 ++-- .../src/PointerEvent.cpp | 57 +++++++++- .../src/PointerEvent.h | 0 .../system/controllers/handControllerGrab.js | 102 +++++++++++++++++- 9 files changed, 283 insertions(+), 67 deletions(-) rename libraries/{script-engine => shared}/src/PointerEvent.cpp (61%) rename libraries/{script-engine => shared}/src/PointerEvent.h (100%) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1c62b690b8..f8579a04e3 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2629,7 +2629,9 @@ void Application::mouseMoveEvent(QMouseEvent* event) { event->screenPos(), button, buttons, event->modifiers()); - getEntities()->mouseMoveEvent(&mappedEvent); + if (!compositor.getReticleOverDesktop() || getOverlays().getOverlayAtPoint(glm::vec2(transformedPos.x(), transformedPos.y()))) { + getEntities()->mouseMoveEvent(&mappedEvent); + } _controllerScriptingInterface->emitMouseMoveEvent(&mappedEvent); // send events to any registered scripts // if one of our scripts have asked to capture this event, then stop processing it @@ -5641,3 +5643,48 @@ void Application::releaseOverlayTexture(const gpu::TexturePointer& texture) { bool Application::isForeground() const { return _isForeground && !_window->isMinimized(); } + +void Application::sendMousePressOnEntity(QUuid id, PointerEvent event) { + EntityItemID entityItemID(id); + emit getEntities()->mousePressOnEntity(entityItemID, event); +} + +void Application::sendMouseMoveOnEntity(QUuid id, PointerEvent event) { + EntityItemID entityItemID(id); + emit getEntities()->mouseMoveOnEntity(entityItemID, event); +} + +void Application::sendMouseReleaseOnEntity(QUuid id, PointerEvent event) { + EntityItemID entityItemID(id); + emit getEntities()->mouseReleaseOnEntity(entityItemID, event); +} + +void Application::sendClickDownOnEntity(QUuid id, PointerEvent event) { + EntityItemID entityItemID(id); + emit getEntities()->clickDownOnEntity(entityItemID, event); +} + +void Application::sendHoldingClickOnEntity(QUuid id, PointerEvent event) { + EntityItemID entityItemID(id); + emit getEntities()->holdingClickOnEntity(entityItemID, event); +} + +void Application::sendClickReleaseOnEntity(QUuid id, PointerEvent event) { + EntityItemID entityItemID(id); + emit getEntities()->clickReleaseOnEntity(entityItemID, event); +} + +void Application::sendHoverEnterEntity(QUuid id, PointerEvent event) { + EntityItemID entityItemID(id); + emit getEntities()->hoverEnterEntity(entityItemID, event); +} + +void Application::sendHoverOverEntity(QUuid id, PointerEvent event) { + EntityItemID entityItemID(id); + emit getEntities()->hoverOverEntity(entityItemID, event); +} + +void Application::sendHoverLeaveEntity(QUuid id, PointerEvent event) { + EntityItemID entityItemID(id); + emit getEntities()->hoverLeaveEntity(entityItemID, event); +} diff --git a/interface/src/Application.h b/interface/src/Application.h index a5035793ff..8ca17318ae 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -257,6 +257,18 @@ public: gpu::TexturePointer getDefaultSkyboxTexture() const { return _defaultSkyboxTexture; } gpu::TexturePointer getDefaultSkyboxAmbientTexture() const { return _defaultSkyboxAmbientTexture; } + Q_INVOKABLE void sendMousePressOnEntity(QUuid id, PointerEvent event); + Q_INVOKABLE void sendMouseMoveOnEntity(QUuid id, PointerEvent event); + Q_INVOKABLE void sendMouseReleaseOnEntity(QUuid id, PointerEvent event); + + Q_INVOKABLE void sendClickDownOnEntity(QUuid id, PointerEvent event); + Q_INVOKABLE void sendHoldingClickOnEntity(QUuid id, PointerEvent event); + Q_INVOKABLE void sendClickReleaseOnEntity(QUuid id, PointerEvent event); + + Q_INVOKABLE void sendHoverEnterEntity(QUuid id, PointerEvent event); + Q_INVOKABLE void sendHoverOverEntity(QUuid id, PointerEvent event); + Q_INVOKABLE void sendHoverLeaveEntity(QUuid id, PointerEvent event); + signals: void svoImportRequested(const QString& url); diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 5e29a3a3da..79af620661 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -92,8 +92,8 @@ bool RenderableWebEntityItem::buildWebSurface(EntityTreeRenderer* renderer) { point.setPos(windowPoint); QList touchPoints; touchPoints.push_back(point); - QTouchEvent touchEvent = QTouchEvent(QEvent::TouchEnd, nullptr, Qt::NoModifier, Qt::TouchPointReleased, touchPoints); - QCoreApplication::sendEvent(_webSurface->getWindow(), &touchEvent); + QTouchEvent* touchEvent = new QTouchEvent(QEvent::TouchEnd, nullptr, Qt::NoModifier, Qt::TouchPointReleased, touchPoints); + QCoreApplication::postEvent(_webSurface->getWindow(), touchEvent); } }); return true; @@ -188,25 +188,19 @@ void RenderableWebEntityItem::handlePointerEvent(const PointerEvent& event) { glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * DPI); QPointF windowPoint(windowPos.x, windowPos.y); - /* - qDebug() << "AJT: RenderableWebEntityItem::handlePointerEvent!"; - qDebug() << "AJT: type = " << (int)event.getType(); - qDebug() << "AJT: id = " << event.getID(); - qDebug() << "AJT: pos2D = " << event.getPos2D(); - qDebug() << "AJT: pos3D = " << event.getPos3D(); - qDebug() << "AJT: normal = " << event.getNormal(); - qDebug() << "AJT: direction = " << event.getDirection(); - qDebug() << "AJT: button = " << (int)event.getButton(); - qDebug() << "AJT: buttons = " << event.getButtons(); - qDebug() << "AJT: windowPos = " << windowPos; - */ - - if (event.getType() == PointerEvent::Move && event.getButtons() == PointerEvent::NoButtons) { + if (event.getType() == PointerEvent::Move) { // Forward a mouse move event to webSurface - QMouseEvent mouseEvent(QEvent::MouseMove, windowPoint, Qt::NoButton, Qt::NoButton, Qt::NoModifier); - QCoreApplication::sendEvent(_webSurface->getWindow(), &mouseEvent); - } else { + QMouseEvent* mouseEvent = new QMouseEvent(QEvent::MouseMove, windowPoint, windowPoint, windowPoint, Qt::NoButton, Qt::NoButton, Qt::NoModifier); + QCoreApplication::postEvent(_webSurface->getWindow(), mouseEvent); + } + + { // Forward a touch update event to webSurface + if (event.getType() == PointerEvent::Press) { + this->_pressed = true; + } else if (event.getType() == PointerEvent::Release) { + this->_pressed = false; + } QEvent::Type type; Qt::TouchPointState touchPointState; @@ -234,17 +228,21 @@ void RenderableWebEntityItem::handlePointerEvent(const PointerEvent& event) { QList touchPoints; touchPoints.push_back(point); - QTouchEvent touchEvent(type, nullptr, Qt::NoModifier, touchPointState, touchPoints); - QCoreApplication::sendEvent(_webSurface->getWindow(), &touchEvent); + QTouchEvent* touchEvent = new QTouchEvent(type); + touchEvent->setWindow(nullptr); + touchEvent->setDevice(nullptr); + touchEvent->setTarget(nullptr); + touchEvent->setTouchPoints(touchPoints); + touchEvent->setTouchPointStates(touchPointState); - _lastTouchEvent = touchEvent; + _lastTouchEvent = *touchEvent; + + QCoreApplication::postEvent(_webSurface->getWindow(), touchEvent); } } void RenderableWebEntityItem::destroyWebSurface() { if (_webSurface) { - qDebug() << "AJT: destroyWebSurface!"; - --_currentWebCount; _webSurface->pause(); _webSurface->disconnect(_connection); diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 988ebbfefe..8d3b8877a4 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -1194,34 +1194,40 @@ void EntityScriptingInterface::setKeyboardFocusEntity(QUuid id) { QMetaObject::invokeMethod(qApp, "setKeyboardFocusEntity", Qt::QueuedConnection, Q_ARG(QUuid, id)); } -void EntityScriptingInterface::sendEntityMouseMoveEvent(QUuid id, glm::vec3 intersectionPoint) { - // AJT: TODO CURRENTLY BROKEN - //QMetaObject::invokeMethod(qApp, "sendEntityMouseMoveEvent", Qt::QueuedConnection, Q_ARG(QUuid, id), Q_ARG(glm::vec3, intersectionPoint)); +void EntityScriptingInterface::sendMousePressOnEntity(QUuid id, PointerEvent event) { + QMetaObject::invokeMethod(qApp, "sendMousePressOnEntity", Qt::QueuedConnection, Q_ARG(QUuid, id), Q_ARG(PointerEvent, event)); } -void EntityScriptingInterface::sendEntityLeftMouseDownEvent(QUuid id, glm::vec3 intersectionPoint) { - // AJT: TODO CURRENTLY BROKEN - //QMetaObject::invokeMethod(qApp, "sendEntityLeftMouseDownEvent", Qt::QueuedConnection, Q_ARG(QUuid, id), Q_ARG(glm::vec3, intersectionPoint)); +void EntityScriptingInterface::sendMouseMoveOnEntity(QUuid id, PointerEvent event) { + QMetaObject::invokeMethod(qApp, "sendMouseMoveOnEntity", Qt::QueuedConnection, Q_ARG(QUuid, id), Q_ARG(PointerEvent, event)); } -void EntityScriptingInterface::sendEntityLeftMouseUpEvent(QUuid id, glm::vec3 intersectionPoint) { - // AJT: TODO CURRENTLY BROKEN - //QMetaObject::invokeMethod(qApp, "sendEntityLeftMouseUpEvent", Qt::QueuedConnection, Q_ARG(QUuid, id), Q_ARG(glm::vec3, intersectionPoint)); +void EntityScriptingInterface::sendMouseReleaseOnEntity(QUuid id, PointerEvent event) { + QMetaObject::invokeMethod(qApp, "sendMouseReleaseOnEntity", Qt::QueuedConnection, Q_ARG(QUuid, id), Q_ARG(PointerEvent, event)); } -void EntityScriptingInterface::sendEntityTouchUpdateEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint) { - // AJT: TODO CURRENTLY BROKEN - //QMetaObject::invokeMethod(qApp, "sendEntityTouchUpdateEvent", Qt::QueuedConnection, Q_ARG(QUuid, entityID), Q_ARG(int, fingerID), Q_ARG(glm::vec3, intersectionPoint)); +void EntityScriptingInterface::sendClickDownOnEntity(QUuid id, PointerEvent event) { + QMetaObject::invokeMethod(qApp, "sendClickDownOnEntity", Qt::QueuedConnection, Q_ARG(QUuid, id), Q_ARG(PointerEvent, event)); } -void EntityScriptingInterface::sendEntityTouchBeginEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint) { - // AJT: TODO CURRENTLY BROKEN - //QMetaObject::invokeMethod(qApp, "sendEntityTouchBeginEvent", Qt::QueuedConnection, Q_ARG(QUuid, entityID), Q_ARG(int, fingerID), Q_ARG(glm::vec3, intersectionPoint)); +void EntityScriptingInterface::sendHoldingClickOnEntity(QUuid id, PointerEvent event) { + QMetaObject::invokeMethod(qApp, "sendHoldingClickOnEntity", Qt::QueuedConnection, Q_ARG(QUuid, id), Q_ARG(PointerEvent, event)); } -void EntityScriptingInterface::sendEntityTouchEndEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint) { - // AJT: TODO CURRENTLY BROKEN - //QMetaObject::invokeMethod(qApp, "sendEntityTouchEndEvent", Qt::QueuedConnection, Q_ARG(QUuid, entityID), Q_ARG(int, fingerID), Q_ARG(glm::vec3, intersectionPoint)); +void EntityScriptingInterface::sendClickReleaseOnEntity(QUuid id, PointerEvent event) { + QMetaObject::invokeMethod(qApp, "sendClickReleaseOnEntity", Qt::QueuedConnection, Q_ARG(QUuid, id), Q_ARG(PointerEvent, event)); +} + +void EntityScriptingInterface::sendHoverEnterEntity(QUuid id, PointerEvent event) { + QMetaObject::invokeMethod(qApp, "sendHoverEnterEntity", Qt::QueuedConnection, Q_ARG(QUuid, id), Q_ARG(PointerEvent, event)); +} + +void EntityScriptingInterface::sendHoverOverEntity(QUuid id, PointerEvent event) { + QMetaObject::invokeMethod(qApp, "sendHoverOverEntity", Qt::QueuedConnection, Q_ARG(QUuid, id), Q_ARG(PointerEvent, event)); +} + +void EntityScriptingInterface::sendHoverLeaveEntity(QUuid id, PointerEvent event) { + QMetaObject::invokeMethod(qApp, "sendHoverLeaveEntity", Qt::QueuedConnection, Q_ARG(QUuid, id), Q_ARG(PointerEvent, event)); } float EntityScriptingInterface::calculateCost(float mass, float oldVelocity, float newVelocity) { diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 8737209d8d..8cf35d2aab 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -23,6 +23,7 @@ #include #include #include +#include #include "PolyVoxEntityItem.h" #include "LineEntityItem.h" @@ -34,7 +35,6 @@ #include "EntityItemProperties.h" class EntityTree; -class PointerEvent; class RayToEntityIntersectionResult { public: @@ -181,14 +181,17 @@ public slots: Q_INVOKABLE QUuid getKeyboardFocusEntity() const; Q_INVOKABLE void setKeyboardFocusEntity(QUuid id); - // AJT: TODO CURRENTLY BROKEN - Q_INVOKABLE void sendEntityMouseMoveEvent(QUuid id, glm::vec3 intersectionPoint); - Q_INVOKABLE void sendEntityLeftMouseDownEvent(QUuid id, glm::vec3 intersectionPoint); - Q_INVOKABLE void sendEntityLeftMouseUpEvent(QUuid id, glm::vec3 intersectionPoint); - Q_INVOKABLE void sendEntityTouchUpdateEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint); - Q_INVOKABLE void sendEntityTouchBeginEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint); - Q_INVOKABLE void sendEntityTouchEndEvent(QUuid entityID, int fingerID, glm::vec3 intersectionPoint); + Q_INVOKABLE void sendMousePressOnEntity(QUuid id, PointerEvent event); + Q_INVOKABLE void sendMouseMoveOnEntity(QUuid id, PointerEvent event); + Q_INVOKABLE void sendMouseReleaseOnEntity(QUuid id, PointerEvent event); + Q_INVOKABLE void sendClickDownOnEntity(QUuid id, PointerEvent event); + Q_INVOKABLE void sendHoldingClickOnEntity(QUuid id, PointerEvent event); + Q_INVOKABLE void sendClickReleaseOnEntity(QUuid id, PointerEvent event); + + Q_INVOKABLE void sendHoverEnterEntity(QUuid id, PointerEvent event); + Q_INVOKABLE void sendHoverOverEntity(QUuid id, PointerEvent event); + Q_INVOKABLE void sendHoverLeaveEntity(QUuid id, PointerEvent event); signals: void collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision); diff --git a/libraries/entities/src/WebEntityItem.cpp b/libraries/entities/src/WebEntityItem.cpp index 5f113f1de4..4f074b8ac3 100644 --- a/libraries/entities/src/WebEntityItem.cpp +++ b/libraries/entities/src/WebEntityItem.cpp @@ -99,19 +99,24 @@ void WebEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitst } bool WebEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - bool& keepSearching, OctreeElementPointer& element, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, - void** intersectedObject, bool precisionPicking) const { + bool& keepSearching, OctreeElementPointer& element, float& distance, + BoxFace& face, glm::vec3& surfaceNormal, + void** intersectedObject, bool precisionPicking) const { glm::vec3 dimensions = getDimensions(); glm::vec2 xyDimensions(dimensions.x, dimensions.y); glm::quat rotation = getRotation(); - glm::vec3 position = getPosition() + rotation * - (dimensions * (getRegistrationPoint() - ENTITY_ITEM_DEFAULT_REGISTRATION_POINT)); - // FIXME - should set face and surfaceNormal - return findRayRectangleIntersection(origin, direction, rotation, position, xyDimensions, distance); + glm::vec3 position = getPosition() + rotation * (dimensions * (getRegistrationPoint() - ENTITY_ITEM_DEFAULT_REGISTRATION_POINT)); + + if (findRayRectangleIntersection(origin, direction, rotation, position, xyDimensions, distance)) { + surfaceNormal = rotation * Vectors::UNIT_Z; + face = glm::dot(surfaceNormal, direction) > 0 ? MIN_Z_FACE : MAX_Z_FACE; + return true; + } else { + return false; + } } - -void WebEntityItem::setSourceUrl(const QString& value) { + +void WebEntityItem::setSourceUrl(const QString& value) { if (_sourceUrl != value) { _sourceUrl = value; } diff --git a/libraries/script-engine/src/PointerEvent.cpp b/libraries/shared/src/PointerEvent.cpp similarity index 61% rename from libraries/script-engine/src/PointerEvent.cpp rename to libraries/shared/src/PointerEvent.cpp index 1dcf9fdc43..66289a35dd 100644 --- a/libraries/script-engine/src/PointerEvent.cpp +++ b/libraries/shared/src/PointerEvent.cpp @@ -9,10 +9,12 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "PointerEvent.h" + #include #include -#include "PointerEvent.h" +#include "RegisteredMetaTypes.h" static bool areFlagsSet(uint32_t flags, uint32_t mask) { return (flags & mask) != 0; @@ -106,5 +108,56 @@ QScriptValue PointerEvent::toScriptValue(QScriptEngine* engine, const PointerEve } void PointerEvent::fromScriptValue(const QScriptValue& object, PointerEvent& event) { - // nothing for now... + if (object.isObject()) { + QScriptValue type = object.property("type"); + QString typeStr = type.isString() ? type.toString() : "Move"; + if (typeStr == "Press") { + event._type = Press; + } else if (typeStr == "Release") { + event._type = Release; + } else { + event._type = Move; + } + + QScriptValue id = object.property("id"); + event._id = type.isNumber() ? (uint32_t)type.toNumber() : 0; + + glm::vec2 pos2D; + vec2FromScriptValue(object.property("pos2D"), event._pos2D); + + glm::vec3 pos3D; + vec3FromScriptValue(object.property("pos3D"), event._pos3D); + + glm::vec3 normal; + vec3FromScriptValue(object.property("normal"), event._normal); + + glm::vec3 direction; + vec3FromScriptValue(object.property("direction"), event._direction); + + QScriptValue button = object.property("button"); + QString buttonStr = type.isString() ? type.toString() : "NoButtons"; + if (buttonStr == "Primary") { + event._button = PrimaryButton; + } else if (buttonStr == "Secondary") { + event._button = SecondaryButton; + } else if (buttonStr == "Tertiary") { + event._button = TertiaryButton; + } else { + event._button = NoButtons; + } + + bool primary = object.property("isPrimary").toBool() || object.property("isLeftButton").toBool(); + bool secondary = object.property("isSecondary").toBool() || object.property("isRightButton").toBool(); + bool tertiary = object.property("isTertiary").toBool() || object.property("isMiddleButton").toBool(); + event._buttons = 0; + if (primary) { + event._buttons |= PrimaryButton; + } + if (secondary) { + event._buttons |= SecondaryButton; + } + if (tertiary) { + event._buttons |= TertiaryButton; + } + } } diff --git a/libraries/script-engine/src/PointerEvent.h b/libraries/shared/src/PointerEvent.h similarity index 100% rename from libraries/script-engine/src/PointerEvent.h rename to libraries/shared/src/PointerEvent.h diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 96482c509d..d4fb787cf1 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -136,6 +136,7 @@ var PICKS_PER_SECOND_PER_HAND = 60; var MSECS_PER_SEC = 1000.0; var GRABBABLE_PROPERTIES = [ "position", + "registrationPoint", "rotation", "gravity", "collidesWith", @@ -233,6 +234,18 @@ CONTROLLER_STATE_MACHINE[STATE_ENTITY_TOUCHING] = { updateMethod: "entityTouching" }; +function projectOntoEntityXYPlane(entityID, worldPos) { + var props = entityPropertiesCache.getProps(entityID); + var invRot = Quat.inverse(props.rotation); + var localPos = Vec3.multiplyQbyV(invRot, Vec3.subtract(worldPos, props.position)); + var invDimensions = { x: 1 / props.dimensions.x, + y: 1 / props.dimensions.y, + z: 1 / props.dimensions.z }; + var normalizedPos = Vec3.sum(Vec3.multiplyVbyV(localPos, invDimensions), props.registrationPoint); + return { x: normalizedPos.x * props.dimensions.x, + y: (1 - normalizedPos.y) * props.dimensions.y }; // flip y-axis +} + function handLaserIntersectWebEntity(entityID, hand) { var standardControllerValue = (hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; var pose = Controller.getPoseValue(standardControllerValue); @@ -253,6 +266,7 @@ function handLaserIntersectWebEntity(entityID, hand) { intersectionPoint = planePosition; } intersectionInfo.point = intersectionPoint; + intersectionInfo.normal = planeNormal; intersectionInfo.searchRay = { origin: rayStart, direction: rayDirection, @@ -1071,7 +1085,8 @@ function MyController(hand) { overlayID: intersection.overlayID, searchRay: pickRay, distance: Vec3.distance(pickRay.origin, intersection.intersection), - intersection: intersection.intersection + intersection: intersection.intersection, + normal: intersection.surfaceNormal }; } else { return result; @@ -1392,16 +1407,49 @@ function MyController(hand) { entity = rayPickInfo.entityID; name = entityPropertiesCache.getProps(entity).name; + var pointerEvent; + if (Entities.keyboardFocusEntity != entity) { Entities.keyboardFocusEntity = entity; + + pointerEvent = { + type: "Move", + id: this.hand + 1, // 0 is reserved for hardware mouse + pos2D: projectOntoEntityXYPlane(entity, rayPickInfo.intersection), + pos3D: rayPickInfo.intersection, + normal: rayPickInfo.normal, + direction: rayPickInfo.searchRay.direction, + button: "None", + isPrimaryButton: false, + isSecondaryButton: false, + isTertiaryButton: false + }; + + Entities.sendHoverEnterEntity(entity, pointerEvent); + // AJT: TODO: send hover leave entity at some point as well!!??! } // send mouse events for button highlights and tooltips. if (this.hand == mostRecentSearchingHand || (this.hand !== mostRecentSearchingHand && this.getOtherHandController().state !== STATE_SEARCHING && this.getOtherHandController().state !== STATE_ENTITY_TOUCHING)) { + // most recently searching hand has priority over other hand, for the purposes of button highlighting. - Entities.sendEntityMouseMoveEvent(entity, rayPickInfo.intersection); + pointerEvent = { + type: "Move", + id: this.hand + 1, // 0 is reserved for hardware mouse + pos2D: projectOntoEntityXYPlane(entity, rayPickInfo.intersection), + pos3D: rayPickInfo.intersection, + normal: rayPickInfo.normal, + direction: rayPickInfo.searchRay.direction, + button: "None", + isPrimaryButton: false, + isSecondaryButton: false, + isTertiaryButton: false + }; + + Entities.sendMouseMoveOnEntity(entity, pointerEvent); + Entities.sendHoverOverEntity(entity, pointerEvent); } if (this.triggerSmoothedGrab() && !isEditing()) { @@ -2054,13 +2102,43 @@ function MyController(hand) { this.entityTouchingEnter = function() { // test for intersection between controller laser and web entity plane. var intersectInfo = handLaserIntersectWebEntity(this.grabbedEntity, this.hand); - Entities.sendEntityTouchBeginEvent(this.grabbedEntity, this.hand, intersectInfo.point); + + var pointerEvent = { + type: "Press", + id: this.hand + 1, // 0 is reserved for hardware mouse + pos2D: projectOntoEntityXYPlane(this.grabbedEntity, intersectInfo.point), + pos3D: intersectInfo.point, + normal: intersectInfo.normal, + direction: intersectInfo.searchRay.direction, + button: "Primary", + isPrimaryButton: true, + isSecondaryButton: false, + isTertiaryButton: false + }; + + Entities.sendMousePressOnEntity(this.grabbedEntity, pointerEvent); + Entities.sendClickDownOnEntity(this.grabbedEntity, pointerEvent); }; this.entityTouchingExit = function() { // test for intersection between controller laser and web entity plane. var intersectInfo = handLaserIntersectWebEntity(this.grabbedEntity, this.hand); - Entities.sendEntityTouchEndEvent(this.grabbedEntity, this.hand, intersectInfo.point); + + var pointerEvent = { + type: "Release", + id: this.hand + 1, // 0 is reserved for hardware mouse + pos2D: projectOntoEntityXYPlane(this.grabbedEntity, intersectInfo.point), + pos3D: intersectInfo.point, + normal: intersectInfo.normal, + direction: intersectInfo.searchRay.direction, + button: "Primary", + isPrimaryButton: true, + isSecondaryButton: false, + isTertiaryButton: false + }; + + Entities.sendMouseReleaseOnEntity(this.grabbedEntity, pointerEvent); + Entities.sendClickReleaseOnEntity(this.grabbedEntity, pointerEvent); }; this.entityTouching = function() { @@ -2078,7 +2156,21 @@ function MyController(hand) { Entities.keyboardFocusEntity = this.grabbedEntity; } - Entities.sendEntityTouchUpdateEvent(this.grabbedEntity, this.hand, intersectInfo.point); + var pointerEvent = { + type: "Move", + id: this.hand + 1, // 0 is reserved for hardware mouse + pos2D: projectOntoEntityXYPlane(this.grabbedEntity, intersectInfo.point), + pos3D: intersectInfo.point, + normal: intersectInfo.normal, + direction: intersectInfo.searchRay.direction, + button: "NoButtons", + isPrimaryButton: true, + isSecondaryButton: false, + isTertiaryButton: false + }; + + Entities.sendMouseMoveOnEntity(this.grabbedEntity, pointerEvent); + Entities.sendHoldingClickOnEntity(this.grabbedEntity, pointerEvent); this.intersectionDistance = intersectInfo.distance; this.searchIndicatorOn(intersectInfo.searchRay); From a595a72d0afd432e31e76b9f22fe52b484b1aa79 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 16 Aug 2016 15:39:02 -0700 Subject: [PATCH 10/20] handControllerGrab now calls Entities.sendHoverLeaveEntity() --- scripts/system/controllers/handControllerGrab.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index d4fb787cf1..f9568df74a 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1425,8 +1425,8 @@ function MyController(hand) { isTertiaryButton: false }; + this.hoverEntity = entity; Entities.sendHoverEnterEntity(entity, pointerEvent); - // AJT: TODO: send hover leave entity at some point as well!!??! } // send mouse events for button highlights and tooltips. @@ -1457,6 +1457,13 @@ function MyController(hand) { this.setState(STATE_ENTITY_TOUCHING, "begin touching entity '" + name + "'"); return; } + } else if (this.hoverEntity) { + var pinterEvent = { + type: "Move", + id: this.hand + 1 + }; + Entities.sendHoverLeaveEntity(this.hoverEntity, pointerEvent); + this.hoverEntity = null; } if (rayPickInfo.entityID) { @@ -2139,6 +2146,8 @@ function MyController(hand) { Entities.sendMouseReleaseOnEntity(this.grabbedEntity, pointerEvent); Entities.sendClickReleaseOnEntity(this.grabbedEntity, pointerEvent); + Entities.sendHoverLeaveEntity(this.grabbedEntity, pointerEvent); + this.focusedEntity = null; }; this.entityTouching = function() { From 36d87ddd7cf92000f464adc9fefcae6f803815f7 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 16 Aug 2016 16:23:45 -0700 Subject: [PATCH 11/20] Added EntityItem::wantsHandControllerPointerEvents method --- interface/src/Application.cpp | 2 +- libraries/entities/src/EntityItem.h | 2 ++ .../entities/src/EntityScriptingInterface.cpp | 13 +++++++++++++ libraries/entities/src/EntityScriptingInterface.h | 2 ++ libraries/entities/src/WebEntityItem.cpp | 6 +++--- libraries/entities/src/WebEntityItem.h | 14 ++++++++------ scripts/system/controllers/handControllerGrab.js | 10 +++++----- 7 files changed, 34 insertions(+), 15 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f8579a04e3..1c8afc2f31 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2629,7 +2629,7 @@ void Application::mouseMoveEvent(QMouseEvent* event) { event->screenPos(), button, buttons, event->modifiers()); - if (!compositor.getReticleOverDesktop() || getOverlays().getOverlayAtPoint(glm::vec2(transformedPos.x(), transformedPos.y()))) { + if (!isHMDMode() || !compositor.getReticleOverDesktop() || getOverlays().getOverlayAtPoint(glm::vec2(transformedPos.x(), transformedPos.y()))) { getEntities()->mouseMoveEvent(&mappedEvent); } _controllerScriptingInterface->emitMouseMoveEvent(&mappedEvent); // send events to any registered scripts diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index f12075d191..adfde55a07 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -438,6 +438,8 @@ public: virtual bool isTransparent() { return _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f : false; } + virtual bool wantsHandControllerPointerEvents() const { return false; } + protected: void setSimulated(bool simulated) { _simulated = simulated; } diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 8d3b8877a4..78f2698ef6 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -1230,6 +1230,19 @@ void EntityScriptingInterface::sendHoverLeaveEntity(QUuid id, PointerEvent event QMetaObject::invokeMethod(qApp, "sendHoverLeaveEntity", Qt::QueuedConnection, Q_ARG(QUuid, id), Q_ARG(PointerEvent, event)); } +bool EntityScriptingInterface::wantsHandControllerPointerEvents(QUuid id) { + bool result = false; + if (_entityTree) { + _entityTree->withReadLock([&] { + EntityItemPointer entity = _entityTree->findEntityByEntityItemID(EntityItemID(id)); + if (entity) { + result = entity->wantsHandControllerPointerEvents(); + } + }); + } + return result; +} + float EntityScriptingInterface::calculateCost(float mass, float oldVelocity, float newVelocity) { return std::abs(mass * (newVelocity - oldVelocity)); } diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 8cf35d2aab..1371feaa77 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -193,6 +193,8 @@ public slots: Q_INVOKABLE void sendHoverOverEntity(QUuid id, PointerEvent event); Q_INVOKABLE void sendHoverLeaveEntity(QUuid id, PointerEvent event); + Q_INVOKABLE bool wantsHandControllerPointerEvents(QUuid id); + signals: void collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision); diff --git a/libraries/entities/src/WebEntityItem.cpp b/libraries/entities/src/WebEntityItem.cpp index 4f074b8ac3..b31e47608f 100644 --- a/libraries/entities/src/WebEntityItem.cpp +++ b/libraries/entities/src/WebEntityItem.cpp @@ -61,7 +61,7 @@ bool WebEntityItem::setProperties(const EntityItemProperties& properties) { } setLastEdited(properties._lastEdited); } - + return somethingChanged; } @@ -91,8 +91,8 @@ void WebEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitst EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { + int& propertyCount, + OctreeElement::AppendState& appendState) const { bool successPropertyFits = true; APPEND_ENTITY_PROPERTY(PROP_SOURCE_URL, _sourceUrl); diff --git a/libraries/entities/src/WebEntityItem.h b/libraries/entities/src/WebEntityItem.h index 8e9d924cde..f985e4aa79 100644 --- a/libraries/entities/src/WebEntityItem.h +++ b/libraries/entities/src/WebEntityItem.h @@ -9,7 +9,7 @@ #ifndef hifi_WebEntityItem_h #define hifi_WebEntityItem_h -#include "EntityItem.h" +#include "EntityItem.h" class WebEntityItem : public EntityItem { public: @@ -18,13 +18,13 @@ public: static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); WebEntityItem(const EntityItemID& entityItemID); - + ALLOW_INSTANTIATION // This class can be instantiated /// set dimensions in domain scale units (0.0 - 1.0) this will also reset radius appropriately virtual void setDimensions(const glm::vec3& value); virtual ShapeType getShapeType() const { return SHAPE_TYPE_BOX; } - + // methods for getting/setting all properties of an entity virtual EntityItemProperties getProperties(EntityPropertyFlags desiredProperties = EntityPropertyFlags()) const; virtual bool setProperties(const EntityItemProperties& properties); @@ -32,15 +32,15 @@ public: // TODO: eventually only include properties changed since the params.lastViewFrustumSent time virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const; - virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, + int& propertyCount, OctreeElement::AppendState& appendState) const; - virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged); @@ -54,6 +54,8 @@ public: virtual void setSourceUrl(const QString& value); const QString& getSourceUrl() const; + bool wantsHandControllerPointerEvents() const override { return true; } + protected: QString _sourceUrl; }; diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index f9568df74a..9a73905373 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -246,7 +246,7 @@ function projectOntoEntityXYPlane(entityID, worldPos) { y: (1 - normalizedPos.y) * props.dimensions.y }; // flip y-axis } -function handLaserIntersectWebEntity(entityID, hand) { +function handLaserIntersectEntity(entityID, hand) { var standardControllerValue = (hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; var pose = Controller.getPoseValue(standardControllerValue); var worldHandPosition = Vec3.sum(Vec3.multiplyQbyV(MyAvatar.orientation, pose.translation), MyAvatar.position); @@ -1403,7 +1403,7 @@ function MyController(hand) { } } - if (rayPickInfo.entityID && entityPropertiesCache.getProps(rayPickInfo.entityID).type === "Web") { + if (rayPickInfo.entityID && Entities.wantsHandControllerPointerEvents(rayPickInfo.entityID)) { entity = rayPickInfo.entityID; name = entityPropertiesCache.getProps(entity).name; @@ -2108,7 +2108,7 @@ function MyController(hand) { this.entityTouchingEnter = function() { // test for intersection between controller laser and web entity plane. - var intersectInfo = handLaserIntersectWebEntity(this.grabbedEntity, this.hand); + var intersectInfo = handLaserIntersectEntity(this.grabbedEntity, this.hand); var pointerEvent = { type: "Press", @@ -2129,7 +2129,7 @@ function MyController(hand) { this.entityTouchingExit = function() { // test for intersection between controller laser and web entity plane. - var intersectInfo = handLaserIntersectWebEntity(this.grabbedEntity, this.hand); + var intersectInfo = handLaserIntersectEntity(this.grabbedEntity, this.hand); var pointerEvent = { type: "Release", @@ -2159,7 +2159,7 @@ function MyController(hand) { } // test for intersection between controller laser and web entity plane. - var intersectInfo = handLaserIntersectWebEntity(this.grabbedEntity, this.hand); + var intersectInfo = handLaserIntersectEntity(this.grabbedEntity, this.hand); if (Entities.keyboardFocusEntity != this.grabbedEntity) { Entities.keyboardFocusEntity = this.grabbedEntity; From 03322a9c4964e9289152ceaea71cc9f3a4565d7c Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 16 Aug 2016 17:09:59 -0700 Subject: [PATCH 12/20] Fix for using hardware mouse in HMD mode --- interface/src/Application.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 67f9b9865d..070be759f3 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2612,7 +2612,8 @@ void Application::mouseMoveEvent(QMouseEvent* event) { event->screenPos(), button, buttons, event->modifiers()); - if (!isHMDMode() || !compositor.getReticleOverDesktop() || getOverlays().getOverlayAtPoint(glm::vec2(transformedPos.x(), transformedPos.y()))) { + if (compositor.getReticleVisible() || !isHMDMode() || !compositor.getReticleOverDesktop() || + getOverlays().getOverlayAtPoint(glm::vec2(transformedPos.x(), transformedPos.y()))) { getEntities()->mouseMoveEvent(&mappedEvent); } _controllerScriptingInterface->emitMouseMoveEvent(&mappedEvent); // send events to any registered scripts From d4e1f535816a8c57eb4e931de0a571fe9a796931 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 16 Aug 2016 17:48:28 -0700 Subject: [PATCH 13/20] Fix for inconsistent override warning in WebEntityItem.h --- libraries/entities/src/WebEntityItem.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities/src/WebEntityItem.h b/libraries/entities/src/WebEntityItem.h index f985e4aa79..b63edc99ea 100644 --- a/libraries/entities/src/WebEntityItem.h +++ b/libraries/entities/src/WebEntityItem.h @@ -54,7 +54,7 @@ public: virtual void setSourceUrl(const QString& value); const QString& getSourceUrl() const; - bool wantsHandControllerPointerEvents() const override { return true; } + virtual bool wantsHandControllerPointerEvents() const { return true; } protected: QString _sourceUrl; From 4db1687746801a9e427376a92b036f80282e9b95 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 18 Aug 2016 18:37:52 -0700 Subject: [PATCH 14/20] bug fix for when web-entity is deleted while being scrolled --- .../system/controllers/handControllerGrab.js | 155 ++++++++++-------- 1 file changed, 84 insertions(+), 71 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index bb94285af9..cef2c4fdda 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -253,26 +253,32 @@ function handLaserIntersectEntity(entityID, hand) { var worldHandRotation = Quat.multiply(MyAvatar.orientation, pose.rotation); var props = entityPropertiesCache.getProps(entityID); - var planePosition = props.position; - var planeNormal = Vec3.multiplyQbyV(props.rotation, {x: 0, y: 0, z: 1.0}); - var rayStart = worldHandPosition; - var rayDirection = Quat.getUp(worldHandRotation); - var intersectionInfo = rayIntersectPlane(planePosition, planeNormal, rayStart, rayDirection); - var intersectionPoint = planePosition; - if (intersectionInfo.hit && intersectionInfo.distance > 0) { - intersectionPoint = Vec3.sum(rayStart, Vec3.multiply(intersectionInfo.distance, rayDirection)); + if (props.position) { + var planePosition = props.position; + var planeNormal = Vec3.multiplyQbyV(props.rotation, {x: 0, y: 0, z: 1.0}); + var rayStart = worldHandPosition; + var rayDirection = Quat.getUp(worldHandRotation); + var intersectionInfo = rayIntersectPlane(planePosition, planeNormal, rayStart, rayDirection); + + var intersectionPoint = planePosition; + if (intersectionInfo.hit && intersectionInfo.distance > 0) { + intersectionPoint = Vec3.sum(rayStart, Vec3.multiply(intersectionInfo.distance, rayDirection)); + } else { + intersectionPoint = planePosition; + } + intersectionInfo.point = intersectionPoint; + intersectionInfo.normal = planeNormal; + intersectionInfo.searchRay = { + origin: rayStart, + direction: rayDirection, + length: PICK_MAX_DISTANCE + }; + return intersectionInfo; } else { - intersectionPoint = planePosition; + // entity has been destroyed? or is no longer in cache + return null; } - intersectionInfo.point = intersectionPoint; - intersectionInfo.normal = planeNormal; - intersectionInfo.searchRay = { - origin: rayStart, - direction: rayDirection, - length: PICK_MAX_DISTANCE - }; - return intersectionInfo; } function rayIntersectPlane(planePosition, planeNormal, rayStart, rayDirection) { @@ -2109,45 +2115,47 @@ function MyController(hand) { this.entityTouchingEnter = function() { // test for intersection between controller laser and web entity plane. var intersectInfo = handLaserIntersectEntity(this.grabbedEntity, this.hand); + if (intersectInfo) { + var pointerEvent = { + type: "Press", + id: this.hand + 1, // 0 is reserved for hardware mouse + pos2D: projectOntoEntityXYPlane(this.grabbedEntity, intersectInfo.point), + pos3D: intersectInfo.point, + normal: intersectInfo.normal, + direction: intersectInfo.searchRay.direction, + button: "Primary", + isPrimaryButton: true, + isSecondaryButton: false, + isTertiaryButton: false + }; - var pointerEvent = { - type: "Press", - id: this.hand + 1, // 0 is reserved for hardware mouse - pos2D: projectOntoEntityXYPlane(this.grabbedEntity, intersectInfo.point), - pos3D: intersectInfo.point, - normal: intersectInfo.normal, - direction: intersectInfo.searchRay.direction, - button: "Primary", - isPrimaryButton: true, - isSecondaryButton: false, - isTertiaryButton: false - }; - - Entities.sendMousePressOnEntity(this.grabbedEntity, pointerEvent); - Entities.sendClickDownOnEntity(this.grabbedEntity, pointerEvent); + Entities.sendMousePressOnEntity(this.grabbedEntity, pointerEvent); + Entities.sendClickDownOnEntity(this.grabbedEntity, pointerEvent); + } }; this.entityTouchingExit = function() { // test for intersection between controller laser and web entity plane. var intersectInfo = handLaserIntersectEntity(this.grabbedEntity, this.hand); + if (intersectInfo) { + var pointerEvent = { + type: "Release", + id: this.hand + 1, // 0 is reserved for hardware mouse + pos2D: projectOntoEntityXYPlane(this.grabbedEntity, intersectInfo.point), + pos3D: intersectInfo.point, + normal: intersectInfo.normal, + direction: intersectInfo.searchRay.direction, + button: "Primary", + isPrimaryButton: true, + isSecondaryButton: false, + isTertiaryButton: false + }; - var pointerEvent = { - type: "Release", - id: this.hand + 1, // 0 is reserved for hardware mouse - pos2D: projectOntoEntityXYPlane(this.grabbedEntity, intersectInfo.point), - pos3D: intersectInfo.point, - normal: intersectInfo.normal, - direction: intersectInfo.searchRay.direction, - button: "Primary", - isPrimaryButton: true, - isSecondaryButton: false, - isTertiaryButton: false - }; - - Entities.sendMouseReleaseOnEntity(this.grabbedEntity, pointerEvent); - Entities.sendClickReleaseOnEntity(this.grabbedEntity, pointerEvent); - Entities.sendHoverLeaveEntity(this.grabbedEntity, pointerEvent); - this.focusedEntity = null; + Entities.sendMouseReleaseOnEntity(this.grabbedEntity, pointerEvent); + Entities.sendClickReleaseOnEntity(this.grabbedEntity, pointerEvent); + Entities.sendHoverLeaveEntity(this.grabbedEntity, pointerEvent); + this.focusedEntity = null; + } }; this.entityTouching = function() { @@ -2160,30 +2168,35 @@ function MyController(hand) { // test for intersection between controller laser and web entity plane. var intersectInfo = handLaserIntersectEntity(this.grabbedEntity, this.hand); + if (intersectInfo) { - if (Entities.keyboardFocusEntity != this.grabbedEntity) { - Entities.keyboardFocusEntity = this.grabbedEntity; + if (Entities.keyboardFocusEntity != this.grabbedEntity) { + Entities.keyboardFocusEntity = this.grabbedEntity; + } + + var pointerEvent = { + type: "Move", + id: this.hand + 1, // 0 is reserved for hardware mouse + pos2D: projectOntoEntityXYPlane(this.grabbedEntity, intersectInfo.point), + pos3D: intersectInfo.point, + normal: intersectInfo.normal, + direction: intersectInfo.searchRay.direction, + button: "NoButtons", + isPrimaryButton: true, + isSecondaryButton: false, + isTertiaryButton: false + }; + + Entities.sendMouseMoveOnEntity(this.grabbedEntity, pointerEvent); + Entities.sendHoldingClickOnEntity(this.grabbedEntity, pointerEvent); + + this.intersectionDistance = intersectInfo.distance; + this.searchIndicatorOn(intersectInfo.searchRay); + Reticle.setVisible(false); + } else { + this.setState(STATE_OFF, "grabbed entity was destroyed"); + return; } - - var pointerEvent = { - type: "Move", - id: this.hand + 1, // 0 is reserved for hardware mouse - pos2D: projectOntoEntityXYPlane(this.grabbedEntity, intersectInfo.point), - pos3D: intersectInfo.point, - normal: intersectInfo.normal, - direction: intersectInfo.searchRay.direction, - button: "NoButtons", - isPrimaryButton: true, - isSecondaryButton: false, - isTertiaryButton: false - }; - - Entities.sendMouseMoveOnEntity(this.grabbedEntity, pointerEvent); - Entities.sendHoldingClickOnEntity(this.grabbedEntity, pointerEvent); - - this.intersectionDistance = intersectInfo.distance; - this.searchIndicatorOn(intersectInfo.searchRay); - Reticle.setVisible(false); }; this.release = function() { From ff95ef2492cef4661e254edf5813c3437e8e7a0b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 19 Aug 2016 08:53:33 -0700 Subject: [PATCH 15/20] ignoreIK flag on action-grabbable objects works again --- scripts/system/controllers/handControllerGrab.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 1264502aa7..470c227f7b 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1674,12 +1674,12 @@ function MyController(hand) { var handRotation = (this.hand === RIGHT_HAND) ? MyAvatar.getRightPalmRotation() : MyAvatar.getLeftPalmRotation(); var handPosition = this.getHandPosition(); + var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); + this.ignoreIK = grabbableData.ignoreIK ? grabbableData.ignoreIK : false; + var hasPresetPosition = false; if (this.state == STATE_HOLD && this.grabbedHotspot) { - var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA); // if an object is "equipped" and has a predefined offset, use it. - this.ignoreIK = grabbableData.ignoreIK ? grabbableData.ignoreIK : false; - var offsets = USE_ATTACH_POINT_SETTINGS && getAttachPointForHotspotFromSettings(this.grabbedHotspot, this.hand); if (offsets) { this.offsetPosition = offsets[0]; @@ -1694,8 +1694,6 @@ function MyController(hand) { } } } else { - this.ignoreIK = false; - var objectRotation = grabbedProperties.rotation; this.offsetRotation = Quat.multiply(Quat.inverse(handRotation), objectRotation); From 8bb6bb7c6f260d8fc6ae3d4be22cbee28df29810 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 19 Aug 2016 10:56:52 -0700 Subject: [PATCH 16/20] generalized keyboard focus interface Removed dynamic casts to RenderableWebEntityItem, instead virtual methods were added to EntityItem to facilitate this. * bool wantsKeyboardFocus() * void setProxyWindow() * QObject* getEventHandler() --- interface/src/Application.cpp | 23 ++++++++----------- .../src/RenderableWebEntityItem.h | 6 +++-- libraries/entities/src/EntityItem.h | 5 ++++ libraries/entities/src/WebEntityItem.h | 2 -- 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 070be759f3..ebae24b3b8 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2048,10 +2048,9 @@ bool Application::event(QEvent* event) { case QEvent::KeyRelease: { auto entityScriptingInterface = DependencyManager::get(); auto entity = entityScriptingInterface->getEntityTree()->findEntityByID(_keyboardFocusedItem.get()); - RenderableWebEntityItem* webEntity = dynamic_cast(entity.get()); - if (webEntity && webEntity->getEventHandler()) { + if (entity && entity->getEventHandler()) { event->setAccepted(false); - QCoreApplication::sendEvent(webEntity->getEventHandler(), event); + QCoreApplication::sendEvent(entity->getEventHandler(), event); if (event->isAccepted()) { _lastAcceptedKeyPress = usecTimestampNow(); return true; @@ -2931,10 +2930,9 @@ void Application::idle(float nsecsElapsed) { // update position of highlight overlay auto entityScriptingInterface = DependencyManager::get(); auto entity = entityScriptingInterface->getEntityTree()->findEntityByID(_keyboardFocusedItem.get()); - RenderableWebEntityItem* webEntity = dynamic_cast(entity.get()); - if (webEntity && _keyboardFocusHighlight) { - _keyboardFocusHighlight->setRotation(webEntity->getRotation()); - _keyboardFocusHighlight->setPosition(webEntity->getPosition()); + if (entity && _keyboardFocusHighlight) { + _keyboardFocusHighlight->setRotation(entity->getRotation()); + _keyboardFocusHighlight->setPosition(entity->getPosition()); } } } @@ -3540,9 +3538,8 @@ void Application::setKeyboardFocusEntity(EntityItemID entityItemID) { auto properties = entityScriptingInterface->getEntityProperties(entityItemID); if (EntityTypes::Web == properties.getType() && !properties.getLocked() && properties.getVisible()) { auto entity = entityScriptingInterface->getEntityTree()->findEntityByID(entityItemID); - RenderableWebEntityItem* webEntity = dynamic_cast(entity.get()); - if (webEntity) { - webEntity->setProxyWindow(_window->windowHandle()); + if (entity && entity->wantsKeyboardFocus()) { + entity->setProxyWindow(_window->windowHandle()); if (_keyboardMouseDevice->isActive()) { _keyboardMouseDevice->pluginFocusOutEvent(); } @@ -3560,9 +3557,9 @@ void Application::setKeyboardFocusEntity(EntityItemID entityItemID) { _keyboardFocusHighlight->setIgnoreRayIntersection(true); _keyboardFocusHighlight->setDrawInFront(false); } - _keyboardFocusHighlight->setRotation(webEntity->getRotation()); - _keyboardFocusHighlight->setPosition(webEntity->getPosition()); - _keyboardFocusHighlight->setDimensions(webEntity->getDimensions() * 1.05f); + _keyboardFocusHighlight->setRotation(entity->getRotation()); + _keyboardFocusHighlight->setPosition(entity->getPosition()); + _keyboardFocusHighlight->setDimensions(entity->getDimensions() * 1.05f); _keyboardFocusHighlight->setVisible(true); _keyboardFocusHighlightID = getOverlays().addOverlay(_keyboardFocusHighlight); } diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h index 578bd2f3b1..ee8a484109 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -32,8 +32,10 @@ public: virtual void render(RenderArgs* args) override; virtual void setSourceUrl(const QString& value) override; - void setProxyWindow(QWindow* proxyWindow); - QObject* getEventHandler(); + virtual bool wantsHandControllerPointerEvents() const override { return true; } + virtual bool wantsKeyboardFocus() const override { return true; } + virtual void setProxyWindow(QWindow* proxyWindow) override; + virtual QObject* getEventHandler() override; void handlePointerEvent(const PointerEvent& event); diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index e2bc9b568b..5427514573 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -17,6 +17,8 @@ #include +#include + #include // for Animation, AnimationCache, and AnimationPointer classes #include // for EncodeBitstreamParams class #include // for OctreeElement::AppendState @@ -435,6 +437,9 @@ public: virtual bool isTransparent() { return _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f : false; } virtual bool wantsHandControllerPointerEvents() const { return false; } + virtual bool wantsKeyboardFocus() const { return false; } + virtual void setProxyWindow(QWindow* proxyWindow) {} + virtual QObject* getEventHandler() { return nullptr; } protected: diff --git a/libraries/entities/src/WebEntityItem.h b/libraries/entities/src/WebEntityItem.h index b63edc99ea..538d0c2f45 100644 --- a/libraries/entities/src/WebEntityItem.h +++ b/libraries/entities/src/WebEntityItem.h @@ -54,8 +54,6 @@ public: virtual void setSourceUrl(const QString& value); const QString& getSourceUrl() const; - virtual bool wantsHandControllerPointerEvents() const { return true; } - protected: QString _sourceUrl; }; From ff4c194756874829c10c19900f122ce5bebca869 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 19 Aug 2016 15:31:57 -0700 Subject: [PATCH 17/20] eslint fix --- scripts/system/controllers/handControllerGrab.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index cef2c4fdda..9cbdebb93d 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1409,12 +1409,11 @@ function MyController(hand) { } } + var pointerEvent; if (rayPickInfo.entityID && Entities.wantsHandControllerPointerEvents(rayPickInfo.entityID)) { entity = rayPickInfo.entityID; name = entityPropertiesCache.getProps(entity).name; - var pointerEvent; - if (Entities.keyboardFocusEntity != entity) { Entities.keyboardFocusEntity = entity; @@ -1464,7 +1463,7 @@ function MyController(hand) { return; } } else if (this.hoverEntity) { - var pinterEvent = { + pointerEvent = { type: "Move", id: this.hand + 1 }; From d098a1ae28d9a55f222f9a245951a1bfb4ebbdda Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 22 Aug 2016 10:50:39 -0700 Subject: [PATCH 18/20] allow temporary domains to use an access token when present --- domain-server/src/DomainServer.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index d5f23f7c4e..cbf533bf64 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -118,8 +118,8 @@ DomainServer::DomainServer(int argc, char* argv[]) : setupNodeListAndAssignments(); - if (_type == MetaverseDomain) { - // if we have a metaverse domain, we'll need an access token to heartbeat handle auto-networking + if (_type != NonMetaverse) { + // if we have a metaverse domain, we'll use an access token for API calls resetAccountManagerAccessToken(); } @@ -469,8 +469,8 @@ bool DomainServer::resetAccountManagerAccessToken() { if (accessTokenVariant && accessTokenVariant->canConvert(QMetaType::QString)) { accessToken = accessTokenVariant->toString(); } else { - qDebug() << "A domain-server feature that requires authentication is enabled but no access token is present."; - qDebug() << "Set an access token via the web interface, in your user or master config" + qWarning() << "No access token is present. Some operations that use the metaverse API will fail."; + qDebug() << "Set an access token via the web interface, in your user config" << "at keypath metaverse.access_token or in your ENV at key DOMAIN_SERVER_ACCESS_TOKEN"; // clear any existing access token from AccountManager @@ -480,7 +480,7 @@ bool DomainServer::resetAccountManagerAccessToken() { } } else { qDebug() << "Using access token from DOMAIN_SERVER_ACCESS_TOKEN in env. This overrides any access token present" - << " in the user or master config."; + << " in the user config."; } // give this access token to the AccountManager From b6a490406845d4473bdcc1b8400cfd472da59f30 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 22 Aug 2016 10:56:35 -0700 Subject: [PATCH 19/20] remove explicit check for WebEntity from setKeyboardFocus Also, don't get entityTree from entityScriptInterface instead go thru EntityTreeRenderer --- interface/src/Application.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ebae24b3b8..29bff8e341 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2047,7 +2047,7 @@ bool Application::event(QEvent* event) { case QEvent::KeyPress: case QEvent::KeyRelease: { auto entityScriptingInterface = DependencyManager::get(); - auto entity = entityScriptingInterface->getEntityTree()->findEntityByID(_keyboardFocusedItem.get()); + auto entity = getEntities()->getTree()->findEntityByID(_keyboardFocusedItem.get()); if (entity && entity->getEventHandler()) { event->setAccepted(false); QCoreApplication::sendEvent(entity->getEventHandler(), event); @@ -2929,7 +2929,7 @@ void Application::idle(float nsecsElapsed) { } else { // update position of highlight overlay auto entityScriptingInterface = DependencyManager::get(); - auto entity = entityScriptingInterface->getEntityTree()->findEntityByID(_keyboardFocusedItem.get()); + auto entity = getEntities()->getTree()->findEntityByID(_keyboardFocusedItem.get()); if (entity && _keyboardFocusHighlight) { _keyboardFocusHighlight->setRotation(entity->getRotation()); _keyboardFocusHighlight->setPosition(entity->getPosition()); @@ -3536,8 +3536,8 @@ void Application::setKeyboardFocusEntity(EntityItemID entityItemID) { if (_keyboardFocusedItem.get() != entityItemID) { _keyboardFocusedItem.set(UNKNOWN_ENTITY_ID); auto properties = entityScriptingInterface->getEntityProperties(entityItemID); - if (EntityTypes::Web == properties.getType() && !properties.getLocked() && properties.getVisible()) { - auto entity = entityScriptingInterface->getEntityTree()->findEntityByID(entityItemID); + if (!properties.getLocked() && properties.getVisible()) { + auto entity = getEntities()->getTree()->findEntityByID(entityItemID); if (entity && entity->wantsKeyboardFocus()) { entity->setProxyWindow(_window->windowHandle()); if (_keyboardMouseDevice->isActive()) { From cbb115bbcc0aa286ed2ba93e7b50235bf1cb937f Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Mon, 22 Aug 2016 16:34:23 -0700 Subject: [PATCH 20/20] fix typo argee->agree --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 836b48b3fb..34c3494371 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4941,7 +4941,7 @@ bool Application::askToSetAvatarUrl(const QString& url) { modelLicense = simpleWordWrap(modelLicense, MAX_CHARACTERS_PER_LINE); agreeToLicence = QMessageBox::Yes == OffscreenUi::question("Avatar Usage License", - modelLicense + "\nDo you argee to these terms?", + modelLicense + "\nDo you agree to these terms?", QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); }