mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-07-23 12:24:29 +02:00
hacking on using sorted avatars for bandwidth budget
This commit is contained in:
parent
06d78d488f
commit
2017ea4491
10 changed files with 199 additions and 64 deletions
|
@ -74,14 +74,22 @@ bool AvatarMixerClientData::checkAndSetHasReceivedFirstPacketsFrom(const QUuid&
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint64_t AvatarMixerClientData::getLastBroadcastTime(const QUuid& nodeUUID) const {
|
||||||
|
// return the matching PacketSequenceNumber, or the default if we don't have it
|
||||||
|
auto nodeMatch = _lastBroadcastTimes.find(nodeUUID);
|
||||||
|
if (nodeMatch != _lastBroadcastTimes.end()) {
|
||||||
|
return nodeMatch->second;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
uint16_t AvatarMixerClientData::getLastBroadcastSequenceNumber(const QUuid& nodeUUID) const {
|
uint16_t AvatarMixerClientData::getLastBroadcastSequenceNumber(const QUuid& nodeUUID) const {
|
||||||
// return the matching PacketSequenceNumber, or the default if we don't have it
|
// return the matching PacketSequenceNumber, or the default if we don't have it
|
||||||
auto nodeMatch = _lastBroadcastSequenceNumbers.find(nodeUUID);
|
auto nodeMatch = _lastBroadcastSequenceNumbers.find(nodeUUID);
|
||||||
if (nodeMatch != _lastBroadcastSequenceNumbers.end()) {
|
if (nodeMatch != _lastBroadcastSequenceNumbers.end()) {
|
||||||
return nodeMatch->second;
|
return nodeMatch->second;
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarMixerClientData::ignoreOther(SharedNodePointer self, SharedNodePointer other) {
|
void AvatarMixerClientData::ignoreOther(SharedNodePointer self, SharedNodePointer other) {
|
||||||
|
|
|
@ -43,6 +43,7 @@ public:
|
||||||
int parseData(ReceivedMessage& message) override;
|
int parseData(ReceivedMessage& message) override;
|
||||||
AvatarData& getAvatar() { return *_avatar; }
|
AvatarData& getAvatar() { return *_avatar; }
|
||||||
const AvatarData* getConstAvatarData() const { return _avatar.get(); }
|
const AvatarData* getConstAvatarData() const { return _avatar.get(); }
|
||||||
|
AvatarSharedPointer getAvatarSharedPointer() const { return _avatar; }
|
||||||
|
|
||||||
bool checkAndSetHasReceivedFirstPacketsFrom(const QUuid& uuid);
|
bool checkAndSetHasReceivedFirstPacketsFrom(const QUuid& uuid);
|
||||||
|
|
||||||
|
@ -51,6 +52,12 @@ public:
|
||||||
{ _lastBroadcastSequenceNumbers[nodeUUID] = sequenceNumber; }
|
{ _lastBroadcastSequenceNumbers[nodeUUID] = sequenceNumber; }
|
||||||
Q_INVOKABLE void removeLastBroadcastSequenceNumber(const QUuid& nodeUUID) { _lastBroadcastSequenceNumbers.erase(nodeUUID); }
|
Q_INVOKABLE void removeLastBroadcastSequenceNumber(const QUuid& nodeUUID) { _lastBroadcastSequenceNumbers.erase(nodeUUID); }
|
||||||
|
|
||||||
|
uint64_t getLastBroadcastTime(const QUuid& nodeUUID) const;
|
||||||
|
void setLastBroadcastTime(const QUuid& nodeUUID, uint64_t broadcastTime) {
|
||||||
|
_lastBroadcastTimes[nodeUUID] = broadcastTime;
|
||||||
|
}
|
||||||
|
Q_INVOKABLE void removeLastBroadcastTime(const QUuid& nodeUUID) { _lastBroadcastTimes.erase(nodeUUID); }
|
||||||
|
|
||||||
uint16_t getLastReceivedSequenceNumber() const { return _lastReceivedSequenceNumber; }
|
uint16_t getLastReceivedSequenceNumber() const { return _lastReceivedSequenceNumber; }
|
||||||
|
|
||||||
HRCTime getIdentityChangeTimestamp() const { return _identityChangeTimestamp; }
|
HRCTime getIdentityChangeTimestamp() const { return _identityChangeTimestamp; }
|
||||||
|
@ -106,6 +113,9 @@ public:
|
||||||
bool getRequestsDomainListData() { return _requestsDomainListData; }
|
bool getRequestsDomainListData() { return _requestsDomainListData; }
|
||||||
void setRequestsDomainListData(bool requesting) { _requestsDomainListData = requesting; }
|
void setRequestsDomainListData(bool requesting) { _requestsDomainListData = requesting; }
|
||||||
|
|
||||||
|
ViewFrustum getViewFrustom() const { return _currentViewFrustum; }
|
||||||
|
|
||||||
|
|
||||||
quint64 getLastOtherAvatarEncodeTime(QUuid otherAvatar) {
|
quint64 getLastOtherAvatarEncodeTime(QUuid otherAvatar) {
|
||||||
quint64 result = 0;
|
quint64 result = 0;
|
||||||
if (_lastOtherAvatarEncodeTime.find(otherAvatar) != _lastOtherAvatarEncodeTime.end()) {
|
if (_lastOtherAvatarEncodeTime.find(otherAvatar) != _lastOtherAvatarEncodeTime.end()) {
|
||||||
|
@ -134,6 +144,7 @@ private:
|
||||||
uint16_t _lastReceivedSequenceNumber { 0 };
|
uint16_t _lastReceivedSequenceNumber { 0 };
|
||||||
std::unordered_map<QUuid, uint16_t> _lastBroadcastSequenceNumbers;
|
std::unordered_map<QUuid, uint16_t> _lastBroadcastSequenceNumbers;
|
||||||
std::unordered_set<QUuid> _hasReceivedFirstPacketsFrom;
|
std::unordered_set<QUuid> _hasReceivedFirstPacketsFrom;
|
||||||
|
std::unordered_map<QUuid, uint64_t> _lastBroadcastTimes;
|
||||||
|
|
||||||
// this is a map of the last time we encoded an "other" avatar for
|
// this is a map of the last time we encoded an "other" avatar for
|
||||||
// sending to "this" node
|
// sending to "this" node
|
||||||
|
|
|
@ -187,9 +187,75 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) {
|
||||||
// setup a PacketList for the avatarPackets
|
// setup a PacketList for the avatarPackets
|
||||||
auto avatarPacketList = NLPacketList::create(PacketType::BulkAvatarData);
|
auto avatarPacketList = NLPacketList::create(PacketType::BulkAvatarData);
|
||||||
|
|
||||||
|
|
||||||
|
QList<AvatarSharedPointer> avatarList;
|
||||||
|
std::unordered_map<AvatarSharedPointer, SharedNodePointer> avatarDataToNodes;
|
||||||
|
|
||||||
|
//qDebug() << "------------------------------";
|
||||||
|
int listItem = 0;
|
||||||
|
std::for_each(_begin, _end, [&](const SharedNodePointer& otherNode) {
|
||||||
|
const AvatarMixerClientData* otherNodeData = reinterpret_cast<const AvatarMixerClientData*>(otherNode->getLinkedData());
|
||||||
|
if (otherNodeData) {
|
||||||
|
listItem++;
|
||||||
|
AvatarSharedPointer otherAvatar = otherNodeData->getAvatarSharedPointer();
|
||||||
|
avatarList << otherAvatar;
|
||||||
|
avatarDataToNodes[otherAvatar] = otherNode;
|
||||||
|
/*
|
||||||
|
qDebug() << "listItem [" << listItem << "] "
|
||||||
|
<< "otherNode:" << otherNode.data()
|
||||||
|
<< "otherNode->getUUID():" << otherNode->getUUID()
|
||||||
|
<< "otherNodeData:" << otherNodeData
|
||||||
|
<< "otherAvatar:" << otherAvatar.get();
|
||||||
|
|
||||||
|
qDebug() << "avatarDataToNodes[" << otherAvatar.get() << "]=" << otherNode.data();
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
/*
|
||||||
|
qDebug() << "------------------------------";
|
||||||
|
qDebug() << "avatarList.size:" << avatarList.size();
|
||||||
|
qDebug() << "avatarDataToNodes.size:" << avatarDataToNodes.size();
|
||||||
|
*/
|
||||||
|
|
||||||
|
ViewFrustum cameraView = nodeData->getViewFrustom();
|
||||||
|
std::priority_queue<AvatarPriority> sortedAvatars = AvatarData::sortAvatars(
|
||||||
|
avatarList, cameraView,
|
||||||
|
|
||||||
|
[&](AvatarSharedPointer avatar)->uint64_t{
|
||||||
|
auto avatarNode = avatarDataToNodes[avatar];
|
||||||
|
if (avatarNode) {
|
||||||
|
return nodeData->getLastBroadcastTime(avatarNode->getUUID());
|
||||||
|
}
|
||||||
|
return 0; // ???
|
||||||
|
},
|
||||||
|
|
||||||
|
[this](AvatarSharedPointer avatar)->bool{
|
||||||
|
// FIXME -- when to ignore this node
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
// this is an AGENT we have received head data from
|
// this is an AGENT we have received head data from
|
||||||
// send back a packet with other active node data to this node
|
// send back a packet with other active node data to this node
|
||||||
std::for_each(_begin, _end, [&](const SharedNodePointer& otherNode) {
|
//std::for_each(_begin, _end, [&](const SharedNodePointer& otherNode) {
|
||||||
|
|
||||||
|
int avatarRank = 0;
|
||||||
|
while (!sortedAvatars.empty()) {
|
||||||
|
AvatarPriority sortData = sortedAvatars.top();
|
||||||
|
sortedAvatars.pop();
|
||||||
|
const auto& avatarData = sortData.avatar;
|
||||||
|
avatarRank++;
|
||||||
|
|
||||||
|
auto otherNode = avatarDataToNodes[avatarData];
|
||||||
|
|
||||||
|
//qDebug() << "otherNode (" << otherNode.data() << ")= avatarDataToNodes[" << avatarData.get() << "]";
|
||||||
|
|
||||||
|
if (!otherNode) {
|
||||||
|
//qDebug() << "For viewer:" << node->getUUID() << "... process other avatar [" << avatarRank << ":" << avatarData.get() << "... otherNode unknown!!";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
//qDebug() << "For viewer:" << node->getUUID() << "... process other avatar [" << avatarRank << "] avatarData: " << avatarData.get() << " otherNode:" << otherNode->getUUID() << " ... ";
|
||||||
|
|
||||||
bool shouldConsider = false;
|
bool shouldConsider = false;
|
||||||
quint64 startIgnoreCalculation = usecTimestampNow();
|
quint64 startIgnoreCalculation = usecTimestampNow();
|
||||||
|
@ -286,7 +352,8 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) {
|
||||||
// are out of view, this also appears to disable this random distribution.
|
// are out of view, this also appears to disable this random distribution.
|
||||||
if (distanceToAvatar != 0.0f
|
if (distanceToAvatar != 0.0f
|
||||||
&& !getsOutOfView
|
&& !getsOutOfView
|
||||||
&& distribution(generator) > (nodeData->getFullRateDistance() / distanceToAvatar)) {
|
// -- && distribution(generator) > (nodeData->getFullRateDistance() / distanceToAvatar) /// FIX ME... no longer doing random
|
||||||
|
) {
|
||||||
|
|
||||||
quint64 endAvatarDataPacking = usecTimestampNow();
|
quint64 endAvatarDataPacking = usecTimestampNow();
|
||||||
_stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking);
|
_stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking);
|
||||||
|
@ -395,6 +462,9 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) {
|
||||||
nodeData->setLastBroadcastSequenceNumber(otherNode->getUUID(),
|
nodeData->setLastBroadcastSequenceNumber(otherNode->getUUID(),
|
||||||
otherNodeData->getLastReceivedSequenceNumber());
|
otherNodeData->getLastReceivedSequenceNumber());
|
||||||
|
|
||||||
|
// remember the last time we sent details about this other node to the receiver
|
||||||
|
nodeData->setLastBroadcastTime(otherNode->getUUID(), start);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -406,7 +476,7 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
quint64 startPacketSending = usecTimestampNow();
|
quint64 startPacketSending = usecTimestampNow();
|
||||||
|
|
||||||
|
|
|
@ -334,11 +334,6 @@ void Avatar::updateAvatarEntities() {
|
||||||
setAvatarEntityDataChanged(false);
|
setAvatarEntityDataChanged(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Avatar::shouldDie() const {
|
|
||||||
const qint64 AVATAR_SILENCE_THRESHOLD_USECS = 5 * USECS_PER_SECOND;
|
|
||||||
return _owningAvatarMixer.isNull() || getUsecsSinceLastUpdate() > AVATAR_SILENCE_THRESHOLD_USECS;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Avatar::simulate(float deltaTime, bool inView) {
|
void Avatar::simulate(float deltaTime, bool inView) {
|
||||||
PROFILE_RANGE(simulation, "simulate");
|
PROFILE_RANGE(simulation, "simulate");
|
||||||
|
|
||||||
|
|
|
@ -178,7 +178,6 @@ public:
|
||||||
uint64_t getLastRenderUpdateTime() const { return _lastRenderUpdateTime; }
|
uint64_t getLastRenderUpdateTime() const { return _lastRenderUpdateTime; }
|
||||||
void setLastRenderUpdateTime(uint64_t time) { _lastRenderUpdateTime = time; }
|
void setLastRenderUpdateTime(uint64_t time) { _lastRenderUpdateTime = time; }
|
||||||
|
|
||||||
bool shouldDie() const;
|
|
||||||
void animateScaleChanges(float deltaTime);
|
void animateScaleChanges(float deltaTime);
|
||||||
void setTargetScale(float targetScale) override;
|
void setTargetScale(float targetScale) override;
|
||||||
|
|
||||||
|
|
|
@ -148,16 +148,6 @@ float AvatarManager::getAvatarSimulationRate(const QUuid& sessionID, const QStri
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class AvatarPriority {
|
|
||||||
public:
|
|
||||||
AvatarPriority(AvatarSharedPointer a, float p) : avatar(a), priority(p) {}
|
|
||||||
AvatarSharedPointer avatar;
|
|
||||||
float priority;
|
|
||||||
// NOTE: we invert the less-than operator to sort high priorities to front
|
|
||||||
bool operator<(const AvatarPriority& other) const { return priority > other.priority; }
|
|
||||||
};
|
|
||||||
|
|
||||||
void AvatarManager::updateOtherAvatars(float deltaTime) {
|
void AvatarManager::updateOtherAvatars(float deltaTime) {
|
||||||
// lock the hash for read to check the size
|
// lock the hash for read to check the size
|
||||||
QReadLocker lock(&_hashLock);
|
QReadLocker lock(&_hashLock);
|
||||||
|
@ -173,57 +163,31 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
||||||
QList<AvatarSharedPointer> avatarList = avatarMap.values();
|
QList<AvatarSharedPointer> avatarList = avatarMap.values();
|
||||||
ViewFrustum cameraView;
|
ViewFrustum cameraView;
|
||||||
qApp->copyDisplayViewFrustum(cameraView);
|
qApp->copyDisplayViewFrustum(cameraView);
|
||||||
glm::vec3 frustumCenter = cameraView.getPosition();
|
|
||||||
|
|
||||||
const float OUT_OF_VIEW_PENALTY = -10.0;
|
std::priority_queue<AvatarPriority> sortedAvatars = AvatarData::sortAvatars(
|
||||||
|
avatarList, cameraView,
|
||||||
|
|
||||||
std::priority_queue<AvatarPriority> sortedAvatars;
|
[](AvatarSharedPointer avatar)->uint64_t{
|
||||||
{
|
return std::static_pointer_cast<Avatar>(avatar)->getLastRenderUpdateTime();
|
||||||
PROFILE_RANGE(simulation, "sort");
|
},
|
||||||
for (int32_t i = 0; i < avatarList.size(); ++i) {
|
|
||||||
const auto& avatar = std::static_pointer_cast<Avatar>(avatarList.at(i));
|
[this](AvatarSharedPointer avatar)->bool{
|
||||||
if (avatar == _myAvatar || !avatar->isInitialized()) {
|
const auto& castedAvatar = std::static_pointer_cast<Avatar>(avatar);
|
||||||
|
if (castedAvatar == _myAvatar || !castedAvatar->isInitialized()) {
|
||||||
// DO NOT update _myAvatar! Its update has already been done earlier in the main loop.
|
// DO NOT update _myAvatar! Its update has already been done earlier in the main loop.
|
||||||
// DO NOT update or fade out uninitialized Avatars
|
// DO NOT update or fade out uninitialized Avatars
|
||||||
continue;
|
return true; // ignore it
|
||||||
}
|
}
|
||||||
if (avatar->shouldDie()) {
|
if (avatar->shouldDie()) {
|
||||||
removeAvatar(avatar->getID());
|
removeAvatar(avatar->getID());
|
||||||
continue;
|
return true; // ignore it
|
||||||
}
|
}
|
||||||
if (avatar->isDead()) {
|
if (avatar->isDead()) {
|
||||||
continue;
|
return true; // ignore it
|
||||||
}
|
}
|
||||||
|
|
||||||
// priority = weighted linear combination of:
|
return false;
|
||||||
// (a) apparentSize
|
});
|
||||||
// (b) proximity to center of view
|
|
||||||
// (c) time since last update
|
|
||||||
// (d) TIME_PENALTY to help recently updated entries sort toward back
|
|
||||||
glm::vec3 avatarPosition = avatar->getPosition();
|
|
||||||
glm::vec3 offset = avatarPosition - frustumCenter;
|
|
||||||
float distance = glm::length(offset) + 0.001f; // add 1mm to avoid divide by zero
|
|
||||||
float radius = avatar->getBoundingRadius();
|
|
||||||
const glm::vec3& forward = cameraView.getDirection();
|
|
||||||
float apparentSize = radius / distance;
|
|
||||||
float cosineAngle = glm::length(offset - glm::dot(offset, forward) * forward) / distance;
|
|
||||||
const float TIME_PENALTY = 0.080f; // seconds
|
|
||||||
float age = (float)(startTime - avatar->getLastRenderUpdateTime()) / (float)(USECS_PER_SECOND) - TIME_PENALTY;
|
|
||||||
// NOTE: we are adding values of different units to get a single measure of "priority".
|
|
||||||
// Thus we multiply each component by a conversion "weight" that scales its units
|
|
||||||
// relative to the others. These weights are pure magic tuning and are hard coded in the
|
|
||||||
// relation below: (hint: unitary weights are not explicityly shown)
|
|
||||||
float priority = apparentSize + 0.25f * cosineAngle + age;
|
|
||||||
|
|
||||||
// decrement priority of avatars outside keyhole
|
|
||||||
if (distance > cameraView.getCenterRadius()) {
|
|
||||||
if (!cameraView.sphereIntersectsFrustum(avatarPosition, radius)) {
|
|
||||||
priority += OUT_OF_VIEW_PENALTY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sortedAvatars.push(AvatarPriority(avatar, priority));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render::PendingChanges pendingChanges;
|
render::PendingChanges pendingChanges;
|
||||||
const uint64_t RENDER_UPDATE_BUDGET = 1500; // usec
|
const uint64_t RENDER_UPDATE_BUDGET = 1500; // usec
|
||||||
|
@ -256,7 +220,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
||||||
uint64_t now = usecTimestampNow();
|
uint64_t now = usecTimestampNow();
|
||||||
if (now < renderExpiry) {
|
if (now < renderExpiry) {
|
||||||
// we're within budget
|
// we're within budget
|
||||||
const float OUT_OF_VIEW_THRESHOLD = 0.5f * OUT_OF_VIEW_PENALTY;
|
const float OUT_OF_VIEW_THRESHOLD = 0.5f * AvatarData::OUT_OF_VIEW_PENALTY;
|
||||||
bool inView = sortData.priority > OUT_OF_VIEW_THRESHOLD;
|
bool inView = sortData.priority > OUT_OF_VIEW_THRESHOLD;
|
||||||
avatar->simulate(deltaTime, inView);
|
avatar->simulate(deltaTime, inView);
|
||||||
avatar->updateRenderItem(pendingChanges);
|
avatar->updateRenderItem(pendingChanges);
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
#include <shared/JSONHelpers.h>
|
#include <shared/JSONHelpers.h>
|
||||||
#include <ShapeInfo.h>
|
#include <ShapeInfo.h>
|
||||||
#include <AudioHelpers.h>
|
#include <AudioHelpers.h>
|
||||||
|
#include <Profile.h>
|
||||||
|
|
||||||
#include "AvatarLogging.h"
|
#include "AvatarLogging.h"
|
||||||
|
|
||||||
|
@ -2309,3 +2310,65 @@ void RayToAvatarIntersectionResultFromScriptValue(const QScriptValue& object, Ra
|
||||||
vec3FromScriptValue(intersection, value.intersection);
|
vec3FromScriptValue(intersection, value.intersection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const float AvatarData::OUT_OF_VIEW_PENALTY = -10.0f;
|
||||||
|
|
||||||
|
std::priority_queue<AvatarPriority> AvatarData::sortAvatars(
|
||||||
|
QList<AvatarSharedPointer> avatarList,
|
||||||
|
const ViewFrustum& cameraView,
|
||||||
|
std::function<uint64_t(AvatarSharedPointer)> lastUpdated,
|
||||||
|
std::function<bool(AvatarSharedPointer)> shouldIgnore) {
|
||||||
|
|
||||||
|
uint64_t startTime = usecTimestampNow();
|
||||||
|
|
||||||
|
glm::vec3 frustumCenter = cameraView.getPosition();
|
||||||
|
|
||||||
|
std::priority_queue<AvatarPriority> sortedAvatars;
|
||||||
|
{
|
||||||
|
PROFILE_RANGE(simulation, "sort");
|
||||||
|
for (int32_t i = 0; i < avatarList.size(); ++i) {
|
||||||
|
const auto& avatar = avatarList.at(i);
|
||||||
|
|
||||||
|
// FIXME - probably some lambda that allows the caller to reject some avatars
|
||||||
|
|
||||||
|
if (shouldIgnore(avatar)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// priority = weighted linear combination of:
|
||||||
|
// (a) apparentSize
|
||||||
|
// (b) proximity to center of view
|
||||||
|
// (c) time since last update
|
||||||
|
// (d) TIME_PENALTY to help recently updated entries sort toward back
|
||||||
|
glm::vec3 avatarPosition = avatar->getPosition();
|
||||||
|
glm::vec3 offset = avatarPosition - frustumCenter;
|
||||||
|
float distance = glm::length(offset) + 0.001f; // add 1mm to avoid divide by zero
|
||||||
|
|
||||||
|
// FIXME - AvatarData has something equivolent to this
|
||||||
|
float radius = 1.0f; // avatar->getBoundingRadius();
|
||||||
|
|
||||||
|
const glm::vec3& forward = cameraView.getDirection();
|
||||||
|
float apparentSize = radius / distance;
|
||||||
|
float cosineAngle = glm::length(glm::dot(offset, forward) * forward) / distance;
|
||||||
|
|
||||||
|
// FIXME - probably some lambda that allows the caller to specify the "updated since"
|
||||||
|
uint64_t lastUpdatedTime = lastUpdated(avatar); // avatar->getLastRenderUpdateTime()
|
||||||
|
float age = (float)(startTime - lastUpdatedTime) / (float)(USECS_PER_SECOND);
|
||||||
|
|
||||||
|
// NOTE: we are adding values of different units to get a single measure of "priority".
|
||||||
|
// Thus we multiply each component by a conversion "weight" that scales its units
|
||||||
|
// relative to the others. These weights are pure magic tuning and are hard coded in the
|
||||||
|
// relation below: (hint: unitary weights are not explicityly shown)
|
||||||
|
float priority = apparentSize + 0.25f * cosineAngle + age;
|
||||||
|
|
||||||
|
// decrement priority of avatars outside keyhole
|
||||||
|
if (distance > cameraView.getCenterRadius()) {
|
||||||
|
if (!cameraView.sphereIntersectsFrustum(avatarPosition, radius)) {
|
||||||
|
priority += OUT_OF_VIEW_PENALTY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sortedAvatars.push(AvatarPriority(avatar, priority));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sortedAvatars;
|
||||||
|
}
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
/* VS2010 defines stdint.h, but not inttypes.h */
|
/* VS2010 defines stdint.h, but not inttypes.h */
|
||||||
#if defined(_MSC_VER)
|
#if defined(_MSC_VER)
|
||||||
typedef signed char int8_t;
|
typedef signed char int8_t;
|
||||||
|
@ -57,6 +59,7 @@ typedef unsigned long long quint64;
|
||||||
#include <ThreadSafeValueCache.h>
|
#include <ThreadSafeValueCache.h>
|
||||||
#include <SharedUtil.h>
|
#include <SharedUtil.h>
|
||||||
#include <shared/RateCounter.h>
|
#include <shared/RateCounter.h>
|
||||||
|
#include <ViewFrustum.h>
|
||||||
|
|
||||||
#include "AABox.h"
|
#include "AABox.h"
|
||||||
#include "HeadData.h"
|
#include "HeadData.h"
|
||||||
|
@ -304,6 +307,14 @@ public:
|
||||||
RateCounter<> jointDataRate;
|
RateCounter<> jointDataRate;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class AvatarPriority {
|
||||||
|
public:
|
||||||
|
AvatarPriority(AvatarSharedPointer a, float p) : avatar(a), priority(p) {}
|
||||||
|
AvatarSharedPointer avatar;
|
||||||
|
float priority;
|
||||||
|
// NOTE: we invert the less-than operator to sort high priorities to front
|
||||||
|
bool operator<(const AvatarPriority& other) const { return priority < other.priority; }
|
||||||
|
};
|
||||||
|
|
||||||
class AvatarData : public QObject, public SpatiallyNestable {
|
class AvatarData : public QObject, public SpatiallyNestable {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -539,7 +550,7 @@ public:
|
||||||
|
|
||||||
void setOwningAvatarMixer(const QWeakPointer<Node>& owningAvatarMixer) { _owningAvatarMixer = owningAvatarMixer; }
|
void setOwningAvatarMixer(const QWeakPointer<Node>& owningAvatarMixer) { _owningAvatarMixer = owningAvatarMixer; }
|
||||||
|
|
||||||
const AABox& getLocalAABox() const { return _localAABox; }
|
//const AABox& getLocalAABox() const { return _localAABox; }
|
||||||
|
|
||||||
int getUsecsSinceLastUpdate() const { return _averageBytesReceived.getUsecsSinceLastEvent(); }
|
int getUsecsSinceLastUpdate() const { return _averageBytesReceived.getUsecsSinceLastEvent(); }
|
||||||
int getAverageBytesReceivedPerSecond() const;
|
int getAverageBytesReceivedPerSecond() const;
|
||||||
|
@ -578,6 +589,20 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool shouldDie() const {
|
||||||
|
const qint64 AVATAR_SILENCE_THRESHOLD_USECS = 5 * USECS_PER_SECOND;
|
||||||
|
return _owningAvatarMixer.isNull() || getUsecsSinceLastUpdate() > AVATAR_SILENCE_THRESHOLD_USECS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const float OUT_OF_VIEW_PENALTY;
|
||||||
|
|
||||||
|
static std::priority_queue<AvatarPriority> sortAvatars(
|
||||||
|
QList<AvatarSharedPointer> avatarList,
|
||||||
|
const ViewFrustum& cameraView,
|
||||||
|
std::function<uint64_t(AvatarSharedPointer)> lastUpdated,
|
||||||
|
std::function<bool(AvatarSharedPointer)> shouldIgnore);
|
||||||
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void sendAvatarDataPacket();
|
void sendAvatarDataPacket();
|
||||||
void sendIdentityPacket();
|
void sendIdentityPacket();
|
||||||
|
|
|
@ -190,3 +190,4 @@ void AvatarHashMap::sessionUUIDChanged(const QUuid& sessionUUID, const QUuid& ol
|
||||||
_lastOwnerSessionUUID = oldUUID;
|
_lastOwnerSessionUUID = oldUUID;
|
||||||
emit avatarSessionChangedEvent(sessionUUID, oldUUID);
|
emit avatarSessionChangedEvent(sessionUUID, oldUUID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@
|
||||||
|
|
||||||
#include "AvatarData.h"
|
#include "AvatarData.h"
|
||||||
|
|
||||||
|
|
||||||
class AvatarHashMap : public QObject, public Dependency {
|
class AvatarHashMap : public QObject, public Dependency {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
SINGLETON_DEPENDENCY
|
SINGLETON_DEPENDENCY
|
||||||
|
|
Loading…
Reference in a new issue