Merge pull request #11699 from SamGondelman/ui

Pointers generate PointerEvents (WIP)
This commit is contained in:
Bradley Austin Davis 2017-10-31 10:01:15 -07:00 committed by GitHub
commit 55d819d7e3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 625 additions and 206 deletions

View file

@ -623,6 +623,12 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
DependencyManager::registerInheritance<SpatialParentFinder, InterfaceParentFinder>();
// Set dependencies
DependencyManager::set<PickManager>();
DependencyManager::set<PointerManager>();
DependencyManager::set<LaserPointerScriptingInterface>();
DependencyManager::set<RayPickScriptingInterface>();
DependencyManager::set<PointerScriptingInterface>();
DependencyManager::set<PickScriptingInterface>();
DependencyManager::set<Cursor::Manager>();
DependencyManager::set<AccountManager>(std::bind(&Application::getUserAgent, qApp));
DependencyManager::set<StatTracker>();
@ -703,13 +709,6 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
DependencyManager::set<FadeEffect>();
DependencyManager::set<PickManager>();
DependencyManager::set<PointerManager>();
DependencyManager::set<LaserPointerScriptingInterface>();
DependencyManager::set<RayPickScriptingInterface>();
DependencyManager::set<PointerScriptingInterface>();
DependencyManager::set<PickScriptingInterface>();
return previousSessionCrashed;
}
@ -1474,13 +1473,15 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
// 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<EntityScriptingInterface>();
connect(entityScriptingInterface.data(), &EntityScriptingInterface::clickDownOnEntity,
connect(entityScriptingInterface.data(), &EntityScriptingInterface::mousePressOnEntity,
[this](const EntityItemID& entityItemID, const PointerEvent& event) {
if (getEntities()->wantsKeyboardFocus(entityItemID)) {
setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID);
setKeyboardFocusEntity(entityItemID);
} else {
setKeyboardFocusEntity(UNKNOWN_ENTITY_ID);
if (event.shouldFocus()) {
if (getEntities()->wantsKeyboardFocus(entityItemID)) {
setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID);
setKeyboardFocusEntity(entityItemID);
} else {
setKeyboardFocusEntity(UNKNOWN_ENTITY_ID);
}
}
});
@ -5899,6 +5900,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
qScriptRegisterMetaType(scriptEngine.data(), OverlayIDtoScriptValue, OverlayIDfromScriptValue);
DependencyManager::get<PickScriptingInterface>()->registerMetaTypes(scriptEngine.data());
// connect this script engines printedMessage signal to the global ScriptEngines these various messages
connect(scriptEngine.data(), &ScriptEngine::printedMessage,
DependencyManager::get<ScriptEngines>().data(), &ScriptEngines::onPrintedMessage);

View file

@ -17,10 +17,10 @@
#include <pointers/PickManager.h>
#include "PickScriptingInterface.h"
LaserPointer::LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates,
const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool enabled) :
Pointer(DependencyManager::get<PickScriptingInterface>()->createRayPick(rayProps)),
_renderingEnabled(enabled),
LaserPointer::LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, bool hover,
const PointerTriggers& triggers, bool faceAvatar, bool centerEndY, bool lockEnd, bool distanceScaleEnd, bool enabled) :
Pointer(DependencyManager::get<PickScriptingInterface>()->createRayPick(rayProps), enabled, hover),
_triggers(triggers),
_renderStates(renderStates),
_defaultRenderStates(defaultRenderStates),
_faceAvatar(faceAvatar),
@ -49,28 +49,6 @@ LaserPointer::~LaserPointer() {
}
}
void LaserPointer::enable() {
Pointer::enable();
withWriteLock([&] {
_renderingEnabled = true;
});
}
void LaserPointer::disable() {
Pointer::disable();
withWriteLock([&] {
_renderingEnabled = false;
if (!_currentRenderState.empty()) {
if (_renderStates.find(_currentRenderState) != _renderStates.end()) {
disableRenderState(_renderStates[_currentRenderState]);
}
if (_defaultRenderStates.find(_currentRenderState) != _defaultRenderStates.end()) {
disableRenderState(_defaultRenderStates[_currentRenderState].second);
}
}
});
}
void LaserPointer::setRenderState(const std::string& state) {
withWriteLock([&] {
if (!_currentRenderState.empty() && state != _currentRenderState) {
@ -105,7 +83,7 @@ void LaserPointer::updateRenderStateOverlay(const OverlayID& id, const QVariant&
}
}
void LaserPointer::updateRenderState(const RenderState& renderState, const IntersectionType type, const float distance, const QUuid& objectID, const PickRay& pickRay, const bool defaultState) {
void LaserPointer::updateRenderState(const RenderState& renderState, const IntersectionType type, float distance, const QUuid& objectID, const PickRay& pickRay, bool defaultState) {
if (!renderState.getStartID().isNull()) {
QVariantMap startProps;
startProps.insert("position", vec3toVariant(pickRay.origin));
@ -199,35 +177,46 @@ void LaserPointer::disableRenderState(const RenderState& renderState) {
}
}
void LaserPointer::update() {
// This only needs to be a read lock because update won't change any of the properties that can be modified from scripts
withReadLock([&] {
QVariantMap prevRayPickResult = DependencyManager::get<PickManager>()->getPrevPickResult(_pickUID);
IntersectionType type = IntersectionType(prevRayPickResult["type"].toInt());
PickRay pickRay = PickRay(prevRayPickResult["searchRay"].toMap());
QUuid uid = prevRayPickResult["objectID"].toUuid();
if (_renderingEnabled && !_currentRenderState.empty() && _renderStates.find(_currentRenderState) != _renderStates.end() &&
(type != IntersectionType::NONE || _laserLength > 0.0f || !_objectLockEnd.first.isNull())) {
float distance = _laserLength > 0.0f ? _laserLength : prevRayPickResult["distance"].toFloat();
updateRenderState(_renderStates[_currentRenderState], type, distance, uid, pickRay, false);
disableRenderState(_defaultRenderStates[_currentRenderState].second);
} else if (_renderingEnabled && !_currentRenderState.empty() && _defaultRenderStates.find(_currentRenderState) != _defaultRenderStates.end()) {
disableRenderState(_renderStates[_currentRenderState]);
updateRenderState(_defaultRenderStates[_currentRenderState].second, IntersectionType::NONE, _defaultRenderStates[_currentRenderState].first, QUuid(), pickRay, true);
} else if (!_currentRenderState.empty()) {
disableRenderState(_renderStates[_currentRenderState]);
disableRenderState(_defaultRenderStates[_currentRenderState].second);
}
});
void LaserPointer::updateVisuals(const QVariantMap& prevRayPickResult) {
IntersectionType type = IntersectionType(prevRayPickResult["type"].toInt());
PickRay pickRay = PickRay(prevRayPickResult["searchRay"].toMap());
QUuid uid = prevRayPickResult["objectID"].toUuid();
if (_enabled && !_currentRenderState.empty() && _renderStates.find(_currentRenderState) != _renderStates.end() &&
(type != IntersectionType::NONE || _laserLength > 0.0f || !_objectLockEnd.first.isNull())) {
float distance = _laserLength > 0.0f ? _laserLength : prevRayPickResult["distance"].toFloat();
updateRenderState(_renderStates[_currentRenderState], type, distance, uid, pickRay, false);
disableRenderState(_defaultRenderStates[_currentRenderState].second);
} else if (_enabled && !_currentRenderState.empty() && _defaultRenderStates.find(_currentRenderState) != _defaultRenderStates.end()) {
disableRenderState(_renderStates[_currentRenderState]);
updateRenderState(_defaultRenderStates[_currentRenderState].second, IntersectionType::NONE, _defaultRenderStates[_currentRenderState].first, QUuid(), pickRay, true);
} else if (!_currentRenderState.empty()) {
disableRenderState(_renderStates[_currentRenderState]);
disableRenderState(_defaultRenderStates[_currentRenderState].second);
}
}
void LaserPointer::setLength(const float length) {
Pointer::PickedObject LaserPointer::getHoveredObject(const QVariantMap& pickResult) {
return Pointer::PickedObject(pickResult["objectID"].toUuid(), IntersectionType(pickResult["type"].toUInt()));
}
Pointer::Buttons LaserPointer::getPressedButtons() {
std::unordered_set<std::string> toReturn;
for (const PointerTrigger& trigger : _triggers) {
// TODO: right now, LaserPointers don't support axes, only on/off buttons
if (trigger.getEndpoint()->peek() >= 1.0f) {
toReturn.insert(trigger.getButton());
}
}
return toReturn;
}
void LaserPointer::setLength(float length) {
withWriteLock([&] {
_laserLength = length;
});
}
void LaserPointer::setLockEndUUID(QUuid objectID, const bool isOverlay) {
void LaserPointer::setLockEndUUID(const QUuid& objectID, bool isOverlay) {
withWriteLock([&] {
_objectLockEnd = std::pair<QUuid, bool>(objectID, isOverlay);
});
@ -290,4 +279,80 @@ RenderState LaserPointer::buildRenderState(const QVariantMap& propMap) {
}
return RenderState(startID, pathID, endID);
}
PointerEvent LaserPointer::buildPointerEvent(const PickedObject& target, const QVariantMap& pickResult) const {
uint32_t id = 0;
glm::vec3 intersection = vec3FromVariant(pickResult["intersection"]);
glm::vec3 surfaceNormal = vec3FromVariant(pickResult["surfaceNormal"]);
QVariantMap searchRay = pickResult["searchRay"].toMap();
glm::vec3 direction = vec3FromVariant(searchRay["direction"]);
QUuid pickedID = pickResult["objectID"].toUuid();
glm::vec2 pos2D;
if (pickedID != target.objectID) {
glm::vec3 origin = vec3FromVariant(searchRay["origin"]);
if (target.type == ENTITY) {
intersection = intersectRayWithEntityXYPlane(target.objectID, origin, direction);
} else if (target.type == OVERLAY) {
intersection = intersectRayWithOverlayXYPlane(target.objectID, origin, direction);
}
}
if (target.type == ENTITY) {
pos2D = projectOntoEntityXYPlane(target.objectID, intersection);
} else if (target.type == OVERLAY) {
pos2D = projectOntoOverlayXYPlane(target.objectID, intersection);
}
return PointerEvent(PointerEvent::Move, id, pos2D, intersection, surfaceNormal, direction, PointerEvent::NoButtons);
}
glm::vec3 LaserPointer::intersectRayWithXYPlane(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& point, const glm::quat rotation, const glm::vec3& registration) const {
glm::vec3 n = rotation * Vectors::FRONT;
float t = glm::dot(n, point - origin) / glm::dot(n, direction);
return origin + t * direction;
}
glm::vec3 LaserPointer::intersectRayWithOverlayXYPlane(const QUuid& overlayID, const glm::vec3& origin, const glm::vec3& direction) const {
glm::vec3 position = vec3FromVariant(qApp->getOverlays().getProperty(overlayID, "position").value);
glm::quat rotation = quatFromVariant(qApp->getOverlays().getProperty(overlayID, "rotation").value);
const glm::vec3 DEFAULT_REGISTRATION_POINT = glm::vec3(0.5f);
return intersectRayWithXYPlane(origin, direction, position, rotation, DEFAULT_REGISTRATION_POINT);
}
glm::vec3 LaserPointer::intersectRayWithEntityXYPlane(const QUuid& entityID, const glm::vec3& origin, const glm::vec3& direction) const {
auto props = DependencyManager::get<EntityScriptingInterface>()->getEntityProperties(entityID);
return intersectRayWithXYPlane(origin, direction, props.getPosition(), props.getRotation(), props.getRegistrationPoint());
}
glm::vec2 LaserPointer::projectOntoXYPlane(const glm::vec3& worldPos, const glm::vec3& position, const glm::quat& rotation, const glm::vec3& dimensions, const glm::vec3& registrationPoint) const {
glm::quat invRot = glm::inverse(rotation);
glm::vec3 localPos = invRot * (worldPos - position);
glm::vec3 invDimensions = glm::vec3(1.0f / dimensions.x, 1.0f / dimensions.y, 1.0f / dimensions.z);
glm::vec3 normalizedPos = (localPos * invDimensions) + registrationPoint;
return glm::vec2(normalizedPos.x * dimensions.x, (1.0f - normalizedPos.y) * dimensions.y);
}
glm::vec2 LaserPointer::projectOntoOverlayXYPlane(const QUuid& overlayID, const glm::vec3& worldPos) const {
glm::vec3 position = vec3FromVariant(qApp->getOverlays().getProperty(overlayID, "position").value);
glm::quat rotation = quatFromVariant(qApp->getOverlays().getProperty(overlayID, "rotation").value);
glm::vec3 dimensions;
float dpi = qApp->getOverlays().getProperty(overlayID, "dpi").value.toFloat();
if (dpi > 0) {
// Calculate physical dimensions for web3d overlay from resolution and dpi; "dimensions" property is used as a scale.
glm::vec3 resolution = glm::vec3(vec2FromVariant(qApp->getOverlays().getProperty(overlayID, "resolution").value), 1);
glm::vec3 scale = glm::vec3(vec2FromVariant(qApp->getOverlays().getProperty(overlayID, "dimensions").value), 0.01f);
const float INCHES_TO_METERS = 1.0f / 39.3701f;
dimensions = (resolution * INCHES_TO_METERS / dpi) * scale;
} else {
dimensions = glm::vec3(vec2FromVariant(qApp->getOverlays().getProperty(overlayID, "dimensions").value), 0.01);
}
const glm::vec3 DEFAULT_REGISTRATION_POINT = glm::vec3(0.5f);
return projectOntoXYPlane(worldPos, position, rotation, dimensions, DEFAULT_REGISTRATION_POINT);
}
glm::vec2 LaserPointer::projectOntoEntityXYPlane(const QUuid& entityID, const glm::vec3& worldPos) const {
auto props = DependencyManager::get<EntityScriptingInterface>()->getEntityProperties(entityID);
return projectOntoXYPlane(worldPos, props.getPosition(), props.getRotation(), props.getDimensions(), props.getRegistrationPoint());
}

View file

@ -49,31 +49,34 @@ private:
};
class LaserPointer : public Pointer {
using Parent = Pointer;
public:
typedef std::unordered_map<std::string, RenderState> RenderStateMap;
typedef std::unordered_map<std::string, std::pair<float, RenderState>> DefaultRenderStateMap;
LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates,
const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool enabled);
LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, bool hover, const PointerTriggers& triggers,
bool faceAvatar, bool centerEndY, bool lockEnd, bool distanceScaleEnd, bool enabled);
~LaserPointer();
void enable() override;
void disable() override;
void setRenderState(const std::string& state) override;
// You cannot use editRenderState to change the overlay type of any part of the laser pointer. You can only edit the properties of the existing overlays.
void editRenderState(const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) override;
void setLength(const float length) override;
void setLockEndUUID(QUuid objectID, const bool isOverlay) override;
void setLength(float length) override;
void setLockEndUUID(const QUuid& objectID, bool isOverlay) override;
void update() override;
void updateVisuals(const QVariantMap& prevRayPickResult) override;
PickedObject getHoveredObject(const QVariantMap& pickResult) override;
Pointer::Buttons getPressedButtons() override;
static RenderState buildRenderState(const QVariantMap& propMap);
protected:
PointerEvent buildPointerEvent(const PickedObject& target, const QVariantMap& pickResult) const override;
private:
bool _renderingEnabled;
PointerTriggers _triggers;
float _laserLength { 0.0f };
std::string _currentRenderState { "" };
RenderStateMap _renderStates;
@ -85,9 +88,17 @@ private:
std::pair<QUuid, bool> _objectLockEnd { std::pair<QUuid, bool>(QUuid(), false)};
void updateRenderStateOverlay(const OverlayID& id, const QVariant& props);
void updateRenderState(const RenderState& renderState, const IntersectionType type, const float distance, const QUuid& objectID, const PickRay& pickRay, const bool defaultState);
void updateRenderState(const RenderState& renderState, const IntersectionType type, float distance, const QUuid& objectID, const PickRay& pickRay, bool defaultState);
void disableRenderState(const RenderState& renderState);
glm::vec3 intersectRayWithEntityXYPlane(const QUuid& entityID, const glm::vec3& origin, const glm::vec3& direction) const;
glm::vec3 intersectRayWithOverlayXYPlane(const QUuid& overlayID, const glm::vec3& origin, const glm::vec3& direction) const;
glm::vec3 intersectRayWithXYPlane(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& point, const glm::quat rotation, const glm::vec3& registration) const;
glm::vec2 projectOntoEntityXYPlane(const QUuid& entityID, const glm::vec3& worldPos) const;
glm::vec2 projectOntoOverlayXYPlane(const QUuid& overlayID, const glm::vec3& worldPos) const;
glm::vec2 projectOntoXYPlane(const glm::vec3& worldPos, const glm::vec3& position, const glm::quat& rotation, const glm::vec3& dimensions, const glm::vec3& registrationPoint) const;
};
#endif // hifi_LaserPointer_h

View file

@ -17,6 +17,9 @@
#include "JointRayPick.h"
#include "MouseRayPick.h"
#include <pointers/Pick.h>
#include <ScriptEngine.h>
QUuid PickScriptingInterface::createPick(const PickQuery::PickType type, const QVariant& properties) {
switch (type) {
case PickQuery::PickType::Ray:
@ -105,3 +108,22 @@ void PickScriptingInterface::setIgnoreItems(const QUuid& uid, const QScriptValue
void PickScriptingInterface::setIncludeItems(const QUuid& uid, const QScriptValue& includeItems) {
DependencyManager::get<PickManager>()->setIncludeItems(uid, qVectorQUuidFromScriptValue(includeItems));
}
QScriptValue pickTypesToScriptValue(QScriptEngine* engine, const PickQuery::PickType& pickType) {
return pickType;
}
void pickTypesFromScriptValue(const QScriptValue& object, PickQuery::PickType& pickType) {
pickType = static_cast<PickQuery::PickType>(object.toUInt16());
}
void PickScriptingInterface::registerMetaTypes(QScriptEngine* engine) {
QScriptValue pickTypes = engine->newObject();
auto metaEnum = QMetaEnum::fromType<PickQuery::PickType>();
for (int i = 0; i < PickQuery::PickType::NUM_PICK_TYPES; ++i) {
pickTypes.setProperty(metaEnum.key(i), metaEnum.value(i));
}
engine->globalObject().setProperty("PickType", pickTypes);
qScriptRegisterMetaType(engine, pickTypesToScriptValue, pickTypesFromScriptValue);
}

View file

@ -35,6 +35,8 @@ class PickScriptingInterface : public QObject, public Dependency {
public:
QUuid createRayPick(const QVariant& properties);
void registerMetaTypes(QScriptEngine* engine);
public slots:
Q_INVOKABLE QUuid createPick(const PickQuery::PickType type, const QVariant& properties);
Q_INVOKABLE void enablePick(const QUuid& uid);

View file

@ -21,7 +21,7 @@ void PointerScriptingInterface::setIncludeItems(const QUuid& uid, const QScriptV
DependencyManager::get<PointerManager>()->setIncludeItems(uid, qVectorQUuidFromScriptValue(includeItems));
}
QUuid PointerScriptingInterface::createPointer(const PickQuery::PickType type, const QVariant& properties) const {
QUuid PointerScriptingInterface::createPointer(const PickQuery::PickType& type, const QVariant& properties) const {
switch (type) {
case PickQuery::PickType::Ray:
return createLaserPointer(properties);
@ -61,7 +61,7 @@ QUuid PointerScriptingInterface::createLaserPointer(const QVariant& properties)
LaserPointer::RenderStateMap renderStates;
if (propertyMap["renderStates"].isValid()) {
QList<QVariant> renderStateVariants = propertyMap["renderStates"].toList();
for (QVariant& renderStateVariant : renderStateVariants) {
for (const QVariant& renderStateVariant : renderStateVariants) {
if (renderStateVariant.isValid()) {
QVariantMap renderStateMap = renderStateVariant.toMap();
if (renderStateMap["name"].isValid()) {
@ -75,7 +75,7 @@ QUuid PointerScriptingInterface::createLaserPointer(const QVariant& properties)
LaserPointer::DefaultRenderStateMap defaultRenderStates;
if (propertyMap["defaultRenderStates"].isValid()) {
QList<QVariant> renderStateVariants = propertyMap["defaultRenderStates"].toList();
for (QVariant& renderStateVariant : renderStateVariants) {
for (const QVariant& renderStateVariant : renderStateVariants) {
if (renderStateVariant.isValid()) {
QVariantMap renderStateMap = renderStateVariant.toMap();
if (renderStateMap["name"].isValid() && renderStateMap["distance"].isValid()) {
@ -87,7 +87,31 @@ QUuid PointerScriptingInterface::createLaserPointer(const QVariant& properties)
}
}
return DependencyManager::get<PointerManager>()->addPointer(std::make_shared<LaserPointer>(properties, renderStates, defaultRenderStates, faceAvatar, centerEndY, lockEnd, distanceScaleEnd, enabled));
bool hover = false;
if (propertyMap["hover"].isValid()) {
hover = propertyMap["hover"].toBool();
}
PointerTriggers triggers;
auto userInputMapper = DependencyManager::get<UserInputMapper>();
if (propertyMap["triggers"].isValid()) {
QList<QVariant> triggerVariants = propertyMap["triggers"].toList();
for (const QVariant& triggerVariant : triggerVariants) {
if (triggerVariant.isValid()) {
QVariantMap triggerMap = triggerVariant.toMap();
if (triggerMap["action"].isValid() && triggerMap["button"].isValid()) {
controller::Endpoint::Pointer endpoint = userInputMapper->endpointFor(controller::Input(triggerMap["action"].toUInt()));
if (endpoint) {
std::string button = triggerMap["button"].toString().toStdString();
triggers.emplace_back(endpoint, button);
}
}
}
}
}
return DependencyManager::get<PointerManager>()->addPointer(std::make_shared<LaserPointer>(properties, renderStates, defaultRenderStates, hover, triggers,
faceAvatar, centerEndY, lockEnd, distanceScaleEnd, enabled));
}
void PointerScriptingInterface::editRenderState(const QUuid& uid, const QString& renderState, const QVariant& properties) const {

View file

@ -22,13 +22,13 @@ public:
QUuid createLaserPointer(const QVariant& properties) const;
public slots:
Q_INVOKABLE QUuid createPointer(const PickQuery::PickType type, const QVariant& properties) const;
Q_INVOKABLE QUuid createPointer(const PickQuery::PickType& type, const QVariant& properties) const;
Q_INVOKABLE void enablePointer(const QUuid& uid) const { DependencyManager::get<PointerManager>()->enablePointer(uid); }
Q_INVOKABLE void disablePointer(const QUuid& uid) const { DependencyManager::get<PointerManager>()->disablePointer(uid); }
Q_INVOKABLE void removePointer(const QUuid& uid) const { DependencyManager::get<PointerManager>()->removePointer(uid); }
Q_INVOKABLE void editRenderState(const QUuid& uid, const QString& renderState, const QVariant& properties) const;
Q_INVOKABLE void setRenderState(const QUuid& uid, const QString& renderState) const { DependencyManager::get<PointerManager>()->setRenderState(uid, renderState.toStdString()); }
Q_INVOKABLE QVariantMap getPrevPickResult(QUuid uid) const { return DependencyManager::get<PointerManager>()->getPrevPickResult(uid); }
Q_INVOKABLE QVariantMap getPrevPickResult(const QUuid& uid) const { return DependencyManager::get<PointerManager>()->getPrevPickResult(uid); }
Q_INVOKABLE void setPrecisionPicking(const QUuid& uid, bool precisionPicking) const { DependencyManager::get<PointerManager>()->setPrecisionPicking(uid, precisionPicking); }
Q_INVOKABLE void setLaserLength(const QUuid& uid, float laserLength) const { DependencyManager::get<PointerManager>()->setLength(uid, laserLength); }
@ -37,6 +37,14 @@ public slots:
Q_INVOKABLE void setLockEndUUID(const QUuid& uid, const QUuid& objectID, bool isOverlay) const { DependencyManager::get<PointerManager>()->setLockEndUUID(uid, objectID, isOverlay); }
signals:
void triggerBegin(const QUuid& id, const PointerEvent& pointerEvent);
void triggerContinue(const QUuid& id, const PointerEvent& pointerEvent);
void triggerEnd(const QUuid& id, const PointerEvent& pointerEvent);
void hoverBegin(const QUuid& id, const PointerEvent& pointerEvent);
void hoverContinue(const QUuid& id, const PointerEvent& pointerEvent);
void hoverEnd(const QUuid& id, const PointerEvent& pointerEvent);
};
#endif // hifi_PointerScriptingInterface_h

View file

@ -14,7 +14,6 @@
#include <QVariant>
#include "GLMHelpers.h"
#include "PickScriptingInterface.h"
#include <pointers/PickManager.h>
#include "StaticRayPick.h"

View file

@ -16,8 +16,24 @@
#include "RegisteredMetaTypes.h"
#include <DependencyManager.h>
#include "PickScriptingInterface.h"
class RayPickScriptingInterface : public QObject, public Dependency {
Q_OBJECT
Q_PROPERTY(unsigned int PICK_NOTHING READ PICK_NOTHING CONSTANT)
Q_PROPERTY(unsigned int PICK_ENTITIES READ PICK_ENTITIES CONSTANT)
Q_PROPERTY(unsigned int PICK_OVERLAYS READ PICK_OVERLAYS CONSTANT)
Q_PROPERTY(unsigned int PICK_AVATARS READ PICK_AVATARS CONSTANT)
Q_PROPERTY(unsigned int PICK_HUD READ PICK_HUD CONSTANT)
Q_PROPERTY(unsigned int PICK_COARSE READ PICK_COARSE CONSTANT)
Q_PROPERTY(unsigned int PICK_INCLUDE_INVISIBLE READ PICK_INCLUDE_INVISIBLE CONSTANT)
Q_PROPERTY(unsigned int PICK_INCLUDE_NONCOLLIDABLE READ PICK_INCLUDE_NONCOLLIDABLE CONSTANT)
Q_PROPERTY(unsigned int PICK_ALL_INTERSECTIONS READ PICK_ALL_INTERSECTIONS CONSTANT)
Q_PROPERTY(unsigned int INTERSECTED_NONE READ INTERSECTED_NONE CONSTANT)
Q_PROPERTY(unsigned int INTERSECTED_ENTITY READ INTERSECTED_ENTITY CONSTANT)
Q_PROPERTY(unsigned int INTERSECTED_OVERLAY READ INTERSECTED_OVERLAY CONSTANT)
Q_PROPERTY(unsigned int INTERSECTED_AVATAR READ INTERSECTED_AVATAR CONSTANT)
Q_PROPERTY(unsigned int INTERSECTED_HUD READ INTERSECTED_HUD CONSTANT)
SINGLETON_DEPENDENCY
public slots:
@ -30,6 +46,21 @@ public slots:
Q_INVOKABLE void setPrecisionPicking(const QUuid& uid, const bool precisionPicking);
Q_INVOKABLE void setIgnoreItems(const QUuid& uid, const QScriptValue& ignoreEntities);
Q_INVOKABLE void setIncludeItems(const QUuid& uid, const QScriptValue& includeEntities);
static unsigned int PICK_NOTHING() { return PickScriptingInterface::PICK_NOTHING(); }
static unsigned int PICK_ENTITIES() { return PickScriptingInterface::PICK_ENTITIES(); }
static unsigned int PICK_OVERLAYS() { return PickScriptingInterface::PICK_OVERLAYS(); }
static unsigned int PICK_AVATARS() { return PickScriptingInterface::PICK_AVATARS(); }
static unsigned int PICK_HUD() { return PickScriptingInterface::PICK_HUD(); }
static unsigned int PICK_COARSE() { return PickScriptingInterface::PICK_COARSE(); }
static unsigned int PICK_INCLUDE_INVISIBLE() { return PickScriptingInterface::PICK_INCLUDE_INVISIBLE(); }
static unsigned int PICK_INCLUDE_NONCOLLIDABLE() { return PickScriptingInterface::PICK_INCLUDE_NONCOLLIDABLE(); }
static unsigned int PICK_ALL_INTERSECTIONS() { return PickScriptingInterface::PICK_ALL_INTERSECTIONS(); }
static unsigned int INTERSECTED_NONE() { return PickScriptingInterface::INTERSECTED_NONE(); }
static unsigned int INTERSECTED_ENTITY() { return PickScriptingInterface::INTERSECTED_ENTITY(); }
static unsigned int INTERSECTED_OVERLAY() { return PickScriptingInterface::INTERSECTED_OVERLAY(); }
static unsigned int INTERSECTED_AVATAR() { return PickScriptingInterface::INTERSECTED_AVATAR(); }
static unsigned int INTERSECTED_HUD() { return PickScriptingInterface::INTERSECTED_HUD(); }
};
#endif // hifi_RayPickScriptingInterface_h

View file

@ -37,10 +37,22 @@
#include "Web3DOverlay.h"
#include <QtQuick/QQuickWindow>
#include <pointers/PointerManager.h>
Q_LOGGING_CATEGORY(trace_render_overlays, "trace.render.overlays")
extern void initOverlay3DPipelines(render::ShapePlumber& plumber, bool depthTest = false);
Overlays::Overlays() {
auto pointerManager = DependencyManager::get<PointerManager>();
connect(pointerManager.data(), &PointerManager::hoverBeginOverlay, this, &Overlays::hoverEnterOverlay);
connect(pointerManager.data(), &PointerManager::hoverContinueOverlay, this, &Overlays::hoverOverOverlay);
connect(pointerManager.data(), &PointerManager::hoverEndOverlay, this, &Overlays::hoverLeavePointerEvent);
connect(pointerManager.data(), &PointerManager::triggerBeginOverlay, this, &Overlays::mousePressPointerEvent);
connect(pointerManager.data(), &PointerManager::triggerContinueOverlay, this, &Overlays::mouseMovePointerEvent);
connect(pointerManager.data(), &PointerManager::triggerEndOverlay, this, &Overlays::mouseReleasePointerEvent);
}
void Overlays::cleanupAllOverlays() {
QMap<OverlayID, Overlay::Pointer> overlaysHUD;
QMap<OverlayID, Overlay::Pointer> overlaysWorld;
@ -717,15 +729,15 @@ bool Overlays::isAddedOverlay(OverlayID id) {
}
void Overlays::sendMousePressOnOverlay(const OverlayID& overlayID, const PointerEvent& event) {
mousePressEvent(overlayID, event);
mousePressPointerEvent(overlayID, event);
}
void Overlays::sendMouseReleaseOnOverlay(const OverlayID& overlayID, const PointerEvent& event) {
mouseReleaseEvent(overlayID, event);
mouseReleasePointerEvent(overlayID, event);
}
void Overlays::sendMouseMoveOnOverlay(const OverlayID& overlayID, const PointerEvent& event) {
mouseMoveEvent(overlayID, event);
mouseMovePointerEvent(overlayID, event);
}
void Overlays::sendHoverEnterOverlay(const OverlayID& overlayID, const PointerEvent& event) {
@ -737,7 +749,7 @@ void Overlays::sendHoverOverOverlay(const OverlayID& overlayID, const PointerEve
}
void Overlays::sendHoverLeaveOverlay(const OverlayID& overlayID, const PointerEvent& event) {
hoverLeaveEvent(overlayID, event);
hoverLeavePointerEvent(overlayID, event);
}
OverlayID Overlays::getKeyboardFocusOverlay() {
@ -879,7 +891,7 @@ bool Overlays::mousePressEvent(QMouseEvent* event) {
_currentClickingOnOverlayID = rayPickResult.overlayID;
PointerEvent pointerEvent = calculateOverlayPointerEvent(_currentClickingOnOverlayID, ray, rayPickResult, event, PointerEvent::Press);
mousePressEvent(_currentClickingOnOverlayID, pointerEvent);
mousePressPointerEvent(_currentClickingOnOverlayID, pointerEvent);
return true;
}
// if we didn't press on an overlay, disable overlay keyboard focus
@ -889,16 +901,18 @@ bool Overlays::mousePressEvent(QMouseEvent* event) {
return false;
}
void Overlays::mousePressEvent(const OverlayID& overlayID, const PointerEvent& event) {
void Overlays::mousePressPointerEvent(const OverlayID& overlayID, const PointerEvent& event) {
// TODO: generalize this to allow any overlay to recieve events
std::shared_ptr<Web3DOverlay> thisOverlay;
if (getOverlayType(overlayID) == "web3d") {
thisOverlay = std::static_pointer_cast<Web3DOverlay>(getOverlay(overlayID));
}
if (thisOverlay) {
// Focus keyboard on web overlays
DependencyManager::get<EntityScriptingInterface>()->setKeyboardFocusEntity(UNKNOWN_ENTITY_ID);
setKeyboardFocusOverlay(overlayID);
if (event.shouldFocus()) {
// Focus keyboard on web overlays
DependencyManager::get<EntityScriptingInterface>()->setKeyboardFocusEntity(UNKNOWN_ENTITY_ID);
setKeyboardFocusOverlay(overlayID);
}
// Send to web overlay
QMetaObject::invokeMethod(thisOverlay.get(), "handlePointerEvent", Q_ARG(PointerEvent, event));
@ -926,7 +940,7 @@ bool Overlays::mouseDoublePressEvent(QMouseEvent* event) {
return false;
}
void Overlays::hoverLeaveEvent(const OverlayID& overlayID, const PointerEvent& event) {
void Overlays::hoverLeavePointerEvent(const OverlayID& overlayID, const PointerEvent& event) {
// TODO: generalize this to allow any overlay to recieve events
std::shared_ptr<Web3DOverlay> thisOverlay;
if (getOverlayType(overlayID) == "web3d") {
@ -948,14 +962,14 @@ bool Overlays::mouseReleaseEvent(QMouseEvent* event) {
RayToOverlayIntersectionResult rayPickResult = findRayIntersectionForMouseEvent(ray);
if (rayPickResult.intersects) {
auto pointerEvent = calculateOverlayPointerEvent(rayPickResult.overlayID, ray, rayPickResult, event, PointerEvent::Release);
mouseReleaseEvent(rayPickResult.overlayID, pointerEvent);
mouseReleasePointerEvent(rayPickResult.overlayID, pointerEvent);
}
_currentClickingOnOverlayID = UNKNOWN_OVERLAY_ID;
return false;
}
void Overlays::mouseReleaseEvent(const OverlayID& overlayID, const PointerEvent& event) {
void Overlays::mouseReleasePointerEvent(const OverlayID& overlayID, const PointerEvent& event) {
// TODO: generalize this to allow any overlay to recieve events
std::shared_ptr<Web3DOverlay> thisOverlay;
if (getOverlayType(overlayID) == "web3d") {
@ -977,12 +991,12 @@ bool Overlays::mouseMoveEvent(QMouseEvent* event) {
RayToOverlayIntersectionResult rayPickResult = findRayIntersectionForMouseEvent(ray);
if (rayPickResult.intersects) {
auto pointerEvent = calculateOverlayPointerEvent(rayPickResult.overlayID, ray, rayPickResult, event, PointerEvent::Move);
mouseMoveEvent(rayPickResult.overlayID, pointerEvent);
mouseMovePointerEvent(rayPickResult.overlayID, pointerEvent);
// If previously hovering over a different overlay then leave hover on that overlay.
if (_currentHoverOverOverlayID != UNKNOWN_OVERLAY_ID && rayPickResult.overlayID != _currentHoverOverOverlayID) {
auto pointerEvent = calculateOverlayPointerEvent(_currentHoverOverOverlayID, ray, rayPickResult, event, PointerEvent::Move);
hoverLeaveEvent(_currentHoverOverOverlayID, pointerEvent);
hoverLeavePointerEvent(_currentHoverOverOverlayID, pointerEvent);
}
// If hovering over a new overlay then enter hover on that overlay.
@ -998,7 +1012,7 @@ bool Overlays::mouseMoveEvent(QMouseEvent* event) {
// If previously hovering an overlay then leave hover.
if (_currentHoverOverOverlayID != UNKNOWN_OVERLAY_ID) {
auto pointerEvent = calculateOverlayPointerEvent(_currentHoverOverOverlayID, ray, rayPickResult, event, PointerEvent::Move);
hoverLeaveEvent(_currentHoverOverOverlayID, pointerEvent);
hoverLeavePointerEvent(_currentHoverOverOverlayID, pointerEvent);
_currentHoverOverOverlayID = UNKNOWN_OVERLAY_ID;
}
@ -1006,7 +1020,7 @@ bool Overlays::mouseMoveEvent(QMouseEvent* event) {
return false;
}
void Overlays::mouseMoveEvent(const OverlayID& overlayID, const PointerEvent& event) {
void Overlays::mouseMovePointerEvent(const OverlayID& overlayID, const PointerEvent& event) {
// TODO: generalize this to allow any overlay to recieve events
std::shared_ptr<Web3DOverlay> thisOverlay;
if (getOverlayType(overlayID) == "web3d") {

View file

@ -85,7 +85,7 @@ class Overlays : public QObject {
Q_PROPERTY(OverlayID keyboardFocusOverlay READ getKeyboardFocusOverlay WRITE setKeyboardFocusOverlay)
public:
Overlays() {};
Overlays();
void init();
void update(float deltatime);
@ -107,11 +107,6 @@ public:
bool mouseReleaseEvent(QMouseEvent* event);
bool mouseMoveEvent(QMouseEvent* event);
void mousePressEvent(const OverlayID& overlayID, const PointerEvent& event);
void mouseMoveEvent(const OverlayID& overlayID, const PointerEvent& event);
void mouseReleaseEvent(const OverlayID& overlayID, const PointerEvent& event);
void hoverLeaveEvent(const OverlayID& overlayID, const PointerEvent& event);
void cleanupAllOverlays();
public slots:
@ -310,6 +305,11 @@ public slots:
OverlayID getKeyboardFocusOverlay();
void setKeyboardFocusOverlay(const OverlayID& id);
void mousePressPointerEvent(const OverlayID& overlayID, const PointerEvent& event);
void mouseMovePointerEvent(const OverlayID& overlayID, const PointerEvent& event);
void mouseReleasePointerEvent(const OverlayID& overlayID, const PointerEvent& event);
void hoverLeavePointerEvent(const OverlayID& overlayID, const PointerEvent& event);
signals:
/**jsdoc
* Emitted when an overlay is deleted

View file

@ -66,7 +66,7 @@ const QString Web3DOverlay::QML = "Web3DOverlay.qml";
Web3DOverlay::Web3DOverlay() : _dpi(DPI) {
_touchDevice.setCapabilities(QTouchDevice::Position);
_touchDevice.setType(QTouchDevice::TouchScreen);
_touchDevice.setName("RenderableWebEntityItemTouchDevice");
_touchDevice.setName("Web3DOverlayTouchDevice");
_touchDevice.setMaximumTouchPoints(4);
_geometryId = DependencyManager::get<GeometryCache>()->allocateID();
@ -349,6 +349,12 @@ void Web3DOverlay::setProxyWindow(QWindow* proxyWindow) {
}
void Web3DOverlay::handlePointerEvent(const PointerEvent& event) {
if (event.getType() == PointerEvent::Press) {
_pressed = true;
} else if (event.getType() == PointerEvent::Release) {
_pressed = false;
}
if (_inputMode == Touch) {
handlePointerEventAsTouch(event);
} else {
@ -361,19 +367,8 @@ void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) {
return;
}
//do not send secondary button events to tablet
if (event.getButton() == PointerEvent::SecondaryButton ||
//do not block composed events
event.getButtons() == PointerEvent::SecondaryButton) {
return;
}
QPointF windowPoint;
{
glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _dpi);
windowPoint = QPointF(windowPos.x, windowPos.y);
}
glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _dpi);
QPointF windowPoint(windowPos.x, windowPos.y);
Qt::TouchPointState state = Qt::TouchPointStationary;
if (event.getType() == PointerEvent::Press && event.getButton() == PointerEvent::PrimaryButton) {
@ -412,14 +407,13 @@ void Web3DOverlay::handlePointerEventAsTouch(const PointerEvent& event) {
}
touchEvent.setWindow(_webSurface->getWindow());
touchEvent.setDevice(&_touchDevice);
touchEvent.setTarget(_webSurface->getRootItem());
touchEvent.setTouchPoints(touchPoints);
touchEvent.setTouchPointStates(touchPointStates);
}
// Send mouse events to the Web surface so that HTML dialog elements work with mouse press and hover.
// FIXME: Scroll bar dragging is a bit unstable in the tablet (content can jump up and down at times).
// This may be improved in Qt 5.8. Release notes: "Cleaned up touch and mouse event delivery".
//
// In Qt 5.9 mouse events must be sent before touch events to make sure some QtQuick components will
// receive mouse events
@ -466,12 +460,6 @@ void Web3DOverlay::handlePointerEventAsMouse(const PointerEvent& event) {
glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _dpi);
QPointF windowPoint(windowPos.x, windowPos.y);
if (event.getType() == PointerEvent::Press) {
this->_pressed = true;
} else if (event.getType() == PointerEvent::Release) {
this->_pressed = false;
}
Qt::MouseButtons buttons = Qt::NoButton;
if (event.getButtons() & PointerEvent::PrimaryButton) {
buttons |= Qt::LeftButton;

View file

@ -129,6 +129,8 @@ namespace controller {
template <typename F>
void withLock(F&& f) { Locker locker(_lock); f(); }
EndpointPointer endpointFor(const Input& endpoint) const;
signals:
void actionEvent(int action, float state);
void inputEvent(int input, float state);
@ -161,7 +163,6 @@ namespace controller {
void disableMapping(const MappingPointer& mapping);
EndpointPointer endpointFor(const QJSValue& endpoint);
EndpointPointer endpointFor(const QScriptValue& endpoint);
EndpointPointer endpointFor(const Input& endpoint) const;
EndpointPointer compositeEndpointFor(EndpointPointer first, EndpointPointer second);
ConditionalPointer conditionalFor(const QJSValue& endpoint);
ConditionalPointer conditionalFor(const QScriptValue& endpoint);

View file

@ -1,7 +1,7 @@
set(TARGET_NAME entities-renderer)
AUTOSCRIBE_SHADER_LIB(gpu model procedural render render-utils)
setup_hifi_library(Widgets Network Script)
link_hifi_libraries(shared gpu procedural model model-networking script-engine render render-utils image ui)
link_hifi_libraries(shared gpu procedural model model-networking script-engine render render-utils image ui pointers)
include_hifi_library_headers(networking)
include_hifi_library_headers(gl)
include_hifi_library_headers(ktx)
@ -12,6 +12,7 @@ include_hifi_library_headers(animation)
include_hifi_library_headers(fbx)
include_hifi_library_headers(entities)
include_hifi_library_headers(avatars)
include_hifi_library_headers(controllers)
target_bullet()

View file

@ -37,6 +37,8 @@
#include "RenderableWebEntityItem.h"
#include <pointers/PointerManager.h>
size_t std::hash<EntityItemID>::operator()(const EntityItemID& id) const { return qHash(id); }
std::function<bool()> EntityTreeRenderer::_entitiesShouldFadeFunction;
@ -58,6 +60,15 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
_currentHoverOverEntityID = UNKNOWN_ENTITY_ID;
_currentClickingOnEntityID = UNKNOWN_ENTITY_ID;
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
auto pointerManager = DependencyManager::get<PointerManager>();
connect(pointerManager.data(), &PointerManager::hoverBeginEntity, entityScriptingInterface.data(), &EntityScriptingInterface::hoverEnterEntity);
connect(pointerManager.data(), &PointerManager::hoverContinueEntity, entityScriptingInterface.data(), &EntityScriptingInterface::hoverOverEntity);
connect(pointerManager.data(), &PointerManager::hoverEndEntity, entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity);
connect(pointerManager.data(), &PointerManager::triggerBeginEntity, entityScriptingInterface.data(), &EntityScriptingInterface::mousePressOnEntity);
connect(pointerManager.data(), &PointerManager::triggerContinueEntity, entityScriptingInterface.data(), &EntityScriptingInterface::mouseMoveOnEntity);
connect(pointerManager.data(), &PointerManager::triggerEndEntity, entityScriptingInterface.data(), &EntityScriptingInterface::mouseReleaseOnEntity);
// Forward mouse events to web entities
auto handlePointerEvent = [&](const EntityItemID& entityID, const PointerEvent& event) {
std::shared_ptr<render::entities::WebEntityRenderer> thisEntity;
@ -69,7 +80,6 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
QMetaObject::invokeMethod(thisEntity.get(), "handlePointerEvent", Q_ARG(PointerEvent, event));
}
};
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
connect(entityScriptingInterface.data(), &EntityScriptingInterface::mousePressOnEntity, this, handlePointerEvent);
connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseReleaseOnEntity, this, handlePointerEvent);
connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseMoveOnEntity, this, handlePointerEvent);

View file

@ -227,6 +227,8 @@ bool WebEntityRenderer::buildWebSurface(const TypedEntityPointer& entity) {
_webSurface->setMaxFps(DEFAULT_MAX_FPS);
// FIXME - Keyboard HMD only: Possibly add "HMDinfo" object to context for WebView.qml.
_webSurface->getSurfaceContext()->setContextProperty("desktop", QVariant());
// Let us interact with the keyboard
_webSurface->getSurfaceContext()->setContextProperty("tabletInterface", DependencyManager::get<TabletScriptingInterface>().data());
_fadeStartTime = usecTimestampNow();
loadSourceURL();
_webSurface->resume();
@ -315,21 +317,9 @@ void WebEntityRenderer::loadSourceURL() {
void WebEntityRenderer::hoverLeaveEntity(const PointerEvent& event) {
if (!_lastLocked && _webSurface && _pressed) {
// If the user mouses off the entity while the button is down, simulate a touch end.
QTouchEvent::TouchPoint point;
point.setId(event.getID());
point.setState(Qt::TouchPointReleased);
glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _lastDPI);
QPointF windowPoint(windowPos.x, windowPos.y);
point.setScenePos(windowPoint);
point.setPos(windowPoint);
QList<QTouchEvent::TouchPoint> touchPoints;
touchPoints.push_back(point);
QTouchEvent* touchEvent = new QTouchEvent(QEvent::TouchEnd, nullptr,
Qt::NoModifier, Qt::TouchPointReleased, touchPoints);
touchEvent->setWindow(_webSurface->getWindow());
touchEvent->setDevice(&_touchDevice);
touchEvent->setTarget(_webSurface->getRootItem());
QCoreApplication::postEvent(_webSurface->getWindow(), touchEvent);
PointerEvent endEvent(PointerEvent::Release, event.getID(), event.getPos2D(), event.getPos3D(), event.getNormal(), event.getDirection(),
event.getButton(), event.getButtons(), event.getKeyboardModifiers());
handlePointerEvent(endEvent);
}
}
@ -339,57 +329,95 @@ void WebEntityRenderer::handlePointerEvent(const PointerEvent& event) {
return;
}
if (event.getType() == PointerEvent::Press) {
_pressed = true;
} else if (event.getType() == PointerEvent::Release) {
_pressed = false;
}
glm::vec2 windowPos = event.getPos2D() * (METERS_TO_INCHES * _lastDPI);
QPointF windowPoint(windowPos.x, windowPos.y);
if (event.getType() == PointerEvent::Move) {
// Forward a mouse move event to webSurface
QMouseEvent* mouseEvent = new QMouseEvent(QEvent::MouseMove, windowPoint, windowPoint, windowPoint, Qt::NoButton, Qt::NoButton, Qt::NoModifier);
QCoreApplication::postEvent(_webSurface->getWindow(), mouseEvent);
Qt::TouchPointState state = Qt::TouchPointStationary;
if (event.getType() == PointerEvent::Press && event.getButton() == PointerEvent::PrimaryButton) {
state = Qt::TouchPointPressed;
} else if (event.getType() == PointerEvent::Release) {
state = Qt::TouchPointReleased;
} else if (_activeTouchPoints.count(event.getID()) && windowPoint != _activeTouchPoints[event.getID()].pos()) {
state = Qt::TouchPointMoved;
}
QEvent::Type touchType = QEvent::TouchUpdate;
if (_activeTouchPoints.empty()) {
// If the first active touch point is being created, send a begin
touchType = QEvent::TouchBegin;
} if (state == Qt::TouchPointReleased && _activeTouchPoints.size() == 1 && _activeTouchPoints.count(event.getID())) {
// If the last active touch point is being released, send an end
touchType = QEvent::TouchEnd;
}
{
// 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;
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;
}
QTouchEvent::TouchPoint point;
point.setId(event.getID());
point.setState(touchPointState);
point.setState(state);
point.setPos(windowPoint);
point.setScreenPos(windowPoint);
QList<QTouchEvent::TouchPoint> touchPoints;
touchPoints.push_back(point);
QTouchEvent* touchEvent = new QTouchEvent(type);
touchEvent->setWindow(_webSurface->getWindow());
touchEvent->setDevice(&_touchDevice);
touchEvent->setTarget(_webSurface->getRootItem());
touchEvent->setTouchPoints(touchPoints);
touchEvent->setTouchPointStates(touchPointState);
QCoreApplication::postEvent(_webSurface->getWindow(), touchEvent);
_activeTouchPoints[event.getID()] = point;
}
QTouchEvent touchEvent(touchType, &_touchDevice, event.getKeyboardModifiers());
{
QList<QTouchEvent::TouchPoint> touchPoints;
Qt::TouchPointStates touchPointStates;
for (const auto& entry : _activeTouchPoints) {
touchPointStates |= entry.second.state();
touchPoints.push_back(entry.second);
}
touchEvent.setWindow(_webSurface->getWindow());
touchEvent.setDevice(&_touchDevice);
touchEvent.setTarget(_webSurface->getRootItem());
touchEvent.setTouchPoints(touchPoints);
touchEvent.setTouchPointStates(touchPointStates);
}
// Send mouse events to the Web surface so that HTML dialog elements work with mouse press and hover.
//
// In Qt 5.9 mouse events must be sent before touch events to make sure some QtQuick components will
// receive mouse events
Qt::MouseButton button = Qt::NoButton;
Qt::MouseButtons buttons = Qt::NoButton;
if (event.getButton() == PointerEvent::PrimaryButton) {
button = Qt::LeftButton;
}
if (event.getButtons() & PointerEvent::PrimaryButton) {
buttons |= Qt::LeftButton;
}
#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
if (event.getType() == PointerEvent::Move) {
QMouseEvent mouseEvent(QEvent::MouseMove, windowPoint, windowPoint, windowPoint, button, buttons, Qt::NoModifier);
QCoreApplication::sendEvent(_webSurface->getWindow(), &mouseEvent);
}
#endif
if (touchType == QEvent::TouchBegin) {
_touchBeginAccepted = QCoreApplication::sendEvent(_webSurface->getWindow(), &touchEvent);
} else if (_touchBeginAccepted) {
QCoreApplication::sendEvent(_webSurface->getWindow(), &touchEvent);
}
// If this was a release event, remove the point from the active touch points
if (state == Qt::TouchPointReleased) {
_activeTouchPoints.erase(event.getID());
}
#if QT_VERSION < QT_VERSION_CHECK(5, 9, 0)
if (event.getType() == PointerEvent::Move) {
QMouseEvent mouseEvent(QEvent::MouseMove, windowPoint, windowPoint, windowPoint, button, buttons, Qt::NoModifier);
QCoreApplication::sendEvent(_webSurface->getWindow(), &mouseEvent);
}
#endif
}
void WebEntityRenderer::setProxyWindow(QWindow* proxyWindow) {

View file

@ -12,6 +12,8 @@
#include <WebEntityItem.h>
#include "RenderableEntityItem.h"
#include <QTouchEvent>
class OffscreenQmlSurface;
class PointerEvent;
@ -58,13 +60,16 @@ private:
QSharedPointer<OffscreenQmlSurface> _webSurface;
glm::vec3 _contextPosition;
gpu::TexturePointer _texture;
bool _pressed{ false };
QString _lastSourceUrl;
uint16_t _lastDPI;
bool _lastLocked;
QTimer _timer;
uint64_t _lastRenderTime { 0 };
Transform _renderTransform;
bool _pressed{ false };
bool _touchBeginAccepted{ false };
std::map<uint32_t, QTouchEvent::TouchPoint> _activeTouchPoints;
};
} } // namespace

View file

@ -1,5 +1,5 @@
set(TARGET_NAME pointers)
setup_hifi_library()
GroupSources(src)
link_hifi_libraries(shared)
link_hifi_libraries(shared controllers)

View file

@ -9,6 +9,8 @@
const PickFilter PickFilter::NOTHING;
int pickTypeMetaTypeId = qRegisterMetaType<PickQuery::PickType>("PickType");
PickQuery::PickQuery(const PickFilter& filter, const float maxDistance, const bool enabled) :
_filter(filter),
_maxDistance(maxDistance),

View file

@ -136,7 +136,9 @@ public:
enum PickType {
Ray = 0,
Stylus
Stylus,
NUM_PICK_TYPES
};
Q_ENUM(PickType)
@ -189,6 +191,7 @@ private:
QVector<QUuid> _ignoreItems;
QVector<QUuid> _includeItems;
};
Q_DECLARE_METATYPE(PickQuery::PickType)
template<typename T>
class Pick : public PickQuery {

View file

@ -9,6 +9,7 @@
#include <DependencyManager.h>
#include "PickManager.h"
#include "PointerManager.h"
Pointer::~Pointer() {
DependencyManager::get<PickManager>()->removePick(_pickUID);
@ -16,9 +17,16 @@ Pointer::~Pointer() {
void Pointer::enable() {
DependencyManager::get<PickManager>()->enablePick(_pickUID);
withWriteLock([&] {
_enabled = true;
});
}
void Pointer::disable() {
// Disable the pointer first, then the pick, so someone can't try to use it while it's in a bad state
withWriteLock([&] {
_enabled = false;
});
DependencyManager::get<PickManager>()->disablePick(_pickUID);
}
@ -26,7 +34,7 @@ const QVariantMap Pointer::getPrevPickResult() {
return DependencyManager::get<PickManager>()->getPrevPickResult(_pickUID);
}
void Pointer::setPrecisionPicking(const bool precisionPicking) {
void Pointer::setPrecisionPicking(bool precisionPicking) {
DependencyManager::get<PickManager>()->setPrecisionPicking(_pickUID, precisionPicking);
}
@ -36,4 +44,136 @@ void Pointer::setIgnoreItems(const QVector<QUuid>& ignoreItems) const {
void Pointer::setIncludeItems(const QVector<QUuid>& includeItems) const {
DependencyManager::get<PickManager>()->setIncludeItems(_pickUID, includeItems);
}
void Pointer::update() {
// This only needs to be a read lock because update won't change any of the properties that can be modified from scripts
withReadLock([&] {
QVariantMap pickResult = getPrevPickResult();
updateVisuals(pickResult);
generatePointerEvents(pickResult);
});
}
void Pointer::generatePointerEvents(const QVariantMap& pickResult) {
// TODO: avatars/HUD?
auto pointerManager = DependencyManager::get<PointerManager>();
// Hover events
Pointer::PickedObject hoveredObject = getHoveredObject(pickResult);
PointerEvent hoveredEvent = buildPointerEvent(hoveredObject, pickResult);
hoveredEvent.setType(PointerEvent::Move);
// TODO: set buttons on hover events
hoveredEvent.setButton(PointerEvent::NoButtons);
if (_enabled && _hover) {
if (hoveredObject.type == OVERLAY) {
if (_prevHoveredObject.type == OVERLAY) {
if (hoveredObject.objectID == _prevHoveredObject.objectID) {
emit pointerManager->hoverContinueOverlay(hoveredObject.objectID, hoveredEvent);
} else {
PointerEvent prevHoveredEvent = buildPointerEvent(_prevHoveredObject, pickResult);
emit pointerManager->hoverEndOverlay(_prevHoveredObject.objectID, prevHoveredEvent);
emit pointerManager->hoverBeginOverlay(hoveredObject.objectID, hoveredEvent);
}
} else {
emit pointerManager->hoverBeginOverlay(hoveredObject.objectID, hoveredEvent);
if (_prevHoveredObject.type == ENTITY) {
emit pointerManager->hoverEndEntity(_prevHoveredObject.objectID, hoveredEvent);
}
}
}
// TODO: this is basically repeated code. is there a way to clean it up?
if (hoveredObject.type == ENTITY) {
if (_prevHoveredObject.type == ENTITY) {
if (hoveredObject.objectID == _prevHoveredObject.objectID) {
emit pointerManager->hoverContinueEntity(hoveredObject.objectID, hoveredEvent);
} else {
PointerEvent prevHoveredEvent = buildPointerEvent(_prevHoveredObject, pickResult);
emit pointerManager->hoverEndEntity(_prevHoveredObject.objectID, prevHoveredEvent);
emit pointerManager->hoverBeginEntity(hoveredObject.objectID, hoveredEvent);
}
} else {
emit pointerManager->hoverBeginEntity(hoveredObject.objectID, hoveredEvent);
if (_prevHoveredObject.type == OVERLAY) {
emit pointerManager->hoverEndOverlay(_prevHoveredObject.objectID, hoveredEvent);
}
}
}
}
// Trigger events
Buttons buttons;
Buttons newButtons;
Buttons sameButtons;
// NOTE: After this loop: _prevButtons = buttons that were removed
// If !_enabled, release all buttons
if (_enabled) {
buttons = getPressedButtons();
for (const std::string& button : buttons) {
if (_prevButtons.find(button) == _prevButtons.end()) {
newButtons.insert(button);
} else {
sameButtons.insert(button);
_prevButtons.erase(button);
}
}
}
// Trigger begin
const std::string SHOULD_FOCUS_BUTTON = "Focus";
for (const std::string& button : newButtons) {
hoveredEvent.setType(PointerEvent::Press);
hoveredEvent.setButton(chooseButton(button));
hoveredEvent.setShouldFocus(button == SHOULD_FOCUS_BUTTON);
if (hoveredObject.type == ENTITY) {
emit pointerManager->triggerBeginEntity(hoveredObject.objectID, hoveredEvent);
} else if (hoveredObject.type == OVERLAY) {
emit pointerManager->triggerBeginOverlay(hoveredObject.objectID, hoveredEvent);
}
_triggeredObjects[button] = hoveredObject;
}
// Trigger continue
for (const std::string& button : sameButtons) {
PointerEvent triggeredEvent = buildPointerEvent(_triggeredObjects[button], pickResult);
triggeredEvent.setType(PointerEvent::Move);
hoveredEvent.setButton(chooseButton(button));
if (_triggeredObjects[button].type == ENTITY) {
emit pointerManager->triggerContinueEntity(_triggeredObjects[button].objectID, triggeredEvent);
} else if (_triggeredObjects[button].type == OVERLAY) {
emit pointerManager->triggerContinueOverlay(_triggeredObjects[button].objectID, triggeredEvent);
}
}
// Trigger end
for (const std::string& button : _prevButtons) {
PointerEvent triggeredEvent = buildPointerEvent(_triggeredObjects[button], pickResult);
triggeredEvent.setType(PointerEvent::Release);
hoveredEvent.setButton(chooseButton(button));
if (_triggeredObjects[button].type == ENTITY) {
emit pointerManager->triggerEndEntity(_triggeredObjects[button].objectID, triggeredEvent);
} else if (_triggeredObjects[button].type == OVERLAY) {
emit pointerManager->triggerEndOverlay(_triggeredObjects[button].objectID, triggeredEvent);
}
_triggeredObjects.erase(button);
}
_prevHoveredObject = hoveredObject;
_prevButtons = buttons;
}
PointerEvent::Button Pointer::chooseButton(const std::string& button) {
const std::string PRIMARY_BUTTON = "Primary";
const std::string SECONDARY_BUTTON = "Secondary";
const std::string TERTIARY_BUTTON = "Tertiary";
if (button == PRIMARY_BUTTON) {
return PointerEvent::PrimaryButton;
} else if (button == SECONDARY_BUTTON) {
return PointerEvent::SecondaryButton;
} else if (button == TERTIARY_BUTTON) {
return PointerEvent::TertiaryButton;
} else {
return PointerEvent::NoButtons;
}
}

View file

@ -8,15 +8,37 @@
#ifndef hifi_Pointer_h
#define hifi_Pointer_h
#include <unordered_set>
#include <unordered_map>
#include <QtCore/QUuid>
#include <QVector>
#include <QVariant>
#include <shared/ReadWriteLockable.h>
#include <controllers/impl/Endpoint.h>
#include "PointerEvent.h"
#include "Pick.h"
class PointerTrigger {
public:
PointerTrigger(controller::Endpoint::Pointer endpoint, const std::string& button) : _endpoint(endpoint), _button(button) {}
controller::Endpoint::Pointer getEndpoint() const { return _endpoint; }
const std::string& getButton() const { return _button; }
private:
controller::Endpoint::Pointer _endpoint;
std::string _button { "" };
};
using PointerTriggers = std::vector<PointerTrigger>;
class Pointer : protected ReadWriteLockable {
public:
Pointer(const QUuid& uid) : _pickUID(uid) {}
Pointer(const QUuid& uid, bool enabled, bool hover) : _pickUID(uid), _enabled(enabled), _hover(hover) {}
virtual ~Pointer();
@ -27,20 +49,47 @@ public:
virtual void setRenderState(const std::string& state) = 0;
virtual void editRenderState(const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) = 0;
virtual void setPrecisionPicking(const bool precisionPicking);
virtual void setPrecisionPicking(bool precisionPicking);
virtual void setIgnoreItems(const QVector<QUuid>& ignoreItems) const;
virtual void setIncludeItems(const QVector<QUuid>& includeItems) const;
// Pointers can choose to implement these
virtual void setLength(const float length) {}
virtual void setLockEndUUID(QUuid objectID, const bool isOverlay) {}
virtual void setLength(float length) {}
virtual void setLockEndUUID(const QUuid& objectID, bool isOverlay) {}
virtual void update() = 0;
void update();
virtual void updateVisuals(const QVariantMap& pickResult) = 0;
void generatePointerEvents(const QVariantMap& pickResult);
struct PickedObject {
PickedObject() {}
PickedObject(const QUuid& objectID, IntersectionType type) : objectID(objectID), type(type) {}
QUuid objectID;
IntersectionType type;
} typedef PickedObject;
using Buttons = std::unordered_set<std::string>;
virtual PickedObject getHoveredObject(const QVariantMap& pickResult) = 0;
virtual Buttons getPressedButtons() = 0;
QUuid getRayUID() { return _pickUID; }
protected:
const QUuid _pickUID;
bool _enabled;
bool _hover;
virtual PointerEvent buildPointerEvent(const PickedObject& target, const QVariantMap& pickResult) const = 0;
private:
PickedObject _prevHoveredObject;
Buttons _prevButtons;
std::unordered_map<std::string, PickedObject> _triggeredObjects;
PointerEvent::Button chooseButton(const std::string& button);
};
#endif // hifi_Pick_h

View file

@ -79,7 +79,7 @@ void PointerManager::update() {
}
}
void PointerManager::setPrecisionPicking(const QUuid& uid, const bool precisionPicking) const {
void PointerManager::setPrecisionPicking(const QUuid& uid, bool precisionPicking) const {
auto pointer = find(uid);
if (pointer) {
pointer->setPrecisionPicking(precisionPicking);
@ -100,14 +100,14 @@ void PointerManager::setIncludeItems(const QUuid& uid, const QVector<QUuid>& inc
}
}
void PointerManager::setLength(const QUuid& uid, const float length) const {
void PointerManager::setLength(const QUuid& uid, float length) const {
auto pointer = find(uid);
if (pointer) {
pointer->setLength(length);
}
}
void PointerManager::setLockEndUUID(const QUuid& uid, const QUuid& objectID, const bool isOverlay) const {
void PointerManager::setLockEndUUID(const QUuid& uid, const QUuid& objectID, bool isOverlay) const {
auto pointer = find(uid);
if (pointer) {
pointer->setLockEndUUID(objectID, isOverlay);

View file

@ -31,12 +31,12 @@ public:
void editRenderState(const QUuid& uid, const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) const;
const QVariantMap getPrevPickResult(const QUuid& uid) const;
void setPrecisionPicking(const QUuid& uid, const bool precisionPicking) const;
void setPrecisionPicking(const QUuid& uid, bool precisionPicking) const;
void setIgnoreItems(const QUuid& uid, const QVector<QUuid>& ignoreEntities) const;
void setIncludeItems(const QUuid& uid, const QVector<QUuid>& includeEntities) const;
void setLength(const QUuid& uid, const float length) const;
void setLockEndUUID(const QUuid& uid, const QUuid& objectID, const bool isOverlay) const;
void setLength(const QUuid& uid, float length) const;
void setLockEndUUID(const QUuid& uid, const QUuid& objectID, bool isOverlay) const;
void update();
@ -45,13 +45,19 @@ private:
QHash<QUuid, std::shared_ptr<Pointer>> _pointers;
signals:
void triggerBegin(const QUuid& id, const PointerEvent& pointerEvent);
void triggerContinue(const QUuid& id, const PointerEvent& pointerEvent);
void triggerEnd(const QUuid& id, const PointerEvent& pointerEvent);
void triggerBeginOverlay(const QUuid& id, const PointerEvent& pointerEvent);
void triggerContinueOverlay(const QUuid& id, const PointerEvent& pointerEvent);
void triggerEndOverlay(const QUuid& id, const PointerEvent& pointerEvent);
void hoverBeginOverlay(const QUuid& id, const PointerEvent& pointerEvent);
void hoverContinueOverlay(const QUuid& id, const PointerEvent& pointerEvent);
void hoverEndOverlay(const QUuid& id, const PointerEvent& pointerEvent);
void hoverEnter(const QUuid& id, const PointerEvent& pointerEvent);
void hoverOver(const QUuid& id, const PointerEvent& pointerEvent);
void hoverLeave(const QUuid& id, const PointerEvent& pointerEvent);
void triggerBeginEntity(const QUuid& id, const PointerEvent& pointerEvent);
void triggerContinueEntity(const QUuid& id, const PointerEvent& pointerEvent);
void triggerEndEntity(const QUuid& id, const PointerEvent& pointerEvent);
void hoverBeginEntity(const QUuid& id, const PointerEvent& pointerEvent);
void hoverContinueEntity(const QUuid& id, const PointerEvent& pointerEvent);
void hoverEndEntity(const QUuid& id, const PointerEvent& pointerEvent);
};
#endif // hifi_pointers_PointerManager_h

View file

@ -39,7 +39,7 @@ public:
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, Qt::KeyboardModifiers keyboardModifiers);
Button button, uint32_t buttons = NoButtons, Qt::KeyboardModifiers keyboardModifiers = Qt::KeyboardModifier::NoModifier);
static QScriptValue toScriptValue(QScriptEngine* engine, const PointerEvent& event);
static void fromScriptValue(const QScriptValue& object, PointerEvent& event);
@ -55,6 +55,11 @@ public:
Button getButton() const { return _button; }
uint32_t getButtons() const { return _buttons; }
Qt::KeyboardModifiers getKeyboardModifiers() const { return _keyboardModifiers; }
bool shouldFocus() const { return _shouldFocus; }
void setType(EventType type) { _type = type; }
void setButton(Button button) { _button = button; }
void setShouldFocus(bool focus) { _shouldFocus = focus; }
private:
EventType _type;
@ -67,6 +72,8 @@ private:
Button _button { NoButtons }; // button associated with this event, (if type is Press, this will be the button that is pressed)
uint32_t _buttons { NoButtons }; // the current state of all the buttons.
Qt::KeyboardModifiers _keyboardModifiers; // set of keys held when event was generated
bool _shouldFocus { true };
};
QDebug& operator<<(QDebug& dbg, const PointerEvent& p);

View file

@ -32,7 +32,7 @@ var DEFAULT_SCRIPTS_COMBINED = [
"system/tablet-ui/tabletUI.js"
];
var DEFAULT_SCRIPTS_SEPARATE = [
"system/controllers/controllerScripts.js"
//"system/controllers/controllerScripts.js"
// "system/chat.js"
];