From d382893e754cee57b27d1fe32b2d6c69e91b6dc2 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Thu, 28 Feb 2019 16:51:19 -0800 Subject: [PATCH] staging avatar inputs for ignore radius --- interface/src/Application.cpp | 10 ++ interface/src/avatar/AvatarManager.cpp | 2 + interface/src/ui/AvatarInputs.cpp | 3 + interface/src/ui/AvatarInputs.h | 25 +++ interface/src/ui/PrivacyShield.cpp | 147 ++++++++++++++++++ interface/src/ui/PrivacyShield.h | 35 +++++ libraries/avatars/src/AvatarData.h | 1 + .../UserActivityLoggerScriptingInterface.cpp | 8 +- .../UserActivityLoggerScriptingInterface.h | 4 +- scripts/system/bubble.js | 6 +- 10 files changed, 232 insertions(+), 9 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 635932ea1c..e34ae4bcba 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -211,6 +211,7 @@ #include "ui/UpdateDialog.h" #include "ui/DomainConnectionModel.h" #include "ui/Keyboard.h" +#include "ui/PrivacyShield.h" #include "Util.h" #include "InterfaceParentFinder.h" #include "ui/OctreeStatsProvider.h" @@ -927,6 +928,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); + DependencyManager::set(); return previousSessionCrashed; } @@ -2650,6 +2652,9 @@ void Application::cleanupBeforeQuit() { nodeList->getPacketReceiver().setShouldDropPackets(true); } + // destroy privacy shield before entity shutdown. + DependencyManager::get()->destroyPrivacyShield(); + getEntities()->shutdown(); // tell the entities system we're shutting down, so it will stop running scripts // Clear any queued processing (I/O, FBX/OBJ/Texture parsing) @@ -2728,6 +2733,7 @@ void Application::cleanupBeforeQuit() { DependencyManager::destroy(); DependencyManager::destroy(); DependencyManager::destroy(); + DependencyManager::destroy(); DependencyManager::destroy(); qCDebug(interfaceapp) << "Application::cleanupBeforeQuit() complete"; @@ -5528,6 +5534,8 @@ void Application::resumeAfterLoginDialogActionTaken() { menu->getMenu("Developer")->setVisible(_developerMenuVisible); _myCamera.setMode(_previousCameraMode); cameraModeChanged(); + + DependencyManager::get()->createPrivacyShield(); } void Application::loadAvatarScripts(const QVector& urls) { @@ -6486,6 +6494,8 @@ void Application::update(float deltaTime) { updateLoginDialogPosition(); } + DependencyManager::get()->update(deltaTime); + { PROFILE_RANGE_EX(app, "Overlays", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount()); PerformanceTimer perfTimer("overlays"); diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 69f7054953..76612039db 100755 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include "Application.h" #include "InterfaceLogging.h" @@ -536,6 +537,7 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar avatar->removeAvatarEntitiesFromTree(); if (removalReason == KillAvatarReason::TheirAvatarEnteredYourBubble) { + emit AvatarInputs::getInstance()->avatarEnteredIgnoreRadius(avatar->getSessionUUID()); emit DependencyManager::get()->enteredIgnoreRadius(); } else if (removalReason == KillAvatarReason::AvatarDisconnected) { // remove from node sets, if present diff --git a/interface/src/ui/AvatarInputs.cpp b/interface/src/ui/AvatarInputs.cpp index 6d43507a3e..80604f354b 100644 --- a/interface/src/ui/AvatarInputs.cpp +++ b/interface/src/ui/AvatarInputs.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include "Application.h" #include "Menu.h" @@ -31,7 +32,9 @@ AvatarInputs* AvatarInputs::getInstance() { AvatarInputs::AvatarInputs(QObject* parent) : QObject(parent) { _showAudioTools = showAudioToolsSetting.get(); auto nodeList = DependencyManager::get(); + auto usersScriptingInterface = DependencyManager::get(); connect(nodeList.data(), &NodeList::ignoreRadiusEnabledChanged, this, &AvatarInputs::ignoreRadiusEnabledChanged); + connect(usersScriptingInterface.data(), &UsersScriptingInterface::enteredIgnoreRadius, this, &AvatarInputs::enteredIgnoreRadiusChanged); } #define AI_UPDATE(name, src) \ diff --git a/interface/src/ui/AvatarInputs.h b/interface/src/ui/AvatarInputs.h index a41fd0485f..f53adc1749 100644 --- a/interface/src/ui/AvatarInputs.h +++ b/interface/src/ui/AvatarInputs.h @@ -43,6 +43,7 @@ class AvatarInputs : public QObject { Q_PROPERTY(bool showAudioTools READ showAudioTools WRITE setShowAudioTools NOTIFY showAudioToolsChanged) Q_PROPERTY(bool ignoreRadiusEnabled READ getIgnoreRadiusEnabled NOTIFY ignoreRadiusEnabledChanged) + //Q_PROPERTY(bool enteredIgnoreRadius READ getEnteredIgnoreRadius NOTIFY enteredIgnoreRadiusChanged) public: static AvatarInputs* getInstance(); @@ -58,6 +59,7 @@ public: void update(); bool showAudioTools() const { return _showAudioTools; } bool getIgnoreRadiusEnabled() const; + //bool getEnteredIgnoreRadius() const; public slots: @@ -95,6 +97,20 @@ signals: */ void showAudioToolsChanged(bool show); + /**jsdoc + * @function AvatarInputs.avatarEnteredIgnoreRadius + * @param {QUuid} avatarID + * @returns {Signal} + */ + void avatarEnteredIgnoreRadius(QUuid avatarID); + + /**jsdoc + * @function AvatarInputs.avatarLeftIgnoreRadius + * @param {QUuid} avatarID + * @returns {Signal} + */ + void avatarLeftIgnoreRadius(QUuid avatarID); + /**jsdoc * @function AvatarInputs.ignoreRadiusEnabledChanged * @param {boolean} enabled @@ -102,6 +118,13 @@ signals: */ void ignoreRadiusEnabledChanged(bool enabled); + /**jsdoc + * @function AvatarInputs.enteredIgnoreRadiusChanged + * @param {boolean} enabled + * @returns {Signal} + */ + void enteredIgnoreRadiusChanged(); + protected: /**jsdoc @@ -115,6 +138,8 @@ protected: Q_INVOKABLE void toggleCameraMute(); private: + void onAvatarEnteredIgnoreRadius(); + void onAvatarLeftIgnoreRadius(); float _trailingAudioLoudness{ 0 }; bool _showAudioTools { false }; }; diff --git a/interface/src/ui/PrivacyShield.cpp b/interface/src/ui/PrivacyShield.cpp index 12687afbea..e8f61ff5bf 100644 --- a/interface/src/ui/PrivacyShield.cpp +++ b/interface/src/ui/PrivacyShield.cpp @@ -10,3 +10,150 @@ // #include "PrivacyShield.h" + +#include +#include +#include +#include +#include +#include + +#include "Application.h" +#include "PathUtils.h" +#include "GLMHelpers.h" + +const int PRIVACY_SHIELD_VISIBLE_DURATION_MS = 3000; +const int PRIVACY_SHIELD_RAISE_ANIMATION_DURATION_MS = 750; +const int PRIVACY_SHIELD_SOUND_RATE_LIMIT_MS = 15000; +const float PRIVACY_SHIELD_HEIGHT_SCALE = 0.15f; + +PrivacyShield::PrivacyShield() { + auto usersScriptingInterface = DependencyManager::get(); + //connect(usersScriptingInterface.data(), &UsersScriptingInterface::ignoreRadiusEnabledChanged, [this](bool enabled) { + // onPrivacyShieldToggled(enabled); + //}); + //connect(usersScriptingInterface.data(), &UsersScriptingInterface::enteredIgnoreRadius, this, &PrivacyShield::enteredIgnoreRadius); +} + +void PrivacyShield::createPrivacyShield() { + // Affects bubble height + auto myAvatar = DependencyManager::get()->getMyAvatar(); + auto avatarScale = myAvatar->getTargetScale(); + auto avatarSensorToWorldScale = myAvatar->getSensorToWorldScale(); + auto avatarWorldPosition = myAvatar->getWorldPosition(); + auto avatarWorldOrientation = myAvatar->getWorldOrientation(); + EntityItemProperties properties; + properties.setName("Privacy-Shield"); + properties.setModelURL(PathUtils::resourcesUrl("assets/models/Bubble-v14.fbx").toString()); + properties.setDimensions(glm::vec3(avatarSensorToWorldScale, 0.75 * avatarSensorToWorldScale, avatarSensorToWorldScale)); + properties.setPosition(glm::vec3(avatarWorldPosition.x, + -avatarScale * 2 + avatarWorldPosition.y + avatarScale * PRIVACY_SHIELD_HEIGHT_SCALE, avatarWorldPosition.z)); + properties.setRotation(avatarWorldOrientation * Quaternions::Y_180); + properties.setModelScale(glm::vec3(2.0, 0.5 * (avatarScale + 1.0), 2.0)); + properties.setVisible(false); + + _localPrivacyShieldID = DependencyManager::get()->addEntityInternal(properties, entity::HostType::LOCAL); + //_bubbleActivateSound = DependencyManager::get()->getSound(PathUtils::resourcesUrl() + "assets/sounds/bubble.wav"); + + //onPrivacyShieldToggled(DependencyManager::get()->getIgnoreRadiusEnabled(), true); +} + +void PrivacyShield::destroyPrivacyShield() { + DependencyManager::get()->deleteEntity(_localPrivacyShieldID); +} + +void PrivacyShield::update(float deltaTime) { + if (_updateConnected) { + auto now = usecTimestampNow(); + auto delay = (now - _privacyShieldTimestamp); + auto privacyShieldAlpha = 1.0 - (delay / PRIVACY_SHIELD_VISIBLE_DURATION_MS); + if (privacyShieldAlpha > 0) { + auto myAvatar = DependencyManager::get()->getMyAvatar(); + auto avatarScale = myAvatar->getTargetScale(); + auto avatarSensorToWorldScale = myAvatar->getSensorToWorldScale(); + auto avatarWorldPosition = myAvatar->getWorldPosition(); + auto avatarWorldOrientation = myAvatar->getWorldOrientation(); + EntityItemProperties properties; + properties.setDimensions(glm::vec3(avatarSensorToWorldScale, 0.75 * avatarSensorToWorldScale, avatarSensorToWorldScale)); + properties.setRotation(avatarWorldOrientation * Quaternions::Y_180); + if (delay < PRIVACY_SHIELD_RAISE_ANIMATION_DURATION_MS) { + properties.setPosition(glm::vec3(avatarWorldPosition.x, + (-((PRIVACY_SHIELD_RAISE_ANIMATION_DURATION_MS - delay) / PRIVACY_SHIELD_RAISE_ANIMATION_DURATION_MS)) * avatarScale * 2.0 + + avatarWorldPosition.y + avatarScale * PRIVACY_SHIELD_HEIGHT_SCALE, avatarWorldPosition.z)); + properties.setModelScale(glm::vec3(2.0, + ((1 - ((PRIVACY_SHIELD_RAISE_ANIMATION_DURATION_MS - delay) / PRIVACY_SHIELD_RAISE_ANIMATION_DURATION_MS)) * + (0.5 * (avatarScale + 1.0))), 2.0)); + } else { + properties.setPosition(glm::vec3(avatarWorldPosition.x, avatarWorldPosition.y + avatarScale * PRIVACY_SHIELD_HEIGHT_SCALE, avatarWorldPosition.z)); + properties.setModelScale(glm::vec3(2.0, 0.5 * (avatarScale + 1.0), 2.0)); + } + DependencyManager::get()->editEntity(_localPrivacyShieldID, properties); + } + else { + hidePrivacyShield(); + if (_updateConnected) { + _updateConnected = false; + } + } + } +} + +void PrivacyShield::enteredIgnoreRadius() { + showPrivacyShield(); + DependencyManager::get()->privacyShieldActivated(); +} + +void PrivacyShield::onPrivacyShieldToggled(bool enabled, bool doNotLog) { + if (!doNotLog) { + DependencyManager::get()->privacyShieldToggled(enabled); + } + if (enabled) { + showPrivacyShield(); + } else { + hidePrivacyShield(); + if (_updateConnected) { + _updateConnected = false; + } + } +} + +void PrivacyShield::showPrivacyShield() { + auto now = usecTimestampNow(); + auto myAvatar = DependencyManager::get()->getMyAvatar(); + auto avatarScale = myAvatar->getTargetScale(); + auto avatarSensorToWorldScale = myAvatar->getSensorToWorldScale(); + auto avatarWorldPosition = myAvatar->getWorldPosition(); + auto avatarWorldOrientation = myAvatar->getWorldOrientation(); + if (now - _lastPrivacyShieldSoundTimestamp >= PRIVACY_SHIELD_SOUND_RATE_LIMIT_MS) { + AudioInjectorOptions options; + options.position = avatarWorldPosition; + options.localOnly = true; + options.volume = 0.2f; + AudioInjector::playSoundAndDelete(_bubbleActivateSound, options); + _lastPrivacyShieldSoundTimestamp = now; + } + hidePrivacyShield(); + if (_updateConnected) { + _updateConnected = false; + } + + EntityItemProperties properties; + properties.setDimensions(glm::vec3(avatarSensorToWorldScale, 0.75 * avatarSensorToWorldScale, avatarSensorToWorldScale)); + properties.setPosition(glm::vec3(avatarWorldPosition.x, + -avatarScale * 2 + avatarWorldPosition.y + avatarScale * PRIVACY_SHIELD_HEIGHT_SCALE, avatarWorldPosition.z)); + properties.setModelScale(glm::vec3(2.0, 0.5 * (avatarScale + 1.0), 2.0)); + properties.setVisible(true); + + DependencyManager::get()->editEntity(_localPrivacyShieldID, properties); + + _privacyShieldTimestamp = now; + _updateConnected = true; +} + +void PrivacyShield::hidePrivacyShield() { + EntityTreePointer entityTree = qApp->getEntities()->getTree(); + EntityItemPointer privacyShieldEntity = entityTree->findEntityByEntityItemID(EntityItemID(_localPrivacyShieldID)); + if (privacyShieldEntity) { + privacyShieldEntity->setVisible(false); + } +} diff --git a/interface/src/ui/PrivacyShield.h b/interface/src/ui/PrivacyShield.h index a609f2775b..5aecb661f7 100644 --- a/interface/src/ui/PrivacyShield.h +++ b/interface/src/ui/PrivacyShield.h @@ -10,3 +10,38 @@ // #pragma once + +#include +#include +#include + +#include +#include + +class PrivacyShield : public QObject, public Dependency { + Q_OBJECT + SINGLETON_DEPENDENCY + +public: + PrivacyShield(); + void createPrivacyShield(); + void destroyPrivacyShield(); + + bool isVisible() const { return _visible; } + void update(float deltaTime); + +protected slots: + void enteredIgnoreRadius(); + void onPrivacyShieldToggled(bool enabled, bool doNotLog = false); + +private: + void showPrivacyShield(); + void hidePrivacyShield(); + + SharedSoundPointer _bubbleActivateSound; + QUuid _localPrivacyShieldID; + quint64 _privacyShieldTimestamp; + quint64 _lastPrivacyShieldSoundTimestamp; + bool _visible { false }; + bool _updateConnected { false }; +}; diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 95bbcbeb16..32f53f77a3 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -1477,6 +1477,7 @@ protected: glm::vec3 _globalBoundingBoxOffset; AABox _defaultBubbleBox; + AABox _fitBoundingBox; mutable ReadWriteLockable _avatarEntitiesLock; AvatarEntityIDs _avatarEntityRemoved; // recently removed AvatarEntity ids diff --git a/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp b/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp index c63170de75..2f47ef5e00 100644 --- a/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp +++ b/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp @@ -71,12 +71,12 @@ void UserActivityLoggerScriptingInterface::makeUserConnection(QString otherID, b doLogAction("makeUserConnection", payload); } -void UserActivityLoggerScriptingInterface::bubbleToggled(bool newValue) { - doLogAction(newValue ? "bubbleOn" : "bubbleOff"); +void UserActivityLoggerScriptingInterface::privacyShieldToggled(bool newValue) { + doLogAction(newValue ? "privacyShieldOn" : "privacyShieldOff"); } -void UserActivityLoggerScriptingInterface::bubbleActivated() { - doLogAction("bubbleActivated"); +void UserActivityLoggerScriptingInterface::privacyShieldActivated() { + doLogAction("privacyShieldActivated"); } void UserActivityLoggerScriptingInterface::logAction(QString action, QVariantMap details) { diff --git a/libraries/networking/src/UserActivityLoggerScriptingInterface.h b/libraries/networking/src/UserActivityLoggerScriptingInterface.h index 71d411056d..1cda1235e9 100644 --- a/libraries/networking/src/UserActivityLoggerScriptingInterface.h +++ b/libraries/networking/src/UserActivityLoggerScriptingInterface.h @@ -30,8 +30,8 @@ public: Q_INVOKABLE void palAction(QString action, QString target); Q_INVOKABLE void palOpened(float secondsOpen); Q_INVOKABLE void makeUserConnection(QString otherUser, bool success, QString details = ""); - Q_INVOKABLE void bubbleToggled(bool newValue); - Q_INVOKABLE void bubbleActivated(); + Q_INVOKABLE void privacyShieldToggled(bool newValue); + Q_INVOKABLE void privacyShieldActivated(); Q_INVOKABLE void logAction(QString action, QVariantMap details = QVariantMap{}); Q_INVOKABLE void commercePurchaseSuccess(QString marketplaceID, QString contentCreator, int cost, bool firstPurchaseOfThisItem); Q_INVOKABLE void commercePurchaseFailure(QString marketplaceID, QString contentCreator, int cost, bool firstPurchaseOfThisItem, QString errorDetails); diff --git a/scripts/system/bubble.js b/scripts/system/bubble.js index 6ca624872e..eca3b3dcd4 100644 --- a/scripts/system/bubble.js +++ b/scripts/system/bubble.js @@ -90,7 +90,7 @@ // Called from the C++ scripting interface to show the bubble overlay function enteredIgnoreRadius() { createOverlays(); - UserActivityLogger.bubbleActivated(); + UserActivityLogger.privacyShieldActivated(); } // Used to set the state of the bubble HUD button @@ -160,7 +160,7 @@ function onBubbleToggled(enabled, doNotLog) { writeButtonProperties(enabled); if (doNotLog !== true) { - UserActivityLogger.bubbleToggled(enabled); + UserActivityLogger.privacyShieldToggled(enabled); } if (enabled) { createOverlays(); @@ -174,7 +174,7 @@ } // Setup the bubble button - var buttonName = "BUBBLE"; + var buttonName = "SHIELD"; var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); button = tablet.addButton({ icon: "icons/tablet-icons/bubble-i.svg",