diff --git a/assignment-client/src/entities/EntityPriorityQueue.cpp b/assignment-client/src/entities/EntityPriorityQueue.cpp index f6c1161308..6d94f911ea 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::FORCE_REMOVE = -1.0e-5f; const float PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY = 1.0f; void ConicalView::set(const ViewFrustum& viewFrustum) { diff --git a/assignment-client/src/entities/EntityPriorityQueue.h b/assignment-client/src/entities/EntityPriorityQueue.h index 29712b3fd3..a5d0ab05ff 100644 --- a/assignment-client/src/entities/EntityPriorityQueue.h +++ b/assignment-client/src/entities/EntityPriorityQueue.h @@ -40,14 +40,15 @@ private: class PrioritizedEntity { public: static const float DO_NOT_SEND; + static const float FORCE_REMOVE; static const float WHEN_IN_DOUBT_PRIORITY; - PrioritizedEntity(EntityItemPointer entity, float priority, bool forceSend = false) : _weakEntity(entity), _rawEntityPointer(entity.get()), _priority(priority), _forceSend(forceSend) {} + PrioritizedEntity(EntityItemPointer entity, float priority, bool forceRemove = false) : _weakEntity(entity), _rawEntityPointer(entity.get()), _priority(priority), _forceRemove(forceRemove) {} 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; } + bool shouldForceRemove() const { return _forceRemove; } class Compare { public: @@ -59,7 +60,7 @@ private: EntityItemWeakPointer _weakEntity; EntityItem* _rawEntityPointer; float _priority; - bool _forceSend; + bool _forceRemove; }; 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 024855235a..817df55625 100644 --- a/assignment-client/src/entities/EntityTreeSendThread.cpp +++ b/assignment-client/src/entities/EntityTreeSendThread.cpp @@ -92,39 +92,39 @@ void EntityTreeSendThread::preDistributionProcessing() { void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData, bool viewFrustumChanged, bool isFullScene) { - if (nodeData->getUsesFrustum()) { - if (viewFrustumChanged || _traversal.finished()) { - ViewFrustum viewFrustum; - nodeData->copyCurrentViewFrustum(viewFrustum); - EntityTreeElementPointer root = std::dynamic_pointer_cast(_myServer->getOctree()->getRoot()); - int32_t lodLevelOffset = nodeData->getBoundaryLevelAdjust() + (viewFrustumChanged ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST); - startNewTraversal(viewFrustum, root, lodLevelOffset, true); + if (viewFrustumChanged || _traversal.finished()) { + ViewFrustum viewFrustum; + nodeData->copyCurrentViewFrustum(viewFrustum); + EntityTreeElementPointer root = std::dynamic_pointer_cast(_myServer->getOctree()->getRoot()); + int32_t lodLevelOffset = nodeData->getBoundaryLevelAdjust() + (viewFrustumChanged ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST); + startNewTraversal(viewFrustum, root, lodLevelOffset, nodeData->getUsesFrustum()); - // When the viewFrustum changed the sort order may be incorrect, so we re-sort - // and also use the opportunity to cull anything no longer in view - if (viewFrustumChanged && !_sendQueue.empty()) { - EntityPriorityQueue prevSendQueue; - _sendQueue.swap(prevSendQueue); - _entitiesInQueue.clear(); - // 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) { + // When the viewFrustum changed the sort order may be incorrect, so we re-sort + // and also use the opportunity to cull anything no longer in view + if (viewFrustumChanged && !_sendQueue.empty()) { + EntityPriorityQueue prevSendQueue; + _sendQueue.swap(prevSendQueue); + _entitiesInQueue.clear(); + // Re-add elements from previous traversal if they still need to be sent + while (!prevSendQueue.empty()) { + EntityItemPointer entity = prevSendQueue.top().getEntity(); + bool forceRemove = prevSendQueue.top().shouldForceRemove(); + prevSendQueue.pop(); + if (entity) { + if (!forceRemove) { bool success = false; AACube cube = entity->getQueryAACube(success); if (success) { - if (forceSend || _traversal.getCurrentView().cubeIntersectsKeyhole(cube)) { + if (_traversal.getCurrentView().cubeIntersectsKeyhole(cube)) { float priority = _conicalView.computePriority(cube); - if (forceSend || priority != PrioritizedEntity::DO_NOT_SEND) { + if (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, or we need to update them to be out of view - if (forceSend || renderAccuracy > 0.0f) { + // Only send entities if they are large enough to see + if (renderAccuracy > 0.0f) { _sendQueue.push(PrioritizedEntity(entity, priority)); _entitiesInQueue.insert(entity.get()); } @@ -134,15 +134,13 @@ void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, O _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY)); _entitiesInQueue.insert(entity.get()); } + } else { + _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::FORCE_REMOVE, true)); + _entitiesInQueue.insert(entity.get()); } } } } - } else if (_traversal.finished()) { - ViewFrustum viewFrustum; - EntityTreeElementPointer root = std::dynamic_pointer_cast(_myServer->getOctree()->getRoot()); - int32_t lodLevelOffset = nodeData->getBoundaryLevelAdjust() + NO_BOUNDARY_ADJUST; - startNewTraversal(viewFrustum, root, lodLevelOffset, false); } if (!_traversal.finished()) { @@ -230,14 +228,13 @@ bool EntityTreeSendThread::addDescendantsToExtraFlaggedEntities(const QUuid& fil return hasNewChild || hasNewDescendants; } -void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTreeElementPointer root, int32_t lodLevelOffset, bool usesFrustum) { - DiffTraversal::Type type = _traversal.prepareNewTraversal(view, root, lodLevelOffset, usesFrustum); - // there are four types of traversal: +void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTreeElementPointer root, int32_t lodLevelOffset, bool usesViewFrustum) { + DiffTraversal::Type type = _traversal.prepareNewTraversal(view, root, lodLevelOffset, usesViewFrustum); + // there are three types of traversal: // // (1) FirstTime = at login --> find everything in view // (2) Repeat = view hasn't changed --> find what has changed since last complete traversal // (3) Differential = view has changed --> find what has changed or in new view but not old - // (4) FullScene = no view frustum -> send everything // // The "scanCallback" we provide to the traversal depends on the type: // @@ -248,83 +245,118 @@ void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTree switch (type) { case DiffTraversal::First: - _traversal.setScanCallback([&] (DiffTraversal::VisibleElement& next) { - // When we get to a First traversal, clear the _knownState - _knownState.clear(); - next.element->forEachEntity([&](EntityItemPointer entity) { - // Bail early if we've already checked this entity this frame - if (_entitiesInQueue.find(entity.get()) != _entitiesInQueue.end()) { - return; - } - bool success = false; - AACube cube = entity->getQueryAACube(success); - if (success) { - if (_traversal.getCurrentView().cubeIntersectsKeyhole(cube)) { - // Check the size of the entity, it's possible that a "too small to see" entity is included in a - // larger octree cell because of its position (for example if it crosses the boundary of a cell it - // pops to the next higher cell. So we want to check to see that the entity is large enough to be seen - // before we consider including it. - // - // TODO: compare priority against a threshold rather than bother with - // calculateRenderAccuracy(). Would need to replace all calculateRenderAccuracy() - // stuff everywhere with threshold in one sweep. - float renderAccuracy = calculateRenderAccuracy(_traversal.getCurrentView().getPosition(), - cube, - _traversal.getCurrentRootSizeScale(), - _traversal.getCurrentLODOffset()); - - // Only send entities if they are large enough to see - if (renderAccuracy > 0.0f) { - float priority = _conicalView.computePriority(cube); - _sendQueue.push(PrioritizedEntity(entity, priority)); - _entitiesInQueue.insert(entity.get()); - } - } - } else { - _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY)); - _entitiesInQueue.insert(entity.get()); - } - }); - }); - break; - case DiffTraversal::Repeat: - _traversal.setScanCallback([&] (DiffTraversal::VisibleElement& next) { - uint64_t startOfCompletedTraversal = _traversal.getStartOfCompletedTraversal(); - if (next.element->getLastChangedContent() > startOfCompletedTraversal) { + // When we get to a First traversal, clear the _knownState + _knownState.clear(); + if (usesViewFrustum) { + _traversal.setScanCallback([&](DiffTraversal::VisibleElement& next) { next.element->forEachEntity([&](EntityItemPointer entity) { // Bail early if we've already checked this entity this frame if (_entitiesInQueue.find(entity.get()) != _entitiesInQueue.end()) { return; } - if (_knownState.find(entity.get()) == _knownState.end() || - (_knownState.find(entity.get()) != _knownState.end() && entity->getLastEdited() > _knownState[entity.get()])) { - bool success = false; - AACube cube = entity->getQueryAACube(success); - if (success) { - if (next.intersection == ViewFrustum::INSIDE || _traversal.getCurrentView().cubeIntersectsKeyhole(cube)) { - // See the DiffTraversal::First case for an explanation of the "entity is too small" check - float renderAccuracy = calculateRenderAccuracy(_traversal.getCurrentView().getPosition(), - cube, - _traversal.getCurrentRootSizeScale(), - _traversal.getCurrentLODOffset()); + bool success = false; + AACube cube = entity->getQueryAACube(success); + if (success) { + if (_traversal.getCurrentView().cubeIntersectsKeyhole(cube)) { + // Check the size of the entity, it's possible that a "too small to see" entity is included in a + // larger octree cell because of its position (for example if it crosses the boundary of a cell it + // pops to the next higher cell. So we want to check to see that the entity is large enough to be seen + // before we consider including it. + // + // TODO: compare priority against a threshold rather than bother with + // calculateRenderAccuracy(). Would need to replace all calculateRenderAccuracy() + // stuff everywhere with threshold in one sweep. + float renderAccuracy = calculateRenderAccuracy(_traversal.getCurrentView().getPosition(), + cube, + _traversal.getCurrentRootSizeScale(), + _traversal.getCurrentLODOffset()); - // Only send entities if they are large enough to see - if (renderAccuracy > 0.0f) { - float priority = _conicalView.computePriority(cube); - _sendQueue.push(PrioritizedEntity(entity, priority)); - _entitiesInQueue.insert(entity.get()); - } + // Only send entities if they are large enough to see + if (renderAccuracy > 0.0f) { + float priority = _conicalView.computePriority(cube); + _sendQueue.push(PrioritizedEntity(entity, priority)); + _entitiesInQueue.insert(entity.get()); } - } else { + } + } else { + _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY)); + _entitiesInQueue.insert(entity.get()); + } + }); + }); + } else { + _traversal.setScanCallback([&](DiffTraversal::VisibleElement& next) { + next.element->forEachEntity([&](EntityItemPointer entity) { + // Bail early if we've already checked this entity this frame + if (_entitiesInQueue.find(entity.get()) != _entitiesInQueue.end()) { + return; + } + _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY)); + _entitiesInQueue.insert(entity.get()); + }); + }); + } + break; + case DiffTraversal::Repeat: + if (usesViewFrustum) { + _traversal.setScanCallback([&](DiffTraversal::VisibleElement& next) { + uint64_t startOfCompletedTraversal = _traversal.getStartOfCompletedTraversal(); + if (next.element->getLastChangedContent() > startOfCompletedTraversal) { + next.element->forEachEntity([&](EntityItemPointer entity) { + // Bail early if we've already checked this entity this frame + if (_entitiesInQueue.find(entity.get()) != _entitiesInQueue.end()) { + return; + } + auto knownTimestamp = _knownState.find(entity.get()); + if (knownTimestamp == _knownState.end() || + (knownTimestamp != _knownState.end() && entity->getLastEdited() > knownTimestamp->second)) { + bool success = false; + AACube cube = entity->getQueryAACube(success); + if (success) { + if (next.intersection == ViewFrustum::INSIDE || _traversal.getCurrentView().cubeIntersectsKeyhole(cube)) { + // See the DiffTraversal::First case for an explanation of the "entity is too small" check + float renderAccuracy = calculateRenderAccuracy(_traversal.getCurrentView().getPosition(), + cube, + _traversal.getCurrentRootSizeScale(), + _traversal.getCurrentLODOffset()); + + // Only send entities if they are large enough to see + if (renderAccuracy > 0.0f) { + float priority = _conicalView.computePriority(cube); + _sendQueue.push(PrioritizedEntity(entity, priority)); + _entitiesInQueue.insert(entity.get()); + } + } + } else { + _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY)); + _entitiesInQueue.insert(entity.get()); + } + } + }); + } + }); + } else { + _traversal.setScanCallback([&](DiffTraversal::VisibleElement& next) { + uint64_t startOfCompletedTraversal = _traversal.getStartOfCompletedTraversal(); + if (next.element->getLastChangedContent() > startOfCompletedTraversal) { + next.element->forEachEntity([&](EntityItemPointer entity) { + // Bail early if we've already checked this entity this frame + if (_entitiesInQueue.find(entity.get()) != _entitiesInQueue.end()) { + return; + } + auto knownTimestamp = _knownState.find(entity.get()); + if (knownTimestamp == _knownState.end() || + (knownTimestamp != _knownState.end() && entity->getLastEdited() > knownTimestamp->second)) { _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY)); _entitiesInQueue.insert(entity.get()); } - } - }); - } - }); + }); + } + }); + } break; case DiffTraversal::Differential: + assert(usesViewFrustum); _traversal.setScanCallback([&] (DiffTraversal::VisibleElement& next) { // NOTE: for Differential case: next.intersection is against completedView not currentView uint64_t startOfCompletedTraversal = _traversal.getStartOfCompletedTraversal(); @@ -335,8 +367,9 @@ void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTree if (_entitiesInQueue.find(entity.get()) != _entitiesInQueue.end()) { return; } - if (_knownState.find(entity.get()) == _knownState.end() || - (_knownState.find(entity.get()) != _knownState.end() && entity->getLastEdited() > _knownState[entity.get()])) { + auto knownTimestamp = _knownState.find(entity.get()); + if (knownTimestamp == _knownState.end() || + (knownTimestamp != _knownState.end() && entity->getLastEdited() > knownTimestamp->second)) { bool success = false; AACube cube = entity->getQueryAACube(success); if (success) { @@ -377,22 +410,6 @@ void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTree } }); break; - case DiffTraversal::FullScene: - _traversal.setScanCallback([&](DiffTraversal::VisibleElement& next) { - next.element->forEachEntity([&](EntityItemPointer entity) { - // Bail early if we've already checked this entity this frame - if (_entitiesInQueue.find(entity.get()) != _entitiesInQueue.end()) { - return; - } - if (_knownState.find(entity.get()) == _knownState.end() || - (_knownState.find(entity.get()) != _knownState.end() && entity->getLastEdited() > _knownState[entity.get()])) { - // We don't have a view frustum from which to compute priority - _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY)); - _entitiesInQueue.insert(entity.get()); - } - }); - }); - break; } } @@ -453,7 +470,11 @@ bool EntityTreeSendThread::traverseTreeAndBuildNextPacketPayload(EncodeBitstream } ++_numEntities; } - _knownState[entity.get()] = sendTime; + if (queuedItem.shouldForceRemove()) { + _knownState.erase(entity.get()); + } else { + _knownState[entity.get()] = sendTime; + } } _sendQueue.pop(); _entitiesInQueue.erase(entity.get()); @@ -483,9 +504,11 @@ void EntityTreeSendThread::editingEntityPointer(const EntityItemPointer entity) 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()); + // We can force a removal from _knownState if the current view is used and entity is out of view + if (_traversal.doesCurrentUseViewFrustum() && !_traversal.getCurrentView().cubeIntersectsKeyhole(cube)) { + _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::FORCE_REMOVE, true)); + _entitiesInQueue.insert(entity.get()); + } } else { _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY, true)); _entitiesInQueue.insert(entity.get()); diff --git a/assignment-client/src/entities/EntityTreeSendThread.h b/assignment-client/src/entities/EntityTreeSendThread.h index bdda159ed5..c9751c5835 100644 --- a/assignment-client/src/entities/EntityTreeSendThread.h +++ b/assignment-client/src/entities/EntityTreeSendThread.h @@ -39,7 +39,7 @@ private: bool addAncestorsToExtraFlaggedEntities(const QUuid& filteredEntityID, EntityItem& entityItem, EntityNodeData& nodeData); bool addDescendantsToExtraFlaggedEntities(const QUuid& filteredEntityID, EntityItem& entityItem, EntityNodeData& nodeData); - void startNewTraversal(const ViewFrustum& viewFrustum, EntityTreeElementPointer root, int32_t lodLevelOffset, bool usesFrustum); + void startNewTraversal(const ViewFrustum& viewFrustum, EntityTreeElementPointer root, int32_t lodLevelOffset, bool usesViewFrustum); bool traverseTreeAndBuildNextPacketPayload(EncodeBitstreamParams& params, const QJsonObject& jsonFilters) override; DiffTraversal _traversal; diff --git a/libraries/entities/src/DiffTraversal.cpp b/libraries/entities/src/DiffTraversal.cpp index 2cbe3f1064..86296c82de 100644 --- a/libraries/entities/src/DiffTraversal.cpp +++ b/libraries/entities/src/DiffTraversal.cpp @@ -33,18 +33,30 @@ void DiffTraversal::Waypoint::getNextVisibleElementFirstTime(DiffTraversal::Visi } else if (_nextIndex < NUMBER_OF_CHILDREN) { EntityTreeElementPointer element = _weakElement.lock(); if (element) { - // check for LOD truncation - float visibleLimit = boundaryDistanceForRenderLevel(element->getLevel() + view.lodLevelOffset, view.rootSizeScale); - float distance2 = glm::distance2(view.viewFrustum.getPosition(), element->getAACube().calcCenter()); - if (distance2 < visibleLimit * visibleLimit) { + // No LOD truncation if we aren't using the view frustum + if (!view.usesViewFrustum) { while (_nextIndex < NUMBER_OF_CHILDREN) { EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex); ++_nextIndex; - if (nextElement && view.viewFrustum.cubeIntersectsKeyhole(nextElement->getAACube())) { + if (nextElement) { next.element = nextElement; return; } } + } else { + // check for LOD truncation + float visibleLimit = boundaryDistanceForRenderLevel(element->getLevel() + view.lodLevelOffset, view.rootSizeScale); + float distance2 = glm::distance2(view.viewFrustum.getPosition(), element->getAACube().calcCenter()); + if (distance2 < visibleLimit * visibleLimit) { + while (_nextIndex < NUMBER_OF_CHILDREN) { + EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex); + ++_nextIndex; + if (nextElement && view.viewFrustum.cubeIntersectsKeyhole(nextElement->getAACube())) { + next.element = nextElement; + return; + } + } + } } } } @@ -66,19 +78,32 @@ void DiffTraversal::Waypoint::getNextVisibleElementRepeat( if (_nextIndex < NUMBER_OF_CHILDREN) { EntityTreeElementPointer element = _weakElement.lock(); if (element) { - // check for LOD truncation - float visibleLimit = boundaryDistanceForRenderLevel(element->getLevel() + view.lodLevelOffset, view.rootSizeScale); - float distance2 = glm::distance2(view.viewFrustum.getPosition(), element->getAACube().calcCenter()); - if (distance2 < visibleLimit * visibleLimit) { + // No LOD truncation if we aren't using the view frustum + if (!view.usesViewFrustum) { while (_nextIndex < NUMBER_OF_CHILDREN) { EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex); ++_nextIndex; if (nextElement && nextElement->getLastChanged() > lastTime) { - ViewFrustum::intersection intersection = view.viewFrustum.calculateCubeKeyholeIntersection(nextElement->getAACube()); - if (intersection != ViewFrustum::OUTSIDE) { - next.element = nextElement; - next.intersection = intersection; - return; + next.element = nextElement; + next.intersection = ViewFrustum::INSIDE; + return; + } + } + } else { + // check for LOD truncation + float visibleLimit = boundaryDistanceForRenderLevel(element->getLevel() + view.lodLevelOffset, view.rootSizeScale); + float distance2 = glm::distance2(view.viewFrustum.getPosition(), element->getAACube().calcCenter()); + if (distance2 < visibleLimit * visibleLimit) { + while (_nextIndex < NUMBER_OF_CHILDREN) { + EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex); + ++_nextIndex; + if (nextElement && nextElement->getLastChanged() > lastTime) { + ViewFrustum::intersection intersection = view.viewFrustum.calculateCubeKeyholeIntersection(nextElement->getAACube()); + if (intersection != ViewFrustum::OUTSIDE) { + next.element = nextElement; + next.intersection = intersection; + return; + } } } } @@ -142,45 +167,18 @@ void DiffTraversal::Waypoint::getNextVisibleElementDifferential(DiffTraversal::V next.intersection = ViewFrustum::OUTSIDE; } -void DiffTraversal::Waypoint::getNextVisibleElementFullScene(DiffTraversal::VisibleElement& next, uint64_t lastTime) { - // NOTE: no need to set next.intersection or check LOD truncation in the "FullScene" context - if (_nextIndex == -1) { - ++_nextIndex; - EntityTreeElementPointer element = _weakElement.lock(); - if (element->getLastChangedContent() > lastTime) { - next.element = element; - return; - } - } - if (_nextIndex < NUMBER_OF_CHILDREN) { - EntityTreeElementPointer element = _weakElement.lock(); - if (element) { - while (_nextIndex < NUMBER_OF_CHILDREN) { - EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex); - ++_nextIndex; - if (nextElement && nextElement->getLastChanged() > lastTime) { - next.element = nextElement; - return; - } - } - } - } - next.element.reset(); -} - DiffTraversal::DiffTraversal() { const int32_t MIN_PATH_DEPTH = 16; _path.reserve(MIN_PATH_DEPTH); } -DiffTraversal::Type DiffTraversal::prepareNewTraversal(const ViewFrustum& viewFrustum, EntityTreeElementPointer root, int32_t lodLevelOffset, bool usesFrustum) { +DiffTraversal::Type DiffTraversal::prepareNewTraversal(const ViewFrustum& viewFrustum, EntityTreeElementPointer root, int32_t lodLevelOffset, bool usesViewFrustum) { assert(root); - // there are four types of traversal: + // there are three types of traversal: // // (1) First = fresh view --> find all elements in view // (2) Repeat = view hasn't changed --> find elements changed since last complete traversal // (3) Differential = view has changed --> find elements changed or in new view but not old - // (4) FullScene = no view frustum -> send everything // // for each traversal type we assign the appropriate _getNextVisibleElementCallback // @@ -190,14 +188,11 @@ DiffTraversal::Type DiffTraversal::prepareNewTraversal(const ViewFrustum& viewFr // // external code should update the _scanElementCallback after calling prepareNewTraversal // + _currentView.usesViewFrustum = usesViewFrustum; Type type; - if (!usesFrustum) { - type = Type::FullScene; - _getNextVisibleElementCallback = [&](DiffTraversal::VisibleElement& next) { - _path.back().getNextVisibleElementFullScene(next, _completedView.startTime); - }; - } else if (_completedView.startTime == 0) { + // If usesViewFrustum changes, treat it as a First traversal + if (_completedView.startTime == 0 || _currentView.usesViewFrustum != _completedView.usesViewFrustum) { type = Type::First; _currentView.viewFrustum = viewFrustum; _currentView.lodLevelOffset = root->getLevel() + lodLevelOffset - 1; // -1 because true root has level=1 @@ -205,7 +200,7 @@ DiffTraversal::Type DiffTraversal::prepareNewTraversal(const ViewFrustum& viewFr _getNextVisibleElementCallback = [&](DiffTraversal::VisibleElement& next) { _path.back().getNextVisibleElementFirstTime(next, _currentView); }; - } else if (_completedView.viewFrustum.isVerySimilar(viewFrustum) && lodLevelOffset == _completedView.lodLevelOffset) { + } else if (!_currentView.usesViewFrustum || (_completedView.viewFrustum.isVerySimilar(viewFrustum) && lodLevelOffset == _completedView.lodLevelOffset)) { type = Type::Repeat; _getNextVisibleElementCallback = [&](DiffTraversal::VisibleElement& next) { _path.back().getNextVisibleElementRepeat(next, _completedView, _completedView.startTime); diff --git a/libraries/entities/src/DiffTraversal.h b/libraries/entities/src/DiffTraversal.h index 83b2c99bb9..2bd44d041e 100644 --- a/libraries/entities/src/DiffTraversal.h +++ b/libraries/entities/src/DiffTraversal.h @@ -36,6 +36,7 @@ public: uint64_t startTime { 0 }; float rootSizeScale { 1.0f }; int32_t lodLevelOffset { 0 }; + bool usesViewFrustum { true }; }; // Waypoint is an bookmark in a "path" of waypoints during a traversal. @@ -46,7 +47,6 @@ public: void getNextVisibleElementFirstTime(VisibleElement& next, const View& view); void getNextVisibleElementRepeat(VisibleElement& next, const View& view, uint64_t lastTime); void getNextVisibleElementDifferential(VisibleElement& next, const View& view, const View& lastView); - void getNextVisibleElementFullScene(VisibleElement& next, uint64_t lastTime); int8_t getNextIndex() const { return _nextIndex; } void initRootNextIndex() { _nextIndex = -1; } @@ -56,15 +56,16 @@ public: int8_t _nextIndex; }; - typedef enum { First, Repeat, Differential, FullScene } Type; + typedef enum { First, Repeat, Differential } Type; DiffTraversal(); - Type prepareNewTraversal(const ViewFrustum& viewFrustum, EntityTreeElementPointer root, int32_t lodLevelOffset, bool isFullScene); + Type prepareNewTraversal(const ViewFrustum& viewFrustum, EntityTreeElementPointer root, int32_t lodLevelOffset, bool usesViewFrustum); const ViewFrustum& getCurrentView() const { return _currentView.viewFrustum; } const ViewFrustum& getCompletedView() const { return _completedView.viewFrustum; } + bool doesCurrentUseViewFrustum() const { return _currentView.usesViewFrustum; } float getCurrentRootSizeScale() const { return _currentView.rootSizeScale; } float getCompletedRootSizeScale() const { return _completedView.rootSizeScale; } float getCurrentLODOffset() const { return _currentView.lodLevelOffset; } diff --git a/libraries/shared/src/ViewFrustum.cpp b/libraries/shared/src/ViewFrustum.cpp index 7e4f64686b..d84826cf01 100644 --- a/libraries/shared/src/ViewFrustum.cpp +++ b/libraries/shared/src/ViewFrustum.cpp @@ -403,12 +403,12 @@ bool ViewFrustum::isVerySimilar(const ViewFrustum& compareTo, bool debug) const bool result = testMatches(0, positionDistance, POSITION_SIMILAR_ENOUGH) && testMatches(0, angleOrientation, ORIENTATION_SIMILAR_ENOUGH) && - testMatches(compareTo._fieldOfView, _fieldOfView) && - testMatches(compareTo._aspectRatio, _aspectRatio) && - testMatches(compareTo._nearClip, _nearClip) && - testMatches(compareTo._farClip, _farClip) && - testMatches(compareTo._focalLength, _focalLength); - + testMatches(compareTo._centerSphereRadius, _centerSphereRadius) && + testMatches(compareTo._fieldOfView, _fieldOfView) && + testMatches(compareTo._aspectRatio, _aspectRatio) && + testMatches(compareTo._nearClip, _nearClip) && + testMatches(compareTo._farClip, _farClip) && + testMatches(compareTo._focalLength, _focalLength); if (!result && debug) { qCDebug(shared, "ViewFrustum::isVerySimilar()... result=%s\n", debug::valueOf(result));