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