From 0e6d9a1eecbbc82da30a5409f0c30912750ecbac Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sat, 7 May 2016 14:48:31 -0700 Subject: [PATCH 01/25] avatar mixer can relay "client-only" entities between interfaces -- the entity server wont know about them. --- interface/src/Application.cpp | 6 ++ interface/src/avatar/Avatar.cpp | 85 +++++++++++++++++ interface/src/avatar/Avatar.h | 1 + interface/src/avatar/MyAvatar.cpp | 28 +++++- libraries/avatars/src/AvatarData.cpp | 93 ++++++++++++++++++- libraries/avatars/src/AvatarData.h | 20 ++++ libraries/avatars/src/AvatarHashMap.cpp | 9 +- .../src/RenderableWebEntityItem.cpp | 5 + .../entities/src/EntityEditPacketSender.cpp | 45 ++++++++- .../entities/src/EntityEditPacketSender.h | 11 ++- libraries/entities/src/EntityItem.h | 14 ++- libraries/entities/src/EntityItemProperties.h | 9 ++ .../entities/src/EntityScriptingInterface.cpp | 17 ++-- .../entities/src/EntityScriptingInterface.h | 2 +- libraries/entities/src/EntityTree.cpp | 5 +- libraries/physics/src/EntityMotionState.cpp | 12 ++- 16 files changed, 336 insertions(+), 26 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4a829b3191..0b1154d983 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -796,6 +796,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : // Tell our entity edit sender about our known jurisdictions _entityEditSender.setServerJurisdictions(&_entityServerJurisdictions); + _entityEditSender.setMyAvatar(getMyAvatar()); // For now we're going to set the PPS for outbound packets to be super high, this is // probably not the right long term solution. But for now, we're going to do this to @@ -1087,6 +1088,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : // Make sure we don't time out during slow operations at startup updateHeartbeat(); + OctreeEditPacketSender* packetSender = entityScriptingInterface->getPacketSender(); + EntityEditPacketSender* entityPacketSender = static_cast(packetSender); + entityPacketSender->setMyAvatar(getMyAvatar()); + connect(this, &Application::applicationStateChanged, this, &Application::activeChanged); qCDebug(interfaceapp, "Startup time: %4.2f seconds.", (double)startupTimer.elapsed() / 1000.0); @@ -4186,6 +4191,7 @@ void Application::clearDomainOctreeDetails() { qCDebug(interfaceapp) << "Clearing domain octree details..."; resetPhysicsReadyInformation(); + getMyAvatar()->setAvatarEntityDataChanged(true); // to recreate worn entities // reset our node to stats and node to jurisdiction maps... since these must be changing... _entityServerJurisdictions.withWriteLock([&] { diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 2cacb81ce4..0090dcedf6 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include "Application.h" #include "Avatar.h" @@ -160,6 +161,89 @@ void Avatar::animateScaleChanges(float deltaTime) { } } +void Avatar::updateAvatarEntities() { + // - if queueEditEntityMessage sees clientOnly flag it does _myAvatar->updateAvatarEntity() + // - updateAvatarEntity saves the bytes and sets _avatarEntityDataLocallyEdited + // - MyAvatar::update notices _avatarEntityDataLocallyEdited and calls sendIdentityPacket + // - sendIdentityPacket sends the entity bytes to the server which relays them to other interfaces + // - AvatarHashMap::processAvatarIdentityPacket's on other interfaces call avatar->setAvatarEntityData() + // - setAvatarEntityData saves the bytes and sets _avatarEntityDataChanged = true + // - (My)Avatar::simulate notices _avatarEntityDataChanged and here we are... + + quint64 now = usecTimestampNow(); + + const static quint64 refreshTime = 3 * USECS_PER_SECOND; + if (!_avatarEntityDataChanged && now - _avatarEntityChangedTime < refreshTime) { + return; + } + + EntityTreeRenderer* treeRenderer = qApp->getEntities(); + EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; + if (!entityTree) { + return; + } + + bool success = true; + QScriptEngine scriptEngine; + entityTree->withWriteLock([&] { + AvatarEntityMap avatarEntities = getAvatarEntityData(); + for (auto entityID : avatarEntities.keys()) { + // see EntityEditPacketSender::queueEditEntityMessage for the other end of this. unpack properties + // and either add or update the entity. + QByteArray jsonByteArray = avatarEntities.value(entityID); + QJsonDocument jsonProperties = QJsonDocument::fromBinaryData(jsonByteArray); + if (!jsonProperties.isObject()) { + qDebug() << "got bad avatarEntity json"; + continue; + } + QVariant variantProperties = jsonProperties.toVariant(); + QVariantMap asMap = variantProperties.toMap(); + QScriptValue scriptProperties = variantMapToScriptValue(asMap, scriptEngine); + EntityItemProperties properties; + EntityItemPropertiesFromScriptValueHonorReadOnly(scriptProperties, properties); + properties.setClientOnly(true); + properties.setOwningAvatarID(getID()); + + if (properties.getParentID() == AVATAR_SELF_ID) { + properties.setParentID(getID()); + } + + EntityItemPointer entity = entityTree->findEntityByEntityItemID(EntityItemID(entityID)); + + if (entity) { + if (!entityTree->updateEntity(entityID, properties)) { + qDebug() << "AVATAR-ENTITES -- updateEntity failed: " << properties.getType(); + success = false; + } + } else { + entity = entityTree->addEntity(entityID, properties); + if (!entity) { + qDebug() << "AVATAR-ENTITES -- addEntity failed: " << properties.getType(); + success = false; + } + } + } + }); + + AvatarEntityIDs recentlyDettachedAvatarEntities = getAndClearRecentlyDetachedIDs(); + foreach (auto entityID, recentlyDettachedAvatarEntities) { + if (!_avatarEntityData.contains(entityID)) { + EntityItemPointer dettachedEntity = entityTree->findEntityByEntityItemID(EntityItemID(entityID)); + if (dettachedEntity) { + // this will cause this interface to listen to data from the entity-server about this entity. + dettachedEntity->setClientOnly(false); + } + } + } + + if (success) { + setAvatarEntityDataChanged(false); + _avatarEntityChangedTime = now; + } +} + + + void Avatar::simulate(float deltaTime) { PerformanceTimer perfTimer("simulate"); @@ -228,6 +312,7 @@ void Avatar::simulate(float deltaTime) { simulateAttachments(deltaTime); updatePalms(); + updateAvatarEntities(); } bool Avatar::isLookingAtMe(AvatarSharedPointer avatar) const { diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index cb35fbb5eb..ded5ee6433 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -64,6 +64,7 @@ public: typedef std::shared_ptr PayloadPointer; void init(); + void updateAvatarEntities(); void simulate(float deltaTime); virtual void simulateAttachments(float deltaTime); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index bad60643ec..f9daad923b 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -311,6 +311,10 @@ void MyAvatar::update(float deltaTime) { head->setAudioLoudness(audio->getLastInputLoudness()); head->setAudioAverageLoudness(audio->getAudioAverageInputLoudness()); + if (_avatarEntityDataLocallyEdited) { + sendIdentityPacket(); + } + simulate(deltaTime); currentEnergy += energyChargeRate; @@ -443,7 +447,7 @@ void MyAvatar::simulate(float deltaTime) { EntityItemProperties properties = entity->getProperties(); properties.setQueryAACubeDirty(); properties.setLastEdited(now); - packetSender->queueEditEntityMessage(PacketType::EntityEdit, entity->getID(), properties); + packetSender->queueEditEntityMessage(PacketType::EntityEdit, entityTree, entity->getID(), properties); entity->setLastBroadcast(usecTimestampNow()); } } @@ -455,6 +459,8 @@ void MyAvatar::simulate(float deltaTime) { } }); } + + updateAvatarEntities(); } // thread-safe @@ -696,6 +702,16 @@ void MyAvatar::saveData() { } settings.endArray(); + settings.beginWriteArray("avatarEntityData"); + int avatarEntityIndex = 0; + for (auto entityID : _avatarEntityData.keys()) { + settings.setArrayIndex(avatarEntityIndex); + settings.setValue("id", entityID); + settings.setValue("properties", _avatarEntityData.value(entityID)); + avatarEntityIndex++; + } + settings.endArray(); + settings.setValue("displayName", _displayName); settings.setValue("collisionSoundURL", _collisionSoundURL); settings.setValue("useSnapTurn", _useSnapTurn); @@ -807,6 +823,16 @@ void MyAvatar::loadData() { settings.endArray(); setAttachmentData(attachmentData); + int avatarEntityCount = settings.beginReadArray("avatarEntityData"); + for (int i = 0; i < avatarEntityCount; i++) { + settings.setArrayIndex(i); + QUuid entityID = settings.value("id").toUuid(); + QByteArray properties = settings.value("properties").toByteArray(); + updateAvatarEntity(entityID, properties); + } + settings.endArray(); + setAvatarEntityDataChanged(true); + setDisplayName(settings.value("displayName").toString()); setCollisionSoundURL(settings.value("collisionSoundURL", DEFAULT_AVATAR_COLLISION_SOUND_URL).toString()); setSnapTurn(settings.value("useSnapTurn", _useSnapTurn).toBool()); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index fd13f8c370..9a0fc1a835 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -962,8 +962,9 @@ bool AvatarData::hasIdentityChangedAfterParsing(const QByteArray& data) { QUrl unusedModelURL; // legacy faceModel support QUrl skeletonModelURL; QVector attachmentData; + AvatarEntityMap avatarEntityData; QString displayName; - packetStream >> avatarUUID >> unusedModelURL >> skeletonModelURL >> attachmentData >> displayName; + packetStream >> avatarUUID >> unusedModelURL >> skeletonModelURL >> attachmentData >> displayName >> avatarEntityData; bool hasIdentityChanged = false; @@ -983,6 +984,11 @@ bool AvatarData::hasIdentityChangedAfterParsing(const QByteArray& data) { hasIdentityChanged = true; } + if (avatarEntityData != _avatarEntityData) { + setAvatarEntityData(avatarEntityData); + hasIdentityChanged = true; + } + return hasIdentityChanged; } @@ -994,7 +1000,7 @@ QByteArray AvatarData::identityByteArray() { QUrl unusedModelURL; // legacy faceModel support - identityStream << QUuid() << unusedModelURL << urlToSend << _attachmentData << _displayName; + identityStream << QUuid() << unusedModelURL << urlToSend << _attachmentData << _displayName << _avatarEntityData; return identityData; } @@ -1202,6 +1208,8 @@ void AvatarData::sendIdentityPacket() { [&](const SharedNodePointer& node) { nodeList->sendPacketList(std::move(packetList), *node); }); + + _avatarEntityDataLocallyEdited = false; } void AvatarData::sendBillboardPacket() { @@ -1389,6 +1397,7 @@ static const QString JSON_AVATAR_HEAD_MODEL = QStringLiteral("headModel"); static const QString JSON_AVATAR_BODY_MODEL = QStringLiteral("bodyModel"); static const QString JSON_AVATAR_DISPLAY_NAME = QStringLiteral("displayName"); static const QString JSON_AVATAR_ATTACHEMENTS = QStringLiteral("attachments"); +static const QString JSON_AVATAR_ENTITIES = QStringLiteral("attachedEntities"); static const QString JSON_AVATAR_SCALE = QStringLiteral("scale"); QJsonValue toJsonValue(const JointData& joint) { @@ -1427,6 +1436,17 @@ QJsonObject AvatarData::toJson() const { root[JSON_AVATAR_ATTACHEMENTS] = attachmentsJson; } + if (!_avatarEntityData.empty()) { + QJsonArray avatarEntityJson; + for (auto entityID : _avatarEntityData.keys()) { + QVariantMap entityData; + entityData.insert("id", entityID); + entityData.insert("properties", _avatarEntityData.value(entityID)); + avatarEntityJson.push_back(QVariant(entityData).toJsonObject()); + } + root[JSON_AVATAR_ENTITIES] = avatarEntityJson; + } + auto recordingBasis = getRecordingBasis(); bool success; Transform avatarTransform = getTransform(success); @@ -1526,6 +1546,13 @@ void AvatarData::fromJson(const QJsonObject& json) { setAttachmentData(attachments); } + // if (json.contains(JSON_AVATAR_ENTITIES) && json[JSON_AVATAR_ENTITIES].isArray()) { + // QJsonArray attachmentsJson = json[JSON_AVATAR_ATTACHEMENTS].toArray(); + // for (auto attachmentJson : attachmentsJson) { + // // TODO -- something + // } + // } + // Joint rotations are relative to the avatar, so they require no basis correction if (json.contains(JSON_AVATAR_JOINT_ARRAY)) { QVector jointArray; @@ -1678,9 +1705,69 @@ void AvatarData::setAttachmentsVariant(const QVariantList& variant) { QVector newAttachments; newAttachments.reserve(variant.size()); for (const auto& attachmentVar : variant) { - AttachmentData attachment; + AttachmentData attachment; attachment.fromVariant(attachmentVar); newAttachments.append(attachment); } setAttachmentData(newAttachments); } + +void AvatarData::updateAvatarEntity(const QUuid& entityID, const QByteArray& entityData) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "updateAvatarEntity", Q_ARG(const QUuid&, entityID), Q_ARG(QByteArray, entityData)); + return; + } + _avatarEntityData.insert(entityID, entityData); + _avatarEntityDataLocallyEdited = true; +} + +void AvatarData::clearAvatarEntity(const QUuid& entityID) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "clearAvatarEntity", Q_ARG(const QUuid&, entityID)); + return; + } + _avatarEntityData.remove(entityID); + _avatarEntityDataLocallyEdited = true; +} + +AvatarEntityMap AvatarData::getAvatarEntityData() const { + if (QThread::currentThread() != thread()) { + AvatarEntityMap result; + QMetaObject::invokeMethod(const_cast(this), "getAvatarEntityData", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(AvatarEntityMap, result)); + return result; + } + return _avatarEntityData; +} + +void AvatarData::setAvatarEntityData(const AvatarEntityMap& avatarEntityData) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "setAvatarEntityData", Q_ARG(const AvatarEntityMap&, avatarEntityData)); + return; + } + if (_avatarEntityData != avatarEntityData) { + // keep track of entities that were attached to this avatar but no longer are + AvatarEntityIDs previousAvatarEntityIDs = QSet::fromList(_avatarEntityData.keys()); + + _avatarEntityData = avatarEntityData; + setAvatarEntityDataChanged(true); + + foreach (auto entityID, previousAvatarEntityIDs) { + if (!_avatarEntityData.contains(entityID)) { + _avatarEntityDetached.insert(entityID); + } + } + } +} + +AvatarEntityIDs AvatarData::getAndClearRecentlyDetachedIDs() { + if (QThread::currentThread() != thread()) { + AvatarEntityIDs result; + QMetaObject::invokeMethod(const_cast(this), "getRecentlyDetachedIDs", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(AvatarEntityIDs, result)); + return result; + } + AvatarEntityIDs result = _avatarEntityDetached; + _avatarEntityDetached.clear(); + return result; +} diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 900da38ffa..72d34af9d9 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -61,6 +61,8 @@ typedef unsigned long long quint64; using AvatarSharedPointer = std::shared_ptr; using AvatarWeakPointer = std::weak_ptr; using AvatarHash = QHash; +using AvatarEntityMap = QMap; +using AvatarEntityIDs = QSet; using AvatarDataSequenceNumber = uint16_t; @@ -135,6 +137,10 @@ class AttachmentData; class Transform; using TransformPointer = std::shared_ptr; +// When writing out avatarEntities to a QByteArray, if the parentID is the ID of MyAvatar, use this ID instead. This allows +// the value to be reset when the sessionID changes. +const QUuid AVATAR_SELF_ID = QUuid("{00000000-0000-0000-0000-000000000001}"); + class AvatarData : public QObject, public SpatiallyNestable { Q_OBJECT @@ -274,6 +280,9 @@ public: Q_INVOKABLE QVariantList getAttachmentsVariant() const; Q_INVOKABLE void setAttachmentsVariant(const QVariantList& variant); + Q_INVOKABLE void updateAvatarEntity(const QUuid& entityID, const QByteArray& entityData); + Q_INVOKABLE void clearAvatarEntity(const QUuid& entityID); + void setForceFaceTrackerConnected(bool connected) { _forceFaceTrackerConnected = connected; } // key state @@ -333,6 +342,11 @@ public: glm::vec3 getClientGlobalPosition() { return _globalPosition; } + Q_INVOKABLE AvatarEntityMap getAvatarEntityData() const; + Q_INVOKABLE void setAvatarEntityData(const AvatarEntityMap& avatarEntityData); + void setAvatarEntityDataChanged(bool value) { _avatarEntityDataChanged = value; } + AvatarEntityIDs getAndClearRecentlyDetachedIDs(); + public slots: void sendAvatarDataPacket(); void sendIdentityPacket(); @@ -405,6 +419,12 @@ protected: // updates about one avatar to another. glm::vec3 _globalPosition; + AvatarEntityIDs _avatarEntityDetached; // recently detached from this avatar + AvatarEntityMap _avatarEntityData; + bool _avatarEntityDataLocallyEdited { false }; + bool _avatarEntityDataChanged { false }; + quint64 _avatarEntityChangedTime { 0 }; + private: friend void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar); static QUrl _defaultFullAvatarModelUrl; diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 75fb5e6028..62e87ce285 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -111,17 +111,18 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer QDataStream identityStream(message->getMessage()); QUuid sessionUUID; - + while (!identityStream.atEnd()) { QUrl faceMeshURL, skeletonURL; QVector attachmentData; + AvatarEntityMap avatarEntityData; QString displayName; - identityStream >> sessionUUID >> faceMeshURL >> skeletonURL >> attachmentData >> displayName; + identityStream >> sessionUUID >> faceMeshURL >> skeletonURL >> attachmentData >> displayName >> avatarEntityData; // mesh URL for a UUID, find avatar in our list auto avatar = newOrExistingAvatar(sessionUUID, sendingNode); - + if (avatar->getSkeletonModelURL().isEmpty() || (avatar->getSkeletonModelURL() != skeletonURL)) { avatar->setSkeletonModelURL(skeletonURL); // Will expand "" to default and so will not continuously fire } @@ -130,6 +131,8 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer avatar->setAttachmentData(attachmentData); } + avatar->setAvatarEntityData(avatarEntityData); + if (avatar->getDisplayName() != displayName) { avatar->setDisplayName(displayName); } diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 26aecf6050..891e1dca3b 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -174,9 +174,14 @@ void RenderableWebEntityItem::render(RenderArgs* args) { #endif if (!_webSurface) { + #if defined(Q_OS_LINUX) + // these don't seem to work on Linux + return; + #else if (!buildWebSurface(static_cast(args->_renderer))) { return; } + #endif } _lastRenderTime = usecTimestampNow(); diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp index 1e38c32964..ea86d3d542 100644 --- a/libraries/entities/src/EntityEditPacketSender.cpp +++ b/libraries/entities/src/EntityEditPacketSender.cpp @@ -10,6 +10,7 @@ // #include +#include #include #include #include @@ -35,18 +36,54 @@ void EntityEditPacketSender::adjustEditPacketForClockSkew(PacketType type, QByte } } -void EntityEditPacketSender::queueEditEntityMessage(PacketType type, EntityItemID modelID, - const EntityItemProperties& properties) { +void EntityEditPacketSender::queueEditEntityMessage(PacketType type, + EntityTreePointer entityTree, + EntityItemID entityItemID, + const EntityItemProperties& properties) { if (!_shouldSend) { return; // bail early } + if (properties.getClientOnly()) { + // this is an avatar-based entity. update our avatar-data rather than sending to the entity-server + assert(_myAvatar); + + if (!entityTree) { + qDebug() << "EntityEditPacketSender::queueEditEntityMessage null entityTree."; + return; + } + EntityItemPointer entity = entityTree->findEntityByEntityItemID(entityItemID); + if (!entity) { + qDebug() << "EntityEditPacketSender::queueEditEntityMessage can't find entity."; + return; + } + + // the properties that get serialized into the avatar identity packet should be the entire set + // rather than just the ones being edited. + entity->setProperties(properties); + EntityItemProperties entityProperties = entity->getProperties(); + + QScriptValue scriptProperties = EntityItemNonDefaultPropertiesToScriptValue(&_scriptEngine, entityProperties); + QVariant variantProperties = scriptProperties.toVariant(); + QJsonDocument jsonProperties = QJsonDocument::fromVariant(variantProperties); + + // the ID of the parent/avatar changes from session to session. use a special UUID to indicate the avatar + QJsonObject jsonObject = jsonProperties.object(); + if (QUuid(jsonObject["parentID"].toString()) == _myAvatar->getID()) { + jsonObject["parentID"] = AVATAR_SELF_ID.toString(); + } + jsonProperties = QJsonDocument(jsonObject); + + QByteArray binaryProperties = jsonProperties.toBinaryData(); + _myAvatar->updateAvatarEntity(entityItemID, binaryProperties); + return; + } QByteArray bufferOut(NLPacket::maxPayloadSize(type), 0); - if (EntityItemProperties::encodeEntityEditPacket(type, modelID, properties, bufferOut)) { + if (EntityItemProperties::encodeEntityEditPacket(type, entityItemID, properties, bufferOut)) { #ifdef WANT_DEBUG qCDebug(entities) << "calling queueOctreeEditMessage()..."; - qCDebug(entities) << " id:" << modelID; + qCDebug(entities) << " id:" << entityItemID; qCDebug(entities) << " properties:" << properties; #endif queueOctreeEditMessage(type, bufferOut); diff --git a/libraries/entities/src/EntityEditPacketSender.h b/libraries/entities/src/EntityEditPacketSender.h index 26e4dd83ff..90c6cb988d 100644 --- a/libraries/entities/src/EntityEditPacketSender.h +++ b/libraries/entities/src/EntityEditPacketSender.h @@ -15,6 +15,7 @@ #include #include "EntityItem.h" +#include "AvatarData.h" /// Utility for processing, packing, queueing and sending of outbound edit voxel messages. class EntityEditPacketSender : public OctreeEditPacketSender { @@ -22,11 +23,17 @@ class EntityEditPacketSender : public OctreeEditPacketSender { public: EntityEditPacketSender(); + void setMyAvatar(AvatarData* myAvatar) { _myAvatar = myAvatar; } + AvatarData* getMyAvatar() { return _myAvatar; } + void clearAvatarEntity(QUuid entityID) { assert(_myAvatar); _myAvatar->clearAvatarEntity(entityID); } + /// Queues an array of several voxel edit messages. Will potentially send a pending multi-command packet. Determines /// which voxel-server node or nodes the packet should be sent to. Can be called even before voxel servers are known, in /// which case up to MaxPendingMessages will be buffered and processed when voxel servers are known. /// NOTE: EntityItemProperties assumes that all distances are in meter units - void queueEditEntityMessage(PacketType type, EntityItemID modelID, const EntityItemProperties& properties); + void queueEditEntityMessage(PacketType type, EntityTreePointer entityTree, + EntityItemID entityItemID, const EntityItemProperties& properties); + void queueEraseEntityMessage(const EntityItemID& entityItemID); @@ -40,5 +47,7 @@ public slots: private: bool _shouldProcessNack = true; + AvatarData* _myAvatar { nullptr }; + QScriptEngine _scriptEngine; }; #endif // hifi_EntityEditPacketSender_h diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index ecb9800e70..61f7fb0082 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -420,12 +420,19 @@ public: /// entity to definitively state if the preload signal should be sent. /// /// We only want to preload if: - /// there is some script, and either the script value or the scriptTimestamp + /// there is some script, and either the script value or the scriptTimestamp /// value have changed since our last preload - bool shouldPreloadScript() const { return !_script.isEmpty() && + bool shouldPreloadScript() const { return !_script.isEmpty() && ((_loadedScript != _script) || (_loadedScriptTimestamp != _scriptTimestamp)); } void scriptHasPreloaded() { _loadedScript = _script; _loadedScriptTimestamp = _scriptTimestamp; } + bool getClientOnly() const { return _clientOnly; } + void setClientOnly(bool clientOnly) { _clientOnly = clientOnly; } + // if this entity is client-only, which avatar is it associated with? + QUuid getOwningAvatarID() const { return _owningAvatarID; } + void setOwningAvatarID(const QUuid& owningAvatarID) { _owningAvatarID = owningAvatarID; } + + protected: void setSimulated(bool simulated) { _simulated = simulated; } @@ -537,6 +544,9 @@ protected: mutable QHash _previouslyDeletedActions; QUuid _sourceUUID; /// the server node UUID we came from + + bool _clientOnly { false }; + QUuid _owningAvatarID; }; #endif // hifi_EntityItem_h diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 2cf31e5632..fb24e711f4 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -276,6 +276,12 @@ public: void setJointRotationsDirty() { _jointRotationsSetChanged = true; _jointRotationsChanged = true; } void setJointTranslationsDirty() { _jointTranslationsSetChanged = true; _jointTranslationsChanged = true; } + bool getClientOnly() const { return _clientOnly; } + void setClientOnly(bool clientOnly) { _clientOnly = clientOnly; } + // if this entity is client-only, which avatar is it associated with? + QUuid getOwningAvatarID() const { return _owningAvatarID; } + void setOwningAvatarID(const QUuid& owningAvatarID) { _owningAvatarID = owningAvatarID; } + protected: QString getCollisionMaskAsString() const; void setCollisionMaskFromString(const QString& maskString); @@ -302,6 +308,9 @@ private: glm::vec3 _naturalPosition; EntityPropertyFlags _desiredProperties; // if set will narrow scopes of copy/to/from to just these properties + + bool _clientOnly { false }; + QUuid _owningAvatarID; }; Q_DECLARE_METATYPE(EntityItemProperties); diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 093fa73ace..0869ac40da 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -36,7 +36,7 @@ EntityScriptingInterface::EntityScriptingInterface(bool bidOnSimulationOwnership void EntityScriptingInterface::queueEntityMessage(PacketType packetType, EntityItemID entityID, const EntityItemProperties& properties) { - getEntityPacketSender()->queueEditEntityMessage(packetType, entityID, properties); + getEntityPacketSender()->queueEditEntityMessage(packetType, _entityTree, entityID, properties); } bool EntityScriptingInterface::canAdjustLocks() { @@ -123,9 +123,10 @@ EntityItemProperties convertLocationFromScriptSemantics(const EntityItemProperti } -QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties) { +QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties, bool clientOnly) { EntityItemProperties propertiesWithSimID = convertLocationFromScriptSemantics(properties); propertiesWithSimID.setDimensionsInitialized(properties.dimensionsChanged()); + propertiesWithSimID = clientOnly; auto dimensions = propertiesWithSimID.getDimensions(); float volume = dimensions.x * dimensions.y * dimensions.z; @@ -272,13 +273,15 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& bool updatedEntity = false; _entityTree->withWriteLock([&] { + EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID); + if (!entity) { + return; + } + if (scriptSideProperties.parentRelatedPropertyChanged()) { // All of parentID, parentJointIndex, position, rotation are needed to make sense of any of them. // If any of these changed, pull any missing properties from the entity. - EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID); - if (!entity) { - return; - } + //existing entity, retrieve old velocity for check down below oldVelocity = entity->getVelocity().length(); @@ -296,6 +299,8 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& } } properties = convertLocationFromScriptSemantics(properties); + properties.setClientOnly(entity->getClientOnly()); + properties.setOwningAvatarID(entity->getOwningAvatarID()); float cost = calculateCost(density * volume, oldVelocity, newVelocity); cost *= costMultiplier; diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index e5f913dbf8..2bd08f8e3f 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -82,7 +82,7 @@ public slots: Q_INVOKABLE bool canRez(); /// adds a model with the specific properties - Q_INVOKABLE QUuid addEntity(const EntityItemProperties& properties); + Q_INVOKABLE QUuid addEntity(const EntityItemProperties& properties, bool clientOnly = false); /// temporary method until addEntity can be used from QJSEngine Q_INVOKABLE QUuid addModelEntity(const QString& name, const QString& modelUrl, const glm::vec3& position); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index b4f0c484d5..86bbf0b74d 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1373,8 +1373,11 @@ bool EntityTree::sendEntitiesOperation(OctreeElementPointer element, void* extra } properties.markAllChanged(); // so the entire property set is considered new, since we're making a new entity + EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); + EntityTreePointer tree = entityTreeElement->getTree(); + // queue the packet to send to the server - args->packetSender->queueEditEntityMessage(PacketType::EntityAdd, newID, properties); + args->packetSender->queueEditEntityMessage(PacketType::EntityAdd, tree, newID, properties); // also update the local tree instantly (note: this is not our tree, but an alternate tree) if (args->otherTree) { diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index f0539110d3..070bf81e3a 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -547,8 +547,11 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ qCDebug(physics) << "EntityMotionState::sendUpdate()... calling queueEditEntityMessage()..."; #endif - entityPacketSender->queueEditEntityMessage(PacketType::EntityEdit, id, properties); - _entity->setLastBroadcast(usecTimestampNow()); + EntityTreeElementPointer element = _entity->getElement(); + EntityTreePointer tree = element ? element->getTree() : nullptr; + + entityPacketSender->queueEditEntityMessage(PacketType::EntityEdit, tree, id, properties); + _entity->setLastBroadcast(now); // if we've moved an entity with children, check/update the queryAACube of all descendents and tell the server // if they've changed. @@ -559,8 +562,9 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ EntityItemProperties newQueryCubeProperties; newQueryCubeProperties.setQueryAACube(descendant->getQueryAACube()); newQueryCubeProperties.setLastEdited(properties.getLastEdited()); - entityPacketSender->queueEditEntityMessage(PacketType::EntityEdit, descendant->getID(), newQueryCubeProperties); - entityDescendant->setLastBroadcast(usecTimestampNow()); + entityPacketSender->queueEditEntityMessage(PacketType::EntityEdit, tree, + descendant->getID(), newQueryCubeProperties); + entityDescendant->setLastBroadcast(now); } } }); From 473010f634ea9fc68821d02c06c0ba028f0bb832 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sat, 7 May 2016 16:45:09 -0700 Subject: [PATCH 02/25] addEntity has a clientOnly flag now --- libraries/entities/src/EntityScriptingInterface.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 0869ac40da..499b146d30 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -126,7 +126,13 @@ EntityItemProperties convertLocationFromScriptSemantics(const EntityItemProperti QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties, bool clientOnly) { EntityItemProperties propertiesWithSimID = convertLocationFromScriptSemantics(properties); propertiesWithSimID.setDimensionsInitialized(properties.dimensionsChanged()); - propertiesWithSimID = clientOnly; + + if (clientOnly) { + auto nodeList = DependencyManager::get(); + const QUuid myNodeID = nodeList->getSessionUUID(); + propertiesWithSimID.setClientOnly(clientOnly); + propertiesWithSimID.setOwningAvatarID(myNodeID); + } auto dimensions = propertiesWithSimID.getDimensions(); float volume = dimensions.x * dimensions.y * dimensions.z; From 91ff851bf8851189dc28c59d37862e7cfd580301 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sat, 7 May 2016 16:59:54 -0700 Subject: [PATCH 03/25] fix call to queueEditEntityMessage --- libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 6c4e3994c6..858b34c97f 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -981,7 +981,7 @@ void RenderablePolyVoxEntityItem::compressVolumeDataAndSendEditPacket() { PhysicalEntitySimulation* peSimulation = static_cast(simulation); EntityEditPacketSender* packetSender = peSimulation ? peSimulation->getPacketSender() : nullptr; if (packetSender) { - packetSender->queueEditEntityMessage(PacketType::EntityEdit, entity->getID(), properties); + packetSender->queueEditEntityMessage(PacketType::EntityEdit, tree, entity->getID(), properties); } }); }); From 46c1049a353f5d6317c6c4adf91890c6ee4eb823 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sat, 7 May 2016 17:48:06 -0700 Subject: [PATCH 04/25] bump protocol version --- libraries/avatars/src/AvatarData.cpp | 1 + libraries/avatars/src/AvatarHashMap.cpp | 1 + libraries/networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 3 ++- 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 9a0fc1a835..c68240bdca 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -956,6 +956,7 @@ void AvatarData::clearJointsData() { } bool AvatarData::hasIdentityChangedAfterParsing(const QByteArray& data) { + // this is used by the avatar-mixer QDataStream packetStream(data); QUuid avatarUUID; diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 62e87ce285..612f4c6f96 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -107,6 +107,7 @@ void AvatarHashMap::processAvatarDataPacket(QSharedPointer mess } void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer message, SharedNodePointer sendingNode) { + // this is used by clients // setup a data stream to parse the packet QDataStream identityStream(message->getMessage()); diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index e4aab94090..fbe31d1e8d 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -50,7 +50,7 @@ PacketVersion versionForPacketType(PacketType packetType) { return VERSION_LIGHT_HAS_FALLOFF_RADIUS; case PacketType::AvatarData: case PacketType::BulkAvatarData: - return static_cast(AvatarMixerPacketVersion::SoftAttachmentSupport); + return static_cast(AvatarMixerPacketVersion::AvatarEntities); case PacketType::ICEServerHeartbeat: return 18; // ICE Server Heartbeat signing case PacketType::AssetGetInfo: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index b98a87e439..b29fccb45e 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -174,7 +174,8 @@ const PacketVersion VERSION_LIGHT_HAS_FALLOFF_RADIUS = 57; enum class AvatarMixerPacketVersion : PacketVersion { TranslationSupport = 17, - SoftAttachmentSupport + SoftAttachmentSupport, + AvatarEntities }; #endif // hifi_PacketHeaders_h From 0ab0409979b20c39ca61dbf6d1a46c2d87e66df0 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sat, 7 May 2016 17:48:37 -0700 Subject: [PATCH 05/25] revert accidental change --- libraries/entities-renderer/src/RenderableWebEntityItem.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 891e1dca3b..26aecf6050 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -174,14 +174,9 @@ void RenderableWebEntityItem::render(RenderArgs* args) { #endif if (!_webSurface) { - #if defined(Q_OS_LINUX) - // these don't seem to work on Linux - return; - #else if (!buildWebSurface(static_cast(args->_renderer))) { return; } - #endif } _lastRenderTime = usecTimestampNow(); From c572e1dc3a837e0c1f0d42678ba0ba67591697b6 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 10 May 2016 10:30:49 -0700 Subject: [PATCH 06/25] delete avatar-associated entities when the avatar goes away --- interface/src/avatar/Avatar.cpp | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 1819611af6..67f8d9c967 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -107,6 +107,18 @@ Avatar::Avatar(RigPointer rig) : Avatar::~Avatar() { assert(isDead()); // mark dead before calling the dtor + + EntityTreeRenderer* treeRenderer = qApp->getEntities(); + EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; + if (entityTree) { + entityTree->withWriteLock([&] { + AvatarEntityMap avatarEntities = getAvatarEntityData(); + for (auto entityID : avatarEntities.keys()) { + entityTree->deleteEntity(entityID, true, true); + } + }); + } + if (_motionState) { delete _motionState; _motionState = nullptr; @@ -167,7 +179,7 @@ void Avatar::updateAvatarEntities() { // - updateAvatarEntity saves the bytes and sets _avatarEntityDataLocallyEdited // - MyAvatar::update notices _avatarEntityDataLocallyEdited and calls sendIdentityPacket // - sendIdentityPacket sends the entity bytes to the server which relays them to other interfaces - // - AvatarHashMap::processAvatarIdentityPacket's on other interfaces call avatar->setAvatarEntityData() + // - AvatarHashMap::processAvatarIdentityPacket on other interfaces call avatar->setAvatarEntityData() // - setAvatarEntityData saves the bytes and sets _avatarEntityDataChanged = true // - (My)Avatar::simulate notices _avatarEntityDataChanged and here we are... @@ -224,18 +236,14 @@ void Avatar::updateAvatarEntities() { } } } - }); - AvatarEntityIDs recentlyDettachedAvatarEntities = getAndClearRecentlyDetachedIDs(); - foreach (auto entityID, recentlyDettachedAvatarEntities) { - if (!_avatarEntityData.contains(entityID)) { - EntityItemPointer dettachedEntity = entityTree->findEntityByEntityItemID(EntityItemID(entityID)); - if (dettachedEntity) { - // this will cause this interface to listen to data from the entity-server about this entity. - dettachedEntity->setClientOnly(false); + AvatarEntityIDs recentlyDettachedAvatarEntities = getAndClearRecentlyDetachedIDs(); + foreach (auto entityID, recentlyDettachedAvatarEntities) { + if (!_avatarEntityData.contains(entityID)) { + entityTree->deleteEntity(entityID, true, true); } } - } + }); if (success) { setAvatarEntityDataChanged(false); From de4c9530c9d81953f517f9c4cb3fc5185cd37684 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 10 May 2016 14:20:58 -0700 Subject: [PATCH 07/25] carry clientOnly flag over from properties when addEntity is called --- libraries/entities/src/EntityTree.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 1cd2b4a47b..9d9c0fdba9 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -311,7 +311,9 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const EntityItemProperties& properties) { EntityItemPointer result = NULL; - if (getIsClient()) { + bool clientOnly = properties.getClientOnly(); + + if (!clientOnly && getIsClient()) { // if our Node isn't allowed to create entities in this domain, don't try. auto nodeList = DependencyManager::get(); if (nodeList && !nodeList->getThisNodeCanRez()) { @@ -337,6 +339,7 @@ EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const Enti // construct the instance of the entity EntityTypes::EntityType type = properties.getType(); result = EntityTypes::constructEntityItem(type, entityID, properties); + result->setClientOnly(clientOnly); if (result) { if (recordCreationTime) { From 1e849956c9ed74dd9156e44f94173b2a4bda9263 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 10 May 2016 14:47:41 -0700 Subject: [PATCH 08/25] get rid of _avatarEntityChangedTime --- interface/src/avatar/Avatar.cpp | 5 +---- libraries/avatars/src/AvatarData.h | 1 - 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 67f8d9c967..d3664b9f20 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -183,10 +183,8 @@ void Avatar::updateAvatarEntities() { // - setAvatarEntityData saves the bytes and sets _avatarEntityDataChanged = true // - (My)Avatar::simulate notices _avatarEntityDataChanged and here we are... - quint64 now = usecTimestampNow(); - const static quint64 refreshTime = 3 * USECS_PER_SECOND; - if (!_avatarEntityDataChanged && now - _avatarEntityChangedTime < refreshTime) { + if (!_avatarEntityDataChanged) { return; } @@ -247,7 +245,6 @@ void Avatar::updateAvatarEntities() { if (success) { setAvatarEntityDataChanged(false); - _avatarEntityChangedTime = now; } } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 72d34af9d9..2242860e22 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -423,7 +423,6 @@ protected: AvatarEntityMap _avatarEntityData; bool _avatarEntityDataLocallyEdited { false }; bool _avatarEntityDataChanged { false }; - quint64 _avatarEntityChangedTime { 0 }; private: friend void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar); From 4b13fd969e0609e9abc5ac8e08daf92835f19e7e Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 10 May 2016 15:37:54 -0700 Subject: [PATCH 09/25] split code that sends edits via avatar-mixer out of queueEditEntityMessage --- interface/src/avatar/Avatar.cpp | 1 - .../entities/src/EntityEditPacketSender.cpp | 78 +++++++++++-------- .../entities/src/EntityEditPacketSender.h | 4 + 3 files changed, 51 insertions(+), 32 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index d3664b9f20..80e7aaa8a7 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -183,7 +183,6 @@ void Avatar::updateAvatarEntities() { // - setAvatarEntityData saves the bytes and sets _avatarEntityDataChanged = true // - (My)Avatar::simulate notices _avatarEntityDataChanged and here we are... - const static quint64 refreshTime = 3 * USECS_PER_SECOND; if (!_avatarEntityDataChanged) { return; } diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp index ea86d3d542..416d3c971e 100644 --- a/libraries/entities/src/EntityEditPacketSender.cpp +++ b/libraries/entities/src/EntityEditPacketSender.cpp @@ -36,6 +36,51 @@ void EntityEditPacketSender::adjustEditPacketForClockSkew(PacketType type, QByte } } +void EntityEditPacketSender::queueEditAvatarEntityMessage(PacketType type, + EntityTreePointer entityTree, + EntityItemID entityItemID, + const EntityItemProperties& properties) { + if (!_shouldSend) { + return; // bail early + } + + assert(properties.getClientOnly()); + + // this is an avatar-based entity. update our avatar-data rather than sending to the entity-server + assert(_myAvatar); + + if (!entityTree) { + qDebug() << "EntityEditPacketSender::queueEditEntityMessage null entityTree."; + return; + } + EntityItemPointer entity = entityTree->findEntityByEntityItemID(entityItemID); + if (!entity) { + qDebug() << "EntityEditPacketSender::queueEditEntityMessage can't find entity."; + return; + } + + // the properties that get serialized into the avatar identity packet should be the entire set + // rather than just the ones being edited. + entity->setProperties(properties); + EntityItemProperties entityProperties = entity->getProperties(); + + QScriptValue scriptProperties = EntityItemNonDefaultPropertiesToScriptValue(&_scriptEngine, entityProperties); + QVariant variantProperties = scriptProperties.toVariant(); + QJsonDocument jsonProperties = QJsonDocument::fromVariant(variantProperties); + + // the ID of the parent/avatar changes from session to session. use a special UUID to indicate the avatar + QJsonObject jsonObject = jsonProperties.object(); + if (QUuid(jsonObject["parentID"].toString()) == _myAvatar->getID()) { + jsonObject["parentID"] = AVATAR_SELF_ID.toString(); + } + jsonProperties = QJsonDocument(jsonObject); + + QByteArray binaryProperties = jsonProperties.toBinaryData(); + _myAvatar->updateAvatarEntity(entityItemID, binaryProperties); + return; +} + + void EntityEditPacketSender::queueEditEntityMessage(PacketType type, EntityTreePointer entityTree, EntityItemID entityItemID, @@ -43,38 +88,9 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type, if (!_shouldSend) { return; // bail early } + if (properties.getClientOnly()) { - // this is an avatar-based entity. update our avatar-data rather than sending to the entity-server - assert(_myAvatar); - - if (!entityTree) { - qDebug() << "EntityEditPacketSender::queueEditEntityMessage null entityTree."; - return; - } - EntityItemPointer entity = entityTree->findEntityByEntityItemID(entityItemID); - if (!entity) { - qDebug() << "EntityEditPacketSender::queueEditEntityMessage can't find entity."; - return; - } - - // the properties that get serialized into the avatar identity packet should be the entire set - // rather than just the ones being edited. - entity->setProperties(properties); - EntityItemProperties entityProperties = entity->getProperties(); - - QScriptValue scriptProperties = EntityItemNonDefaultPropertiesToScriptValue(&_scriptEngine, entityProperties); - QVariant variantProperties = scriptProperties.toVariant(); - QJsonDocument jsonProperties = QJsonDocument::fromVariant(variantProperties); - - // the ID of the parent/avatar changes from session to session. use a special UUID to indicate the avatar - QJsonObject jsonObject = jsonProperties.object(); - if (QUuid(jsonObject["parentID"].toString()) == _myAvatar->getID()) { - jsonObject["parentID"] = AVATAR_SELF_ID.toString(); - } - jsonProperties = QJsonDocument(jsonObject); - - QByteArray binaryProperties = jsonProperties.toBinaryData(); - _myAvatar->updateAvatarEntity(entityItemID, binaryProperties); + queueEditAvatarEntityMessage(type, entityTree, entityItemID, properties); return; } diff --git a/libraries/entities/src/EntityEditPacketSender.h b/libraries/entities/src/EntityEditPacketSender.h index 90c6cb988d..9366fc9329 100644 --- a/libraries/entities/src/EntityEditPacketSender.h +++ b/libraries/entities/src/EntityEditPacketSender.h @@ -27,6 +27,10 @@ public: AvatarData* getMyAvatar() { return _myAvatar; } void clearAvatarEntity(QUuid entityID) { assert(_myAvatar); _myAvatar->clearAvatarEntity(entityID); } + void queueEditAvatarEntityMessage(PacketType type, EntityTreePointer entityTree, + EntityItemID entityItemID, const EntityItemProperties& properties); + + /// Queues an array of several voxel edit messages. Will potentially send a pending multi-command packet. Determines /// which voxel-server node or nodes the packet should be sent to. Can be called even before voxel servers are known, in /// which case up to MaxPendingMessages will be buffered and processed when voxel servers are known. From b05ab1b17e219186e2c8c64d122b64e41b9ca342 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 10 May 2016 15:59:25 -0700 Subject: [PATCH 10/25] set simulationOwner to be the same as the owningAvatar --- interface/src/avatar/Avatar.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 80e7aaa8a7..cbe69185af 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -214,6 +214,10 @@ void Avatar::updateAvatarEntities() { properties.setClientOnly(true); properties.setOwningAvatarID(getID()); + // there's not entity-server to tell us we're the simulation owner, so always set the + // simulationOwner to the owningAvatarID and a high priority. + properties.setSimulationOwner(getID(), 129); + if (properties.getParentID() == AVATAR_SELF_ID) { properties.setParentID(getID()); } From 144715f00cf7b8bb33ed551d9f57e4fed5859051 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 10 May 2016 16:05:40 -0700 Subject: [PATCH 11/25] don't make changes to other avatar's avatarEntities --- libraries/entities/src/EntityScriptingInterface.cpp | 6 ++++++ libraries/physics/src/EntityMotionState.cpp | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index e7b386e544..b160a23ced 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -284,6 +284,12 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& return; } + auto nodeList = DependencyManager::get(); + if (entity->getClientOnly() && entity->getOwningAvatarID() != nodeList->getSessionUUID()) { + // don't edit other avatar's avatarEntities + return; + } + if (scriptSideProperties.parentRelatedPropertyChanged()) { // All of parentID, parentJointIndex, position, rotation are needed to make sense of any of them. // If any of these changed, pull any missing properties from the entity. diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 070bf81e3a..f1897275ed 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -404,6 +404,11 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) { assert(_body); assert(entityTreeIsLocked()); + if (_entity->getClientOnly() && _entity->getOwningAvatarID() != Physics::getSessionUUID()) { + // don't send updates for someone else's avatarEntities + return false; + } + if (_entity->actionDataNeedsTransmit()) { return true; } From 356ccdba26cb668e999c4af45dc691f134b17f8f Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 10 May 2016 16:16:06 -0700 Subject: [PATCH 12/25] debug icon for clientOnly --- .../src/RenderableEntityItem.cpp | 21 ++++++++++++++++--- .../src/RenderableEntityItem.h | 1 + 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index d148145dde..011675fc82 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -47,6 +47,9 @@ namespace render { } void makeEntityItemStatusGetters(EntityItemPointer entity, render::Item::Status::Getters& statusGetters) { + auto nodeList = DependencyManager::get(); + const QUuid& myNodeID = nodeList->getSessionUUID(); + statusGetters.push_back([entity] () -> render::Item::Status::Value { quint64 delta = usecTimestampNow() - entity->getLastEditedFromRemote(); const float WAIT_THRESHOLD_INV = 1.0f / (0.2f * USECS_PER_SECOND); @@ -81,9 +84,7 @@ void makeEntityItemStatusGetters(EntityItemPointer entity, render::Item::Status: (unsigned char)RenderItemStatusIcon::ACTIVE_IN_BULLET); }); - statusGetters.push_back([entity] () -> render::Item::Status::Value { - auto nodeList = DependencyManager::get(); - const QUuid& myNodeID = nodeList->getSessionUUID(); + statusGetters.push_back([entity, myNodeID] () -> render::Item::Status::Value { bool weOwnSimulation = entity->getSimulationOwner().matchesValidID(myNodeID); bool otherOwnSimulation = !weOwnSimulation && !entity->getSimulationOwner().isNull(); @@ -106,4 +107,18 @@ void makeEntityItemStatusGetters(EntityItemPointer entity, render::Item::Status: return render::Item::Status::Value(0.0f, render::Item::Status::Value::GREEN, (unsigned char)RenderItemStatusIcon::HAS_ACTIONS); }); + + statusGetters.push_back([entity, myNodeID] () -> render::Item::Status::Value { + if (entity->getClientOnly()) { + if (entity->getOwningAvatarID() == myNodeID) { + return render::Item::Status::Value(1.0f, render::Item::Status::Value::GREEN, + (unsigned char)RenderItemStatusIcon::CLIENT_ONLY); + } else { + return render::Item::Status::Value(1.0f, render::Item::Status::Value::RED, + (unsigned char)RenderItemStatusIcon::CLIENT_ONLY); + } + } + return render::Item::Status::Value(0.0f, render::Item::Status::Value::GREEN, + (unsigned char)RenderItemStatusIcon::CLIENT_ONLY); + }); } diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index 09451e87d4..9840bf3150 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -26,6 +26,7 @@ enum class RenderItemStatusIcon { SIMULATION_OWNER = 3, HAS_ACTIONS = 4, OTHER_SIMULATION_OWNER = 5, + CLIENT_ONLY = 6, NONE = 255 }; From de12680ff1dab61f4eb97b431c2d8e2228194499 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 10 May 2016 16:43:27 -0700 Subject: [PATCH 13/25] don't put actions on other people's avatarEntities --- interface/src/avatar/Avatar.cpp | 4 ++++ libraries/entities/src/EntityEditPacketSender.cpp | 4 ++++ libraries/entities/src/EntityScriptingInterface.cpp | 7 +++++++ 3 files changed, 15 insertions(+) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index cbe69185af..83cec224b6 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -187,6 +187,10 @@ void Avatar::updateAvatarEntities() { return; } + if (getID() == QUuid()) { + return; // wait until MyAvatar gets an ID before doing this. + } + EntityTreeRenderer* treeRenderer = qApp->getEntities(); EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; if (!entityTree) { diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp index 416d3c971e..6cb0b6ef60 100644 --- a/libraries/entities/src/EntityEditPacketSender.cpp +++ b/libraries/entities/src/EntityEditPacketSender.cpp @@ -44,6 +44,10 @@ void EntityEditPacketSender::queueEditAvatarEntityMessage(PacketType type, return; // bail early } + if (properties.getOwningAvatarID() != _myAvatar->getID()) { + return; // don't send updates for someone else's avatarEntity + } + assert(properties.getClientOnly()); // this is an avatar-based entity. update our avatar-data rather than sending to the entity-server diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index b160a23ced..3b06c5a998 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -788,6 +788,9 @@ bool EntityScriptingInterface::actionWorker(const QUuid& entityID, return false; } + auto nodeList = DependencyManager::get(); + const QUuid myNodeID = nodeList->getSessionUUID(); + EntityItemPointer entity; bool doTransmit = false; _entityTree->withWriteLock([&] { @@ -803,6 +806,10 @@ bool EntityScriptingInterface::actionWorker(const QUuid& entityID, return; } + if (entity->getClientOnly() && entity->getOwningAvatarID() != nodeList->getSessionUUID()) { + return; + } + doTransmit = actor(simulation, entity); if (doTransmit) { _entityTree->entityChanged(entity); From e4e0be8fa3ba0baf38eaa5b5a0588c7a9f630dc7 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 10 May 2016 16:51:38 -0700 Subject: [PATCH 14/25] relay owningAvatar from properties to entity on entity creation --- libraries/entities/src/EntityTree.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 9d9c0fdba9..2da3604c2a 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -312,6 +312,7 @@ EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const Enti EntityItemPointer result = NULL; bool clientOnly = properties.getClientOnly(); + QUuid owningAvatarID = properties.getOwningAvatarID(); if (!clientOnly && getIsClient()) { // if our Node isn't allowed to create entities in this domain, don't try. @@ -340,6 +341,7 @@ EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const Enti EntityTypes::EntityType type = properties.getType(); result = EntityTypes::constructEntityItem(type, entityID, properties); result->setClientOnly(clientOnly); + result->setOwningAvatarID(owningAvatarID); if (result) { if (recordCreationTime) { From 872f1b0c64d9dc7479dbc1a9de3615263dc21818 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 10 May 2016 17:48:55 -0700 Subject: [PATCH 15/25] clear avatarEntity data for entities that are deleted --- interface/src/avatar/Avatar.cpp | 5 ++++- interface/src/avatar/MyAvatar.cpp | 1 + libraries/entities/src/EntityEditPacketSender.cpp | 6 ++++++ libraries/entities/src/EntityItem.h | 1 + libraries/entities/src/EntityScriptingInterface.cpp | 10 +++++++++- 5 files changed, 21 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 83cec224b6..d959b298ef 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -229,7 +229,10 @@ void Avatar::updateAvatarEntities() { EntityItemPointer entity = entityTree->findEntityByEntityItemID(EntityItemID(entityID)); if (entity) { - if (!entityTree->updateEntity(entityID, properties)) { + if (entityTree->updateEntity(entityID, properties)) { + entity->markAsChangedOnServer(); + entity->updateLastEditedFromRemote(); + } else { qDebug() << "AVATAR-ENTITES -- updateEntity failed: " << properties.getType(); success = false; } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index dc0d6d7e97..304aabdcc6 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -828,6 +828,7 @@ void MyAvatar::loadData() { for (int i = 0; i < avatarEntityCount; i++) { settings.setArrayIndex(i); QUuid entityID = settings.value("id").toUuid(); + // QUuid entityID = QUuid::createUuid(); // generate a new ID QByteArray properties = settings.value("properties").toByteArray(); updateAvatarEntity(entityID, properties); } diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp index 6cb0b6ef60..28f1871346 100644 --- a/libraries/entities/src/EntityEditPacketSender.cpp +++ b/libraries/entities/src/EntityEditPacketSender.cpp @@ -81,6 +81,8 @@ void EntityEditPacketSender::queueEditAvatarEntityMessage(PacketType type, QByteArray binaryProperties = jsonProperties.toBinaryData(); _myAvatar->updateAvatarEntity(entityItemID, binaryProperties); + + entity->setLastBroadcast(usecTimestampNow()); return; } @@ -115,6 +117,10 @@ void EntityEditPacketSender::queueEraseEntityMessage(const EntityItemID& entityI return; // bail early } + // in case this was a clientOnly entity: + assert(_myAvatar); + _myAvatar->clearAvatarEntity(entityItemID); + QByteArray bufferOut(NLPacket::maxPayloadSize(PacketType::EntityErase), 0); if (EntityItemProperties::encodeEraseEntityMessage(entityItemID, bufferOut)) { diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 4286a9d6ae..c2e497e602 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -375,6 +375,7 @@ public: glm::vec3 entityToWorld(const glm::vec3& point) const; quint64 getLastEditedFromRemote() const { return _lastEditedFromRemote; } + void updateLastEditedFromRemote() { _lastEditedFromRemote = usecTimestampNow(); } void getAllTerseUpdateProperties(EntityItemProperties& properties) const; diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 3b06c5a998..ade8ade1c4 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -401,6 +401,14 @@ void EntityScriptingInterface::deleteEntity(QUuid id) { EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID); if (entity) { + auto nodeList = DependencyManager::get(); + const QUuid myNodeID = nodeList->getSessionUUID(); + if (entity->getClientOnly() && entity->getOwningAvatarID() != myNodeID) { + // don't delete other avatar's avatarEntities + shouldDelete = false; + return; + } + auto dimensions = entity->getDimensions(); float volume = dimensions.x * dimensions.y * dimensions.z; auto density = entity->getDensity(); @@ -806,7 +814,7 @@ bool EntityScriptingInterface::actionWorker(const QUuid& entityID, return; } - if (entity->getClientOnly() && entity->getOwningAvatarID() != nodeList->getSessionUUID()) { + if (entity->getClientOnly() && entity->getOwningAvatarID() != myNodeID) { return; } From ac506dc4b77de0bd8831796de3ffe6fad855a6c9 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 11 May 2016 12:46:16 -0700 Subject: [PATCH 16/25] where needed, copy clientOnly into properties before calling queueEditEntityMessage --- interface/src/avatar/MyAvatar.cpp | 1 + libraries/entities/src/EntityItem.cpp | 11 ++++++++++ .../entities/src/EntityItemProperties.cpp | 20 +++++++++++++++++++ libraries/entities/src/EntityItemProperties.h | 15 ++++++-------- libraries/entities/src/EntityPropertyFlags.h | 3 +++ .../entities/src/EntityScriptingInterface.cpp | 5 ++++- libraries/entities/src/EntityTree.cpp | 3 --- libraries/physics/src/EntityMotionState.cpp | 7 +++++++ 8 files changed, 52 insertions(+), 13 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 304aabdcc6..33905d5e81 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -446,6 +446,7 @@ void MyAvatar::simulate(float deltaTime) { EntityItemProperties properties = entity->getProperties(); properties.setQueryAACubeDirty(); properties.setLastEdited(now); + packetSender->queueEditEntityMessage(PacketType::EntityEdit, entityTree, entity->getID(), properties); entity->setLastBroadcast(usecTimestampNow()); } diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 187c4f51be..04307892f1 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -138,6 +138,9 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param requestedProperties += PROP_PARENT_JOINT_INDEX; requestedProperties += PROP_QUERY_AA_CUBE; + requestedProperties += PROP_CLIENT_ONLY; + requestedProperties += PROP_OWNING_AVATAR_ID; + return requestedProperties; } @@ -1094,6 +1097,8 @@ EntityItemProperties EntityItem::getProperties(EntityPropertyFlags desiredProper properties._id = getID(); properties._idSet = true; properties._created = _created; + properties.setClientOnly(_clientOnly); + properties.setOwningAvatarID(_owningAvatarID); properties._type = getType(); @@ -1135,6 +1140,9 @@ EntityItemProperties EntityItem::getProperties(EntityPropertyFlags desiredProper COPY_ENTITY_PROPERTY_TO_PROPERTIES(localPosition, getLocalPosition); COPY_ENTITY_PROPERTY_TO_PROPERTIES(localRotation, getLocalOrientation); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(clientOnly, getClientOnly); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(owningAvatarID, getOwningAvatarID); + properties._defaultSettings = false; return properties; @@ -1225,6 +1233,9 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(parentJointIndex, setParentJointIndex); SET_ENTITY_PROPERTY_FROM_PROPERTIES(queryAACube, setQueryAACube); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(clientOnly, setClientOnly); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(owningAvatarID, setOwningAvatarID); + AACube saveQueryAACube = _queryAACube; checkAndAdjustQueryAACube(); if (saveQueryAACube != _queryAACube) { diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 738e8910fe..354cbd03ef 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -309,6 +309,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_QUERY_AA_CUBE, queryAACube); CHECK_PROPERTY_CHANGE(PROP_LOCAL_POSITION, localPosition); CHECK_PROPERTY_CHANGE(PROP_LOCAL_ROTATION, localRotation); + CHECK_PROPERTY_CHANGE(PROP_CLIENT_ONLY, clientOnly); + CHECK_PROPERTY_CHANGE(PROP_OWNING_AVATAR_ID, owningAvatarID); changedProperties += _animation.getChangedProperties(); changedProperties += _keyLight.getChangedProperties(); @@ -541,6 +543,12 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCAL_POSITION, localPosition); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCAL_ROTATION, localRotation); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLIENT_ONLY, clientOnly); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_OWNING_AVATAR_ID, owningAvatarID); + + properties.setProperty("clientOnly", convertScriptValue(engine, getClientOnly())); + properties.setProperty("owningAvatarID", convertScriptValue(engine, getOwningAvatarID())); + // FIXME - I don't think these properties are supported any more //COPY_PROPERTY_TO_QSCRIPTVALUE(glowLevel); //COPY_PROPERTY_TO_QSCRIPTVALUE(localRenderAlpha); @@ -679,6 +687,9 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(jointTranslationsSet, qVectorBool, setJointTranslationsSet); COPY_PROPERTY_FROM_QSCRIPTVALUE(jointTranslations, qVectorVec3, setJointTranslations); + COPY_PROPERTY_FROM_QSCRIPTVALUE(clientOnly, bool, setClientOnly); + COPY_PROPERTY_FROM_QSCRIPTVALUE(owningAvatarID, QUuid, setOwningAvatarID); + _lastEdited = usecTimestampNow(); } @@ -1564,6 +1575,9 @@ void EntityItemProperties::markAllChanged() { _jointTranslationsChanged = true; _queryAACubeChanged = true; + + _clientOnlyChanged = true; + _owningAvatarIDChanged = true; } // The minimum bounding box for the entity. @@ -1884,6 +1898,12 @@ QList EntityItemProperties::listChangedProperties() { if (queryAACubeChanged()) { out += "queryAACube"; } + if (clientOnlyChanged()) { + out += "clientOnly"; + } + if (owningAvatarIDChanged()) { + out += "owningAvatarID"; + } getAnimation().listChangedProperties(out); getKeyLight().listChangedProperties(out); diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index fb24e711f4..fb36834238 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -205,6 +205,9 @@ public: DEFINE_PROPERTY_REF(PROP_JOINT_TRANSLATIONS_SET, JointTranslationsSet, jointTranslationsSet, QVector, QVector()); DEFINE_PROPERTY_REF(PROP_JOINT_TRANSLATIONS, JointTranslations, jointTranslations, QVector, QVector()); + DEFINE_PROPERTY(PROP_CLIENT_ONLY, ClientOnly, clientOnly, bool, false); + DEFINE_PROPERTY_REF(PROP_OWNING_AVATAR_ID, OwningAvatarID, owningAvatarID, QUuid, UNKNOWN_ENTITY_ID); + static QString getBackgroundModeString(BackgroundMode mode); @@ -276,12 +279,6 @@ public: void setJointRotationsDirty() { _jointRotationsSetChanged = true; _jointRotationsChanged = true; } void setJointTranslationsDirty() { _jointTranslationsSetChanged = true; _jointTranslationsChanged = true; } - bool getClientOnly() const { return _clientOnly; } - void setClientOnly(bool clientOnly) { _clientOnly = clientOnly; } - // if this entity is client-only, which avatar is it associated with? - QUuid getOwningAvatarID() const { return _owningAvatarID; } - void setOwningAvatarID(const QUuid& owningAvatarID) { _owningAvatarID = owningAvatarID; } - protected: QString getCollisionMaskAsString() const; void setCollisionMaskFromString(const QString& maskString); @@ -308,9 +305,6 @@ private: glm::vec3 _naturalPosition; EntityPropertyFlags _desiredProperties; // if set will narrow scopes of copy/to/from to just these properties - - bool _clientOnly { false }; - QUuid _owningAvatarID; }; Q_DECLARE_METATYPE(EntityItemProperties); @@ -430,6 +424,9 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { DEBUG_PROPERTY_IF_CHANGED(debug, properties, JointTranslationsSet, jointTranslationsSet, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, JointTranslations, jointTranslations, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, ClientOnly, clientOnly, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, OwningAvatarID, owningAvatarID, ""); + properties.getAnimation().debugDump(); properties.getSkybox().debugDump(); properties.getStage().debugDump(); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 90a7c1e2f7..394f61b5e8 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -169,6 +169,9 @@ enum EntityPropertyList { PROP_FALLOFF_RADIUS, // for Light entity + PROP_CLIENT_ONLY, // doesn't go over wire + PROP_OWNING_AVATAR_ID, // doesn't go over wire + //////////////////////////////////////////////////////////////////////////////////////////////////// // ATTENTION: add new properties to end of list just ABOVE this line PROP_AFTER_LAST_ITEM, diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index ade8ade1c4..15c2bffd80 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -799,6 +799,8 @@ bool EntityScriptingInterface::actionWorker(const QUuid& entityID, auto nodeList = DependencyManager::get(); const QUuid myNodeID = nodeList->getSessionUUID(); + EntityItemProperties properties; + EntityItemPointer entity; bool doTransmit = false; _entityTree->withWriteLock([&] { @@ -820,13 +822,14 @@ bool EntityScriptingInterface::actionWorker(const QUuid& entityID, doTransmit = actor(simulation, entity); if (doTransmit) { + properties.setClientOnly(entity->getClientOnly()); + properties.setOwningAvatarID(entity->getOwningAvatarID()); _entityTree->entityChanged(entity); } }); // transmit the change if (doTransmit) { - EntityItemProperties properties; _entityTree->withReadLock([&] { properties = entity->getProperties(); }); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 2da3604c2a..087225c865 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -312,7 +312,6 @@ EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const Enti EntityItemPointer result = NULL; bool clientOnly = properties.getClientOnly(); - QUuid owningAvatarID = properties.getOwningAvatarID(); if (!clientOnly && getIsClient()) { // if our Node isn't allowed to create entities in this domain, don't try. @@ -340,8 +339,6 @@ EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const Enti // construct the instance of the entity EntityTypes::EntityType type = properties.getType(); result = EntityTypes::constructEntityItem(type, entityID, properties); - result->setClientOnly(clientOnly); - result->setOwningAvatarID(owningAvatarID); if (result) { if (recordCreationTime) { diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index f1897275ed..053bfcbd85 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -555,6 +555,9 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ EntityTreeElementPointer element = _entity->getElement(); EntityTreePointer tree = element ? element->getTree() : nullptr; + properties.setClientOnly(_entity->getClientOnly()); + properties.setOwningAvatarID(_entity->getOwningAvatarID()); + entityPacketSender->queueEditEntityMessage(PacketType::EntityEdit, tree, id, properties); _entity->setLastBroadcast(now); @@ -567,6 +570,10 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ EntityItemProperties newQueryCubeProperties; newQueryCubeProperties.setQueryAACube(descendant->getQueryAACube()); newQueryCubeProperties.setLastEdited(properties.getLastEdited()); + + newQueryCubeProperties.setClientOnly(entityDescendant->getClientOnly()); + newQueryCubeProperties.setOwningAvatarID(entityDescendant->getOwningAvatarID()); + entityPacketSender->queueEditEntityMessage(PacketType::EntityEdit, tree, descendant->getID(), newQueryCubeProperties); entityDescendant->setLastBroadcast(now); From 73c06b3bf42524d5d1898f60ef2be4d059a936ee Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 11 May 2016 14:13:08 -0700 Subject: [PATCH 17/25] updating attachedEntitiesManager to work with new system --- scripts/system/assets/images/lock.svg | 70 +++++++++++++++++++ scripts/system/assets/images/unlock.svg | 70 +++++++++++++++++++ scripts/system/attachedEntitiesManager.js | 84 ++++++++++------------- scripts/system/libraries/toolBars.js | 14 +++- 4 files changed, 188 insertions(+), 50 deletions(-) create mode 100644 scripts/system/assets/images/lock.svg create mode 100644 scripts/system/assets/images/unlock.svg diff --git a/scripts/system/assets/images/lock.svg b/scripts/system/assets/images/lock.svg new file mode 100644 index 0000000000..1480359f43 --- /dev/null +++ b/scripts/system/assets/images/lock.svg @@ -0,0 +1,70 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/scripts/system/assets/images/unlock.svg b/scripts/system/assets/images/unlock.svg new file mode 100644 index 0000000000..a7664c1f59 --- /dev/null +++ b/scripts/system/assets/images/unlock.svg @@ -0,0 +1,70 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/scripts/system/attachedEntitiesManager.js b/scripts/system/attachedEntitiesManager.js index 9ddb040297..124b2f76ec 100644 --- a/scripts/system/attachedEntitiesManager.js +++ b/scripts/system/attachedEntitiesManager.js @@ -22,7 +22,7 @@ var MINIMUM_DROP_DISTANCE_FROM_JOINT = 0.8; var ATTACHED_ENTITY_SEARCH_DISTANCE = 10.0; var ATTACHED_ENTITIES_SETTINGS_KEY = "ATTACHED_ENTITIES"; var DRESSING_ROOM_DISTANCE = 2.0; -var SHOW_TOOL_BAR = false; +var SHOW_TOOL_BAR = true; // tool bar @@ -30,34 +30,20 @@ if (SHOW_TOOL_BAR) { var BUTTON_SIZE = 32; var PADDING = 3; Script.include(["libraries/toolBars.js"]); - var toolBar = new ToolBar(0, 0, ToolBar.VERTICAL, "highfidelity.attachedEntities.toolbar", function(screenSize) { - return { - x: (BUTTON_SIZE + PADDING), - y: (screenSize.y / 2 - BUTTON_SIZE * 2 + PADDING) - }; - }); - var saveButton = toolBar.addOverlay("image", { + + var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL, "highfidelity.attachedEntities.toolbar"); + var lockButton = toolBar.addTool({ width: BUTTON_SIZE, height: BUTTON_SIZE, - imageURL: ".../save.png", + imageURL: Script.resolvePath("assets/images/lock.svg"), color: { red: 255, green: 255, blue: 255 }, - alpha: 1 - }); - var loadButton = toolBar.addOverlay("image", { - width: BUTTON_SIZE, - height: BUTTON_SIZE, - imageURL: ".../load.png", - color: { - red: 255, - green: 255, - blue: 255 - }, - alpha: 1 - }); + alpha: 1, + visible: true + }, false); } @@ -67,10 +53,8 @@ function mousePressEvent(event) { y: event.y }); - if (clickedOverlay == saveButton) { - manager.saveAttachedEntities(); - } else if (clickedOverlay == loadButton) { - manager.loadAttachedEntities(); + if (lockButton === toolBar.clicked(clickedOverlay)) { + manager.toggleLocked(); } } @@ -92,6 +76,8 @@ Script.scriptEnding.connect(scriptEnding); function AttachedEntitiesManager() { + var clothingLocked = true; + this.subscribeToMessages = function() { Messages.subscribe('Hifi-Object-Manipulation'); Messages.messageReceived.connect(this.handleWearableMessages); @@ -128,19 +114,6 @@ function AttachedEntitiesManager() { } } - this.avatarIsInDressingRoom = function() { - // return true if MyAvatar is near the dressing room - var possibleDressingRoom = Entities.findEntities(MyAvatar.position, DRESSING_ROOM_DISTANCE); - for (i = 0; i < possibleDressingRoom.length; i++) { - var entityID = possibleDressingRoom[i]; - var props = Entities.getEntityProperties(entityID); - if (props.name == 'Hifi-Dressing-Room-Base') { - return true; - } - } - return false; - } - this.handleEntityRelease = function(grabbedEntity, releasedFromJoint) { // if this is still equipped, just rewrite the position information. var grabData = getEntityCustomData('grabKey', grabbedEntity, {}); @@ -179,21 +152,23 @@ function AttachedEntitiesManager() { } if (bestJointIndex != -1) { - var wearProps = { - parentID: MyAvatar.sessionUUID, - parentJointIndex: bestJointIndex - }; + var wearProps = Entities.getEntityProperties(grabbedEntity); + wearProps.parentID = MyAvatar.sessionUUID; + wearProps.parentJointIndex = bestJointIndex; if (bestJointOffset && bestJointOffset.constructor === Array) { - if (this.avatarIsInDressingRoom() || bestJointOffset.length < 2) { + if (!clothingLocked || bestJointOffset.length < 2) { this.updateRelativeOffsets(grabbedEntity); } else { - // don't snap the entity to the preferred position if the avatar is in the dressing room. + // don't snap the entity to the preferred position if unlocked wearProps.localPosition = bestJointOffset[0]; wearProps.localRotation = bestJointOffset[1]; } } - Entities.editEntity(grabbedEntity, wearProps); + + // Entities.editEntity(grabbedEntity, wearProps); + Entities.deleteEntity(grabbedEntity); + Entities.addEntity(wearProps, true); } else if (props.parentID != NULL_UUID) { // drop the entity and set it to have no parent (not on the avatar), unless it's being equipped in a hand. if (props.parentID === MyAvatar.sessionUUID && @@ -201,7 +176,11 @@ function AttachedEntitiesManager() { props.parentJointIndex == MyAvatar.getJointIndex("LeftHand"))) { // this is equipped on a hand -- don't clear the parent. } else { - Entities.editEntity(grabbedEntity, { parentID: NULL_UUID }); + var wearProps = Entities.getEntityProperties(grabbedEntity); + wearProps.parentID = NULL_UUID; + wearProps.parentJointIndex = -1; + Entities.deleteEntity(grabbedEntity); + Entities.addEntity(wearProps, false); } } } @@ -221,6 +200,17 @@ function AttachedEntitiesManager() { return false; } + this.toggleLocked = function() { + print("toggleLocked"); + if (clothingLocked) { + clothingLocked = false; + toolBar.setImageURL(Script.resolvePath("assets/images/unlock.svg"), lockButton); + } else { + clothingLocked = true; + toolBar.setImageURL(Script.resolvePath("assets/images/lock.svg"), lockButton); + } + } + this.saveAttachedEntities = function() { print("--- saving attached entities ---"); saveData = []; diff --git a/scripts/system/libraries/toolBars.js b/scripts/system/libraries/toolBars.js index d97575d349..9efe533457 100644 --- a/scripts/system/libraries/toolBars.js +++ b/scripts/system/libraries/toolBars.js @@ -56,6 +56,10 @@ Overlay2D = function(properties, overlay) { // overlay is an optional variable properties.alpha = alpha; Overlays.editOverlay(overlay, { alpha: alpha }); } + this.setImageURL = function(imageURL) { + properties.imageURL = imageURL; + Overlays.editOverlay(overlay, { imageURL: imageURL }); + } this.show = function(doShow) { properties.visible = doShow; Overlays.editOverlay(overlay, { visible: doShow }); @@ -254,7 +258,7 @@ ToolBar = function(x, y, direction, optionalPersistenceKey, optionalInitialPosit } this.save(); } - + this.setAlpha = function(alpha, tool) { if(typeof(tool) === 'undefined') { for(var tool in this.tools) { @@ -268,7 +272,11 @@ ToolBar = function(x, y, direction, optionalPersistenceKey, optionalInitialPosit this.tools[tool].setAlpha(alpha); } } - + + this.setImageURL = function(imageURL, tool) { + this.tools[tool].setImageURL(imageURL); + } + this.setBack = function(color, alpha) { if (color == null) { Overlays.editOverlay(this.back, { visible: false }); @@ -478,4 +486,4 @@ ToolBar = function(x, y, direction, optionalPersistenceKey, optionalInitialPosit } ToolBar.SPACING = 6; ToolBar.VERTICAL = 0; -ToolBar.HORIZONTAL = 1; \ No newline at end of file +ToolBar.HORIZONTAL = 1; From 032fbd9a55b6ff220c7ac0e17f989ded353bae6d Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 11 May 2016 16:41:00 -0700 Subject: [PATCH 18/25] taking something off now makes it an entity-server-aware entity --- scripts/system/attachedEntitiesManager.js | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/scripts/system/attachedEntitiesManager.js b/scripts/system/attachedEntitiesManager.js index 124b2f76ec..e0261b3c7a 100644 --- a/scripts/system/attachedEntitiesManager.js +++ b/scripts/system/attachedEntitiesManager.js @@ -166,7 +166,6 @@ function AttachedEntitiesManager() { } } - // Entities.editEntity(grabbedEntity, wearProps); Entities.deleteEntity(grabbedEntity); Entities.addEntity(wearProps, true); } else if (props.parentID != NULL_UUID) { @@ -179,8 +178,23 @@ function AttachedEntitiesManager() { var wearProps = Entities.getEntityProperties(grabbedEntity); wearProps.parentID = NULL_UUID; wearProps.parentJointIndex = -1; + + delete wearProps.id; + delete wearProps.created; + delete wearProps.age; + delete wearProps.ageAsText; + delete wearProps.naturalDimensions; + delete wearProps.naturalPosition; + delete wearProps.actionData; + delete wearProps.sittingPoints; + delete wearProps.boundingBox; + delete wearProps.clientOnly; + delete wearProps.owningAvatarID; + delete wearProps.localPosition; + delete wearProps.localRotation; + Entities.deleteEntity(grabbedEntity); - Entities.addEntity(wearProps, false); + Entities.addEntity(wearProps); } } } @@ -271,7 +285,7 @@ function AttachedEntitiesManager() { this.scrubProperties(savedProps); delete savedProps["id"]; savedProps.parentID = MyAvatar.sessionUUID; // this will change between sessions - var loadedEntityID = Entities.addEntity(savedProps); + var loadedEntityID = Entities.addEntity(savedProps, true); Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({ action: 'loaded', From c784ca9771b9e733d45e25083fdfb43c0376ee8a Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 11 May 2016 18:45:56 -0700 Subject: [PATCH 19/25] debugging --- interface/src/avatar/Avatar.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 1e8ff7775e..95a3ef6132 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -197,6 +197,7 @@ void Avatar::updateAvatarEntities() { QScriptEngine scriptEngine; entityTree->withWriteLock([&] { AvatarEntityMap avatarEntities = getAvatarEntityData(); + qDebug() << "---------------"; for (auto entityID : avatarEntities.keys()) { // see EntityEditPacketSender::queueEditEntityMessage for the other end of this. unpack properties // and either add or update the entity. @@ -206,6 +207,9 @@ void Avatar::updateAvatarEntities() { qDebug() << "got bad avatarEntity json"; continue; } + + qDebug() << jsonProperties.toJson(); + QVariant variantProperties = jsonProperties.toVariant(); QVariantMap asMap = variantProperties.toMap(); QScriptValue scriptProperties = variantMapToScriptValue(asMap, scriptEngine); @@ -214,7 +218,7 @@ void Avatar::updateAvatarEntities() { properties.setClientOnly(true); properties.setOwningAvatarID(getID()); - // there's not entity-server to tell us we're the simulation owner, so always set the + // there's no entity-server to tell us we're the simulation owner, so always set the // simulationOwner to the owningAvatarID and a high priority. properties.setSimulationOwner(getID(), 129); @@ -225,17 +229,19 @@ void Avatar::updateAvatarEntities() { EntityItemPointer entity = entityTree->findEntityByEntityItemID(EntityItemID(entityID)); if (entity) { + qDebug() << "avatar-entities existing entity, element =" << entity->getElement().get(); if (entityTree->updateEntity(entityID, properties)) { - entity->markAsChangedOnServer(); entity->updateLastEditedFromRemote(); + qDebug() << "avatar-entities after entityTree->updateEntity(), element =" << entity->getElement().get(); } else { - qDebug() << "AVATAR-ENTITES -- updateEntity failed: " << properties.getType(); + qDebug() << "AVATAR-ENTITIES -- updateEntity failed: " << properties.getType(); success = false; } } else { + qDebug() << "avatar-entities new entity"; entity = entityTree->addEntity(entityID, properties); if (!entity) { - qDebug() << "AVATAR-ENTITES -- addEntity failed: " << properties.getType(); + qDebug() << "AVATAR-ENTITIES -- addEntity failed: " << properties.getType(); success = false; } } From 30fb9349c46727a5700b4ca9e54a83eaa7767198 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 12 May 2016 17:52:40 -0700 Subject: [PATCH 20/25] dry up some code --- .../src/RenderableModelEntityItem.cpp | 50 +++++++++---------- .../src/RenderableModelEntityItem.h | 2 + 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index c4ac9b09e5..a9aee1e527 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -162,6 +162,19 @@ void RenderableModelEntityItem::remapTextures() { } } +void RenderableModelEntityItem::doInitialModelSimulation() { + _model->setScaleToFit(true, getDimensions()); + _model->setSnapModelToRegistrationPoint(true, getRegistrationPoint()); + _model->setRotation(getRotation()); + _model->setTranslation(getPosition()); + { + PerformanceTimer perfTimer("_model->simulate"); + _model->simulate(0.0f); + } + _needsInitialSimulation = false; +} + + // TODO: we need a solution for changes to the postion/rotation/etc of a model... // this current code path only addresses that in this setup case... not the changing/moving case bool RenderableModelEntityItem::readyToAddToScene(RenderArgs* renderArgs) { @@ -172,22 +185,12 @@ bool RenderableModelEntityItem::readyToAddToScene(RenderArgs* renderArgs) { getModel(renderer); } if (renderArgs && _model && _needsInitialSimulation && _model->isActive() && _model->isLoaded()) { - _model->setScaleToFit(true, getDimensions()); - _model->setSnapModelToRegistrationPoint(true, getRegistrationPoint()); - _model->setRotation(getRotation()); - _model->setTranslation(getPosition()); - // make sure to simulate so everything gets set up correctly for rendering - { - PerformanceTimer perfTimer("_model->simulate"); - _model->simulate(0.0f); - } - _needsInitialSimulation = false; - + doInitialModelSimulation(); _model->renderSetup(renderArgs); } bool ready = !_needsInitialSimulation && _model && _model->readyToAddToScene(renderArgs); - return ready; + return ready; } class RenderableModelEntityItemMeta { @@ -348,18 +351,7 @@ void RenderableModelEntityItem::updateModelBounds() { _model->getRotation() != getRotation() || _model->getRegistrationPoint() != getRegistrationPoint()) && _model->isActive() && _dimensionsInitialized) { - _model->setScaleToFit(true, dimensions); - _model->setSnapModelToRegistrationPoint(true, getRegistrationPoint()); - _model->setRotation(getRotation()); - _model->setTranslation(getPosition()); - - // make sure to simulate so everything gets set up correctly for rendering - { - PerformanceTimer perfTimer("_model->simulate"); - _model->simulate(0.0f); - } - - _needsInitialSimulation = false; + doInitialModelSimulation(); _needsJointSimulation = false; } } @@ -592,8 +584,7 @@ bool RenderableModelEntityItem::isReadyToComputeShape() { if (_needsInitialSimulation) { // the _model's offset will be wrong until _needsInitialSimulation is false PerformanceTimer perfTimer("_model->simulate"); - _model->simulate(0.0f); - _needsInitialSimulation = false; + doInitialModelSimulation(); } return true; @@ -807,6 +798,13 @@ void RenderableModelEntityItem::locationChanged(bool tellPhysics) { if (_model && _model->isActive()) { _model->setRotation(getRotation()); _model->setTranslation(getPosition()); + + // { + // render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); + // render::PendingChanges pendingChanges; + // pendingChanges.updateItem(_myMetaItem, [](RenderableModelEntityItemMeta& data){}); + // scene->enqueuePendingChanges(pendingChanges); + // } } } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 59208d209d..5fd767c6ee 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -38,6 +38,8 @@ public: EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged) override; + void doInitialModelSimulation(); + virtual bool readyToAddToScene(RenderArgs* renderArgs = nullptr); virtual bool addToScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) override; virtual void removeFromScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) override; From efe02c0fa120666b5296d0e559e272b67b2fb412 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 13 May 2016 10:57:41 -0700 Subject: [PATCH 21/25] make lock/unlock button easier to see --- scripts/system/assets/images/lock.svg | 32 ++++++++++++++++++----- scripts/system/assets/images/unlock.svg | 30 +++++++++++++++++---- scripts/system/attachedEntitiesManager.js | 4 +-- 3 files changed, 53 insertions(+), 13 deletions(-) diff --git a/scripts/system/assets/images/lock.svg b/scripts/system/assets/images/lock.svg index 1480359f43..bb9658de00 100644 --- a/scripts/system/assets/images/lock.svg +++ b/scripts/system/assets/images/lock.svg @@ -2,11 +2,13 @@ + id="defs4"> + + + + + image/svg+xml - + @@ -54,15 +74,15 @@ id="layer1" transform="translate(0,-825.59055)"> diff --git a/scripts/system/assets/images/unlock.svg b/scripts/system/assets/images/unlock.svg index a7664c1f59..789a8b0ed5 100644 --- a/scripts/system/assets/images/unlock.svg +++ b/scripts/system/assets/images/unlock.svg @@ -2,11 +2,13 @@ + id="defs4"> + + + + + image/svg+xml - + @@ -54,14 +74,14 @@ id="layer1" transform="translate(0,-825.59055)"> Date: Mon, 16 May 2016 09:52:48 -0700 Subject: [PATCH 22/25] update attachedEntitiesManager to work better with avatarEntities --- scripts/system/attachedEntitiesManager.js | 104 ++++++++++++---------- 1 file changed, 55 insertions(+), 49 deletions(-) diff --git a/scripts/system/attachedEntitiesManager.js b/scripts/system/attachedEntitiesManager.js index 780cfe06fb..79c5973a6e 100644 --- a/scripts/system/attachedEntitiesManager.js +++ b/scripts/system/attachedEntitiesManager.js @@ -117,10 +117,11 @@ function AttachedEntitiesManager() { this.handleEntityRelease = function(grabbedEntity, releasedFromJoint) { // if this is still equipped, just rewrite the position information. var grabData = getEntityCustomData('grabKey', grabbedEntity, {}); - if ("refCount" in grabData && grabData.refCount > 0) { - manager.updateRelativeOffsets(grabbedEntity); - return; - } + // if ("refCount" in grabData && grabData.refCount > 0) { + // // for adjusting things in your other hand + // manager.updateRelativeOffsets(grabbedEntity); + // return; + // } var allowedJoints = getEntityCustomData('wearable', grabbedEntity, DEFAULT_WEARABLE_DATA).joints; @@ -156,9 +157,11 @@ function AttachedEntitiesManager() { wearProps.parentID = MyAvatar.sessionUUID; wearProps.parentJointIndex = bestJointIndex; + var updatePresets = false; if (bestJointOffset && bestJointOffset.constructor === Array) { if (!clothingLocked || bestJointOffset.length < 2) { - this.updateRelativeOffsets(grabbedEntity); + // we're unlocked or this thing didn't have a preset position, so update it + updatePresets = true; } else { // don't snap the entity to the preferred position if unlocked wearProps.localPosition = bestJointOffset[0]; @@ -167,7 +170,10 @@ function AttachedEntitiesManager() { } Entities.deleteEntity(grabbedEntity); - Entities.addEntity(wearProps, true); + grabbedEntity = Entities.addEntity(wearProps, true); + if (updatePresets) { + this.updateRelativeOffsets(grabbedEntity); + } } else if (props.parentID != NULL_UUID) { // drop the entity and set it to have no parent (not on the avatar), unless it's being equipped in a hand. if (props.parentID === MyAvatar.sessionUUID && @@ -225,20 +231,20 @@ function AttachedEntitiesManager() { } } - this.saveAttachedEntities = function() { - print("--- saving attached entities ---"); - saveData = []; - var nearbyEntities = Entities.findEntities(MyAvatar.position, ATTACHED_ENTITY_SEARCH_DISTANCE); - for (i = 0; i < nearbyEntities.length; i++) { - var entityID = nearbyEntities[i]; - if (this.updateRelativeOffsets(entityID)) { - var props = Entities.getEntityProperties(entityID); // refresh, because updateRelativeOffsets changed them - this.scrubProperties(props); - saveData.push(props); - } - } - Settings.setValue(ATTACHED_ENTITIES_SETTINGS_KEY, JSON.stringify(saveData)); - } + // this.saveAttachedEntities = function() { + // print("--- saving attached entities ---"); + // saveData = []; + // var nearbyEntities = Entities.findEntities(MyAvatar.position, ATTACHED_ENTITY_SEARCH_DISTANCE); + // for (i = 0; i < nearbyEntities.length; i++) { + // var entityID = nearbyEntities[i]; + // if (this.updateRelativeOffsets(entityID)) { + // var props = Entities.getEntityProperties(entityID); // refresh, because updateRelativeOffsets changed them + // this.scrubProperties(props); + // saveData.push(props); + // } + // } + // Settings.setValue(ATTACHED_ENTITIES_SETTINGS_KEY, JSON.stringify(saveData)); + // } this.scrubProperties = function(props) { var toScrub = ["queryAACube", "position", "rotation", @@ -262,37 +268,37 @@ function AttachedEntitiesManager() { } } - this.loadAttachedEntities = function(grabbedEntity) { - print("--- loading attached entities ---"); - jsonAttachmentData = Settings.getValue(ATTACHED_ENTITIES_SETTINGS_KEY); - var loadData = []; - try { - loadData = JSON.parse(jsonAttachmentData); - } catch (e) { - print('error parsing saved attachment data'); - return; - } + // this.loadAttachedEntities = function(grabbedEntity) { + // print("--- loading attached entities ---"); + // jsonAttachmentData = Settings.getValue(ATTACHED_ENTITIES_SETTINGS_KEY); + // var loadData = []; + // try { + // loadData = JSON.parse(jsonAttachmentData); + // } catch (e) { + // print('error parsing saved attachment data'); + // return; + // } - for (i = 0; i < loadData.length; i ++) { - var savedProps = loadData[ i ]; - var currentProps = Entities.getEntityProperties(savedProps.id); - if (currentProps.id == savedProps.id && - // TODO -- also check that parentJointIndex matches? - currentProps.parentID == MyAvatar.sessionUUID) { - // entity is already in-world. TODO -- patch it up? - continue; - } - this.scrubProperties(savedProps); - delete savedProps["id"]; - savedProps.parentID = MyAvatar.sessionUUID; // this will change between sessions - var loadedEntityID = Entities.addEntity(savedProps, true); + // for (i = 0; i < loadData.length; i ++) { + // var savedProps = loadData[ i ]; + // var currentProps = Entities.getEntityProperties(savedProps.id); + // if (currentProps.id == savedProps.id && + // // TODO -- also check that parentJointIndex matches? + // currentProps.parentID == MyAvatar.sessionUUID) { + // // entity is already in-world. TODO -- patch it up? + // continue; + // } + // this.scrubProperties(savedProps); + // delete savedProps["id"]; + // savedProps.parentID = MyAvatar.sessionUUID; // this will change between sessions + // var loadedEntityID = Entities.addEntity(savedProps, true); - Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({ - action: 'loaded', - grabbedEntity: loadedEntityID - })); - } - } + // Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({ + // action: 'loaded', + // grabbedEntity: loadedEntityID + // })); + // } + // } } var manager = new AttachedEntitiesManager(); From cf829f1babf58f4d442bdebdb8696f2c7f164fdf Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 17 May 2016 19:14:44 -0700 Subject: [PATCH 23/25] magic number --- interface/src/avatar/Avatar.cpp | 2 +- libraries/entities/src/SimulationOwner.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 95a3ef6132..efaed5b83a 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -220,7 +220,7 @@ void Avatar::updateAvatarEntities() { // there's no entity-server to tell us we're the simulation owner, so always set the // simulationOwner to the owningAvatarID and a high priority. - properties.setSimulationOwner(getID(), 129); + properties.setSimulationOwner(getID(), AVATAR_ENTITY_SIMULATION_PRIORITY); if (properties.getParentID() == AVATAR_SELF_ID) { properties.setParentID(getID()); diff --git a/libraries/entities/src/SimulationOwner.h b/libraries/entities/src/SimulationOwner.h index 1afec426d7..5f940bbe25 100644 --- a/libraries/entities/src/SimulationOwner.h +++ b/libraries/entities/src/SimulationOwner.h @@ -27,6 +27,7 @@ const quint8 RECRUIT_SIMULATION_PRIORITY = VOLUNTEER_SIMULATION_PRIORITY + 1; // When poking objects with scripts an observer will bid at SCRIPT_EDIT priority. const quint8 SCRIPT_GRAB_SIMULATION_PRIORITY = 0x80; const quint8 SCRIPT_POKE_SIMULATION_PRIORITY = SCRIPT_GRAB_SIMULATION_PRIORITY - 1; +const quint8 AVATAR_ENTITY_SIMULATION_PRIORITY = SCRIPT_GRAB_SIMULATION_PRIORITY + 1; // PERSONAL priority (needs a better name) is the level at which a simulation observer owns its own avatar // which really just means: things that collide with it will be bid at a priority level one lower From d95d3ff3ac61c722a109fe50fe99859b31eee585 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 20 May 2016 14:56:47 -0700 Subject: [PATCH 24/25] clean up debugging prints --- interface/src/avatar/Avatar.cpp | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index efaed5b83a..d12306a122 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -197,19 +197,16 @@ void Avatar::updateAvatarEntities() { QScriptEngine scriptEngine; entityTree->withWriteLock([&] { AvatarEntityMap avatarEntities = getAvatarEntityData(); - qDebug() << "---------------"; for (auto entityID : avatarEntities.keys()) { // see EntityEditPacketSender::queueEditEntityMessage for the other end of this. unpack properties // and either add or update the entity. QByteArray jsonByteArray = avatarEntities.value(entityID); QJsonDocument jsonProperties = QJsonDocument::fromBinaryData(jsonByteArray); if (!jsonProperties.isObject()) { - qDebug() << "got bad avatarEntity json"; + qCDebug(interfaceapp) << "got bad avatarEntity json" << QString(jsonByteArray.toHex()); continue; } - qDebug() << jsonProperties.toJson(); - QVariant variantProperties = jsonProperties.toVariant(); QVariantMap asMap = variantProperties.toMap(); QScriptValue scriptProperties = variantMapToScriptValue(asMap, scriptEngine); @@ -229,19 +226,14 @@ void Avatar::updateAvatarEntities() { EntityItemPointer entity = entityTree->findEntityByEntityItemID(EntityItemID(entityID)); if (entity) { - qDebug() << "avatar-entities existing entity, element =" << entity->getElement().get(); if (entityTree->updateEntity(entityID, properties)) { entity->updateLastEditedFromRemote(); - qDebug() << "avatar-entities after entityTree->updateEntity(), element =" << entity->getElement().get(); } else { - qDebug() << "AVATAR-ENTITIES -- updateEntity failed: " << properties.getType(); success = false; } } else { - qDebug() << "avatar-entities new entity"; entity = entityTree->addEntity(entityID, properties); if (!entity) { - qDebug() << "AVATAR-ENTITIES -- addEntity failed: " << properties.getType(); success = false; } } @@ -1194,7 +1186,7 @@ void Avatar::setParentID(const QUuid& parentID) { if (success) { setTransform(beforeChangeTransform, success); if (!success) { - qDebug() << "Avatar::setParentID failed to reset avatar's location."; + qCDebug(interfaceapp) << "Avatar::setParentID failed to reset avatar's location."; } } } @@ -1209,7 +1201,7 @@ void Avatar::setParentJointIndex(quint16 parentJointIndex) { if (success) { setTransform(beforeChangeTransform, success); if (!success) { - qDebug() << "Avatar::setParentJointIndex failed to reset avatar's location."; + qCDebug(interfaceapp) << "Avatar::setParentJointIndex failed to reset avatar's location."; } } } From 637735bbc3e6099ed6648d31812a027d866ffcfb Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 20 May 2016 18:07:38 -0700 Subject: [PATCH 25/25] unmangle merge --- libraries/entities/src/EntityItemProperties.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 03dc4a0557..99285b4986 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -690,13 +690,11 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(jointTranslationsSet, qVectorBool, setJointTranslationsSet); COPY_PROPERTY_FROM_QSCRIPTVALUE(jointTranslations, qVectorVec3, setJointTranslations); -<<<<<<< HEAD - COPY_PROPERTY_FROM_QSCRIPTVALUE(clientOnly, bool, setClientOnly); - COPY_PROPERTY_FROM_QSCRIPTVALUE(owningAvatarID, QUuid, setOwningAvatarID); -======= COPY_PROPERTY_FROM_QSCRIPTVALUE(flyingAllowed, bool, setFlyingAllowed); COPY_PROPERTY_FROM_QSCRIPTVALUE(ghostingAllowed, bool, setGhostingAllowed); ->>>>>>> e012630db23d8aba81fae0721f69322220b21be2 + + COPY_PROPERTY_FROM_QSCRIPTVALUE(clientOnly, bool, setClientOnly); + COPY_PROPERTY_FROM_QSCRIPTVALUE(owningAvatarID, QUuid, setOwningAvatarID); _lastEdited = usecTimestampNow(); } @@ -1592,13 +1590,11 @@ void EntityItemProperties::markAllChanged() { _queryAACubeChanged = true; -<<<<<<< HEAD - _clientOnlyChanged = true; - _owningAvatarIDChanged = true; -======= _flyingAllowedChanged = true; _ghostingAllowedChanged = true; ->>>>>>> e012630db23d8aba81fae0721f69322220b21be2 + + _clientOnlyChanged = true; + _owningAvatarIDChanged = true; } // The minimum bounding box for the entity.