diff --git a/assignment-client/src/entities/EntityPriorityQueue.cpp b/assignment-client/src/entities/EntityPriorityQueue.cpp index 3755e6a5c7..f6c1161308 100644 --- a/assignment-client/src/entities/EntityPriorityQueue.cpp +++ b/assignment-client/src/entities/EntityPriorityQueue.cpp @@ -12,6 +12,7 @@ #include "EntityPriorityQueue.h" const float PrioritizedEntity::DO_NOT_SEND = -1.0e-6f; +const float PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY = 1.0f; void ConicalView::set(const ViewFrustum& viewFrustum) { // The ConicalView has two parts: a central sphere (same as ViewFrustum) and a circular cone that bounds the frustum part. diff --git a/assignment-client/src/entities/EntityPriorityQueue.h b/assignment-client/src/entities/EntityPriorityQueue.h index 10db22d695..29712b3fd3 100644 --- a/assignment-client/src/entities/EntityPriorityQueue.h +++ b/assignment-client/src/entities/EntityPriorityQueue.h @@ -40,12 +40,14 @@ private: class PrioritizedEntity { public: static const float DO_NOT_SEND; + static const float WHEN_IN_DOUBT_PRIORITY; - PrioritizedEntity(EntityItemPointer entity, float priority) : _weakEntity(entity), _rawEntityPointer(entity.get()), _priority(priority) {} + PrioritizedEntity(EntityItemPointer entity, float priority, bool forceSend = false) : _weakEntity(entity), _rawEntityPointer(entity.get()), _priority(priority), _forceSend(forceSend) {} float updatePriority(const ConicalView& view); EntityItemPointer getEntity() const { return _weakEntity.lock(); } EntityItem* getRawEntityPointer() const { return _rawEntityPointer; } float getPriority() const { return _priority; } + bool shouldForceSend() const { return _forceSend; } class Compare { public: @@ -57,6 +59,7 @@ private: EntityItemWeakPointer _weakEntity; EntityItem* _rawEntityPointer; float _priority; + bool _forceSend; }; using EntityPriorityQueue = std::priority_queue< PrioritizedEntity, std::vector, PrioritizedEntity::Compare >; diff --git a/assignment-client/src/entities/EntityTreeSendThread.cpp b/assignment-client/src/entities/EntityTreeSendThread.cpp index e71331177b..e34dfd97e7 100644 --- a/assignment-client/src/entities/EntityTreeSendThread.cpp +++ b/assignment-client/src/entities/EntityTreeSendThread.cpp @@ -22,6 +22,7 @@ EntityTreeSendThread::EntityTreeSendThread(OctreeServer* myServer, const SharedNodePointer& node) : OctreeSendThread(myServer, node) { + connect(std::static_pointer_cast(myServer->getOctree()).get(), &EntityTree::editingEntityPointer, this, &EntityTreeSendThread::editingEntityPointer, Qt::QueuedConnection); connect(std::static_pointer_cast(myServer->getOctree()).get(), &EntityTree::deletingEntityPointer, this, &EntityTreeSendThread::deletingEntityPointer, Qt::QueuedConnection); } @@ -108,29 +109,29 @@ void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, O // Re-add elements from previous traversal if they still need to be sent while (!prevSendQueue.empty()) { EntityItemPointer entity = prevSendQueue.top().getEntity(); + bool forceSend = prevSendQueue.top().shouldForceSend(); prevSendQueue.pop(); if (entity) { bool success = false; AACube cube = entity->getQueryAACube(success); if (success) { - if (_traversal.getCurrentView().cubeIntersectsKeyhole(cube)) { + if (forceSend || _traversal.getCurrentView().cubeIntersectsKeyhole(cube)) { float priority = _conicalView.computePriority(cube); - if (priority != PrioritizedEntity::DO_NOT_SEND) { + if (forceSend || priority != PrioritizedEntity::DO_NOT_SEND) { float renderAccuracy = calculateRenderAccuracy(_traversal.getCurrentView().getPosition(), cube, _traversal.getCurrentRootSizeScale(), lodLevelOffset); - // Only send entities if they are large enough to see - if (renderAccuracy > 0.0f) { + // Only send entities if they are large enough to see, or we need to update them to be out of view + if (forceSend || renderAccuracy > 0.0f) { _sendQueue.push(PrioritizedEntity(entity, priority)); _entitiesInQueue.insert(entity.get()); } } } } else { - const float WHEN_IN_DOUBT_PRIORITY = 1.0f; - _sendQueue.push(PrioritizedEntity(entity, WHEN_IN_DOUBT_PRIORITY)); + _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY)); _entitiesInQueue.insert(entity.get()); } } @@ -274,8 +275,7 @@ void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTree } } } else { - const float WHEN_IN_DOUBT_PRIORITY = 1.0f; - _sendQueue.push(PrioritizedEntity(entity, WHEN_IN_DOUBT_PRIORITY)); + _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY)); _entitiesInQueue.insert(entity.get()); } }); @@ -310,8 +310,7 @@ void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTree } } } else { - const float WHEN_IN_DOUBT_PRIORITY = 1.0f; - _sendQueue.push(PrioritizedEntity(entity, WHEN_IN_DOUBT_PRIORITY)); + _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY)); _entitiesInQueue.insert(entity.get()); } } @@ -364,8 +363,7 @@ void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTree } } } else { - const float WHEN_IN_DOUBT_PRIORITY = 1.0f; - _sendQueue.push(PrioritizedEntity(entity, WHEN_IN_DOUBT_PRIORITY)); + _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY)); _entitiesInQueue.insert(entity.get()); } } @@ -454,6 +452,23 @@ bool EntityTreeSendThread::traverseTreeAndBuildNextPacketPayload(EncodeBitstream #endif // SEND_SORTED_ENTITIES } +void EntityTreeSendThread::editingEntityPointer(const EntityItemPointer entity) { + if (entity) { + if (_entitiesInQueue.find(entity.get()) == _entitiesInQueue.end() && _knownState.find(entity.get()) != _knownState.end()) { + bool success = false; + AACube cube = entity->getQueryAACube(success); + if (success) { + float priority = _conicalView.computePriority(cube); + _sendQueue.push(PrioritizedEntity(entity, priority, true)); + _entitiesInQueue.insert(entity.get()); + } else { + _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY, true)); + _entitiesInQueue.insert(entity.get()); + } + } + } +} + void EntityTreeSendThread::deletingEntityPointer(EntityItem* entity) { _knownState.erase(entity); } \ No newline at end of file diff --git a/assignment-client/src/entities/EntityTreeSendThread.h b/assignment-client/src/entities/EntityTreeSendThread.h index db59d97e6f..8b763a1c94 100644 --- a/assignment-client/src/entities/EntityTreeSendThread.h +++ b/assignment-client/src/entities/EntityTreeSendThread.h @@ -54,6 +54,7 @@ private: uint16_t _numEntities { 0 }; private slots: + void editingEntityPointer(const EntityItemPointer entity); void deletingEntityPointer(EntityItem* entity); }; diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 71b119f415..6e2e52b380 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -32,7 +32,8 @@ #include "EntitySimulation.h" #include "EntityDynamicFactoryInterface.h" - +Q_DECLARE_METATYPE(EntityItemPointer); +int entityItemPointernMetaTypeId = qRegisterMetaType(); int EntityItem::_maxActionsDataSize = 800; quint64 EntityItem::_rememberDeletedActionTime = 20 * USECS_PER_SECOND; diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 4eac23c867..88750da463 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -62,7 +62,8 @@ class MeshProxyList; /// EntityItem class this is the base class for all entity types. It handles the basic properties and functionality available /// to all other entity types. In particular: postion, size, rotation, age, lifetime, velocity, gravity. You can not instantiate /// one directly, instead you must only construct one of it's derived classes with additional features. -class EntityItem : public SpatiallyNestable, public ReadWriteLockable { +class EntityItem : public QObject, public SpatiallyNestable, public ReadWriteLockable { + Q_OBJECT // These two classes manage lists of EntityItem pointers and must be able to cleanup pointers when an EntityItem is deleted. // To make the cleanup robust each EntityItem has backpointers to its manager classes (which are only ever set/cleared by // the managers themselves, hence they are fiends) whose NULL status can be used to determine which managers still need to diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index ebf479574b..99ab5a7677 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -307,7 +307,9 @@ bool EntityTree::updateEntity(EntityItemPointer entity, const EntityItemProperti } UpdateEntityOperator theOperator(getThisPointer(), containingElement, entity, queryCube); recurseTreeWithOperator(&theOperator); - entity->setProperties(tempProperties); + if (entity->setProperties(tempProperties)) { + emit editingEntityPointer(entity); + } _isDirty = true; } } @@ -382,7 +384,9 @@ bool EntityTree::updateEntity(EntityItemPointer entity, const EntityItemProperti } UpdateEntityOperator theOperator(getThisPointer(), containingElement, entity, newQueryAACube); recurseTreeWithOperator(&theOperator); - entity->setProperties(properties); + if (entity->setProperties(properties)) { + emit editingEntityPointer(entity); + } // if the entity has children, run UpdateEntityOperator on them. If the children have children, recurse QQueue toProcess; @@ -1257,7 +1261,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c if (!isPhysics) { properties.setLastEditedBy(senderNode->getUUID()); } - updateEntity(entityItemID, properties, senderNode); + updateEntity(existingEntity, properties, senderNode); existingEntity->markAsChangedOnServer(); endUpdate = usecTimestampNow(); _totalUpdates++; diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index b4155f474b..d0448f438a 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -271,6 +271,7 @@ signals: void deletingEntity(const EntityItemID& entityID); void deletingEntityPointer(EntityItem* entityID); void addingEntity(const EntityItemID& entityID); + void editingEntityPointer(const EntityItemPointer& entityID); void entityScriptChanging(const EntityItemID& entityItemID, const bool reload); void entityServerScriptChanging(const EntityItemID& entityItemID, const bool reload); void newCollisionSoundURL(const QUrl& url, const EntityItemID& entityID);