mirror of
https://github.com/overte-org/overte.git
synced 2025-04-08 04:34:38 +02:00
laserpointers generate pointerevents
This commit is contained in:
parent
befe583c5e
commit
cf34fe3345
20 changed files with 375 additions and 56 deletions
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
set(TARGET_NAME pointers)
|
||||
setup_hifi_library()
|
||||
GroupSources(src)
|
||||
link_hifi_libraries(shared)
|
||||
link_hifi_libraries(shared controllers)
|
||||
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
];
|
||||
|
||||
|
|
Loading…
Reference in a new issue