mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 04:44:11 +02:00
add LOD culling in DiffTraversal
This commit is contained in:
parent
0758b60afc
commit
8b7c43f3b1
3 changed files with 109 additions and 63 deletions
|
@ -11,29 +11,39 @@
|
|||
|
||||
#include "DiffTraversal.h"
|
||||
|
||||
#include <OctreeUtils.h>
|
||||
|
||||
|
||||
DiffTraversal::Waypoint::Waypoint(EntityTreeElementPointer& element) : _nextIndex(0) {
|
||||
assert(element);
|
||||
_weakElement = element;
|
||||
}
|
||||
|
||||
void DiffTraversal::Waypoint::getNextVisibleElementFirstTime(DiffTraversal::VisibleElement& next, const ViewFrustum& view) {
|
||||
// TODO: add LOD culling of elements
|
||||
void DiffTraversal::Waypoint::getNextVisibleElementFirstTime(DiffTraversal::VisibleElement& next,
|
||||
const DiffTraversal::View& view) {
|
||||
// NOTE: no need to set next.intersection in the "FirstTime" context
|
||||
if (_nextIndex == -1) {
|
||||
// only get here for the root Waypoint at the very beginning of traversal
|
||||
// safe to assume this element intersects view
|
||||
// root case is special:
|
||||
// its intersection is always INTERSECT,
|
||||
// we never bother checking for LOD culling, and
|
||||
// we can skip it if the content hasn't changed
|
||||
++_nextIndex;
|
||||
next.element = _weakElement.lock();
|
||||
return;
|
||||
} else if (_nextIndex < NUMBER_OF_CHILDREN) {
|
||||
EntityTreeElementPointer element = _weakElement.lock();
|
||||
if (element) {
|
||||
while (_nextIndex < NUMBER_OF_CHILDREN) {
|
||||
EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex);
|
||||
++_nextIndex;
|
||||
if (nextElement && view.cubeIntersectsKeyhole(nextElement->getAACube())) {
|
||||
next.element = nextElement;
|
||||
return;
|
||||
// check for LOD truncation
|
||||
float visibleLimit = boundaryDistanceForRenderLevel(element->getLevel() + view.rootLevel, 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -41,15 +51,12 @@ void DiffTraversal::Waypoint::getNextVisibleElementFirstTime(DiffTraversal::Visi
|
|||
next.element.reset();
|
||||
}
|
||||
|
||||
void DiffTraversal::Waypoint::getNextVisibleElementRepeat(DiffTraversal::VisibleElement& next, const ViewFrustum& view, uint64_t lastTime) {
|
||||
// TODO: add LOD culling of elements
|
||||
void DiffTraversal::Waypoint::getNextVisibleElementRepeat(
|
||||
DiffTraversal::VisibleElement& next, const DiffTraversal::View& view, uint64_t lastTime) {
|
||||
if (_nextIndex == -1) {
|
||||
// only get here for the root Waypoint at the very beginning of traversal
|
||||
// safe to assume this element intersects view
|
||||
// root case is special
|
||||
++_nextIndex;
|
||||
EntityTreeElementPointer element = _weakElement.lock();
|
||||
// root case is special: its intersection is always INTERSECT
|
||||
// and we can skip it if the content hasn't changed
|
||||
if (element->getLastChangedContent() > lastTime) {
|
||||
next.element = element;
|
||||
next.intersection = ViewFrustum::INTERSECT;
|
||||
|
@ -59,15 +66,20 @@ void DiffTraversal::Waypoint::getNextVisibleElementRepeat(DiffTraversal::Visible
|
|||
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) {
|
||||
ViewFrustum::intersection intersection = view.calculateCubeKeyholeIntersection(nextElement->getAACube());
|
||||
if (intersection != ViewFrustum::OUTSIDE) {
|
||||
next.element = nextElement;
|
||||
next.intersection = intersection;
|
||||
return;
|
||||
// check for LOD truncation
|
||||
float visibleLimit = boundaryDistanceForRenderLevel(element->getLevel() - view.rootLevel + 1, 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -83,15 +95,11 @@ DiffTraversal::DiffTraversal() {
|
|||
}
|
||||
|
||||
void DiffTraversal::Waypoint::getNextVisibleElementDifferential(DiffTraversal::VisibleElement& next,
|
||||
const ViewFrustum& view, const ViewFrustum& lastView, uint64_t lastTime) {
|
||||
// TODO: add LOD culling of elements
|
||||
const DiffTraversal::View& view, const DiffTraversal::View& lastView, uint64_t lastTime) {
|
||||
if (_nextIndex == -1) {
|
||||
// only get here for the root Waypoint at the very beginning of traversal
|
||||
// safe to assume this element intersects view
|
||||
// root case is special
|
||||
++_nextIndex;
|
||||
EntityTreeElementPointer element = _weakElement.lock();
|
||||
// root case is special: its intersection is always INTERSECT
|
||||
// and we can skip it if the content hasn't changed
|
||||
if (element->getLastChangedContent() > lastTime) {
|
||||
next.element = element;
|
||||
next.intersection = ViewFrustum::INTERSECT;
|
||||
|
@ -101,18 +109,39 @@ void DiffTraversal::Waypoint::getNextVisibleElementDifferential(DiffTraversal::V
|
|||
if (_nextIndex < NUMBER_OF_CHILDREN) {
|
||||
EntityTreeElementPointer element = _weakElement.lock();
|
||||
if (element) {
|
||||
while (_nextIndex < NUMBER_OF_CHILDREN) {
|
||||
EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex);
|
||||
++_nextIndex;
|
||||
if (nextElement) {
|
||||
AACube cube = nextElement->getAACube();
|
||||
// NOTE: for differential case next.intersection is against the _completedView
|
||||
ViewFrustum::intersection intersection = lastView.calculateCubeKeyholeIntersection(cube);
|
||||
if ( lastView.calculateCubeKeyholeIntersection(cube) != ViewFrustum::OUTSIDE &&
|
||||
!(intersection == ViewFrustum::INSIDE && nextElement->getLastChanged() < lastTime)) {
|
||||
next.element = nextElement;
|
||||
next.intersection = intersection;
|
||||
return;
|
||||
// check for LOD truncation
|
||||
uint32_t level = element->getLevel() - view.rootLevel + 1;
|
||||
float visibleLimit = boundaryDistanceForRenderLevel(level, 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) {
|
||||
AACube cube = nextElement->getAACube();
|
||||
if ( view.viewFrustum.calculateCubeKeyholeIntersection(cube) != ViewFrustum::OUTSIDE) {
|
||||
ViewFrustum::intersection lastIntersection = lastView.viewFrustum.calculateCubeKeyholeIntersection(cube);
|
||||
if (!(lastIntersection == ViewFrustum::INSIDE && nextElement->getLastChanged() < lastTime)) {
|
||||
next.element = nextElement;
|
||||
// NOTE: for differential case next.intersection is against the lastView
|
||||
// because this helps the "external scan" optimize its culling
|
||||
next.intersection = lastIntersection;
|
||||
return;
|
||||
} else {
|
||||
// check for LOD truncation in the last traversal because
|
||||
// we may need to traverse this element after all if the lastView skipped it for LOD
|
||||
//
|
||||
// NOTE: the element's "level" must be invariant (the differntial algorithm doesn't work otherwise)
|
||||
// so we recycle the value computed higher up
|
||||
visibleLimit = boundaryDistanceForRenderLevel(level, lastView.rootSizeScale);
|
||||
distance2 = glm::distance2(lastView.viewFrustum.getPosition(), element->getAACube().calcCenter());
|
||||
if (distance2 >= visibleLimit * visibleLimit) {
|
||||
next.element = nextElement;
|
||||
next.intersection = lastIntersection;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -125,27 +154,27 @@ void DiffTraversal::Waypoint::getNextVisibleElementDifferential(DiffTraversal::V
|
|||
DiffTraversal::Type DiffTraversal::prepareNewTraversal(const ViewFrustum& viewFrustum, EntityTreeElementPointer root) {
|
||||
// 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
|
||||
// (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
|
||||
//
|
||||
// For each traversal type we define assign the appropriate _getNextVisibleElementCallback
|
||||
// for each traversal type we assign the appropriate _getNextVisibleElementCallback
|
||||
//
|
||||
// _getNextVisibleElementCallback = identifies elements that need to be traversed,
|
||||
// updates VisibleElement ref argument with pointer-to-element and view-intersection
|
||||
// (INSIDE, INTERSECT, or OUTSIDE)
|
||||
// _getNextVisibleElementCallback = identifies elements that need to be traversed,
|
||||
// updates VisibleElement ref argument with pointer-to-element and view-intersection
|
||||
// (INSIDE, INTERSECT, or OUTSIDE)
|
||||
//
|
||||
// External code should update the _scanElementCallback after calling prepareNewTraversal
|
||||
// external code should update the _scanElementCallback after calling prepareNewTraversal
|
||||
//
|
||||
|
||||
Type type = Type::First;
|
||||
if (_startOfCompletedTraversal == 0) {
|
||||
// first time
|
||||
_currentView = viewFrustum;
|
||||
_currentView.viewFrustum = viewFrustum;
|
||||
_getNextVisibleElementCallback = [&](DiffTraversal::VisibleElement& next) {
|
||||
_path.back().getNextVisibleElementFirstTime(next, _currentView);
|
||||
};
|
||||
} else if (_currentView.isVerySimilar(viewFrustum)) {
|
||||
} else if (_currentView.viewFrustum.isVerySimilar(viewFrustum)) {
|
||||
// again
|
||||
_getNextVisibleElementCallback = [&](DiffTraversal::VisibleElement& next) {
|
||||
_path.back().getNextVisibleElementRepeat(next, _currentView, _startOfCompletedTraversal);
|
||||
|
@ -153,7 +182,7 @@ DiffTraversal::Type DiffTraversal::prepareNewTraversal(const ViewFrustum& viewFr
|
|||
type = Type::Repeat;
|
||||
} else {
|
||||
// differential
|
||||
_currentView = viewFrustum;
|
||||
_currentView.viewFrustum = viewFrustum;
|
||||
_getNextVisibleElementCallback = [&](DiffTraversal::VisibleElement& next) {
|
||||
_path.back().getNextVisibleElementDifferential(next, _currentView, _completedView, _startOfCompletedTraversal);
|
||||
};
|
||||
|
@ -165,6 +194,11 @@ DiffTraversal::Type DiffTraversal::prepareNewTraversal(const ViewFrustum& viewFr
|
|||
_path.push_back(DiffTraversal::Waypoint(root));
|
||||
// set root fork's index such that root element returned at getNextElement()
|
||||
_path.back().initRootNextIndex();
|
||||
|
||||
// cache LOD parameters in the _currentView
|
||||
_currentView.rootLevel = root->getLevel();
|
||||
_currentView.rootSizeScale = root->getScale() * MAX_VISIBILITY_DISTANCE_FOR_UNIT_ELEMENT;
|
||||
|
||||
_startOfCurrentTraversal = usecTimestampNow();
|
||||
|
||||
return type;
|
||||
|
@ -180,7 +214,6 @@ void DiffTraversal::getNextVisibleElement(DiffTraversal::VisibleElement& next) {
|
|||
if (next.element) {
|
||||
int8_t nextIndex = _path.back().getNextIndex();
|
||||
if (nextIndex > 0) {
|
||||
// next.element needs to be added to the path
|
||||
_path.push_back(DiffTraversal::Waypoint(next.element));
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -29,14 +29,22 @@ public:
|
|||
ViewFrustum::intersection intersection { ViewFrustum::OUTSIDE };
|
||||
};
|
||||
|
||||
// View is a struct with a ViewFrustum and LOD parameters
|
||||
class View {
|
||||
public:
|
||||
ViewFrustum viewFrustum;
|
||||
float rootSizeScale { 1.0f };
|
||||
float rootLevel { 0.0f };
|
||||
};
|
||||
|
||||
// Waypoint is an bookmark in a "path" of waypoints during a traversal.
|
||||
class Waypoint {
|
||||
public:
|
||||
Waypoint(EntityTreeElementPointer& element);
|
||||
|
||||
void getNextVisibleElementFirstTime(VisibleElement& next, const ViewFrustum& view);
|
||||
void getNextVisibleElementRepeat(VisibleElement& next, const ViewFrustum& view, uint64_t lastTime);
|
||||
void getNextVisibleElementDifferential(VisibleElement& next, const ViewFrustum& view, const ViewFrustum& lastView, uint64_t lastTime);
|
||||
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, uint64_t lastTime);
|
||||
|
||||
int8_t getNextIndex() const { return _nextIndex; }
|
||||
void initRootNextIndex() { _nextIndex = -1; }
|
||||
|
@ -52,8 +60,8 @@ public:
|
|||
|
||||
DiffTraversal::Type prepareNewTraversal(const ViewFrustum& viewFrustum, EntityTreeElementPointer root);
|
||||
|
||||
const ViewFrustum& getCurrentView() const { return _currentView; }
|
||||
const ViewFrustum& getCompletedView() const { return _completedView; }
|
||||
const ViewFrustum& getCurrentView() const { return _currentView.viewFrustum; }
|
||||
const ViewFrustum& getCompletedView() const { return _completedView.viewFrustum; }
|
||||
|
||||
uint64_t getStartOfCompletedTraversal() const { return _startOfCompletedTraversal; }
|
||||
bool finished() const { return _path.empty(); }
|
||||
|
@ -66,13 +74,17 @@ public:
|
|||
private:
|
||||
void getNextVisibleElement(VisibleElement& next);
|
||||
|
||||
ViewFrustum _currentView;
|
||||
ViewFrustum _completedView;
|
||||
View _currentView;
|
||||
View _completedView;
|
||||
std::vector<Waypoint> _path;
|
||||
std::function<void (VisibleElement&)> _getNextVisibleElementCallback { nullptr };
|
||||
std::function<void (VisibleElement&)> _scanElementCallback { [](VisibleElement& e){} };
|
||||
uint64_t _startOfCompletedTraversal { 0 };
|
||||
uint64_t _startOfCurrentTraversal { 0 };
|
||||
|
||||
// LOD stuff
|
||||
float _rootSizeScale { 1.0f };
|
||||
uint32_t _rootLevel { 0 };
|
||||
};
|
||||
|
||||
#endif // hifi_EntityPriorityQueue_h
|
||||
|
|
|
@ -21,7 +21,8 @@ const int TREE_SCALE = 32768; // ~20 miles.. This is the number of meters of the
|
|||
const int HALF_TREE_SCALE = TREE_SCALE / 2;
|
||||
|
||||
// This controls the LOD. Larger number will make smaller voxels visible at greater distance.
|
||||
const float DEFAULT_OCTREE_SIZE_SCALE = TREE_SCALE * 400.0f;
|
||||
const float MAX_VISIBILITY_DISTANCE_FOR_UNIT_ELEMENT = 400.0f; // max distance where a 1x1x1 cube is visible for 20:20 vision
|
||||
const float DEFAULT_OCTREE_SIZE_SCALE = TREE_SCALE * MAX_VISIBILITY_DISTANCE_FOR_UNIT_ELEMENT;
|
||||
|
||||
// Since entities like models live inside of octree cells, and they themselves can have very small mesh parts,
|
||||
// we want to have some constant that controls have big a mesh part must be to render even if the octree cell itself
|
||||
|
|
Loading…
Reference in a new issue