edited entities are not repeatedly sent if out of view, handles cases where usesViewFrustum changes

This commit is contained in:
SamGondelman 2017-09-06 13:42:32 -07:00 committed by Andrew Meadows
parent 7938e301e7
commit defed80be7
7 changed files with 203 additions and 182 deletions

View file

@ -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) {

View file

@ -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>, PrioritizedEntity::Compare >;

View file

@ -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<EntityTreeElement>(_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<EntityTreeElement>(_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<EntityTreeElement>(_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());

View file

@ -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;

View file

@ -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);

View file

@ -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; }

View file

@ -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));