diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 7cf42a7759..3803607a44 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1802,10 +1802,11 @@ Application::~Application() { _physicsEngine->setCharacterController(nullptr); // remove avatars from physics engine - DependencyManager::get()->clearAllAvatars(); + DependencyManager::get()->clearOtherAvatars(); VectorOfMotionStates motionStates; DependencyManager::get()->getObjectsToRemoveFromPhysics(motionStates); _physicsEngine->removeObjects(motionStates); + DependencyManager::get()->deleteAllAvatars(); DependencyManager::destroy(); DependencyManager::destroy(); diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index ddd41f3cc2..083b513a31 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -193,6 +193,9 @@ void Avatar::animateScaleChanges(float deltaTime) { } setScale(glm::vec3(animatedScale)); // avatar scale is uniform + // flag the joints as having changed for force update to RenderItem + _hasNewJointData = true; + // TODO: rebuilding the shape constantly is somehwat expensive. // We should only rebuild after significant change. rebuildCollisionShape(); @@ -200,8 +203,12 @@ void Avatar::animateScaleChanges(float deltaTime) { } void Avatar::setTargetScale(float targetScale) { - AvatarData::setTargetScale(targetScale); - _isAnimatingScale = true; + float newValue = glm::clamp(targetScale, MIN_AVATAR_SCALE, MAX_AVATAR_SCALE); + if (_targetScale != newValue) { + _targetScale = newValue; + _scaleChanged = usecTimestampNow(); + _isAnimatingScale = true; + } } void Avatar::updateAvatarEntities() { @@ -476,7 +483,7 @@ static TextRenderer3D* textRenderer(TextRendererType type) { return displayNameRenderer; } -bool Avatar::addToScene(AvatarSharedPointer self, std::shared_ptr scene, render::Transaction& transaction) { +void Avatar::addToScene(AvatarSharedPointer self, std::shared_ptr scene, render::Transaction& transaction) { auto avatarPayload = new render::Payload(self); auto avatarPayloadPointer = Avatar::PayloadPointer(avatarPayload); _renderItemID = scene->allocateID(); @@ -486,9 +493,6 @@ bool Avatar::addToScene(AvatarSharedPointer self, std::shared_ptr for (auto& attachmentModel : _attachmentModels) { attachmentModel->addToScene(scene, transaction); } - - _inScene = true; - return true; } void Avatar::removeFromScene(AvatarSharedPointer self, std::shared_ptr scene, render::Transaction& transaction) { @@ -498,7 +502,6 @@ void Avatar::removeFromScene(AvatarSharedPointer self, std::shared_ptrremoveFromScene(scene, transaction); } - _inScene = false; } void Avatar::updateRenderItem(render::Transaction& transaction) { @@ -1450,7 +1453,7 @@ void Avatar::addToScene(AvatarSharedPointer myHandle) { } } void Avatar::ensureInScene(AvatarSharedPointer self) { - if (!_inScene) { + if (!render::Item::isValidID(_renderItemID)) { addToScene(self); } } diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index dbc7ee5011..3e55bedc38 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -81,7 +81,7 @@ public: virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPosition); - bool addToScene(AvatarSharedPointer self, std::shared_ptr scene, + void addToScene(AvatarSharedPointer self, std::shared_ptr scene, render::Transaction& transaction); void removeFromScene(AvatarSharedPointer self, std::shared_ptr scene, @@ -305,6 +305,7 @@ protected: void addToScene(AvatarSharedPointer self); void ensureInScene(AvatarSharedPointer self); + bool isInScene() const { return render::Item::isValidID(_renderItemID); } // Some rate tracking support RateCounter<> _simulationRate; @@ -330,7 +331,6 @@ private: int _nameRectGeometryID { 0 }; bool _initialized; bool _isLookAtTarget { false }; - bool _inScene { false }; bool _isAnimatingScale { false }; float getBoundingRadius() const; diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 48914908c6..f1170a7085 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -68,7 +68,7 @@ void AvatarManager::registerMetaTypes(QScriptEngine* engine) { } AvatarManager::AvatarManager(QObject* parent) : - _avatarFades(), + _avatarsToFade(), _myAvatar(std::make_shared(std::make_shared())) { // register a meta type for the weak pointer we'll use for the owning avatar mixer for each avatar @@ -100,15 +100,16 @@ void AvatarManager::init() { _avatarHash.insert(MY_AVATAR_KEY, _myAvatar); } + _shouldRender = DependencyManager::get()->shouldRenderAvatars(); connect(DependencyManager::get().data(), &SceneScriptingInterface::shouldRenderAvatarsChanged, this, &AvatarManager::updateAvatarRenderStatus, Qt::QueuedConnection); - render::ScenePointer scene = qApp->getMain3DScene(); - render::Transaction transaction; - if (DependencyManager::get()->shouldRenderAvatars()) { + if (_shouldRender) { + render::ScenePointer scene = qApp->getMain3DScene(); + render::Transaction transaction; _myAvatar->addToScene(_myAvatar, scene, transaction); + scene->enqueueTransaction(transaction); } - scene->enqueueTransaction(transaction); } void AvatarManager::updateMyAvatar(float deltaTime) { @@ -151,7 +152,7 @@ float AvatarManager::getAvatarSimulationRate(const QUuid& sessionID, const QStri void AvatarManager::updateOtherAvatars(float deltaTime) { // lock the hash for read to check the size QReadLocker lock(&_hashLock); - if (_avatarHash.size() < 2 && _avatarFades.isEmpty()) { + if (_avatarHash.size() < 2 && _avatarsToFade.isEmpty()) { return; } lock.unlock(); @@ -181,30 +182,24 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { // DO NOT update or fade out uninitialized Avatars return true; // ignore it } - if (avatar->shouldDie()) { - removeAvatar(avatar->getID()); - return true; // ignore it - } - if (avatar->isDead()) { - return true; // ignore it - } - return false; }); - render::Transaction transaction; uint64_t startTime = usecTimestampNow(); const uint64_t UPDATE_BUDGET = 2000; // usec uint64_t updateExpiry = startTime + UPDATE_BUDGET; - int numAvatarsUpdated = 0; int numAVatarsNotUpdated = 0; + + render::Transaction transaction; while (!sortedAvatars.empty()) { const AvatarPriority& sortData = sortedAvatars.top(); const auto& avatar = std::static_pointer_cast(sortData.avatar); // for ALL avatars... - avatar->ensureInScene(avatar); + if (_shouldRender) { + avatar->ensureInScene(avatar); + } if (!avatar->getMotionState()) { ShapeInfo shapeInfo; avatar->computeShapeInfo(shapeInfo); @@ -218,6 +213,10 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { } } avatar->animateScaleChanges(deltaTime); + if (avatar->shouldDie()) { + avatar->die(); + removeAvatar(avatar->getID()); + } const float OUT_OF_VIEW_THRESHOLD = 0.5f * AvatarData::OUT_OF_VIEW_PENALTY; uint64_t now = usecTimestampNow(); @@ -259,10 +258,24 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { sortedAvatars.pop(); } + if (_shouldRender) { + if (!_avatarsToFade.empty()) { + QReadLocker lock(&_hashLock); + QVector::iterator itr = _avatarsToFade.begin(); + while (itr != _avatarsToFade.end() && usecTimestampNow() > updateExpiry) { + auto avatar = std::static_pointer_cast(*itr); + avatar->animateScaleChanges(deltaTime); + avatar->simulate(deltaTime, true); + avatar->updateRenderItem(transaction); + ++itr; + } + } + qApp->getMain3DScene()->enqueueTransaction(transaction); + } + _avatarSimulationTime = (float)(usecTimestampNow() - startTime) / (float)USECS_PER_MSEC; _numAvatarsUpdated = numAvatarsUpdated; _numAvatarsNotUpdated = numAVatarsNotUpdated; - qApp->getMain3DScene()->enqueueTransaction(transaction); simulateAvatarFades(deltaTime); } @@ -277,54 +290,76 @@ void AvatarManager::postUpdate(float deltaTime) { } void AvatarManager::simulateAvatarFades(float deltaTime) { - QVector::iterator fadingIterator = _avatarFades.begin(); + if (_avatarsToFade.empty()) { + return; + } const float SHRINK_RATE = 0.15f; const float MIN_FADE_SCALE = MIN_AVATAR_SCALE; - render::ScenePointer scene = qApp->getMain3DScene(); - render::Transaction transaction; - while (fadingIterator != _avatarFades.end()) { - auto avatar = std::static_pointer_cast(*fadingIterator); + QReadLocker locker(&_hashLock); + QVector::iterator itr = _avatarsToFade.begin(); + while (itr != _avatarsToFade.end()) { + auto avatar = std::static_pointer_cast(*itr); avatar->setTargetScale(avatar->getUniformScale() * SHRINK_RATE); avatar->animateScaleChanges(deltaTime); if (avatar->getTargetScale() <= MIN_FADE_SCALE) { - avatar->removeFromScene(*fadingIterator, scene, transaction); - // only remove from _avatarFades if we're sure its motionState has been removed from PhysicsEngine + // fading to zero is such a rare event we push unique transaction for each one + if (avatar->isInScene()) { + render::ScenePointer scene = qApp->getMain3DScene(); + render::Transaction transaction; + avatar->removeFromScene(*itr, scene, transaction); + if (scene) { + scene->enqueueTransaction(transaction); + } + } + + // only remove from _avatarsToFade if we're sure its motionState has been removed from PhysicsEngine if (_motionStatesToRemoveFromPhysics.empty()) { - fadingIterator = _avatarFades.erase(fadingIterator); + itr = _avatarsToFade.erase(itr); } else { - ++fadingIterator; + ++itr; } } else { const bool inView = true; // HACK avatar->simulate(deltaTime, inView); - ++fadingIterator; + ++itr; } } - scene->enqueueTransaction(transaction); } AvatarSharedPointer AvatarManager::newSharedAvatar() { return std::make_shared(std::make_shared()); } -AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) { - auto newAvatar = AvatarHashMap::addAvatar(sessionUUID, mixerWeakPointer); - auto rawRenderableAvatar = std::static_pointer_cast(newAvatar); - - rawRenderableAvatar->addToScene(rawRenderableAvatar); - - return newAvatar; -} - -// virtual -void AvatarManager::removeAvatar(const QUuid& sessionUUID, KillAvatarReason removalReason) { - QWriteLocker locker(&_hashLock); - - auto removedAvatar = _avatarHash.take(sessionUUID); - if (removedAvatar) { - handleRemovedAvatar(removedAvatar, removalReason); +void AvatarManager::processAvatarDataPacket(QSharedPointer message, SharedNodePointer sendingNode) { + PerformanceTimer perfTimer("receiveAvatar"); + // enumerate over all of the avatars in this packet + // only add them if mixerWeakPointer points to something (meaning that mixer is still around) + while (message->getBytesLeftToRead()) { + AvatarSharedPointer avatarData = parseAvatarData(message, sendingNode); + if (avatarData) { + auto avatar = std::static_pointer_cast(avatarData); + if (avatar->isInScene()) { + if (!_shouldRender) { + // rare transition so we process the transaction immediately + render::ScenePointer scene = qApp->getMain3DScene(); + if (scene) { + render::Transaction transaction; + avatar->removeFromScene(avatar, scene, transaction); + scene->enqueueTransaction(transaction); + } + } + } else if (_shouldRender) { + // very rare transition so we process the transaction immediately + render::ScenePointer scene = qApp->getMain3DScene(); + if (scene) { + render::Transaction transaction; + avatar->addToScene(avatar, scene, transaction); + scene->enqueueTransaction(transaction); + } + } + } } } @@ -353,35 +388,46 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar DependencyManager::get()->removeFromIgnoreMuteSets(avatar->getSessionUUID()); DependencyManager::get()->avatarDisconnected(avatar->getSessionUUID()); } - _avatarFades.push_back(removedAvatar); + _avatarsToFade.push_back(removedAvatar); } void AvatarManager::clearOtherAvatars() { - // clear any avatars that came from an avatar-mixer - QWriteLocker locker(&_hashLock); + // Remove other avatars from the world but don't actually remove them from _avatarHash + // each will either be removed on timeout or will re-added to the world on receipt of update. + render::ScenePointer scene = qApp->getMain3DScene(); + render::Transaction transaction; + QReadLocker locker(&_hashLock); AvatarHash::iterator avatarIterator = _avatarHash.begin(); while (avatarIterator != _avatarHash.end()) { auto avatar = std::static_pointer_cast(avatarIterator.value()); - if (avatar == _myAvatar || !avatar->isInitialized()) { - // don't remove myAvatar or uninitialized avatars from the list - ++avatarIterator; - } else { - auto removedAvatar = avatarIterator.value(); - avatarIterator = _avatarHash.erase(avatarIterator); - - handleRemovedAvatar(removedAvatar); + if (avatar != _myAvatar) { + if (avatar->isInScene()) { + avatar->removeFromScene(avatar, scene, transaction); + } + AvatarMotionState* motionState = avatar->getMotionState(); + if (motionState) { + _motionStatesThatMightUpdate.remove(motionState); + _motionStatesToAddToPhysics.remove(motionState); + _motionStatesToRemoveFromPhysics.push_back(motionState); + } } + ++avatarIterator; + } + if (scene) { + scene->enqueueTransaction(transaction); } _myAvatar->clearLookAtTargetAvatar(); } -void AvatarManager::clearAllAvatars() { - clearOtherAvatars(); - - QWriteLocker locker(&_hashLock); - - handleRemovedAvatar(_myAvatar); +void AvatarManager::deleteAllAvatars() { + QReadLocker locker(&_hashLock); + AvatarHash::iterator avatarIterator = _avatarHash.begin(); + while (avatarIterator != _avatarHash.end()) { + auto avatar = std::static_pointer_cast(avatarIterator.value()); + avatarIterator = _avatarHash.erase(avatarIterator); + avatar->die(); + } } void AvatarManager::setLocalLights(const QVector& localLights) { @@ -475,26 +521,25 @@ void AvatarManager::handleCollisionEvents(const CollisionEvents& collisionEvents } void AvatarManager::updateAvatarRenderStatus(bool shouldRenderAvatars) { - if (DependencyManager::get()->shouldRenderAvatars()) { + _shouldRender = shouldRenderAvatars; + render::ScenePointer scene = qApp->getMain3DScene(); + render::Transaction transaction; + if (_shouldRender) { for (auto avatarData : _avatarHash) { auto avatar = std::static_pointer_cast(avatarData); - render::ScenePointer scene = qApp->getMain3DScene(); - render::Transaction transaction; avatar->addToScene(avatar, scene, transaction); - scene->enqueueTransaction(transaction); } } else { for (auto avatarData : _avatarHash) { auto avatar = std::static_pointer_cast(avatarData); - render::ScenePointer scene = qApp->getMain3DScene(); - render::Transaction transaction; avatar->removeFromScene(avatar, scene, transaction); - scene->enqueueTransaction(transaction); } } + if (scene) { + scene->enqueueTransaction(transaction); + } } - AvatarSharedPointer AvatarManager::getAvatarBySessionID(const QUuid& sessionID) const { if (sessionID == AVATAR_SELF_ID || sessionID == _myAvatar->getSessionUUID()) { return _myAvatar; diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index b94f9e6a96..c8229115bd 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -53,7 +53,7 @@ public: void postUpdate(float deltaTime); void clearOtherAvatars(); - void clearAllAvatars(); + void deleteAllAvatars(); bool shouldShowReceiveStats() const { return _shouldShowReceiveStats; } @@ -91,8 +91,8 @@ public slots: void setShouldShowReceiveStats(bool shouldShowReceiveStats) { _shouldShowReceiveStats = shouldShowReceiveStats; } void updateAvatarRenderStatus(bool shouldRenderAvatars); -private slots: - virtual void removeAvatar(const QUuid& sessionUUID, KillAvatarReason removalReason = KillAvatarReason::NoReason) override; +protected slots: + void processAvatarDataPacket(QSharedPointer message, SharedNodePointer sendingNode) override; private: explicit AvatarManager(QObject* parent = 0); @@ -100,12 +100,15 @@ private: void simulateAvatarFades(float deltaTime); - // virtual overrides - virtual AvatarSharedPointer newSharedAvatar() override; - virtual AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) override; - virtual void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason = KillAvatarReason::NoReason) override; + AvatarSharedPointer newSharedAvatar() override; + void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason = KillAvatarReason::NoReason) override; + + QVector _avatarsToFade; + + SetOfAvatarMotionStates _motionStatesThatMightUpdate; + VectorOfMotionStates _motionStatesToRemoveFromPhysics; + SetOfMotionStates _motionStatesToAddToPhysics; - QVector _avatarFades; std::shared_ptr _myAvatar; quint64 _lastSendAvatarDataTime = 0; // Controls MyAvatar send data rate. @@ -115,14 +118,11 @@ private: std::list> _collisionInjectors; - SetOfAvatarMotionStates _motionStatesThatMightUpdate; - SetOfMotionStates _motionStatesToAddToPhysics; - VectorOfMotionStates _motionStatesToRemoveFromPhysics; - RateCounter<> _myAvatarSendRate; int _numAvatarsUpdated { 0 }; int _numAvatarsNotUpdated { 0 }; float _avatarSimulationTime { 0.0f }; + bool _shouldRender { true }; }; Q_DECLARE_METATYPE(AvatarManager::LocalLight) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index d1edf9d44e..2a616a7a2a 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -54,6 +54,7 @@ #include "DebugDraw.h" #include "EntityEditPacketSender.h" #include "MovingEntitiesOperator.h" +#include "SceneScriptingInterface.h" using namespace std; @@ -1634,7 +1635,7 @@ void MyAvatar::postUpdate(float deltaTime) { Avatar::postUpdate(deltaTime); render::ScenePointer scene = qApp->getMain3DScene(); - if (_skeletonModel->initWhenReady(scene)) { + if (DependencyManager::get()->shouldRenderAvatars() && _skeletonModel->initWhenReady(scene)) { initHeadBones(); _skeletonModel->setCauterizeBoneSet(_headBoneSet); _fstAnimGraphOverrideUrl = _skeletonModel->getGeometry()->getAnimGraphOverrideUrl(); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index a1ea103edb..23aa0cd811 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -123,6 +123,7 @@ void AvatarData::setTargetScale(float targetScale) { if (_targetScale != newValue) { _targetScale = newValue; _scaleChanged = usecTimestampNow(); + _avatarScaleChanged = _scaleChanged; } } diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 48e5d673c9..8708030190 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -80,13 +80,10 @@ AvatarSharedPointer AvatarHashMap::addAvatar(const QUuid& sessionUUID, const QWe AvatarSharedPointer AvatarHashMap::newOrExistingAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) { QWriteLocker locker(&_hashLock); - auto avatar = _avatarHash.value(sessionUUID); - if (!avatar) { avatar = addAvatar(sessionUUID, mixerWeakPointer); } - return avatar; } @@ -103,27 +100,33 @@ void AvatarHashMap::processAvatarDataPacket(QSharedPointer mess // enumerate over all of the avatars in this packet // only add them if mixerWeakPointer points to something (meaning that mixer is still around) while (message->getBytesLeftToRead()) { - QUuid sessionUUID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); + parseAvatarData(message, sendingNode); + } +} - int positionBeforeRead = message->getPosition(); +AvatarSharedPointer AvatarHashMap::parseAvatarData(QSharedPointer message, SharedNodePointer sendingNode) { + QUuid sessionUUID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); - QByteArray byteArray = message->readWithoutCopy(message->getBytesLeftToRead()); + int positionBeforeRead = message->getPosition(); - // make sure this isn't our own avatar data or for a previously ignored node - auto nodeList = DependencyManager::get(); + QByteArray byteArray = message->readWithoutCopy(message->getBytesLeftToRead()); - if (sessionUUID != _lastOwnerSessionUUID && (!nodeList->isIgnoringNode(sessionUUID) || nodeList->getRequestsDomainListData())) { - auto avatar = newOrExistingAvatar(sessionUUID, sendingNode); + // make sure this isn't our own avatar data or for a previously ignored node + auto nodeList = DependencyManager::get(); - // have the matching (or new) avatar parse the data from the packet - int bytesRead = avatar->parseDataFromBuffer(byteArray); - message->seek(positionBeforeRead + bytesRead); - } else { - // create a dummy AvatarData class to throw this data on the ground - AvatarData dummyData; - int bytesRead = dummyData.parseDataFromBuffer(byteArray); - message->seek(positionBeforeRead + bytesRead); - } + if (sessionUUID != _lastOwnerSessionUUID && (!nodeList->isIgnoringNode(sessionUUID) || nodeList->getRequestsDomainListData())) { + auto avatar = newOrExistingAvatar(sessionUUID, sendingNode); + + // have the matching (or new) avatar parse the data from the packet + int bytesRead = avatar->parseDataFromBuffer(byteArray); + message->seek(positionBeforeRead + bytesRead); + return avatar; + } else { + // create a dummy AvatarData class to throw this data on the ground + AvatarData dummyData; + int bytesRead = dummyData.parseDataFromBuffer(byteArray); + message->seek(positionBeforeRead + bytesRead); + return std::make_shared(); } } diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index 104ac83261..346cd36b60 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -49,11 +49,11 @@ signals: public slots: bool isAvatarInRange(const glm::vec3 & position, const float range); - -private slots: + +protected slots: void sessionUUIDChanged(const QUuid& sessionUUID, const QUuid& oldUUID); - - void processAvatarDataPacket(QSharedPointer message, SharedNodePointer sendingNode); + + virtual void processAvatarDataPacket(QSharedPointer message, SharedNodePointer sendingNode); void processAvatarIdentityPacket(QSharedPointer message, SharedNodePointer sendingNode); void processKillAvatar(QSharedPointer message, SharedNodePointer sendingNode); void processExitingSpaceBubble(QSharedPointer message, SharedNodePointer sendingNode); @@ -61,12 +61,13 @@ private slots: protected: AvatarHashMap(); + virtual AvatarSharedPointer parseAvatarData(QSharedPointer message, SharedNodePointer sendingNode); virtual AvatarSharedPointer newSharedAvatar(); virtual AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer); AvatarSharedPointer newOrExistingAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer); virtual AvatarSharedPointer findAvatar(const QUuid& sessionUUID) const; // uses a QReadLocker on the hashLock virtual void removeAvatar(const QUuid& sessionUUID, KillAvatarReason removalReason = KillAvatarReason::NoReason); - + virtual void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason = KillAvatarReason::NoReason); AvatarHash _avatarHash;