diff --git a/interface/src/AvatarBookmarks.cpp b/interface/src/AvatarBookmarks.cpp index d9ac522dc8..2ebe769bec 100644 --- a/interface/src/AvatarBookmarks.cpp +++ b/interface/src/AvatarBookmarks.cpp @@ -212,22 +212,36 @@ void AvatarBookmarks::loadBookmark(const QString& bookmarkName) { auto myAvatar = DependencyManager::get()->getMyAvatar(); auto treeRenderer = DependencyManager::get(); EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; - myAvatar->clearWornAvatarEntities(); + + // Once the skeleton URL has been loaded, add the Avatar Entities. + // We have to wait, because otherwise the avatar entities will try to get attached to the joints + // of the *current* avatar at first. But the current avatar might have a different joints scheme + // from the new avatar, and that would cause the entities to be attached to the wrong joints. + + std::shared_ptr connection1 = std::make_shared(); + *connection1 = connect(myAvatar.get(), &MyAvatar::onLoadComplete, [this, bookmark, bookmarkName, myAvatar, connection1]() { + qCDebug(interfaceapp) << "Finish loading avatar bookmark" << bookmarkName; + QObject::disconnect(*connection1); + myAvatar->clearWornAvatarEntities(); + const float& qScale = bookmark.value(ENTRY_AVATAR_SCALE, 1.0f).toFloat(); + myAvatar->setAvatarScale(qScale); + QList attachments = bookmark.value(ENTRY_AVATAR_ATTACHMENTS, QList()).toList(); + myAvatar->setAttachmentsVariant(attachments); + QVariantList avatarEntities = bookmark.value(ENTRY_AVATAR_ENTITIES, QVariantList()).toList(); + addAvatarEntities(avatarEntities); + emit bookmarkLoaded(bookmarkName); + }); + + std::shared_ptr connection2 = std::make_shared(); + *connection2 = connect(myAvatar.get(), &MyAvatar::onLoadFailed, [this, bookmarkName, connection2]() { + qCDebug(interfaceapp) << "Failed to load avatar bookmark" << bookmarkName; + QObject::disconnect(*connection2); + }); + + qCDebug(interfaceapp) << "Start loading avatar bookmark" << bookmarkName; + const QString& avatarUrl = bookmark.value(ENTRY_AVATAR_URL, "").toString(); myAvatar->useFullAvatarURL(avatarUrl); - qCDebug(interfaceapp) << "Avatar On"; - const QList& attachments = bookmark.value(ENTRY_AVATAR_ATTACHMENTS, QList()).toList(); - - qCDebug(interfaceapp) << "Attach " << attachments; - myAvatar->setAttachmentsVariant(attachments); - - const float& qScale = bookmark.value(ENTRY_AVATAR_SCALE, 1.0f).toFloat(); - myAvatar->setAvatarScale(qScale); - - const QVariantList& avatarEntities = bookmark.value(ENTRY_AVATAR_ENTITIES, QVariantList()).toList(); - addAvatarEntities(avatarEntities); - - emit bookmarkLoaded(bookmarkName); } } } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 0fe8a4e218..50e6fa4207 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -349,7 +349,8 @@ MyAvatar::MyAvatar(QThread* thread) : } }); - connect(&(_skeletonModel->getRig()), SIGNAL(onLoadComplete()), this, SIGNAL(onLoadComplete())); + connect(&(_skeletonModel->getRig()), &Rig::onLoadComplete, this, &MyAvatar::onLoadComplete); + connect(&(_skeletonModel->getRig()), &Rig::onLoadFailed, this, &MyAvatar::onLoadFailed); _characterController.setDensity(_density); } @@ -2635,6 +2636,8 @@ void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelN if (urlString.isEmpty() || (fullAvatarURL != getSkeletonModelURL())) { setSkeletonModelURL(fullAvatarURL); UserActivityLogger::getInstance().changedModel("skeleton", urlString); + } else { + emit onLoadComplete(); } } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 09b1994277..950c7750e6 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -2463,10 +2463,17 @@ signals: /**jsdoc * Triggered when the avatar's model finishes loading. * @function MyAvatar.onLoadComplete - * @returns {Signal} + * @returns {Signal} */ void onLoadComplete(); + /**jsdoc + * Triggered when the avatar's model has failed to load. + * @function MyAvatar.onLoadFailed + * @returns {Signal} + */ + void onLoadFailed(); + /**jsdoc * Triggered when your avatar changes from being active to being away. * @function MyAvatar.wentAway diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index fac4e04ce9..fc1885ea2b 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -2353,6 +2353,7 @@ void Rig::initAnimGraph(const QUrl& url) { // abort load if the previous skeleton was deleted. auto sharedSkeletonPtr = weakSkeletonPtr.lock(); if (!sharedSkeletonPtr) { + emit onLoadFailed(); return; } @@ -2386,8 +2387,9 @@ void Rig::initAnimGraph(const QUrl& url) { } emit onLoadComplete(); }); - connect(_animLoader.get(), &AnimNodeLoader::error, [url](int error, QString str) { + connect(_animLoader.get(), &AnimNodeLoader::error, [this, url](int error, QString str) { qCritical(animation) << "Error loading: code = " << error << "str =" << str; + emit onLoadFailed(); }); connect(_networkLoader.get(), &AnimNodeLoader::success, [this, weakSkeletonPtr, networkUrl](AnimNode::Pointer nodeIn) { @@ -2415,6 +2417,8 @@ void Rig::initAnimGraph(const QUrl& url) { connect(_networkLoader.get(), &AnimNodeLoader::error, [networkUrl](int error, QString str) { qCritical(animation) << "Error loading: code = " << error << "str =" << str; }); + } else { + emit onLoadComplete(); } } diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 8570ae4441..b2b9ecd5b4 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -260,6 +260,7 @@ public: signals: void onLoadComplete(); + void onLoadFailed(); protected: bool isIndexValid(int index) const { return _animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints(); }