From a1660dad9558758a7c0ad1ef42a5f62739262f7b Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Fri, 22 Mar 2019 12:30:49 -0700 Subject: [PATCH 1/9] Follow dynamic updates to hero zones; make reserved fraction a domain setting --- assignment-client/src/avatars/AvatarMixer.cpp | 69 ++++++++++++++++--- assignment-client/src/avatars/AvatarMixer.h | 11 ++- .../src/avatars/AvatarMixerClientData.cpp | 17 ++--- .../src/avatars/AvatarMixerSlave.cpp | 7 +- .../src/avatars/AvatarMixerSlave.h | 4 +- .../src/avatars/AvatarMixerSlavePool.cpp | 3 +- .../src/avatars/AvatarMixerSlavePool.h | 9 ++- assignment-client/src/avatars/MixerAvatar.h | 4 ++ .../resources/describe-settings.json | 9 +++ 9 files changed, 104 insertions(+), 29 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 33e1034128..ffe084bc33 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -253,10 +253,26 @@ void AvatarMixer::start() { int lockWait, nodeTransform, functor; + // Set our query each frame { _entityViewer.queryOctree(); } + // Dirty the hero status if there's been an entity change. + { + if (_dirtyHeroStatus) { + _dirtyHeroStatus = false; + nodeList->nestedEach([&](NodeList::const_iterator cbegin, NodeList::const_iterator cend) { + std::for_each(cbegin, cend, [](const SharedNodePointer& node) { + if (node->getType() == NodeType::Agent) { + auto& avatar = static_cast(node->getLinkedData())->getAvatar(); + avatar.setNeedsHeroCheck(); + } + }); + }); + } + } + // Allow nodes to process any pending/queued packets across our worker threads { auto start = usecTimestampNow(); @@ -973,19 +989,31 @@ void AvatarMixer::parseDomainServerSettings(const QJsonObject& domainSettings) { { const QString CONNECTION_RATE = "connection_rate"; auto nodeList = DependencyManager::get(); - auto defaultConnectionRate = nodeList->getMaxConnectionRate(); - int connectionRate = avatarMixerGroupObject[CONNECTION_RATE].toInt((int)defaultConnectionRate); - nodeList->setMaxConnectionRate(connectionRate); + bool success; + int connectionRate = avatarMixerGroupObject[CONNECTION_RATE].toString().toInt(&success); + if (success) { + nodeList->setMaxConnectionRate(connectionRate); + } + } + + { // Fraction of downstream bandwidth reserved for 'hero' avatars: + static const QString PRIORITY_FRACTION_KEY = "priority_fraction"; + if (avatarMixerGroupObject.contains(PRIORITY_FRACTION_KEY)) { + bool isDouble = avatarMixerGroupObject[PRIORITY_FRACTION_KEY].isDouble(); + float priorityFraction = float(avatarMixerGroupObject[PRIORITY_FRACTION_KEY].toDouble()); + _slavePool.setPriorityReservedFraction(std::min(std::max(0.0f, priorityFraction), 1.0f)); + qCDebug(avatars) << "Avatar mixer reserving" << priorityFraction << "of bandwidth for priority avatars"; + } } const QString AVATARS_SETTINGS_KEY = "avatars"; static const QString MIN_HEIGHT_OPTION = "min_avatar_height"; - float settingMinHeight = domainSettings[AVATARS_SETTINGS_KEY].toObject()[MIN_HEIGHT_OPTION].toDouble(MIN_AVATAR_HEIGHT); + float settingMinHeight = avatarMixerGroupObject[MIN_HEIGHT_OPTION].toDouble(MIN_AVATAR_HEIGHT); _domainMinimumHeight = glm::clamp(settingMinHeight, MIN_AVATAR_HEIGHT, MAX_AVATAR_HEIGHT); static const QString MAX_HEIGHT_OPTION = "max_avatar_height"; - float settingMaxHeight = domainSettings[AVATARS_SETTINGS_KEY].toObject()[MAX_HEIGHT_OPTION].toDouble(MAX_AVATAR_HEIGHT); + float settingMaxHeight = avatarMixerGroupObject[MAX_HEIGHT_OPTION].toDouble(MAX_AVATAR_HEIGHT); _domainMaximumHeight = glm::clamp(settingMaxHeight, MIN_AVATAR_HEIGHT, MAX_AVATAR_HEIGHT); // make sure that the domain owner didn't flip min and max @@ -997,11 +1025,11 @@ void AvatarMixer::parseDomainServerSettings(const QJsonObject& domainSettings) { << "and a maximum avatar height of" << _domainMaximumHeight; static const QString AVATAR_WHITELIST_OPTION = "avatar_whitelist"; - _slaveSharedData.skeletonURLWhitelist = domainSettings[AVATARS_SETTINGS_KEY].toObject()[AVATAR_WHITELIST_OPTION] + _slaveSharedData.skeletonURLWhitelist = avatarMixerGroupObject[AVATAR_WHITELIST_OPTION] .toString().split(',', QString::KeepEmptyParts); static const QString REPLACEMENT_AVATAR_OPTION = "replacement_avatar"; - _slaveSharedData.skeletonReplacementURL = domainSettings[AVATARS_SETTINGS_KEY].toObject()[REPLACEMENT_AVATAR_OPTION] + _slaveSharedData.skeletonReplacementURL = avatarMixerGroupObject[REPLACEMENT_AVATAR_OPTION] .toString(); if (_slaveSharedData.skeletonURLWhitelist.count() == 1 && _slaveSharedData.skeletonURLWhitelist[0].isEmpty()) { @@ -1018,9 +1046,12 @@ void AvatarMixer::parseDomainServerSettings(const QJsonObject& domainSettings) { void AvatarMixer::setupEntityQuery() { _entityViewer.init(); + EntityTreePointer entityTree = _entityViewer.getTree(); DependencyManager::registerInheritance(); - DependencyManager::set(_entityViewer.getTree()); - _slaveSharedData.entityTree = _entityViewer.getTree(); + DependencyManager::set(entityTree); + + connect(entityTree.get(), &EntityTree::addingEntityPointer, this, &AvatarMixer::entityAdded); + connect(entityTree.get(), &EntityTree::deletingEntityPointer, this, &AvatarMixer::entityChange); // ES query: {"avatarPriority": true, "type": "Zone"} QJsonObject priorityZoneQuery; @@ -1028,6 +1059,7 @@ void AvatarMixer::setupEntityQuery() { priorityZoneQuery["type"] = "Zone"; _entityViewer.getOctreeQuery().setJSONParameters(priorityZoneQuery); + _slaveSharedData.entityTree = entityTree; } void AvatarMixer::handleOctreePacket(QSharedPointer message, SharedNodePointer senderNode) { @@ -1064,6 +1096,25 @@ void AvatarMixer::handleOctreePacket(QSharedPointer message, Sh } } +void AvatarMixer::entityAdded(EntityItem* entity) { + if (entity->getType() == EntityTypes::Zone) { + _dirtyHeroStatus = true; + entity->registerChangeHandler([this](const EntityItemID& entityItemID) { + this->entityChange(); + }); + } +} + +void AvatarMixer::entityRemoved(EntityItem * entity) { + if (entity->getType() == EntityTypes::Zone) { + _dirtyHeroStatus = true; + } +} + +void AvatarMixer::entityChange() { + _dirtyHeroStatus = true; +} + void AvatarMixer::aboutToFinish() { DependencyManager::destroy(); DependencyManager::destroy(); diff --git a/assignment-client/src/avatars/AvatarMixer.h b/assignment-client/src/avatars/AvatarMixer.h index 9393ea6c56..f65f04f279 100644 --- a/assignment-client/src/avatars/AvatarMixer.h +++ b/assignment-client/src/avatars/AvatarMixer.h @@ -34,8 +34,8 @@ public: static bool shouldReplicateTo(const Node& from, const Node& to) { return to.getType() == NodeType::DownstreamAvatarMixer && - to.getPublicSocket() != from.getPublicSocket() && - to.getLocalSocket() != from.getLocalSocket(); + to.getPublicSocket() != from.getPublicSocket() && + to.getLocalSocket() != from.getLocalSocket(); } public slots: @@ -80,6 +80,7 @@ private: // Attach to entity tree for avatar-priority zone info. EntityTreeHeadlessViewer _entityViewer; + bool _dirtyHeroStatus { true }; // Dirty the needs-hero-update // FIXME - new throttling - use these values somehow float _trailingMixRatio { 0.0f }; @@ -146,6 +147,12 @@ private: AvatarMixerSlavePool _slavePool; SlaveSharedData _slaveSharedData; + +public slots: + // Avatar zone possibly changed + void entityAdded(EntityItem* entity); + void entityRemoved(EntityItem* entity); + void entityChange(); }; #endif // hifi_AvatarMixer_h diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index 557c5c9fe3..a6b675efa4 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -141,22 +141,15 @@ int AvatarMixerClientData::parseData(ReceivedMessage& message, const SlaveShared _avatar->setHasPriorityWithoutTimestampReset(oldHasPriority); auto newPosition = getPosition(); - if (newPosition != oldPosition) { -//#define AVATAR_HERO_TEST_HACK -#ifdef AVATAR_HERO_TEST_HACK - { - const static QString heroKey { "HERO" }; - _avatar->setPriorityAvatar(_avatar->getDisplayName().contains(heroKey)); - } -#else + if (newPosition != oldPosition || _avatar->getNeedsHeroCheck()) { EntityTree& entityTree = *slaveSharedData.entityTree; FindPriorityZone findPriorityZone { newPosition, false } ; entityTree.recurseTreeWithOperation(&FindPriorityZone::operation, &findPriorityZone); _avatar->setHasPriority(findPriorityZone.isInPriorityZone); - //if (findPriorityZone.isInPriorityZone) { - // qCWarning(avatars) << "Avatar" << _avatar->getSessionDisplayName() << "in hero zone"; - //} -#endif + _avatar->setNeedsHeroCheck(false); + if (findPriorityZone.isInPriorityZone) { + qCWarning(avatars) << "Avatar" << _avatar->getSessionDisplayName() << "in hero zone"; + } } return true; diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index e59c81f4b7..e7de764708 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -43,12 +43,14 @@ void AvatarMixerSlave::configure(ConstIter begin, ConstIter end) { void AvatarMixerSlave::configureBroadcast(ConstIter begin, ConstIter end, p_high_resolution_clock::time_point lastFrameTimestamp, - float maxKbpsPerNode, float throttlingRatio) { + float maxKbpsPerNode, float throttlingRatio, + float priorityReservedFraction) { _begin = begin; _end = end; _lastFrameTimestamp = lastFrameTimestamp; _maxKbpsPerNode = maxKbpsPerNode; _throttlingRatio = throttlingRatio; + _avatarHeroFraction = priorityReservedFraction; } void AvatarMixerSlave::harvestStats(AvatarMixerSlaveStats& stats) { @@ -308,7 +310,6 @@ namespace { } // Close anonymous namespace. void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) { - const float AVATAR_HERO_FRACTION { 0.4f }; const Node* destinationNode = node.data(); auto nodeList = DependencyManager::get(); @@ -343,7 +344,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) // max number of avatarBytes per frame (13 900, typical) const int maxAvatarBytesPerFrame = int(_maxKbpsPerNode * BYTES_PER_KILOBIT / AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND); - const int maxHeroBytesPerFrame = int(maxAvatarBytesPerFrame * AVATAR_HERO_FRACTION); // 5555, typical + const int maxHeroBytesPerFrame = int(maxAvatarBytesPerFrame * _avatarHeroFraction); // 5555, typical // keep track of the number of other avatars held back in this frame int numAvatarsHeldBack = 0; diff --git a/assignment-client/src/avatars/AvatarMixerSlave.h b/assignment-client/src/avatars/AvatarMixerSlave.h index 8c5ad6b181..f14e50e11f 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.h +++ b/assignment-client/src/avatars/AvatarMixerSlave.h @@ -110,7 +110,8 @@ public: void configure(ConstIter begin, ConstIter end); void configureBroadcast(ConstIter begin, ConstIter end, p_high_resolution_clock::time_point lastFrameTimestamp, - float maxKbpsPerNode, float throttlingRatio); + float maxKbpsPerNode, float throttlingRatio, + float priorityReservedFraction); void processIncomingPackets(const SharedNodePointer& node); void broadcastAvatarData(const SharedNodePointer& node); @@ -140,6 +141,7 @@ private: p_high_resolution_clock::time_point _lastFrameTimestamp; float _maxKbpsPerNode { 0.0f }; float _throttlingRatio { 0.0f }; + float _avatarHeroFraction { 0.4f }; AvatarMixerSlaveStats _stats; SlaveSharedData* _sharedData; diff --git a/assignment-client/src/avatars/AvatarMixerSlavePool.cpp b/assignment-client/src/avatars/AvatarMixerSlavePool.cpp index 013d914cbe..357b347a94 100644 --- a/assignment-client/src/avatars/AvatarMixerSlavePool.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlavePool.cpp @@ -76,7 +76,8 @@ void AvatarMixerSlavePool::broadcastAvatarData(ConstIter begin, ConstIter end, float maxKbpsPerNode, float throttlingRatio) { _function = &AvatarMixerSlave::broadcastAvatarData; _configure = [=](AvatarMixerSlave& slave) { - slave.configureBroadcast(begin, end, lastFrameTimestamp, maxKbpsPerNode, throttlingRatio); + slave.configureBroadcast(begin, end, lastFrameTimestamp, maxKbpsPerNode, throttlingRatio, + _priorityReservedFraction); }; run(begin, end); } diff --git a/assignment-client/src/avatars/AvatarMixerSlavePool.h b/assignment-client/src/avatars/AvatarMixerSlavePool.h index 71a9ace0d3..b05abde2a3 100644 --- a/assignment-client/src/avatars/AvatarMixerSlavePool.h +++ b/assignment-client/src/avatars/AvatarMixerSlavePool.h @@ -73,7 +73,10 @@ public: void each(std::function functor); void setNumThreads(int numThreads); - int numThreads() { return _numThreads; } + int numThreads() const { return _numThreads; } + + void setPriorityReservedFraction(float fraction) { _priorityReservedFraction = fraction; } + float getPriorityReservedFraction() const { return _priorityReservedFraction; } private: void run(ConstIter begin, ConstIter end); @@ -91,7 +94,11 @@ private: ConditionVariable _poolCondition; void (AvatarMixerSlave::*_function)(const SharedNodePointer& node); std::function _configure; + + // Set from Domain Settings: + float _priorityReservedFraction { 0.4f }; int _numThreads { 0 }; + int _numStarted { 0 }; // guarded by _mutex int _numFinished { 0 }; // guarded by _mutex int _numStopped { 0 }; // guarded by _mutex diff --git a/assignment-client/src/avatars/MixerAvatar.h b/assignment-client/src/avatars/MixerAvatar.h index 3e80704495..01e5e91b44 100644 --- a/assignment-client/src/avatars/MixerAvatar.h +++ b/assignment-client/src/avatars/MixerAvatar.h @@ -19,8 +19,12 @@ class MixerAvatar : public AvatarData { public: + bool getNeedsHeroCheck() const { return _needsHeroCheck; } + void setNeedsHeroCheck(bool needsHeroCheck = true) + { _needsHeroCheck = needsHeroCheck; } private: + bool _needsHeroCheck { false }; }; using MixerAvatarSharedPointer = std::shared_ptr; diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 140c7d6c17..352106dcf7 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -1310,6 +1310,15 @@ "placeholder": "50", "default": "50", "advanced": true + }, + { + "name": "priority_fraction", + "type": "double", + "label": "Hero Bandwidth", + "help": "Fraction of downstream bandwidth reserved for avatars in 'Hero' zones", + "placeholder": "0.40", + "default": "0.40", + "advanced": true } ] }, From 137c25f907711cc3f926d06b40c463d06518e004 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Fri, 22 Mar 2019 15:40:13 -0700 Subject: [PATCH 2/9] Use a 1 m offset for position test; call-out nonavatars in stats web page --- assignment-client/src/avatars/AvatarMixer.cpp | 8 +++++++- assignment-client/src/avatars/AvatarMixerClientData.cpp | 8 ++++---- assignment-client/src/avatars/AvatarMixerClientData.h | 1 + 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index ffe084bc33..c6cd12f30a 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -843,7 +843,7 @@ void AvatarMixer::sendStatsPacket() { QJsonObject avatarsObject; auto nodeList = DependencyManager::get(); - // add stats for each listerner + // add stats for each listener nodeList->eachNode([&](const SharedNodePointer& node) { QJsonObject avatarStats; @@ -867,6 +867,12 @@ void AvatarMixer::sendStatsPacket() { avatarStats["delta_full_vs_avatar_data_kbps"] = (double)outboundAvatarDataKbps - avatarStats[OUTBOUND_AVATAR_DATA_STATS_KEY].toDouble(); } + + if (node->getType() != NodeType::Agent) { // Nodes that aren't avatars + const QString displayName + { node->getType() == NodeType::EntityScriptServer ? "ENTITY SCRIPT SERVER" : "ENTITY SERVER" }; + avatarStats["display_name"] = displayName; + } } avatarsObject[uuidStringWithoutCurlyBraces(node->getUUID())] = avatarStats; diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index a6b675efa4..4880f73226 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -23,6 +23,9 @@ #include "AvatarMixerSlave.h" +// Offset from reported position for priority-zone purposes: +const glm::vec3 AvatarMixerClientData::AVATAR_CENTER_OFFSET { 0.0f, 1.0f, 0.0 }; + AvatarMixerClientData::AvatarMixerClientData(const QUuid& nodeID, Node::LocalID nodeLocalID) : NodeData(nodeID, nodeLocalID) { // in case somebody calls getSessionUUID on the AvatarData instance, make sure it has the right ID @@ -143,13 +146,10 @@ int AvatarMixerClientData::parseData(ReceivedMessage& message, const SlaveShared auto newPosition = getPosition(); if (newPosition != oldPosition || _avatar->getNeedsHeroCheck()) { EntityTree& entityTree = *slaveSharedData.entityTree; - FindPriorityZone findPriorityZone { newPosition, false } ; + FindPriorityZone findPriorityZone { newPosition + AVATAR_CENTER_OFFSET } ; entityTree.recurseTreeWithOperation(&FindPriorityZone::operation, &findPriorityZone); _avatar->setHasPriority(findPriorityZone.isInPriorityZone); _avatar->setNeedsHeroCheck(false); - if (findPriorityZone.isInPriorityZone) { - qCWarning(avatars) << "Avatar" << _avatar->getSessionDisplayName() << "in hero zone"; - } } return true; diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index 98c8d7e15b..492dfc4720 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -223,6 +223,7 @@ private: PerNodeTraitVersions _perNodeSentTraitVersions; std::atomic_bool _isIgnoreRadiusEnabled { false }; + static const glm::vec3 AVATAR_CENTER_OFFSET; }; #endif // hifi_AvatarMixerClientData_h From d7a1ecdbb3ee5b808acb4f2d95b104e72eb568ca Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Fri, 22 Mar 2019 17:19:39 -0700 Subject: [PATCH 3/9] Expose hero-status to scripts --- interface/src/avatar/MyAvatar.h | 1 + libraries/avatars/src/AvatarData.cpp | 7 ++++--- libraries/avatars/src/AvatarData.h | 2 ++ libraries/avatars/src/ScriptAvatarData.cpp | 8 ++++++++ libraries/avatars/src/ScriptAvatarData.h | 4 ++++ 5 files changed, 19 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index aadc8ee268..ccfa629fea 100755 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -184,6 +184,7 @@ class MyAvatar : public Avatar { * @property {Mat4} controllerLeftHandMatrix Read-only. * @property {Mat4} controllerRightHandMatrix Read-only. * @property {number} sensorToWorldScale Read-only. + * @property {boolean} hasPriority - is the avatar in a Hero zone? Read-only */ // FIXME: `glm::vec3 position` is not accessible from QML, so this exposes position in a QML-native type Q_PROPERTY(QVector3D qmlPosition READ getQmlPosition) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 26407c3564..3355228eea 100755 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1143,10 +1143,11 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { // we store the hand state as well as other items in a shared bitset. The hand state is an octal, but is split // into two sections to maintain backward compatibility. The bits are ordered as such (0-7 left to right). // AA 6/1/18 added three more flags bits 8,9, and 10 for procedural audio, blink, and eye saccade enabled - // +---+-----+-----+--+--+--+--+-----+ - // |x,x|H0,H1|x,x,x|H2|Au|Bl|Ey|xxxxx| - // +---+-----+-----+--+--+--+--+-----+ + // +---+-----+-----+--+--+--+--+--+----+ + // |x,x|H0,H1|x,x,x|H2|Au|Bl|Ey|He|xxxx| + // +---+-----+-----+--+--+--+--+--+----+ // Hand state - H0,H1,H2 is found in the 3rd, 4th, and 8th bits + // Hero-avatar status (He) - 12th bit auto newHandState = getSemiNibbleAt(bitItems, HAND_STATE_START_BIT) + (oneAtBit16(bitItems, HAND_STATE_FINGER_POINTING_BIT) ? IS_FINGER_POINTING_FLAG : 0); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 95bbcbeb16..424384c15e 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -453,6 +453,8 @@ class AvatarData : public QObject, public SpatiallyNestable { Q_PROPERTY(float sensorToWorldScale READ getSensorToWorldScale) + Q_PROPERTY(bool hasPriority READ getHasPriority) + public: virtual QString getName() const override { return QString("Avatar:") + _displayName; } diff --git a/libraries/avatars/src/ScriptAvatarData.cpp b/libraries/avatars/src/ScriptAvatarData.cpp index a716a40ad8..18717c8ca3 100644 --- a/libraries/avatars/src/ScriptAvatarData.cpp +++ b/libraries/avatars/src/ScriptAvatarData.cpp @@ -343,6 +343,14 @@ glm::mat4 ScriptAvatarData::getControllerRightHandMatrix() const { // END // +bool ScriptAvatarData::getHasPriority() const { + if (AvatarSharedPointer sharedAvatarData = _avatarData.lock()) { + return sharedAvatarData->getHasPriority(); + } else { + return false; + } +} + glm::quat ScriptAvatarData::getAbsoluteJointRotationInObjectFrame(int index) const { if (AvatarSharedPointer sharedAvatarData = _avatarData.lock()) { return sharedAvatarData->getAbsoluteJointRotationInObjectFrame(index); diff --git a/libraries/avatars/src/ScriptAvatarData.h b/libraries/avatars/src/ScriptAvatarData.h index 91bac61728..01f7ff360a 100644 --- a/libraries/avatars/src/ScriptAvatarData.h +++ b/libraries/avatars/src/ScriptAvatarData.h @@ -68,6 +68,8 @@ class ScriptAvatarData : public QObject { Q_PROPERTY(glm::mat4 controllerLeftHandMatrix READ getControllerLeftHandMatrix) Q_PROPERTY(glm::mat4 controllerRightHandMatrix READ getControllerRightHandMatrix) + Q_PROPERTY(bool hasPriority READ getHasPriority) + public: ScriptAvatarData(AvatarSharedPointer avatarData); @@ -133,6 +135,8 @@ public: glm::mat4 getControllerLeftHandMatrix() const; glm::mat4 getControllerRightHandMatrix() const; + bool getHasPriority() const; + signals: void displayNameChanged(); void sessionDisplayNameChanged(); From 8872e9e4e7b318fef033ca8b174fc12d80125fcc Mon Sep 17 00:00:00 2001 From: Simon Walton <36682372+SimonWalton-HiFi@users.noreply.github.com> Date: Sat, 23 Mar 2019 20:05:26 -0900 Subject: [PATCH 4/9] Remove unused debugging variable --- assignment-client/src/avatars/AvatarMixer.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index c6cd12f30a..32fb5b9b1a 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -1005,7 +1005,6 @@ void AvatarMixer::parseDomainServerSettings(const QJsonObject& domainSettings) { { // Fraction of downstream bandwidth reserved for 'hero' avatars: static const QString PRIORITY_FRACTION_KEY = "priority_fraction"; if (avatarMixerGroupObject.contains(PRIORITY_FRACTION_KEY)) { - bool isDouble = avatarMixerGroupObject[PRIORITY_FRACTION_KEY].isDouble(); float priorityFraction = float(avatarMixerGroupObject[PRIORITY_FRACTION_KEY].toDouble()); _slavePool.setPriorityReservedFraction(std::min(std::max(0.0f, priorityFraction), 1.0f)); qCDebug(avatars) << "Avatar mixer reserving" << priorityFraction << "of bandwidth for priority avatars"; From 46f897b69330e92e1a41d9130b414afd2a0eceea Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Mon, 25 Mar 2019 15:42:30 -0700 Subject: [PATCH 5/9] Better estimate of avatar centre for zone membership --- assignment-client/src/avatars/AvatarMixerClientData.cpp | 9 +++------ assignment-client/src/avatars/AvatarMixerClientData.h | 1 - assignment-client/src/avatars/MixerAvatar.h | 3 +++ 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index 4880f73226..2175018824 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -23,9 +23,6 @@ #include "AvatarMixerSlave.h" -// Offset from reported position for priority-zone purposes: -const glm::vec3 AvatarMixerClientData::AVATAR_CENTER_OFFSET { 0.0f, 1.0f, 0.0 }; - AvatarMixerClientData::AvatarMixerClientData(const QUuid& nodeID, Node::LocalID nodeLocalID) : NodeData(nodeID, nodeLocalID) { // in case somebody calls getSessionUUID on the AvatarData instance, make sure it has the right ID @@ -132,7 +129,7 @@ int AvatarMixerClientData::parseData(ReceivedMessage& message, const SlaveShared incrementNumOutOfOrderSends(); } _lastReceivedSequenceNumber = sequenceNumber; - glm::vec3 oldPosition = getPosition(); + glm::vec3 oldPosition = _avatar->getCentroidPosition(); bool oldHasPriority = _avatar->getHasPriority(); // compute the offset to the data payload @@ -143,10 +140,10 @@ int AvatarMixerClientData::parseData(ReceivedMessage& message, const SlaveShared // Regardless of what the client says, restore the priority as we know it without triggering any update. _avatar->setHasPriorityWithoutTimestampReset(oldHasPriority); - auto newPosition = getPosition(); + auto newPosition = _avatar->getCentroidPosition(); if (newPosition != oldPosition || _avatar->getNeedsHeroCheck()) { EntityTree& entityTree = *slaveSharedData.entityTree; - FindPriorityZone findPriorityZone { newPosition + AVATAR_CENTER_OFFSET } ; + FindPriorityZone findPriorityZone { newPosition } ; entityTree.recurseTreeWithOperation(&FindPriorityZone::operation, &findPriorityZone); _avatar->setHasPriority(findPriorityZone.isInPriorityZone); _avatar->setNeedsHeroCheck(false); diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index 492dfc4720..98c8d7e15b 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -223,7 +223,6 @@ private: PerNodeTraitVersions _perNodeSentTraitVersions; std::atomic_bool _isIgnoreRadiusEnabled { false }; - static const glm::vec3 AVATAR_CENTER_OFFSET; }; #endif // hifi_AvatarMixerClientData_h diff --git a/assignment-client/src/avatars/MixerAvatar.h b/assignment-client/src/avatars/MixerAvatar.h index 01e5e91b44..f812917614 100644 --- a/assignment-client/src/avatars/MixerAvatar.h +++ b/assignment-client/src/avatars/MixerAvatar.h @@ -22,6 +22,9 @@ public: bool getNeedsHeroCheck() const { return _needsHeroCheck; } void setNeedsHeroCheck(bool needsHeroCheck = true) { _needsHeroCheck = needsHeroCheck; } + // Bounding-box World centre: + glm::vec3 getCentroidPosition() const + { return getWorldPosition() + _globalBoundingBoxOffset; } private: bool _needsHeroCheck { false }; From da6ca38282706ba272b88e47cb6f7155e2839d0c Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Tue, 26 Mar 2019 10:48:59 -0700 Subject: [PATCH 6/9] Revert to using avatar's _globalPosition for zone membership According to Tony this should be the hip position, i.e. a joint pos in T-pose, not neccesarily OK root. Also SpatiallyNestable::WorldPos may depend on parent entity and so not known by mixer. --- assignment-client/src/avatars/AvatarMixerClientData.cpp | 4 ++-- assignment-client/src/avatars/MixerAvatar.h | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index 2175018824..0dbefb0109 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -129,7 +129,7 @@ int AvatarMixerClientData::parseData(ReceivedMessage& message, const SlaveShared incrementNumOutOfOrderSends(); } _lastReceivedSequenceNumber = sequenceNumber; - glm::vec3 oldPosition = _avatar->getCentroidPosition(); + glm::vec3 oldPosition = _avatar->getClientGlobalPosition(); bool oldHasPriority = _avatar->getHasPriority(); // compute the offset to the data payload @@ -140,7 +140,7 @@ int AvatarMixerClientData::parseData(ReceivedMessage& message, const SlaveShared // Regardless of what the client says, restore the priority as we know it without triggering any update. _avatar->setHasPriorityWithoutTimestampReset(oldHasPriority); - auto newPosition = _avatar->getCentroidPosition(); + auto newPosition = _avatar->getClientGlobalPosition(); if (newPosition != oldPosition || _avatar->getNeedsHeroCheck()) { EntityTree& entityTree = *slaveSharedData.entityTree; FindPriorityZone findPriorityZone { newPosition } ; diff --git a/assignment-client/src/avatars/MixerAvatar.h b/assignment-client/src/avatars/MixerAvatar.h index f812917614..01e5e91b44 100644 --- a/assignment-client/src/avatars/MixerAvatar.h +++ b/assignment-client/src/avatars/MixerAvatar.h @@ -22,9 +22,6 @@ public: bool getNeedsHeroCheck() const { return _needsHeroCheck; } void setNeedsHeroCheck(bool needsHeroCheck = true) { _needsHeroCheck = needsHeroCheck; } - // Bounding-box World centre: - glm::vec3 getCentroidPosition() const - { return getWorldPosition() + _globalBoundingBoxOffset; } private: bool _needsHeroCheck { false }; From b8f79d33649b0fa452dd915df48ba56f512fc166 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Tue, 26 Mar 2019 17:41:22 -0700 Subject: [PATCH 7/9] Guard against Node linked-data being null --- assignment-client/src/avatars/AvatarMixer.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 32fb5b9b1a..5f7e197c8f 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -265,8 +265,11 @@ void AvatarMixer::start() { nodeList->nestedEach([&](NodeList::const_iterator cbegin, NodeList::const_iterator cend) { std::for_each(cbegin, cend, [](const SharedNodePointer& node) { if (node->getType() == NodeType::Agent) { - auto& avatar = static_cast(node->getLinkedData())->getAvatar(); - avatar.setNeedsHeroCheck(); + NodeData* nodeData = node->getLinkedData(); + if (nodeData) { + auto& avatar = static_cast(nodeData)->getAvatar(); + avatar.setNeedsHeroCheck(); + } } }); }); From e62270fccf4156a238e4e58cf1b8cd814eb7e1aa Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Wed, 27 Mar 2019 12:00:30 -0700 Subject: [PATCH 8/9] Fixes for inline jsdoc --- assignment-client/src/avatars/ScriptableAvatar.h | 1 + libraries/avatars/src/AvatarData.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/avatars/ScriptableAvatar.h b/assignment-client/src/avatars/ScriptableAvatar.h index fbe5675bd8..e5df411099 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.h +++ b/assignment-client/src/avatars/ScriptableAvatar.h @@ -74,6 +74,7 @@ * avatar. Read-only. * @property {number} sensorToWorldScale - The scale that transforms dimensions in the user's real world to the avatar's * size in the virtual world. Read-only. + * @property {boolean} hasPriority - is the avatar in a Hero zone? Read-only. * * @example Create a scriptable avatar. * (function () { diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index c55d5270e1..43ddbda996 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -479,7 +479,7 @@ class AvatarData : public QObject, public SpatiallyNestable { * avatar. Read-only. * @property {number} sensorToWorldScale - The scale that transforms dimensions in the user's real world to the avatar's * size in the virtual world. Read-only. - * @property {boolean} hasPriority - is the avatar in a Hero zone? Read-only + * @property {boolean} hasPriority - is the avatar in a Hero zone? Read-only. */ Q_PROPERTY(glm::vec3 position READ getWorldPosition WRITE setPositionViaScript) Q_PROPERTY(float scale READ getDomainLimitedScale WRITE setTargetScale) From bc7fb10ab95c3f648a5bb1bd056568410fd4a593 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Thu, 28 Mar 2019 17:54:35 -0700 Subject: [PATCH 9/9] Fixes from review --- assignment-client/src/avatars/AvatarMixer.cpp | 4 ++-- assignment-client/src/avatars/AvatarMixer.h | 11 +++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 5f7e197c8f..9816cebf43 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -262,7 +262,7 @@ void AvatarMixer::start() { { if (_dirtyHeroStatus) { _dirtyHeroStatus = false; - nodeList->nestedEach([&](NodeList::const_iterator cbegin, NodeList::const_iterator cend) { + nodeList->nestedEach([](NodeList::const_iterator cbegin, NodeList::const_iterator cend) { std::for_each(cbegin, cend, [](const SharedNodePointer& node) { if (node->getType() == NodeType::Agent) { NodeData* nodeData = node->getLinkedData(); @@ -1108,7 +1108,7 @@ void AvatarMixer::entityAdded(EntityItem* entity) { if (entity->getType() == EntityTypes::Zone) { _dirtyHeroStatus = true; entity->registerChangeHandler([this](const EntityItemID& entityItemID) { - this->entityChange(); + entityChange(); }); } } diff --git a/assignment-client/src/avatars/AvatarMixer.h b/assignment-client/src/avatars/AvatarMixer.h index f65f04f279..10dff5e8a4 100644 --- a/assignment-client/src/avatars/AvatarMixer.h +++ b/assignment-client/src/avatars/AvatarMixer.h @@ -46,6 +46,11 @@ public slots: void sendStatsPacket() override; + // Avatar zone possibly changed + void entityAdded(EntityItem* entity); + void entityRemoved(EntityItem* entity); + void entityChange(); + private slots: void queueIncomingPacket(QSharedPointer message, SharedNodePointer node); void handleAdjustAvatarSorting(QSharedPointer message, SharedNodePointer senderNode); @@ -147,12 +152,6 @@ private: AvatarMixerSlavePool _slavePool; SlaveSharedData _slaveSharedData; - -public slots: - // Avatar zone possibly changed - void entityAdded(EntityItem* entity); - void entityRemoved(EntityItem* entity); - void entityChange(); }; #endif // hifi_AvatarMixer_h