mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-08 16:29:29 +02:00
293 lines
13 KiB
C++
293 lines
13 KiB
C++
//
|
|
// DiffTraversal.cpp
|
|
// assignment-client/src/entities
|
|
//
|
|
// Created by Andrew Meadows 2017.08.08
|
|
// Copyright 2017 High Fidelity, Inc.
|
|
//
|
|
// Distributed under the Apache License, Version 2.0.
|
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
//
|
|
|
|
#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 DiffTraversal::View& view) {
|
|
// NOTE: no need to set next.intersection in the "FirstTime" context
|
|
if (_nextIndex == -1) {
|
|
// 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) {
|
|
// 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) {
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
next.element.reset();
|
|
}
|
|
|
|
void DiffTraversal::Waypoint::getNextVisibleElementRepeat(
|
|
DiffTraversal::VisibleElement& next, const DiffTraversal::View& view, uint64_t lastTime) {
|
|
if (_nextIndex == -1) {
|
|
// root case is special
|
|
++_nextIndex;
|
|
EntityTreeElementPointer element = _weakElement.lock();
|
|
if (element->getLastChangedContent() > lastTime) {
|
|
next.element = element;
|
|
next.intersection = ViewFrustum::INTERSECT;
|
|
return;
|
|
}
|
|
}
|
|
if (_nextIndex < NUMBER_OF_CHILDREN) {
|
|
EntityTreeElementPointer element = _weakElement.lock();
|
|
if (element) {
|
|
// 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) {
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
next.element.reset();
|
|
next.intersection = ViewFrustum::OUTSIDE;
|
|
}
|
|
|
|
void DiffTraversal::Waypoint::getNextVisibleElementDifferential(DiffTraversal::VisibleElement& next,
|
|
const DiffTraversal::View& view, const DiffTraversal::View& lastView) {
|
|
if (_nextIndex == -1) {
|
|
// root case is special
|
|
++_nextIndex;
|
|
EntityTreeElementPointer element = _weakElement.lock();
|
|
next.element = element;
|
|
next.intersection = ViewFrustum::INTERSECT;
|
|
return;
|
|
}
|
|
if (_nextIndex < NUMBER_OF_CHILDREN) {
|
|
EntityTreeElementPointer element = _weakElement.lock();
|
|
if (element) {
|
|
// check for LOD truncation
|
|
int32_t level = element->getLevel() + view.lodLevelOffset;
|
|
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() > lastView.startTime) {
|
|
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
|
|
int32_t lastLevel = element->getLevel() + lastView.lodLevelOffset;
|
|
visibleLimit = boundaryDistanceForRenderLevel(lastLevel, lastView.rootSizeScale);
|
|
distance2 = glm::distance2(lastView.viewFrustum.getPosition(), element->getAACube().calcCenter());
|
|
if (distance2 >= visibleLimit * visibleLimit) {
|
|
next.element = nextElement;
|
|
// element's intersection with lastView was effectively OUTSIDE
|
|
next.intersection = ViewFrustum::OUTSIDE;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
next.element.reset();
|
|
next.intersection = ViewFrustum::OUTSIDE;
|
|
}
|
|
|
|
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 usesViewFrustum) {
|
|
assert(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
|
|
//
|
|
// 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)
|
|
//
|
|
// external code should update the _scanElementCallback after calling prepareNewTraversal
|
|
//
|
|
_currentView.usesViewFrustum = usesViewFrustum;
|
|
|
|
Type type;
|
|
// 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
|
|
_currentView.rootSizeScale = root->getScale() * MAX_VISIBILITY_DISTANCE_FOR_UNIT_ELEMENT;
|
|
_getNextVisibleElementCallback = [&](DiffTraversal::VisibleElement& next) {
|
|
_path.back().getNextVisibleElementFirstTime(next, _currentView);
|
|
};
|
|
} 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);
|
|
};
|
|
} else {
|
|
type = Type::Differential;
|
|
_currentView.viewFrustum = viewFrustum;
|
|
_currentView.lodLevelOffset = root->getLevel() + lodLevelOffset - 1; // -1 because true root has level=1
|
|
_currentView.rootSizeScale = root->getScale() * MAX_VISIBILITY_DISTANCE_FOR_UNIT_ELEMENT;
|
|
_getNextVisibleElementCallback = [&](DiffTraversal::VisibleElement& next) {
|
|
_path.back().getNextVisibleElementDifferential(next, _currentView, _completedView);
|
|
};
|
|
}
|
|
|
|
_path.clear();
|
|
_path.push_back(DiffTraversal::Waypoint(root));
|
|
// set root fork's index such that root element returned at getNextElement()
|
|
_path.back().initRootNextIndex();
|
|
|
|
_currentView.startTime = usecTimestampNow();
|
|
|
|
return type;
|
|
}
|
|
|
|
void DiffTraversal::getNextVisibleElement(DiffTraversal::VisibleElement& next) {
|
|
if (_path.empty()) {
|
|
next.element.reset();
|
|
next.intersection = ViewFrustum::OUTSIDE;
|
|
return;
|
|
}
|
|
_getNextVisibleElementCallback(next);
|
|
if (next.element) {
|
|
int8_t nextIndex = _path.back().getNextIndex();
|
|
if (nextIndex > 0) {
|
|
_path.push_back(DiffTraversal::Waypoint(next.element));
|
|
}
|
|
} else {
|
|
// we're done at this level
|
|
while (!next.element) {
|
|
// pop one level
|
|
_path.pop_back();
|
|
if (_path.empty()) {
|
|
// we've traversed the entire tree
|
|
_completedView = _currentView;
|
|
return;
|
|
}
|
|
// keep looking for next
|
|
_getNextVisibleElementCallback(next);
|
|
if (next.element) {
|
|
// we've descended one level so add it to the path
|
|
_path.push_back(DiffTraversal::Waypoint(next.element));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void DiffTraversal::setScanCallback(std::function<void (DiffTraversal::VisibleElement&)> cb) {
|
|
if (!cb) {
|
|
_scanElementCallback = [](DiffTraversal::VisibleElement& a){};
|
|
} else {
|
|
_scanElementCallback = cb;
|
|
}
|
|
}
|
|
|
|
// DEBUG method: delete later
|
|
std::ostream& operator<<(std::ostream& s, const DiffTraversal& traversal) {
|
|
for (size_t i = 0; i < traversal._path.size(); ++i) {
|
|
s << (int32_t)(traversal._path[i].getNextIndex());
|
|
if (i < traversal._path.size() - 1) {
|
|
s << ":";
|
|
}
|
|
}
|
|
return s;
|
|
}
|
|
|
|
void DiffTraversal::traverse(uint64_t timeBudget) {
|
|
uint64_t expiry = usecTimestampNow() + timeBudget;
|
|
DiffTraversal::VisibleElement next;
|
|
getNextVisibleElement(next);
|
|
while (next.element) {
|
|
if (next.element->hasContent()) {
|
|
_scanElementCallback(next);
|
|
}
|
|
if (usecTimestampNow() > expiry) {
|
|
break;
|
|
}
|
|
getNextVisibleElement(next);
|
|
}
|
|
}
|