From 026c6301a61cda663aa799958f116a0ca2f73ccf Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 5 Dec 2018 14:34:43 -0800 Subject: [PATCH] correctly load AvatarEntities from settings --- interface/src/avatar/MyAvatar.cpp | 61 +++++++++++++++++-- interface/src/avatar/MyAvatar.h | 3 + libraries/avatars/src/AvatarData.cpp | 15 +++-- libraries/avatars/src/AvatarData.h | 3 +- .../entities/src/EntityEditPacketSender.cpp | 13 ++-- .../entities/src/EntityEditPacketSender.h | 2 +- 6 files changed, 76 insertions(+), 21 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 0e17fa8fbe..197bfcdca5 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -50,6 +50,7 @@ #include #include #include +#include #include "MyHead.h" #include "MySkeletonModel.h" @@ -1437,6 +1438,38 @@ void MyAvatar::setEnableInverseKinematics(bool isEnabled) { _skeletonModel->getRig().setEnableInverseKinematics(isEnabled); } +void MyAvatar::updateAvatarEntity(const QUuid& entityID, const EntityItemProperties& properties) { + auto entityTreeRenderer = qApp->getEntities(); + EntityTreePointer entityTree = entityTreeRenderer ? entityTreeRenderer->getTree() : nullptr; + EntityItemPointer entity; + if (entityTree) { + entity = entityTree->findEntityByID(entityID); + if (!entity) { + entity = entityTree->addEntity(entityID, properties); + if (!entity) { + // unable to create entity + // TODO? handle this case? + return; + } + } else { + // TODO: propagate changes to entity + } + } + + OctreePacketData packetData(false, AvatarTraits::MAXIMUM_TRAIT_SIZE); + EncodeBitstreamParams params; + EntityTreeElementExtraEncodeDataPointer extra { nullptr }; + OctreeElement::AppendState appendState = entity->appendEntityData(&packetData, params, extra); + + if (appendState != OctreeElement::COMPLETED) { + // this entity's data is too big + return; + } + + packetData.shrinkByteArrays(); + storeAvatarEntityDataPayload(entity->getID(), packetData.getUncompressedByteArray()); +} + void MyAvatar::loadData() { getHead()->setBasePitch(_headPitchSetting.get()); @@ -1450,13 +1483,29 @@ void MyAvatar::loadData() { useFullAvatarURL(_fullAvatarURLFromPreferences, _fullAvatarModelName); int numEntities = _avatarEntityCountSetting.get(0); + auto entityTree = DependencyManager::get()->getTree(); - for (int i = 0; i < numEntities; i++) { - resizeAvatarEntitySettingHandles(i); - QUuid entityID = QUuid::createUuid(); // generate a new ID - QString propertiesString = _avatarEntityDataSettings[i].get(); - // TODO: convert propertiesString to EntityItemProperties - //updateAvatarEntity(entityID, properties); + if (numEntities > 0) { + QScriptEngine scriptEngine; + for (int i = 0; i < numEntities; i++) { + resizeAvatarEntitySettingHandles(i); + QUuid entityID = QUuid::createUuid(); // generate a new ID + + // NOTE: this path from EntityItemProperties JSON string to EntityItemProperties is NOT efficient + QString propertiesString = _avatarEntityDataSettings[i].get(); + QJsonDocument propertiesDoc = QJsonDocument::fromJson(propertiesString.toUtf8()); + QJsonObject propertiesObj = propertiesDoc.object(); + QVariant propertiesVariant(propertiesObj); + QVariantMap propertiesMap = propertiesVariant.toMap(); + QScriptValue propertiesScriptValue = variantMapToScriptValue(propertiesMap, scriptEngine); + EntityItemProperties properties; + EntityItemPropertiesFromScriptValueIgnoreReadOnly(propertiesScriptValue, properties); + + // the ClientOnly property can get stripped out elsewhere so we need to always set it true here + properties.setClientOnly(true); + + updateAvatarEntity(entityID, properties); + } } // Flying preferences must be loaded before calling setFlyingEnabled() diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 1984ea9766..b1848a19b9 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -1402,6 +1402,9 @@ public slots: */ bool getEnableMeshVisible() const override; + // TODO: make this invokable, probably also move down to AvatarData + void updateAvatarEntity(const QUuid& entityID, const EntityItemProperties& properties); + /**jsdoc * Set whether or not your avatar mesh is visible. * @function MyAvatar.setEnableMeshVisible diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 2f1c8d3d82..e015410582 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2019,7 +2019,7 @@ void AvatarData::processTrait(AvatarTraits::TraitType traitType, QByteArray trai void AvatarData::processTraitInstance(AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID, QByteArray traitBinaryData) { if (traitType == AvatarTraits::AvatarEntity) { - updateAvatarEntity(instanceID, traitBinaryData); + storeAvatarEntityDataPayload(instanceID, traitBinaryData); } else if (traitType == AvatarTraits::Grab) { updateAvatarGrabData(instanceID, traitBinaryData); } @@ -2569,8 +2569,9 @@ void AvatarData::fromJson(const QJsonObject& json, bool useFrameSkeleton) { if (attachmentJson.isObject()) { QVariantMap entityData = attachmentJson.toObject().toVariantMap(); QUuid entityID = entityData.value("id").toUuid(); + // ADEBUG TODO: fix this broken path QByteArray properties = QByteArray::fromBase64(entityData.value("properties").toByteArray()); - updateAvatarEntity(entityID, properties); + storeAvatarEntityDataPayload(entityID, properties); } } } @@ -2754,15 +2755,15 @@ void AvatarData::setAttachmentsVariant(const QVariantList& variant) { const int MAX_NUM_AVATAR_ENTITIES = 42; -void AvatarData::updateAvatarEntity(const QUuid& entityID, const QByteArray& entityData) { +void AvatarData::storeAvatarEntityDataPayload(const QUuid& entityID, const QByteArray& data) { _avatarEntitiesLock.withWriteLock([&] { AvatarEntityMap::iterator itr = _avatarEntityData.find(entityID); if (itr == _avatarEntityData.end()) { if (_avatarEntityData.size() < MAX_NUM_AVATAR_ENTITIES) { - _avatarEntityData.insert(entityID, entityData); + _avatarEntityData.insert(entityID, data); } } else { - itr.value() = entityData; + itr.value() = data; } }); @@ -2775,6 +2776,10 @@ void AvatarData::updateAvatarEntity(const QUuid& entityID, const QByteArray& ent } } +void AvatarData::updateAvatarEntity(const QUuid& entityID, const QString& entityPropertiesString) { + // TODO: implement this as API exposed to JS +} + void AvatarData::clearAvatarEntity(const QUuid& entityID, bool requiresRemovalFromTree) { bool removedEntity = false; diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index b42c387f61..1137cff869 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -952,13 +952,14 @@ public: // FIXME: Can this name be improved? Can it be deprecated? Q_INVOKABLE virtual void setAttachmentsVariant(const QVariantList& variant); + void storeAvatarEntityDataPayload(const QUuid& entityID, const QByteArray& payload); /**jsdoc * @function MyAvatar.updateAvatarEntity * @param {Uuid} entityID * @param {string} entityData */ - Q_INVOKABLE void updateAvatarEntity(const QUuid& entityID, const QByteArray& entityData); + Q_INVOKABLE void updateAvatarEntity(const QUuid& entityID, const QString& entityPropertiesString); /**jsdoc * @function MyAvatar.clearAvatarEntity diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp index 8d0348bae6..dfc2d45d36 100644 --- a/libraries/entities/src/EntityEditPacketSender.cpp +++ b/libraries/entities/src/EntityEditPacketSender.cpp @@ -39,8 +39,7 @@ void EntityEditPacketSender::adjustEditPacketForClockSkew(PacketType type, QByte } } -void EntityEditPacketSender::queueEditAvatarEntityMessage(PacketType type, - EntityTreePointer entityTree, +void EntityEditPacketSender::queueEditAvatarEntityMessage(EntityTreePointer entityTree, EntityItemID entityItemID, const EntityItemProperties& properties) { assert(_myAvatar); @@ -53,6 +52,7 @@ void EntityEditPacketSender::queueEditAvatarEntityMessage(PacketType type, qCDebug(entities) << "EntityEditPacketSender::queueEditAvatarEntityMessage can't find entity: " << entityItemID; return; } + entity->setLastBroadcast(usecTimestampNow()); // serialize ALL properties in an "AvatarEntity" packet // rather than just the ones being edited. @@ -65,17 +65,14 @@ void EntityEditPacketSender::queueEditAvatarEntityMessage(PacketType type, OctreeElement::AppendState appendState = entity->appendEntityData(&packetData, params, extra); if (appendState != OctreeElement::COMPLETED) { - // this entity is too big + // this entity's payload is too big return; } packetData.shrinkByteArrays(); - _myAvatar->updateAvatarEntity(entityItemID, packetData.getUncompressedByteArray()); - - entity->setLastBroadcast(usecTimestampNow()); + _myAvatar->storeAvatarEntityDataPayload(entity->getID(), packetData.getUncompressedByteArray()); } - void EntityEditPacketSender::queueEditEntityMessage(PacketType type, EntityTreePointer entityTree, EntityItemID entityItemID, @@ -85,7 +82,7 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type, qCWarning(entities) << "Suppressing entity edit message: cannot send avatar entity edit with no myAvatar"; } else if (properties.getOwningAvatarID() == _myAvatar->getID()) { // this is an avatar-based entity --> update our avatar-data rather than sending to the entity-server - queueEditAvatarEntityMessage(type, entityTree, entityItemID, properties); + queueEditAvatarEntityMessage(entityTree, entityItemID, properties); } else { qCWarning(entities) << "Suppressing entity edit message: cannot send avatar entity edit for another avatar"; } diff --git a/libraries/entities/src/EntityEditPacketSender.h b/libraries/entities/src/EntityEditPacketSender.h index 9bf9095f7f..a4ec2c45f9 100644 --- a/libraries/entities/src/EntityEditPacketSender.h +++ b/libraries/entities/src/EntityEditPacketSender.h @@ -50,7 +50,7 @@ public slots: void processEntityEditNackPacket(QSharedPointer message, SharedNodePointer sendingNode); private: - void queueEditAvatarEntityMessage(PacketType type, EntityTreePointer entityTree, + void queueEditAvatarEntityMessage(EntityTreePointer entityTree, EntityItemID entityItemID, const EntityItemProperties& properties); private: