diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 3f738ea4cb..bd05bb284e 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1688,16 +1688,6 @@ void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelN markIdentityDataChanged(); } -void MyAvatar::setAttachmentData(const QVector& attachmentData) { - if (QThread::currentThread() != thread()) { - BLOCKING_INVOKE_METHOD(this, "setAttachmentData", - Q_ARG(const QVector, attachmentData)); - return; - } - Avatar::setAttachmentData(attachmentData); - emit attachmentsChanged(); -} - glm::vec3 MyAvatar::getSkeletonPosition() const { CameraMode mode = qApp->getCamera().getMode(); if (mode == CAMERA_MODE_THIRD_PERSON || mode == CAMERA_MODE_INDEPENDENT) { @@ -1968,20 +1958,165 @@ void MyAvatar::attach(const QString& modelURL, const QString& jointName, float scale, bool isSoft, bool allowDuplicates, bool useSaved) { if (QThread::currentThread() != thread()) { - Avatar::attach(modelURL, jointName, translation, rotation, scale, isSoft, allowDuplicates, useSaved); + BLOCKING_INVOKE_METHOD(this, "attach", + Q_ARG(const QString&, modelURL), + Q_ARG(const QString&, jointName), + Q_ARG(const glm::vec3&, translation), + Q_ARG(const glm::quat&, rotation), + Q_ARG(float, scale), + Q_ARG(bool, isSoft), + Q_ARG(bool, allowDuplicates), + Q_ARG(bool, useSaved) + ); return; } - if (useSaved) { - AttachmentData attachment = loadAttachmentData(modelURL, jointName); - if (attachment.isValid()) { - Avatar::attach(modelURL, attachment.jointName, - attachment.translation, attachment.rotation, - attachment.scale, attachment.isSoft, - allowDuplicates, useSaved); - return; + AttachmentData data; + data.modelURL = modelURL; + data.jointName = jointName; + data.translation = translation; + data.rotation = rotation; + data.scale = scale; + data.isSoft = isSoft; + EntityItemProperties properties; + attachmentDataToEntityProperties(data, properties); + DependencyManager::get()->addEntity(properties, true); + emit attachmentsChanged(); +} + +void MyAvatar::detachOne(const QString& modelURL, const QString& jointName) { + if (QThread::currentThread() != thread()) { + BLOCKING_INVOKE_METHOD(this, "detachOne", + Q_ARG(const QString&, modelURL), + Q_ARG(const QString&, jointName) + ); + return; + } + QUuid entityID; + if (findAvatarEntity(modelURL, jointName, entityID)) { + DependencyManager::get()->deleteEntity(entityID); + } + emit attachmentsChanged(); +} + +void MyAvatar::detachAll(const QString& modelURL, const QString& jointName) { + if (QThread::currentThread() != thread()) { + BLOCKING_INVOKE_METHOD(this, "detachAll", + Q_ARG(const QString&, modelURL), + Q_ARG(const QString&, jointName) + ); + return; + } + QUuid entityID; + while (findAvatarEntity(modelURL, jointName, entityID)) { + DependencyManager::get()->deleteEntity(entityID); + } + emit attachmentsChanged(); +} + +void MyAvatar::setAttachmentData(const QVector& attachmentData) { + if (QThread::currentThread() != thread()) { + BLOCKING_INVOKE_METHOD(this, "setAttachmentData", + Q_ARG(const QVector&, attachmentData)); + return; + } + std::vector newEntitiesProperties; + for (auto& data : attachmentData) { + QUuid entityID; + EntityItemProperties properties; + if (findAvatarEntity(data.modelURL.toString(), data.jointName, entityID)) { + properties = DependencyManager::get()->getEntityProperties(entityID); + } + attachmentDataToEntityProperties(data, properties); + newEntitiesProperties.push_back(properties); + } + removeAvatarEntities(); + for (auto& properties : newEntitiesProperties) { + DependencyManager::get()->addEntity(properties, true); + } + emit attachmentsChanged(); +} + +QVector MyAvatar::getAttachmentData() const { + QVector avatarData; + auto avatarEntities = getAvatarEntityData(); + AvatarEntityMap::const_iterator dataItr = avatarEntities.begin(); + while (dataItr != avatarEntities.end()) { + QUuid entityID = dataItr.key(); + auto properties = DependencyManager::get()->getEntityProperties(entityID); + AttachmentData data = entityPropertiesToAttachmentData(properties); + avatarData.append(data); + dataItr++; + } + return avatarData; +} + +QVariantList MyAvatar::getAttachmentsVariant() const { + QVariantList result; + for (const auto& attachment : getAttachmentData()) { + result.append(attachment.toVariant()); + } + return result; +} + +void MyAvatar::setAttachmentsVariant(const QVariantList& variant) { + if (QThread::currentThread() != thread()) { + BLOCKING_INVOKE_METHOD(this, "setAttachmentsVariant", + Q_ARG(const QVariantList&, variant)); + return; + } + QVector newAttachments; + newAttachments.reserve(variant.size()); + for (const auto& attachmentVar : variant) { + AttachmentData attachment; + if (attachment.fromVariant(attachmentVar)) { + newAttachments.append(attachment); } } - Avatar::attach(modelURL, jointName, translation, rotation, scale, isSoft, allowDuplicates, useSaved); + setAttachmentData(newAttachments); +} + +bool MyAvatar::findAvatarEntity(const QString& modelURL, const QString& jointName, QUuid& entityID) { + auto avatarEntities = getAvatarEntityData(); + AvatarEntityMap::const_iterator dataItr = avatarEntities.begin(); + while (dataItr != avatarEntities.end()) { + entityID = dataItr.key(); + auto props = DependencyManager::get()->getEntityProperties(entityID); + if (props.getModelURL() == modelURL && + (jointName.isEmpty() || props.getParentJointIndex() == getJointIndex(jointName))) { + return true; + } + dataItr++; + } + return false; +} + +AttachmentData MyAvatar::entityPropertiesToAttachmentData(const EntityItemProperties& properties) const { + AttachmentData data; + data.modelURL = properties.getModelURL(); + data.translation = properties.getLocalPosition(); + data.rotation = properties.getLocalRotation(); + data.isSoft = properties.getRelayParentJoints(); + quint16 jointIndex = properties.getParentJointIndex(); + if (jointIndex > -1 && jointIndex < getJointNames().size()) { + data.jointName = getJointNames()[jointIndex]; + } + return data; +} + +void MyAvatar::attachmentDataToEntityProperties(const AttachmentData& data, EntityItemProperties& properties) { + QString url = data.modelURL.toString(); + properties.setName(QFileInfo(url).baseName()); + properties.setType(EntityTypes::Model); + properties.setParentID(getID()); + properties.setOwningAvatarID(getID()); + properties.setLocalPosition(data.translation); + properties.setLocalRotation(data.rotation); + if (!data.isSoft) { + properties.setParentJointIndex(getJointIndex(data.jointName)); + } else { + properties.setRelayParentJoints(true); + } + properties.setModelURL(url); } void MyAvatar::initHeadBones() { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index d36e43ca25..bda6a817ff 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -865,8 +865,6 @@ public: void resetFullAvatarURL(); - virtual void setAttachmentData(const QVector& attachmentData) override; - MyCharacterController* getCharacterController() { return &_characterController; } const MyCharacterController* getCharacterController() const { return &_characterController; } @@ -1082,6 +1080,12 @@ public: float computeStandingHeightMode(const controller::Pose& head); glm::quat computeAverageHeadRotation(const controller::Pose& head); + virtual void setAttachmentData(const QVector& attachmentData) override; + virtual QVector getAttachmentData() const override; + + virtual QVariantList getAttachmentsVariant() const override; + virtual void setAttachmentsVariant(const QVariantList& variant) override; + public slots: /**jsdoc @@ -1506,11 +1510,21 @@ private: void setScriptedMotorTimescale(float timescale); void setScriptedMotorFrame(QString frame); void setScriptedMotorMode(QString mode); + + // Attachments virtual void attach(const QString& modelURL, const QString& jointName = QString(), const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(), float scale = 1.0f, bool isSoft = false, bool allowDuplicates = false, bool useSaved = true) override; + virtual void detachOne(const QString& modelURL, const QString& jointName = QString()) override; + virtual void detachAll(const QString& modelURL, const QString& jointName = QString()) override; + + // Attachments/Avatar Entity + void attachmentDataToEntityProperties(const AttachmentData& data, EntityItemProperties& properties); + AttachmentData entityPropertiesToAttachmentData(const EntityItemProperties& properties) const; + bool findAvatarEntity(const QString& modelURL, const QString& jointName, QUuid& entityID); + bool cameraInsideHead(const glm::vec3& cameraPosition) const; void updateEyeContactTarget(float deltaTime); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 0f850aaf24..147d303871 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -895,14 +895,14 @@ public: * @returns {object} */ // FIXME: Can this name be improved? Can it be deprecated? - Q_INVOKABLE QVariantList getAttachmentsVariant() const; + Q_INVOKABLE virtual QVariantList getAttachmentsVariant() const; /**jsdoc * @function MyAvatar.setAttachmentsVariant * @param {object} variant */ // FIXME: Can this name be improved? Can it be deprecated? - Q_INVOKABLE void setAttachmentsVariant(const QVariantList& variant); + Q_INVOKABLE virtual void setAttachmentsVariant(const QVariantList& variant); /**jsdoc @@ -969,7 +969,7 @@ public: * print (attachments[i].modelURL); * } */ - Q_INVOKABLE QVector getAttachmentData() const; + Q_INVOKABLE virtual QVector getAttachmentData() const; /**jsdoc * Set all models currently attached to your avatar. For example, if you retrieve attachment data using @@ -1040,7 +1040,7 @@ public: * @param {string} [jointName=""] - The name of the joint to detach the model from. If "", then the most * recently attached model is removed from which ever joint it was attached to. */ - Q_INVOKABLE void detachOne(const QString& modelURL, const QString& jointName = QString()); + Q_INVOKABLE virtual void detachOne(const QString& modelURL, const QString& jointName = QString()); /**jsdoc * Detach all instances of a particular model from either a specific joint or all joints. @@ -1049,7 +1049,7 @@ public: * @param {string} [jointName=""] - The name of the joint to detach the model from. If "", then the model is * detached from all joints. */ - Q_INVOKABLE void detachAll(const QString& modelURL, const QString& jointName = QString()); + Q_INVOKABLE virtual void detachAll(const QString& modelURL, const QString& jointName = QString()); QString getSkeletonModelURLFromScript() const { return _skeletonModelURL.toString(); } void setSkeletonModelURLFromScript(const QString& skeletonModelString) { setSkeletonModelURL(QUrl(skeletonModelString)); }