Merge pull request #2488 from birarda/avatar-scaling

avatar-mixer tuning for identity and billboard packets
This commit is contained in:
Philip Rosedale 2014-03-25 11:46:25 -07:00
commit ec3a83820e
6 changed files with 125 additions and 144 deletions

View file

@ -11,7 +11,7 @@
// nodes, and broadcasts that data back to them, every BROADCAST_INTERVAL ms. // nodes, and broadcasts that data back to them, every BROADCAST_INTERVAL ms.
#include <QtCore/QCoreApplication> #include <QtCore/QCoreApplication>
#include <QtCore/QElapsedTimer> #include <QtCore/QDateTime>
#include <QtCore/QJsonObject> #include <QtCore/QJsonObject>
#include <QtCore/QTimer> #include <QtCore/QTimer>
@ -31,10 +31,13 @@ const unsigned int AVATAR_DATA_SEND_INTERVAL_USECS = (1 / 60.0) * 1000 * 1000;
AvatarMixer::AvatarMixer(const QByteArray& packet) : AvatarMixer::AvatarMixer(const QByteArray& packet) :
ThreadedAssignment(packet), ThreadedAssignment(packet),
_lastFrameTimestamp(QDateTime::currentMSecsSinceEpoch()),
_trailingSleepRatio(1.0f), _trailingSleepRatio(1.0f),
_performanceThrottlingRatio(0.0f), _performanceThrottlingRatio(0.0f),
_sumListeners(0), _sumListeners(0),
_numStatFrames(0) _numStatFrames(0),
_sumBillboardPackets(0),
_sumIdentityPackets(0)
{ {
// make sure we hear about node kills so we can tell the other nodes // make sure we hear about node kills so we can tell the other nodes
connect(NodeList::getInstance(), &NodeList::nodeKilled, this, &AvatarMixer::nodeKilled); connect(NodeList::getInstance(), &NodeList::nodeKilled, this, &AvatarMixer::nodeKilled);
@ -46,6 +49,8 @@ void attachAvatarDataToNode(Node* newNode) {
} }
} }
const float BILLBOARD_AND_IDENTITY_SEND_PROBABILITY = 1.0f / 300.0f;
// NOTE: some additional optimizations to consider. // NOTE: some additional optimizations to consider.
// 1) use the view frustum to cull those avatars that are out of view. Since avatar data doesn't need to be present // 1) use the view frustum to cull those avatars that are out of view. Since avatar data doesn't need to be present
// if the avatar is not in view or in the keyhole. // if the avatar is not in view or in the keyhole.
@ -99,6 +104,41 @@ void AvatarMixer::broadcastAvatarData() {
// copy the avatar into the mixedAvatarByteArray packet // copy the avatar into the mixedAvatarByteArray packet
mixedAvatarByteArray.append(avatarByteArray); mixedAvatarByteArray.append(avatarByteArray);
// if the receiving avatar has just connected make sure we send out the mesh and billboard
// for this avatar (assuming they exist)
bool forceSend = !myData->checkAndSetHasReceivedFirstPackets();
// we will also force a send of billboard or identity packet
// if either has changed in the last frame
if (otherNodeData->getBillboardChangeTimestamp() > 0
&& (forceSend
|| otherNodeData->getBillboardChangeTimestamp() > _lastFrameTimestamp
|| randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) {
QByteArray billboardPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarBillboard);
billboardPacket.append(otherNode->getUUID().toRfc4122());
billboardPacket.append(otherNodeData->getAvatar().getBillboard());
nodeList->writeDatagram(billboardPacket, node);
++_sumBillboardPackets;
}
if (otherNodeData->getIdentityChangeTimestamp() > 0
&& (forceSend
|| otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp
|| randFloat() < BILLBOARD_AND_IDENTITY_SEND_PROBABILITY)) {
QByteArray identityPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarIdentity);
QByteArray individualData = otherNodeData->getAvatar().identityByteArray();
individualData.replace(0, NUM_BYTES_RFC4122_UUID, otherNode->getUUID().toRfc4122());
identityPacket.append(individualData);
nodeList->writeDatagram(identityPacket, node);
++_sumIdentityPackets;
}
} }
} }
} }
@ -106,66 +146,8 @@ void AvatarMixer::broadcastAvatarData() {
nodeList->writeDatagram(mixedAvatarByteArray, node); nodeList->writeDatagram(mixedAvatarByteArray, node);
} }
} }
}
void broadcastIdentityPacket() {
NodeList* nodeList = NodeList::getInstance();
QByteArray avatarIdentityPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarIdentity); _lastFrameTimestamp = QDateTime::currentMSecsSinceEpoch();
int numPacketHeaderBytes = avatarIdentityPacket.size();
foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
if (node->getLinkedData() && node->getType() == NodeType::Agent) {
AvatarMixerClientData* nodeData = reinterpret_cast<AvatarMixerClientData*>(node->getLinkedData());
AvatarData& avatar = nodeData->getAvatar();
QByteArray individualData = avatar.identityByteArray();
individualData.replace(0, NUM_BYTES_RFC4122_UUID, node->getUUID().toRfc4122());
if (avatarIdentityPacket.size() + individualData.size() > MAX_PACKET_SIZE) {
// we've hit MTU, send out the current packet before appending
nodeList->broadcastToNodes(avatarIdentityPacket, NodeSet() << NodeType::Agent);
avatarIdentityPacket.resize(numPacketHeaderBytes);
}
// append the individual data to the current the avatarIdentityPacket
avatarIdentityPacket.append(individualData);
// re-set the bool in AvatarMixerClientData so a change between key frames gets sent out
nodeData->setHasSentIdentityBetweenKeyFrames(false);
}
}
// send out the final packet
if (avatarIdentityPacket.size() > numPacketHeaderBytes) {
nodeList->broadcastToNodes(avatarIdentityPacket, NodeSet() << NodeType::Agent);
}
}
void broadcastBillboardPacket(const SharedNodePointer& sendingNode) {
AvatarMixerClientData* nodeData = static_cast<AvatarMixerClientData*>(sendingNode->getLinkedData());
AvatarData& avatar = nodeData->getAvatar();
QByteArray packet = byteArrayWithPopulatedHeader(PacketTypeAvatarBillboard);
packet.append(sendingNode->getUUID().toRfc4122());
packet.append(avatar.getBillboard());
NodeList* nodeList = NodeList::getInstance();
foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
if (node->getType() == NodeType::Agent && node != sendingNode) {
nodeList->writeDatagram(packet, node);
}
}
}
void broadcastBillboardPackets() {
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) {
if (node->getLinkedData() && node->getType() == NodeType::Agent) {
AvatarMixerClientData* nodeData = static_cast<AvatarMixerClientData*>(node->getLinkedData());
broadcastBillboardPacket(node);
nodeData->setHasSentBillboardBetweenKeyFrames(false);
}
}
} }
void AvatarMixer::nodeKilled(SharedNodePointer killedNode) { void AvatarMixer::nodeKilled(SharedNodePointer killedNode) {
@ -202,18 +184,10 @@ void AvatarMixer::readPendingDatagrams() {
if (avatarNode && avatarNode->getLinkedData()) { if (avatarNode && avatarNode->getLinkedData()) {
AvatarMixerClientData* nodeData = reinterpret_cast<AvatarMixerClientData*>(avatarNode->getLinkedData()); AvatarMixerClientData* nodeData = reinterpret_cast<AvatarMixerClientData*>(avatarNode->getLinkedData());
AvatarData& avatar = nodeData->getAvatar(); AvatarData& avatar = nodeData->getAvatar();
if (avatar.hasIdentityChangedAfterParsing(receivedPacket)
&& !nodeData->hasSentIdentityBetweenKeyFrames()) { // parse the identity packet and update the change timestamp if appropriate
// this avatar changed their identity in some way and we haven't sent a packet in this keyframe if (avatar.hasIdentityChangedAfterParsing(receivedPacket)) {
QByteArray identityPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarIdentity); nodeData->setIdentityChangeTimestamp(QDateTime::currentMSecsSinceEpoch());
QByteArray individualByteArray = avatar.identityByteArray();
individualByteArray.replace(0, NUM_BYTES_RFC4122_UUID, avatarNode->getUUID().toRfc4122());
identityPacket.append(individualByteArray);
nodeData->setHasSentIdentityBetweenKeyFrames(true);
nodeList->broadcastToNodes(identityPacket, NodeSet() << NodeType::Agent);
} }
} }
break; break;
@ -226,12 +200,12 @@ void AvatarMixer::readPendingDatagrams() {
if (avatarNode && avatarNode->getLinkedData()) { if (avatarNode && avatarNode->getLinkedData()) {
AvatarMixerClientData* nodeData = static_cast<AvatarMixerClientData*>(avatarNode->getLinkedData()); AvatarMixerClientData* nodeData = static_cast<AvatarMixerClientData*>(avatarNode->getLinkedData());
AvatarData& avatar = nodeData->getAvatar(); AvatarData& avatar = nodeData->getAvatar();
if (avatar.hasBillboardChangedAfterParsing(receivedPacket)
&& !nodeData->hasSentBillboardBetweenKeyFrames()) { // parse the billboard packet and update the change timestamp if appropriate
// this avatar changed their billboard and we haven't sent a packet in this keyframe if (avatar.hasBillboardChangedAfterParsing(receivedPacket)) {
broadcastBillboardPacket(avatarNode); nodeData->setBillboardChangeTimestamp(QDateTime::currentMSecsSinceEpoch());
nodeData->setHasSentBillboardBetweenKeyFrames(true);
} }
} }
break; break;
} }
@ -252,18 +226,20 @@ void AvatarMixer::sendStatsPacket() {
QJsonObject statsObject; QJsonObject statsObject;
statsObject["average_listeners_last_second"] = (float) _sumListeners / (float) _numStatFrames; statsObject["average_listeners_last_second"] = (float) _sumListeners / (float) _numStatFrames;
statsObject["average_billboard_packets_per_frame"] = (float) _sumBillboardPackets / (float) _numStatFrames;
statsObject["average_identity_packets_per_frame"] = (float) _sumIdentityPackets / (float) _numStatFrames;
statsObject["trailing_sleep_percentage"] = _trailingSleepRatio * 100; statsObject["trailing_sleep_percentage"] = _trailingSleepRatio * 100;
statsObject["performance_throttling_ratio"] = _performanceThrottlingRatio; statsObject["performance_throttling_ratio"] = _performanceThrottlingRatio;
ThreadedAssignment::addPacketStatsAndSendStatsPacket(statsObject); ThreadedAssignment::addPacketStatsAndSendStatsPacket(statsObject);
_sumListeners = 0; _sumListeners = 0;
_sumBillboardPackets = 0;
_sumIdentityPackets = 0;
_numStatFrames = 0; _numStatFrames = 0;
} }
const qint64 AVATAR_IDENTITY_KEYFRAME_MSECS = 5000;
const qint64 AVATAR_BILLBOARD_KEYFRAME_MSECS = 5000;
void AvatarMixer::run() { void AvatarMixer::run() {
ThreadedAssignment::commonInit(AVATAR_MIXER_LOGGING_NAME, NodeType::AvatarMixer); ThreadedAssignment::commonInit(AVATAR_MIXER_LOGGING_NAME, NodeType::AvatarMixer);
@ -277,12 +253,6 @@ void AvatarMixer::run() {
gettimeofday(&startTime, NULL); gettimeofday(&startTime, NULL);
QElapsedTimer identityTimer;
identityTimer.start();
QElapsedTimer billboardTimer;
billboardTimer.start();
int usecToSleep = AVATAR_DATA_SEND_INTERVAL_USECS; int usecToSleep = AVATAR_DATA_SEND_INTERVAL_USECS;
const int TRAILING_AVERAGE_FRAMES = 100; const int TRAILING_AVERAGE_FRAMES = 100;
@ -338,19 +308,6 @@ void AvatarMixer::run() {
broadcastAvatarData(); broadcastAvatarData();
if (identityTimer.elapsed() >= AVATAR_IDENTITY_KEYFRAME_MSECS) {
// it's time to broadcast the keyframe identity packets
broadcastIdentityPacket();
// restart the timer so we do it again in AVATAR_IDENTITY_KEYFRAME_MSECS
identityTimer.restart();
}
if (billboardTimer.elapsed() >= AVATAR_BILLBOARD_KEYFRAME_MSECS) {
broadcastBillboardPackets();
billboardTimer.restart();
}
QCoreApplication::processEvents(); QCoreApplication::processEvents();
if (_isFinished) { if (_isFinished) {

View file

@ -30,11 +30,15 @@ public slots:
private: private:
void broadcastAvatarData(); void broadcastAvatarData();
quint64 _lastFrameTimestamp;
float _trailingSleepRatio; float _trailingSleepRatio;
float _performanceThrottlingRatio; float _performanceThrottlingRatio;
int _sumListeners; int _sumListeners;
int _numStatFrames; int _numStatFrames;
int _sumBillboardPackets;
int _sumIdentityPackets;
}; };
#endif /* defined(__hifi__AvatarMixer__) */ #endif /* defined(__hifi__AvatarMixer__) */

View file

@ -10,8 +10,9 @@
AvatarMixerClientData::AvatarMixerClientData() : AvatarMixerClientData::AvatarMixerClientData() :
NodeData(), NodeData(),
_hasSentIdentityBetweenKeyFrames(false), _hasReceivedFirstPackets(false),
_hasSentBillboardBetweenKeyFrames(false) _billboardChangeTimestamp(0),
_identityChangeTimestamp(0)
{ {
} }
@ -21,3 +22,9 @@ int AvatarMixerClientData::parseData(const QByteArray& packet) {
int offset = numBytesForPacketHeader(packet); int offset = numBytesForPacketHeader(packet);
return _avatar.parseDataAtOffset(packet, offset); return _avatar.parseDataAtOffset(packet, offset);
} }
bool AvatarMixerClientData::checkAndSetHasReceivedFirstPackets() {
bool oldValue = _hasReceivedFirstPackets;
_hasReceivedFirstPackets = true;
return oldValue;
}

View file

@ -20,22 +20,21 @@ public:
AvatarMixerClientData(); AvatarMixerClientData();
int parseData(const QByteArray& packet); int parseData(const QByteArray& packet);
bool hasSentIdentityBetweenKeyFrames() const { return _hasSentIdentityBetweenKeyFrames; }
void setHasSentIdentityBetweenKeyFrames(bool hasSentIdentityBetweenKeyFrames)
{ _hasSentIdentityBetweenKeyFrames = hasSentIdentityBetweenKeyFrames; }
bool hasSentBillboardBetweenKeyFrames() const { return _hasSentBillboardBetweenKeyFrames; }
void setHasSentBillboardBetweenKeyFrames(bool hasSentBillboardBetweenKeyFrames)
{ _hasSentBillboardBetweenKeyFrames = hasSentBillboardBetweenKeyFrames; }
AvatarData& getAvatar() { return _avatar; } AvatarData& getAvatar() { return _avatar; }
bool checkAndSetHasReceivedFirstPackets();
quint64 getBillboardChangeTimestamp() const { return _billboardChangeTimestamp; }
void setBillboardChangeTimestamp(quint64 billboardChangeTimestamp) { _billboardChangeTimestamp = billboardChangeTimestamp; }
quint64 getIdentityChangeTimestamp() const { return _identityChangeTimestamp; }
void setIdentityChangeTimestamp(quint64 identityChangeTimestamp) { _identityChangeTimestamp = identityChangeTimestamp; }
private: private:
bool _hasSentIdentityBetweenKeyFrames;
bool _hasSentBillboardBetweenKeyFrames;
AvatarData _avatar; AvatarData _avatar;
bool _hasReceivedFirstPackets;
quint64 _billboardChangeTimestamp;
quint64 _identityChangeTimestamp;
}; };
#endif /* defined(__hifi__AvatarMixerClientData__) */ #endif /* defined(__hifi__AvatarMixerClientData__) */

View file

@ -48,15 +48,12 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
AvatarHash::iterator avatarIterator = _avatarHash.begin(); AvatarHash::iterator avatarIterator = _avatarHash.begin();
while (avatarIterator != _avatarHash.end()) { while (avatarIterator != _avatarHash.end()) {
Avatar* avatar = static_cast<Avatar*>(avatarIterator.value().data()); Avatar* avatar = static_cast<Avatar*>(avatarIterator.value().data());
if (avatar == static_cast<Avatar*>(_myAvatar.data())) { if (avatar == static_cast<Avatar*>(_myAvatar.data()) || !avatar->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.
//updateMyAvatar(deltaTime); // DO NOT update uninitialized Avatars
++avatarIterator; ++avatarIterator;
continue; continue;
} }
if (!avatar->isInitialized()) {
avatar->init();
}
if (avatar->getOwningAvatarMixer()) { if (avatar->getOwningAvatarMixer()) {
// this avatar's mixer is still around, go ahead and simulate it // this avatar's mixer is still around, go ahead and simulate it
avatar->simulate(deltaTime); avatar->simulate(deltaTime);
@ -120,22 +117,40 @@ void AvatarManager::renderAvatarFades(const glm::vec3& cameraPosition, Avatar::R
foreach(const AvatarSharedPointer& fadingAvatar, _avatarFades) { foreach(const AvatarSharedPointer& fadingAvatar, _avatarFades) {
Avatar* avatar = static_cast<Avatar*>(fadingAvatar.data()); Avatar* avatar = static_cast<Avatar*>(fadingAvatar.data());
if (avatar != static_cast<Avatar*>(_myAvatar.data())) { if (avatar != static_cast<Avatar*>(_myAvatar.data()) && avatar->isInitialized()) {
avatar->render(cameraPosition, renderMode); avatar->render(cameraPosition, renderMode);
} }
} }
} }
AvatarSharedPointer AvatarManager::matchingOrNewAvatar(const QUuid& nodeUUID, const QWeakPointer<Node>& mixerWeakPointer) {
AvatarSharedPointer matchingAvatar = _avatarHash.value(nodeUUID);
if (!matchingAvatar) {
// construct a new Avatar for this node
Avatar* avatar = new Avatar();
avatar->setOwningAvatarMixer(mixerWeakPointer);
// insert the new avatar into our hash
matchingAvatar = AvatarSharedPointer(avatar);
_avatarHash.insert(nodeUUID, matchingAvatar);
qDebug() << "Adding avatar with UUID" << nodeUUID << "to AvatarManager hash.";
}
return matchingAvatar;
}
void AvatarManager::processAvatarMixerDatagram(const QByteArray& datagram, const QWeakPointer<Node>& mixerWeakPointer) { void AvatarManager::processAvatarMixerDatagram(const QByteArray& datagram, const QWeakPointer<Node>& mixerWeakPointer) {
switch (packetTypeForPacket(datagram)) { switch (packetTypeForPacket(datagram)) {
case PacketTypeBulkAvatarData: case PacketTypeBulkAvatarData:
processAvatarDataPacket(datagram, mixerWeakPointer); processAvatarDataPacket(datagram, mixerWeakPointer);
break; break;
case PacketTypeAvatarIdentity: case PacketTypeAvatarIdentity:
processAvatarIdentityPacket(datagram); processAvatarIdentityPacket(datagram, mixerWeakPointer);
break; break;
case PacketTypeAvatarBillboard: case PacketTypeAvatarBillboard:
processAvatarBillboardPacket(datagram); processAvatarBillboardPacket(datagram, mixerWeakPointer);
break; break;
case PacketTypeKillAvatar: case PacketTypeKillAvatar:
processKillAvatar(datagram); processKillAvatar(datagram);
@ -154,26 +169,21 @@ void AvatarManager::processAvatarDataPacket(const QByteArray &datagram, const QW
QUuid nodeUUID = QUuid::fromRfc4122(datagram.mid(bytesRead, NUM_BYTES_RFC4122_UUID)); QUuid nodeUUID = QUuid::fromRfc4122(datagram.mid(bytesRead, NUM_BYTES_RFC4122_UUID));
bytesRead += NUM_BYTES_RFC4122_UUID; bytesRead += NUM_BYTES_RFC4122_UUID;
AvatarSharedPointer matchingAvatar = _avatarHash.value(nodeUUID); AvatarSharedPointer matchingAvatarData = matchingOrNewAvatar(nodeUUID, mixerWeakPointer);
if (!matchingAvatar) {
// construct a new Avatar for this node
Avatar* avatar = new Avatar();
avatar->setOwningAvatarMixer(mixerWeakPointer);
// insert the new avatar into our hash
matchingAvatar = AvatarSharedPointer(avatar);
_avatarHash.insert(nodeUUID, matchingAvatar);
qDebug() << "Adding avatar with UUID" << nodeUUID << "to AvatarManager hash.";
}
// have the matching (or new) avatar parse the data from the packet // have the matching (or new) avatar parse the data from the packet
bytesRead += matchingAvatar->parseDataAtOffset(datagram, bytesRead); bytesRead += matchingAvatarData->parseDataAtOffset(datagram, bytesRead);
Avatar* matchingAvatar = reinterpret_cast<Avatar*>(matchingAvatarData.data());
if (!matchingAvatar->isInitialized()) {
// now that we have AvatarData for this Avatar we are go for init
matchingAvatar->init();
}
} }
} }
void AvatarManager::processAvatarIdentityPacket(const QByteArray &packet) { void AvatarManager::processAvatarIdentityPacket(const QByteArray &packet, const QWeakPointer<Node>& mixerWeakPointer) {
// setup a data stream to parse the packet // setup a data stream to parse the packet
QDataStream identityStream(packet); QDataStream identityStream(packet);
identityStream.skipRawData(numBytesForPacketHeader(packet)); identityStream.skipRawData(numBytesForPacketHeader(packet));
@ -187,7 +197,7 @@ void AvatarManager::processAvatarIdentityPacket(const QByteArray &packet) {
identityStream >> nodeUUID >> faceMeshURL >> skeletonURL >> displayName; identityStream >> nodeUUID >> faceMeshURL >> skeletonURL >> displayName;
// mesh URL for a UUID, find avatar in our list // mesh URL for a UUID, find avatar in our list
AvatarSharedPointer matchingAvatar = _avatarHash.value(nodeUUID); AvatarSharedPointer matchingAvatar = matchingOrNewAvatar(nodeUUID, mixerWeakPointer);
if (matchingAvatar) { if (matchingAvatar) {
Avatar* avatar = static_cast<Avatar*>(matchingAvatar.data()); Avatar* avatar = static_cast<Avatar*>(matchingAvatar.data());
@ -206,11 +216,11 @@ void AvatarManager::processAvatarIdentityPacket(const QByteArray &packet) {
} }
} }
void AvatarManager::processAvatarBillboardPacket(const QByteArray& packet) { void AvatarManager::processAvatarBillboardPacket(const QByteArray& packet, const QWeakPointer<Node>& mixerWeakPointer) {
int headerSize = numBytesForPacketHeader(packet); int headerSize = numBytesForPacketHeader(packet);
QUuid nodeUUID = QUuid::fromRfc4122(QByteArray::fromRawData(packet.constData() + headerSize, NUM_BYTES_RFC4122_UUID)); QUuid nodeUUID = QUuid::fromRfc4122(QByteArray::fromRawData(packet.constData() + headerSize, NUM_BYTES_RFC4122_UUID));
AvatarSharedPointer matchingAvatar = _avatarHash.value(nodeUUID); AvatarSharedPointer matchingAvatar = matchingOrNewAvatar(nodeUUID, mixerWeakPointer);
if (matchingAvatar) { if (matchingAvatar) {
Avatar* avatar = static_cast<Avatar*>(matchingAvatar.data()); Avatar* avatar = static_cast<Avatar*>(matchingAvatar.data());
QByteArray billboard = packet.mid(headerSize + NUM_BYTES_RFC4122_UUID); QByteArray billboard = packet.mid(headerSize + NUM_BYTES_RFC4122_UUID);
@ -234,7 +244,9 @@ void AvatarManager::processKillAvatar(const QByteArray& datagram) {
AvatarHash::iterator AvatarManager::erase(const AvatarHash::iterator& iterator) { AvatarHash::iterator AvatarManager::erase(const AvatarHash::iterator& iterator) {
if (iterator.key() != MY_AVATAR_KEY) { if (iterator.key() != MY_AVATAR_KEY) {
qDebug() << "Removing Avatar with UUID" << iterator.key() << "from AvatarManager hash."; qDebug() << "Removing Avatar with UUID" << iterator.key() << "from AvatarManager hash.";
_avatarFades.push_back(iterator.value()); if (reinterpret_cast<Avatar*>(iterator.value().data())->isInitialized()) {
_avatarFades.push_back(iterator.value());
}
return AvatarHashMap::erase(iterator); return AvatarHashMap::erase(iterator);
} else { } else {
// never remove _myAvatar from the list // never remove _myAvatar from the list

View file

@ -39,9 +39,11 @@ public slots:
private: private:
AvatarManager(const AvatarManager& other); AvatarManager(const AvatarManager& other);
AvatarSharedPointer matchingOrNewAvatar(const QUuid& nodeUUID, const QWeakPointer<Node>& mixerWeakPointer);
void processAvatarDataPacket(const QByteArray& packet, const QWeakPointer<Node>& mixerWeakPointer); void processAvatarDataPacket(const QByteArray& packet, const QWeakPointer<Node>& mixerWeakPointer);
void processAvatarIdentityPacket(const QByteArray& packet); void processAvatarIdentityPacket(const QByteArray& packet, const QWeakPointer<Node>& mixerWeakPointer);
void processAvatarBillboardPacket(const QByteArray& packet); void processAvatarBillboardPacket(const QByteArray& packet, const QWeakPointer<Node>& mixerWeakPointer);
void processKillAvatar(const QByteArray& datagram); void processKillAvatar(const QByteArray& datagram);
void simulateAvatarFades(float deltaTime); void simulateAvatarFades(float deltaTime);