laserpointers generate pointerevents

This commit is contained in:
SamGondelman 2017-10-25 16:04:21 -07:00
parent befe583c5e
commit cf34fe3345
20 changed files with 375 additions and 56 deletions

View file

@ -5919,6 +5919,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,
LaserPointer::LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, const PointerTriggers& triggers,
const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool enabled) :
Pointer(DependencyManager::get<PickScriptingInterface>()->createRayPick(rayProps)),
_renderingEnabled(enabled),
Pointer(DependencyManager::get<PickScriptingInterface>()->createRayPick(rayProps), enabled),
_triggers(triggers),
_renderStates(renderStates),
_defaultRenderStates(defaultRenderStates),
_faceAvatar(faceAvatar),
@ -49,17 +49,9 @@ LaserPointer::~LaserPointer() {
}
}
void LaserPointer::enable() {
Pointer::enable();
withWriteLock([&] {
_renderingEnabled = true;
});
}
void LaserPointer::disable() {
Pointer::disable();
Parent::disable();
withWriteLock([&] {
_renderingEnabled = false;
if (!_currentRenderState.empty()) {
if (_renderStates.find(_currentRenderState) != _renderStates.end()) {
disableRenderState(_renderStates[_currentRenderState]);
@ -199,26 +191,37 @@ 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);
}
}
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(const float length) {
@ -290,4 +293,53 @@ RenderState LaserPointer::buildRenderState(const QVariantMap& propMap) {
}
return RenderState(startID, pathID, endID);
}
PointerEvent LaserPointer::buildPointerEvent(const QUuid& uid, const QVariantMap& pickResult) const {
uint32_t id = 0;
glm::vec3 intersection = vec3FromVariant(pickResult["intersection"]);
glm::vec3 surfaceNormal = vec3FromVariant(pickResult["surfaceNormal"]);
glm::vec3 direction = -surfaceNormal;
IntersectionType type = IntersectionType(pickResult["type"].toUInt());
glm::vec2 pos2D;
if (type == ENTITY) {
pos2D = projectOntoEntityXYPlane(uid, intersection);
} else if (type == OVERLAY) {
pos2D = projectOntoOverlayXYPlane(uid, intersection);
}
return PointerEvent(PointerEvent::Move, id, pos2D, intersection, surfaceNormal, direction, PointerEvent::NoButtons);
}
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& entity, const glm::vec3& worldPos) const {
auto props = DependencyManager::get<EntityScriptingInterface>()->getEntityProperties(entity);
return projectOntoXYPlane(worldPos, props.getPosition(), props.getRotation(), props.getDimensions(), props.getRegistrationPoint());
}

View file

@ -49,16 +49,15 @@ 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,
LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, const PointerTriggers& triggers,
const bool faceAvatar, const bool centerEndY, const bool lockEnd, const bool distanceScaleEnd, const bool enabled);
~LaserPointer();
void enable() override;
void disable() override;
void setRenderState(const std::string& state) override;
@ -68,12 +67,18 @@ public:
void setLength(const float length) override;
void setLockEndUUID(QUuid objectID, const 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 QUuid& uid, const QVariantMap& pickResult) const override;
private:
bool _renderingEnabled;
PointerTriggers _triggers;
float _laserLength { 0.0f };
std::string _currentRenderState { "" };
RenderStateMap _renderStates;
@ -88,6 +93,10 @@ private:
void updateRenderState(const RenderState& renderState, const IntersectionType type, const float distance, const QUuid& objectID, const PickRay& pickRay, const bool defaultState);
void disableRenderState(const RenderState& renderState);
glm::vec2 projectOntoEntityXYPlane(const QUuid& entity, 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,26 @@ QUuid PointerScriptingInterface::createLaserPointer(const QVariant& properties)
}
}
return DependencyManager::get<PointerManager>()->addPointer(std::make_shared<LaserPointer>(properties, renderStates, defaultRenderStates, faceAvatar, centerEndY, lockEnd, distanceScaleEnd, enabled));
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, 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

@ -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::hoverLeaveOverlay);
connect(pointerManager.data(), &PointerManager::triggerBeginOverlay, this, &Overlays::mousePressOnOverlay);
connect(pointerManager.data(), &PointerManager::triggerContinueOverlay, this, &Overlays::mouseMoveOnOverlay);
connect(pointerManager.data(), &PointerManager::triggerEndOverlay, this, &Overlays::mouseReleaseOnOverlay);
}
void Overlays::cleanupAllOverlays() {
QMap<OverlayID, Overlay::Pointer> overlaysHUD;
QMap<OverlayID, Overlay::Pointer> overlaysWorld;

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);

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

@ -12,6 +12,8 @@ include_hifi_library_headers(animation)
include_hifi_library_headers(fbx)
include_hifi_library_headers(entities)
include_hifi_library_headers(avatars)
include_hifi_library_headers(pointers)
include_hifi_library_headers(controllers)
target_bullet()

View file

@ -35,6 +35,8 @@
#include "EntitiesRendererLogging.h"
#include "RenderableEntityItem.h"
#include <pointers/PointerManager.h>
size_t std::hash<EntityItemID>::operator()(const EntityItemID& id) const { return qHash(id); }
std::function<bool()> EntityTreeRenderer::_entitiesShouldFadeFunction;
@ -55,6 +57,14 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
EntityRenderer::initEntityRenderers();
_currentHoverOverEntityID = UNKNOWN_ENTITY_ID;
_currentClickingOnEntityID = UNKNOWN_ENTITY_ID;
auto pointerManager = DependencyManager::get<PointerManager>();
connect(pointerManager.data(), &PointerManager::hoverBeginEntity, this, &EntityTreeRenderer::hoverEnterEntity);
connect(pointerManager.data(), &PointerManager::hoverContinueEntity, this, &EntityTreeRenderer::hoverOverEntity);
connect(pointerManager.data(), &PointerManager::hoverEndEntity, this, &EntityTreeRenderer::hoverLeaveEntity);
connect(pointerManager.data(), &PointerManager::triggerBeginEntity, this, &EntityTreeRenderer::mousePressOnEntity);
connect(pointerManager.data(), &PointerManager::triggerContinueEntity, this, &EntityTreeRenderer::mouseMoveOnEntity);
connect(pointerManager.data(), &PointerManager::triggerEndEntity, this, &EntityTreeRenderer::mouseReleaseOnEntity);
}
EntityTreeRenderer::~EntityTreeRenderer() {

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,10 +17,16 @@ Pointer::~Pointer() {
void Pointer::enable() {
DependencyManager::get<PickManager>()->enablePick(_pickUID);
withWriteLock([&] {
_enabled = true;
});
}
void Pointer::disable() {
DependencyManager::get<PickManager>()->disablePick(_pickUID);
withWriteLock([&] {
_enabled = false;
});
}
const QVariantMap Pointer::getPrevPickResult() {
@ -36,4 +43,118 @@ 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.objectID, pickResult);
hoveredEvent.setType(PointerEvent::Move);
hoveredEvent.setButton(PointerEvent::NoButtons);
if (_enabled) {
if (hoveredObject.type == OVERLAY) {
if (_prevHoveredObject.type == OVERLAY) {
if (hoveredObject.objectID == _prevHoveredObject.objectID) {
emit pointerManager->hoverContinueOverlay(hoveredObject.objectID, hoveredEvent);
} else {
PointerEvent prevHoveredEvent = buildPointerEvent(_prevHoveredObject.objectID, pickResult);
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.objectID, 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
for (const std::string& button : newButtons) {
hoveredEvent.setType(PointerEvent::Press);
hoveredEvent.setButton(PointerEvent::PrimaryButton);
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].objectID, pickResult);
triggeredEvent.setType(PointerEvent::Move);
triggeredEvent.setButton(PointerEvent::PrimaryButton);
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].objectID, pickResult);
triggeredEvent.setType(PointerEvent::Release);
triggeredEvent.setButton(PointerEvent::PrimaryButton);
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;
}

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) : _pickUID(uid), _enabled(enabled) {}
virtual ~Pointer();
@ -35,12 +57,35 @@ public:
virtual void setLength(const float length) {}
virtual void setLockEndUUID(QUuid objectID, const 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:
bool _enabled;
const QUuid _pickUID;
virtual PointerEvent buildPointerEvent(const QUuid& uid, const QVariantMap& pickResult) const = 0;
private:
PickedObject _prevHoveredObject;
Buttons _prevButtons;
std::unordered_map<std::string, PickedObject> _triggeredObjects;
};
#endif // hifi_Pick_h

View file

@ -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);
@ -56,6 +56,9 @@ public:
uint32_t getButtons() const { return _buttons; }
Qt::KeyboardModifiers getKeyboardModifiers() const { return _keyboardModifiers; }
void setType(EventType type) { _type = type; }
void setButton(Button button) { _button = button; }
private:
EventType _type;
uint32_t _id; // used to identify the pointer. (left vs right hand, for example)

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"
];