EntityServer traversal aware of all ViewFrustums

This commit is contained in:
Clement 2018-04-13 18:01:54 -07:00
parent 12f7dd93dc
commit 49fad3d868
19 changed files with 322 additions and 296 deletions

View file

@ -15,7 +15,7 @@ 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) {
void ConicalViewFrustum::set(const ViewFrustum& viewFrustum) {
// The ConicalView has two parts: a central sphere (same as ViewFrustum) and a circular cone that bounds the frustum part.
// Why? Because approximate intersection tests are much faster to compute for a cone than for a frustum.
_position = viewFrustum.getPosition();
@ -31,7 +31,7 @@ void ConicalView::set(const ViewFrustum& viewFrustum) {
_radius = viewFrustum.getCenterRadius();
}
float ConicalView::computePriority(const AACube& cube) const {
float ConicalViewFrustum::computePriority(const AACube& cube) const {
glm::vec3 p = cube.calcCenter() - _position; // position of bounding sphere in view-frame
float d = glm::length(p); // distance to center of bounding sphere
float r = 0.5f * cube.getScale(); // radius of bounding sphere
@ -51,3 +51,22 @@ float ConicalView::computePriority(const AACube& cube) const {
}
return PrioritizedEntity::DO_NOT_SEND;
}
void ConicalView::set(const DiffTraversal::View& view) {
auto size = view.viewFrustums.size();
_conicalViewFrustums.resize(size);
for (size_t i = 0; i < size; ++i) {
_conicalViewFrustums[i].set(view.viewFrustums[i]);
}
}
float ConicalView::computePriority(const AACube& cube) const {
float priority = PrioritizedEntity::DO_NOT_SEND;
for (const auto& view : _conicalViewFrustums) {
priority = std::max(priority, view.computePriority(cube));
}
return priority;
}

View file

@ -15,16 +15,17 @@
#include <queue>
#include <AACube.h>
#include <DiffTraversal.h>
#include <EntityTreeElement.h>
const float SQRT_TWO_OVER_TWO = 0.7071067811865f;
const float DEFAULT_VIEW_RADIUS = 10.0f;
// ConicalView is an approximation of a ViewFrustum for fast calculation of sort priority.
class ConicalView {
class ConicalViewFrustum {
public:
ConicalView() {}
ConicalView(const ViewFrustum& viewFrustum) { set(viewFrustum); }
ConicalViewFrustum() {}
ConicalViewFrustum(const ViewFrustum& viewFrustum) { set(viewFrustum); }
void set(const ViewFrustum& viewFrustum);
float computePriority(const AACube& cube) const;
private:
@ -35,6 +36,15 @@ private:
float _radius { DEFAULT_VIEW_RADIUS };
};
class ConicalView {
public:
ConicalView() {}
void set(const DiffTraversal::View& view);
float computePriority(const AACube& cube) const;
private:
std::vector<ConicalViewFrustum> _conicalViewFrustums;
};
// PrioritizedEntity is a placeholder in a sorted queue.
class PrioritizedEntity {
public:

View file

@ -103,11 +103,25 @@ void EntityTreeSendThread::preDistributionProcessing() {
void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData,
bool viewFrustumChanged, bool isFullScene) {
if (viewFrustumChanged || _traversal.finished()) {
ViewFrustum viewFrustum;
nodeData->copyCurrentViewFrustum(viewFrustum);
EntityTreeElementPointer root = std::dynamic_pointer_cast<EntityTreeElement>(_myServer->getOctree()->getRoot());
DiffTraversal::View newView;
ViewFrustum viewFrustum;
if (nodeData->hasMainViewFrustum()) {
nodeData->copyCurrentMainViewFrustum(viewFrustum);
newView.viewFrustums.push_back(viewFrustum);
}
if (nodeData->hasSecondaryViewFrustum()) {
nodeData->copyCurrentSecondaryViewFrustum(viewFrustum);
newView.viewFrustums.push_back(viewFrustum);
}
int32_t lodLevelOffset = nodeData->getBoundaryLevelAdjust() + (viewFrustumChanged ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST);
startNewTraversal(viewFrustum, root, lodLevelOffset, nodeData->getUsesFrustum());
newView.lodScaleFactor = powf(2.0f, lodLevelOffset);
startNewTraversal(newView, root);
// 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
@ -116,8 +130,6 @@ void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, O
_sendQueue.swap(prevSendQueue);
_entitiesInQueue.clear();
// Re-add elements from previous traversal if they still need to be sent
float lodScaleFactor = _traversal.getCurrentLODScaleFactor();
glm::vec3 viewPosition = _traversal.getCurrentView().getPosition();
while (!prevSendQueue.empty()) {
EntityItemPointer entity = prevSendQueue.top().getEntity();
bool forceRemove = prevSendQueue.top().shouldForceRemove();
@ -127,12 +139,10 @@ void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, O
bool success = false;
AACube cube = entity->getQueryAACube(success);
if (success) {
if (_traversal.getCurrentView().cubeIntersectsKeyhole(cube)) {
if (_traversal.getCurrentView().intersects(cube)) {
float priority = _conicalView.computePriority(cube);
if (priority != PrioritizedEntity::DO_NOT_SEND) {
float distance = glm::distance(cube.calcCenter(), viewPosition) + MIN_VISIBLE_DISTANCE;
float angularDiameter = cube.getScale() / distance;
if (angularDiameter > MIN_ENTITY_ANGULAR_DIAMETER * lodScaleFactor) {
if (_traversal.getCurrentView().isBigEnough(cube)) {
_sendQueue.push(PrioritizedEntity(entity, priority));
_entitiesInQueue.insert(entity.get());
}
@ -215,10 +225,9 @@ bool EntityTreeSendThread::addDescendantsToExtraFlaggedEntities(const QUuid& fil
return hasNewChild || hasNewDescendants;
}
void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTreeElementPointer root, int32_t lodLevelOffset,
bool usesViewFrustum) {
void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, EntityTreeElementPointer root) {
DiffTraversal::Type type = _traversal.prepareNewTraversal(view, root, lodLevelOffset, usesViewFrustum);
DiffTraversal::Type type = _traversal.prepareNewTraversal(view, root);
// there are three types of traversal:
//
// (1) FirstTime = at login --> find everything in view
@ -236,11 +245,9 @@ void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTree
case DiffTraversal::First:
// When we get to a First traversal, clear the _knownState
_knownState.clear();
if (usesViewFrustum) {
float lodScaleFactor = _traversal.getCurrentLODScaleFactor();
glm::vec3 viewPosition = _traversal.getCurrentView().getPosition();
_traversal.setScanCallback([=](DiffTraversal::VisibleElement& next) {
next.element->forEachEntity([=](EntityItemPointer entity) {
if (view.usesViewFrustums()) {
_traversal.setScanCallback([this](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;
@ -248,14 +255,12 @@ void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTree
bool success = false;
AACube cube = entity->getQueryAACube(success);
if (success) {
if (_traversal.getCurrentView().cubeIntersectsKeyhole(cube)) {
if (_traversal.getCurrentView().intersects(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.
float distance = glm::distance(cube.calcCenter(), viewPosition) + MIN_VISIBLE_DISTANCE;
float angularDiameter = cube.getScale() / distance;
if (angularDiameter > MIN_ENTITY_ANGULAR_DIAMETER * lodScaleFactor) {
if (_traversal.getCurrentView().isBigEnough(cube)) {
float priority = _conicalView.computePriority(cube);
_sendQueue.push(PrioritizedEntity(entity, priority));
_entitiesInQueue.insert(entity.get());
@ -269,7 +274,7 @@ void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTree
});
} else {
_traversal.setScanCallback([this](DiffTraversal::VisibleElement& next) {
next.element->forEachEntity([this](EntityItemPointer entity) {
next.element->forEachEntity([&](EntityItemPointer entity) {
// Bail early if we've already checked this entity this frame
if (_entitiesInQueue.find(entity.get()) != _entitiesInQueue.end()) {
return;
@ -281,13 +286,11 @@ void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTree
}
break;
case DiffTraversal::Repeat:
if (usesViewFrustum) {
float lodScaleFactor = _traversal.getCurrentLODScaleFactor();
glm::vec3 viewPosition = _traversal.getCurrentView().getPosition();
_traversal.setScanCallback([=](DiffTraversal::VisibleElement& next) {
if (view.usesViewFrustums()) {
_traversal.setScanCallback([this](DiffTraversal::VisibleElement& next) {
uint64_t startOfCompletedTraversal = _traversal.getStartOfCompletedTraversal();
if (next.element->getLastChangedContent() > startOfCompletedTraversal) {
next.element->forEachEntity([=](EntityItemPointer entity) {
next.element->forEachEntity([&](EntityItemPointer entity) {
// Bail early if we've already checked this entity this frame
if (_entitiesInQueue.find(entity.get()) != _entitiesInQueue.end()) {
return;
@ -297,11 +300,10 @@ void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTree
bool success = false;
AACube cube = entity->getQueryAACube(success);
if (success) {
if (next.intersection == ViewFrustum::INSIDE || _traversal.getCurrentView().cubeIntersectsKeyhole(cube)) {
if (next.intersection == ViewFrustum::INSIDE ||
_traversal.getCurrentView().intersects(cube)) {
// See the DiffTraversal::First case for an explanation of the "entity is too small" check
float distance = glm::distance(cube.calcCenter(), viewPosition) + MIN_VISIBLE_DISTANCE;
float angularDiameter = cube.getScale() / distance;
if (angularDiameter > MIN_ENTITY_ANGULAR_DIAMETER * lodScaleFactor) {
if (_traversal.getCurrentView().isBigEnough(cube)) {
float priority = _conicalView.computePriority(cube);
_sendQueue.push(PrioritizedEntity(entity, priority));
_entitiesInQueue.insert(entity.get());
@ -325,7 +327,7 @@ void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTree
_traversal.setScanCallback([this](DiffTraversal::VisibleElement& next) {
uint64_t startOfCompletedTraversal = _traversal.getStartOfCompletedTraversal();
if (next.element->getLastChangedContent() > startOfCompletedTraversal) {
next.element->forEachEntity([this](EntityItemPointer entity) {
next.element->forEachEntity([&](EntityItemPointer entity) {
// Bail early if we've already checked this entity this frame
if (_entitiesInQueue.find(entity.get()) != _entitiesInQueue.end()) {
return;
@ -343,13 +345,9 @@ void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTree
}
break;
case DiffTraversal::Differential:
assert(usesViewFrustum);
float lodScaleFactor = _traversal.getCurrentLODScaleFactor();
glm::vec3 viewPosition = _traversal.getCurrentView().getPosition();
float completedLODScaleFactor = _traversal.getCompletedLODScaleFactor();
glm::vec3 completedViewPosition = _traversal.getCompletedView().getPosition();
_traversal.setScanCallback([=] (DiffTraversal::VisibleElement& next) {
next.element->forEachEntity([=](EntityItemPointer entity) {
assert(view.usesViewFrustums());
_traversal.setScanCallback([this] (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;
@ -359,25 +357,19 @@ void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTree
bool success = false;
AACube cube = entity->getQueryAACube(success);
if (success) {
if (_traversal.getCurrentView().cubeIntersectsKeyhole(cube)) {
if (_traversal.getCurrentView().intersects(cube)) {
// See the DiffTraversal::First case for an explanation of the "entity is too small" check
float distance = glm::distance(cube.calcCenter(), viewPosition) + MIN_VISIBLE_DISTANCE;
float angularDiameter = cube.getScale() / distance;
if (angularDiameter > MIN_ENTITY_ANGULAR_DIAMETER * lodScaleFactor) {
if (!_traversal.getCompletedView().cubeIntersectsKeyhole(cube)) {
if (_traversal.getCurrentView().isBigEnough(cube)) {
if (!_traversal.getCompletedView().intersects(cube)) {
float priority = _conicalView.computePriority(cube);
_sendQueue.push(PrioritizedEntity(entity, priority));
_entitiesInQueue.insert(entity.get());
} else {
} else if (!_traversal.getCompletedView().isBigEnough(cube)) {
// If this entity was skipped last time because it was too small, we still need to send it
distance = glm::distance(cube.calcCenter(), completedViewPosition) + MIN_VISIBLE_DISTANCE;
angularDiameter = cube.getScale() / distance;
if (angularDiameter <= MIN_ENTITY_ANGULAR_DIAMETER * completedLODScaleFactor) {
// this object was skipped in last completed traversal
float priority = _conicalView.computePriority(cube);
_sendQueue.push(PrioritizedEntity(entity, priority));
_entitiesInQueue.insert(entity.get());
}
// this object was skipped in last completed traversal
float priority = _conicalView.computePriority(cube);
_sendQueue.push(PrioritizedEntity(entity, priority));
_entitiesInQueue.insert(entity.get());
}
}
}
@ -506,7 +498,7 @@ void EntityTreeSendThread::editingEntityPointer(const EntityItemPointer& entity)
AACube cube = entity->getQueryAACube(success);
if (success) {
// 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)) {
if (_traversal.doesCurrentUseViewFrustum() && !_traversal.getCurrentView().intersects(cube)) {
_sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::FORCE_REMOVE, true));
_entitiesInQueue.insert(entity.get());
}

View file

@ -41,8 +41,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 usesViewFrustum);
void startNewTraversal(const DiffTraversal::View& viewFrustum, EntityTreeElementPointer root);
bool traverseTreeAndBuildNextPacketPayload(EncodeBitstreamParams& params, const QJsonObject& jsonFilters) override;
void preDistributionProcessing() override;

View file

@ -23,14 +23,7 @@ void OctreeHeadlessViewer::queryOctree() {
char serverType = getMyNodeType();
PacketType packetType = getMyQueryMessageType();
_octreeQuery.setCameraPosition(_viewFrustum.getPosition());
_octreeQuery.setCameraOrientation(_viewFrustum.getOrientation());
_octreeQuery.setCameraFov(_viewFrustum.getFieldOfView());
_octreeQuery.setCameraAspectRatio(_viewFrustum.getAspectRatio());
_octreeQuery.setCameraNearClip(_viewFrustum.getNearClip());
_octreeQuery.setCameraFarClip(_viewFrustum.getFarClip());
_octreeQuery.setCameraEyeOffsetPosition(glm::vec3());
_octreeQuery.setCameraCenterRadius(_viewFrustum.getCenterRadius());
_octreeQuery.setMainViewFrustum(_viewFrustum);
_octreeQuery.setOctreeSizeScale(_voxelSizeScale);
_octreeQuery.setBoundaryLevelAdjust(_boundaryLevelAdjust);

View file

@ -330,8 +330,9 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode*
} else {
// we aren't forcing a full scene, check if something else suggests we should
isFullScene = nodeData->haveJSONParametersChanged() ||
(nodeData->getUsesFrustum()
&& ((!viewFrustumChanged && nodeData->getViewFrustumJustStoppedChanging()) || nodeData->hasLodChanged()));
(nodeData->hasMainViewFrustum() &&
(nodeData->getViewFrustumJustStoppedChanging() ||
nodeData->hasLodChanged()));
}
if (nodeData->isPacketWaiting()) {
@ -445,7 +446,6 @@ void OctreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, Octre
params.trackSend = [this](const QUuid& dataID, quint64 dataEdited) {
_myServer->trackSend(dataID, dataEdited, _nodeUuid);
};
nodeData->copyCurrentViewFrustum(params.viewFrustum);
bool somethingToSend = true; // assume we have something
bool hadSomething = hasSomethingToSend(nodeData);

View file

@ -294,7 +294,6 @@ void EntityScriptServer::run() {
queryJSONParameters[EntityJSONQueryProperties::FLAGS_PROPERTY] = queryFlags;
// setup the JSON parameters so that OctreeQuery does not use a frustum and uses our JSON filter
_entityViewer.getOctreeQuery().setUsesFrustum(false);
_entityViewer.getOctreeQuery().setJSONParameters(queryJSONParameters);
entityScriptingInterface->setEntityTree(_entityViewer.getTree());

View file

@ -5791,14 +5791,8 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType) {
ViewFrustum viewFrustum;
copyViewFrustum(viewFrustum);
_octreeQuery.setCameraPosition(viewFrustum.getPosition());
_octreeQuery.setCameraOrientation(viewFrustum.getOrientation());
_octreeQuery.setCameraFov(viewFrustum.getFieldOfView());
_octreeQuery.setCameraAspectRatio(viewFrustum.getAspectRatio());
_octreeQuery.setCameraNearClip(viewFrustum.getNearClip());
_octreeQuery.setCameraFarClip(viewFrustum.getFarClip());
_octreeQuery.setCameraEyeOffsetPosition(glm::vec3());
_octreeQuery.setCameraCenterRadius(viewFrustum.getCenterRadius());
_octreeQuery.setMainViewFrustum(viewFrustum);
auto lodManager = DependencyManager::get<LODManager>();
_octreeQuery.setOctreeSizeScale(lodManager->getOctreeSizeScale());
_octreeQuery.setBoundaryLevelAdjust(lodManager->getBoundaryLevelAdjust());

View file

@ -37,15 +37,14 @@ void DiffTraversal::Waypoint::getNextVisibleElementFirstTime(DiffTraversal::Visi
EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex);
++_nextIndex;
if (nextElement) {
if (!view.usesViewFrustum) {
const auto& cube = nextElement->getAACube();
if (!view.usesViewFrustums()) {
// No LOD truncation if we aren't using the view frustum
next.element = nextElement;
return;
} else if (view.viewFrustum.cubeIntersectsKeyhole(nextElement->getAACube())) {
} else if (view.intersects(cube)) {
// check for LOD truncation
float distance = glm::distance(view.viewFrustum.getPosition(), nextElement->getAACube().calcCenter()) + MIN_VISIBLE_DISTANCE;
float angularDiameter = nextElement->getAACube().getScale() / distance;
if (angularDiameter > MIN_ELEMENT_ANGULAR_DIAMETER * view.lodScaleFactor) {
if (view.isBigEnough(cube, MIN_ELEMENT_ANGULAR_DIAMETER)) {
next.element = nextElement;
return;
}
@ -76,17 +75,16 @@ void DiffTraversal::Waypoint::getNextVisibleElementRepeat(
EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex);
++_nextIndex;
if (nextElement && nextElement->getLastChanged() > lastTime) {
if (!view.usesViewFrustum) {
if (!view.usesViewFrustums()) {
// No LOD truncation if we aren't using the view frustum
next.element = nextElement;
next.intersection = ViewFrustum::INSIDE;
return;
} else {
// check for LOD truncation
float distance = glm::distance(view.viewFrustum.getPosition(), nextElement->getAACube().calcCenter()) + MIN_VISIBLE_DISTANCE;
float angularDiameter = nextElement->getAACube().getScale() / distance;
if (angularDiameter > MIN_ELEMENT_ANGULAR_DIAMETER * view.lodScaleFactor) {
ViewFrustum::intersection intersection = view.viewFrustum.calculateCubeKeyholeIntersection(nextElement->getAACube());
const auto& cube = nextElement->getAACube();
if (view.isBigEnough(cube, MIN_ELEMENT_ANGULAR_DIAMETER)) {
ViewFrustum::intersection intersection = view.calculateIntersection(cube);
if (intersection != ViewFrustum::OUTSIDE) {
next.element = nextElement;
next.intersection = intersection;
@ -118,14 +116,13 @@ void DiffTraversal::Waypoint::getNextVisibleElementDifferential(DiffTraversal::V
EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex);
++_nextIndex;
if (nextElement) {
AACube cube = nextElement->getAACube();
// check for LOD truncation
float distance = glm::distance(view.viewFrustum.getPosition(), cube.calcCenter()) + MIN_VISIBLE_DISTANCE;
float angularDiameter = cube.getScale() / distance;
if (angularDiameter > MIN_ELEMENT_ANGULAR_DIAMETER * view.lodScaleFactor) {
if (view.viewFrustum.calculateCubeKeyholeIntersection(cube) != ViewFrustum::OUTSIDE) {
const auto& cube = nextElement->getAACube();
if (view.isBigEnough(cube, MIN_ELEMENT_ANGULAR_DIAMETER)) {
ViewFrustum::intersection intersection = view.calculateIntersection(cube);
if (intersection != ViewFrustum::OUTSIDE) {
next.element = nextElement;
next.intersection = ViewFrustum::OUTSIDE;
next.intersection = intersection;
return;
}
}
@ -137,13 +134,83 @@ void DiffTraversal::Waypoint::getNextVisibleElementDifferential(DiffTraversal::V
next.intersection = ViewFrustum::OUTSIDE;
}
bool DiffTraversal::View::isBigEnough(const AACube& cube, float minDiameter) const {
if (viewFrustums.empty()) {
// Everything is big enough when not using view frustums
return true;
}
for (const auto& viewFrustum : viewFrustums) {
if (isAngularSizeBigEnough(viewFrustum.getPosition(), cube, lodScaleFactor, minDiameter)) {
return true;
}
}
return false;
}
bool DiffTraversal::View::intersects(const AACube& cube) const {
if (viewFrustums.empty()) {
// Everything intersects when not using view frustums
return true;
}
for (const auto& viewFrustum : viewFrustums) {
if (viewFrustum.cubeIntersectsKeyhole(cube)) {
return true;
}
}
return false;
}
ViewFrustum::intersection DiffTraversal::View::calculateIntersection(const AACube& cube) const {
if (viewFrustums.empty()) {
// Everything is inside when not using view frustums
return ViewFrustum::INSIDE;
}
ViewFrustum::intersection intersection = ViewFrustum::OUTSIDE;
for (const auto& viewFrustum : viewFrustums) {
switch (viewFrustum.calculateCubeKeyholeIntersection(cube)) {
case ViewFrustum::INSIDE:
return ViewFrustum::INSIDE;
case ViewFrustum::INTERSECT:
intersection = ViewFrustum::INTERSECT;
break;
default:
// DO NOTHING
break;
}
}
return intersection;
}
bool DiffTraversal::View::usesViewFrustums() const {
return !viewFrustums.empty();
}
bool DiffTraversal::View::isVerySimilar(const View& view) const {
auto size = view.viewFrustums.size();
if (view.lodScaleFactor != lodScaleFactor ||
viewFrustums.size() != size) {
return false;
}
for (size_t i = 0; i < size; ++i) {
if (!viewFrustums[i].isVerySimilar(view.viewFrustums[i])) {
return false;
}
}
return true;
}
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) {
DiffTraversal::Type DiffTraversal::prepareNewTraversal(const DiffTraversal::View& view, EntityTreeElementPointer root) {
assert(root);
// there are three types of traversal:
//
@ -155,33 +222,29 @@ DiffTraversal::Type DiffTraversal::prepareNewTraversal(const ViewFrustum& viewFr
//
// _getNextVisibleElementCallback = identifies elements that need to be traversed,
// updates VisibleElement ref argument with pointer-to-element and view-intersection
// (INSIDE, INTERSECT, or OUTSIDE)
// (INSIDE, INTERSECtT, or OUTSIDE)
//
// external code should update the _scanElementCallback after calling prepareNewTraversal
//
_currentView.usesViewFrustum = usesViewFrustum;
float lodScaleFactor = powf(2.0f, lodLevelOffset);
Type type;
// If usesViewFrustum changes, treat it as a First traversal
if (_completedView.startTime == 0 || _currentView.usesViewFrustum != _completedView.usesViewFrustum) {
if (_completedView.startTime == 0 || _currentView.usesViewFrustums() != _completedView.usesViewFrustums()) {
type = Type::First;
_currentView.viewFrustum = viewFrustum;
_currentView.lodScaleFactor = lodScaleFactor;
_currentView.viewFrustums = std::move(view.viewFrustums);
_currentView.lodScaleFactor = view.lodScaleFactor;
_getNextVisibleElementCallback = [this](DiffTraversal::VisibleElement& next) {
_path.back().getNextVisibleElementFirstTime(next, _currentView);
};
} else if (!_currentView.usesViewFrustum ||
(_completedView.viewFrustum.isVerySimilar(viewFrustum) &&
lodScaleFactor == _completedView.lodScaleFactor)) {
} else if (!_currentView.usesViewFrustums() || _completedView.isVerySimilar(view)) {
type = Type::Repeat;
_getNextVisibleElementCallback = [this](DiffTraversal::VisibleElement& next) {
_path.back().getNextVisibleElementRepeat(next, _completedView, _completedView.startTime);
};
} else {
type = Type::Differential;
_currentView.viewFrustum = viewFrustum;
_currentView.lodScaleFactor = lodScaleFactor;
_currentView.viewFrustums = std::move(view.viewFrustums);
_currentView.lodScaleFactor = view.lodScaleFactor;
_getNextVisibleElementCallback = [this](DiffTraversal::VisibleElement& next) {
_path.back().getNextVisibleElementDifferential(next, _currentView, _completedView);
};

View file

@ -30,10 +30,15 @@ public:
// View is a struct with a ViewFrustum and LOD parameters
class View {
public:
ViewFrustum viewFrustum;
bool isBigEnough(const AACube& cube, float minDiameter = MIN_ENTITY_ANGULAR_DIAMETER) const;
bool intersects(const AACube& cube) const;
bool usesViewFrustums() const;
bool isVerySimilar(const View& view) const;
ViewFrustum::intersection calculateIntersection(const AACube& cube) const;
std::vector<ViewFrustum> viewFrustums;
uint64_t startTime { 0 };
float lodScaleFactor { 1.0f };
bool usesViewFrustum { true };
};
// Waypoint is an bookmark in a "path" of waypoints during a traversal.
@ -57,15 +62,12 @@ public:
DiffTraversal();
Type prepareNewTraversal(const ViewFrustum& viewFrustum, EntityTreeElementPointer root, int32_t lodLevelOffset,
bool usesViewFrustum);
Type prepareNewTraversal(const DiffTraversal::View& view, EntityTreeElementPointer root);
const ViewFrustum& getCurrentView() const { return _currentView.viewFrustum; }
const ViewFrustum& getCompletedView() const { return _completedView.viewFrustum; }
const View& getCurrentView() const { return _currentView; }
const View& getCompletedView() const { return _completedView; }
bool doesCurrentUseViewFrustum() const { return _currentView.usesViewFrustum; }
float getCurrentLODScaleFactor() const { return _currentView.lodScaleFactor; }
float getCompletedLODScaleFactor() const { return _completedView.lodScaleFactor; }
bool doesCurrentUseViewFrustum() const { return _currentView.usesViewFrustums(); }
uint64_t getStartOfCompletedTraversal() const { return _completedView.startTime; }
bool finished() const { return _path.empty(); }

View file

@ -59,7 +59,6 @@ const int LOW_RES_MOVING_ADJUST = 1;
class EncodeBitstreamParams {
public:
ViewFrustum viewFrustum;
bool includeExistsBits;
NodeData* nodeData;

View file

@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "OctreeQuery.h"
#include <random>
#include <QtCore/QJsonDocument>
@ -16,23 +18,11 @@
#include <GLMHelpers.h>
#include <udt/PacketHeaders.h>
#include "OctreeConstants.h"
#include "OctreeQuery.h"
const float DEFAULT_FOV = 45.0f; // degrees
const float DEFAULT_ASPECT_RATIO = 1.0f;
const float DEFAULT_NEAR_CLIP = 0.1f;
const float DEFAULT_FAR_CLIP = 3.0f;
OctreeQuery::OctreeQuery(bool randomizeConnectionID) :
_cameraFov(DEFAULT_FOV),
_cameraAspectRatio(DEFAULT_ASPECT_RATIO),
_cameraNearClip(DEFAULT_NEAR_CLIP),
_cameraFarClip(DEFAULT_FAR_CLIP),
_cameraCenterRadius(DEFAULT_FAR_CLIP)
{
_maxQueryPPS = DEFAULT_MAX_OCTREE_PPS;
using QueryFlags = uint8_t;
const QueryFlags QUERY_HAS_MAIN_FRUSTUM = 1U << 0;
const QueryFlags QUERY_HAS_SECONDARY_FRUSTUM = 1U << 1;
OctreeQuery::OctreeQuery(bool randomizeConnectionID) {
if (randomizeConnectionID) {
// randomize our initial octree query connection ID using random_device
// the connection ID is 16 bits so we take a generated 32 bit value from random device and chop off the top
@ -47,26 +37,28 @@ int OctreeQuery::getBroadcastData(unsigned char* destinationBuffer) {
// pack the connection ID so the server can detect when we start a new connection
memcpy(destinationBuffer, &_connectionID, sizeof(_connectionID));
destinationBuffer += sizeof(_connectionID);
// back a boolean (cut to 1 byte) to designate if this query uses the sent view frustum
memcpy(destinationBuffer, &_usesFrustum, sizeof(_usesFrustum));
destinationBuffer += sizeof(_usesFrustum);
if (_usesFrustum) {
// TODO: DRY this up to a shared method
// that can pack any type given the number of bytes
// and return the number of bytes to push the pointer
// camera details
memcpy(destinationBuffer, &_cameraPosition, sizeof(_cameraPosition));
destinationBuffer += sizeof(_cameraPosition);
destinationBuffer += packOrientationQuatToBytes(destinationBuffer, _cameraOrientation);
destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _cameraFov);
destinationBuffer += packFloatRatioToTwoByte(destinationBuffer, _cameraAspectRatio);
destinationBuffer += packClipValueToTwoByte(destinationBuffer, _cameraNearClip);
destinationBuffer += packClipValueToTwoByte(destinationBuffer, _cameraFarClip);
memcpy(destinationBuffer, &_cameraEyeOffsetPosition, sizeof(_cameraEyeOffsetPosition));
destinationBuffer += sizeof(_cameraEyeOffsetPosition);
// flags for wether the frustums are present
QueryFlags frustumFlags = 0;
if (_hasMainFrustum) {
frustumFlags |= QUERY_HAS_MAIN_FRUSTUM;
}
if (_hasSecondaryFrustum) {
frustumFlags |= QUERY_HAS_SECONDARY_FRUSTUM;
}
memcpy(destinationBuffer, &frustumFlags, sizeof(frustumFlags));
destinationBuffer += sizeof(frustumFlags);
if (_hasMainFrustum) {
auto byteArray = _mainViewFrustum.toByteArray();
memcpy(destinationBuffer, byteArray.constData(), byteArray.size());
destinationBuffer += byteArray.size();
}
if (_hasSecondaryFrustum) {
auto byteArray = _secondaryViewFrustum.toByteArray();
memcpy(destinationBuffer, byteArray.constData(), byteArray.size());
destinationBuffer += byteArray.size();
}
// desired Max Octree PPS
@ -80,9 +72,6 @@ int OctreeQuery::getBroadcastData(unsigned char* destinationBuffer) {
// desired boundaryLevelAdjust
memcpy(destinationBuffer, &_boundaryLevelAdjust, sizeof(_boundaryLevelAdjust));
destinationBuffer += sizeof(_boundaryLevelAdjust);
memcpy(destinationBuffer, &_cameraCenterRadius, sizeof(_cameraCenterRadius));
destinationBuffer += sizeof(_cameraCenterRadius);
// create a QByteArray that holds the binary representation of the JSON parameters
QByteArray binaryParametersDocument;
@ -110,6 +99,7 @@ int OctreeQuery::getBroadcastData(unsigned char* destinationBuffer) {
int OctreeQuery::parseData(ReceivedMessage& message) {
const unsigned char* startPosition = reinterpret_cast<const unsigned char*>(message.getRawMessage());
const unsigned char* endPosition = startPosition + message.getSize();
const unsigned char* sourceBuffer = startPosition;
// unpack the connection ID
@ -133,20 +123,23 @@ int OctreeQuery::parseData(ReceivedMessage& message) {
}
// check if this query uses a view frustum
memcpy(&_usesFrustum, sourceBuffer, sizeof(_usesFrustum));
sourceBuffer += sizeof(_usesFrustum);
if (_usesFrustum) {
// unpack camera details
memcpy(&_cameraPosition, sourceBuffer, sizeof(_cameraPosition));
sourceBuffer += sizeof(_cameraPosition);
sourceBuffer += unpackOrientationQuatFromBytes(sourceBuffer, _cameraOrientation);
sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &_cameraFov);
sourceBuffer += unpackFloatRatioFromTwoByte(sourceBuffer,_cameraAspectRatio);
sourceBuffer += unpackClipValueFromTwoByte(sourceBuffer,_cameraNearClip);
sourceBuffer += unpackClipValueFromTwoByte(sourceBuffer,_cameraFarClip);
memcpy(&_cameraEyeOffsetPosition, sourceBuffer, sizeof(_cameraEyeOffsetPosition));
sourceBuffer += sizeof(_cameraEyeOffsetPosition);
QueryFlags frustumFlags { 0 };
memcpy(&frustumFlags, sourceBuffer, sizeof(frustumFlags));
sourceBuffer += sizeof(frustumFlags);
_hasMainFrustum = frustumFlags & QUERY_HAS_MAIN_FRUSTUM;
_hasSecondaryFrustum = frustumFlags & QUERY_HAS_SECONDARY_FRUSTUM;
if (_hasMainFrustum) {
auto bytesLeft = endPosition - sourceBuffer;
auto byteArray = QByteArray::fromRawData(reinterpret_cast<const char*>(sourceBuffer), bytesLeft);
sourceBuffer += _mainViewFrustum.fromByteArray(byteArray);
}
if (_hasSecondaryFrustum) {
auto bytesLeft = endPosition - sourceBuffer;
auto byteArray = QByteArray::fromRawData(reinterpret_cast<const char*>(sourceBuffer), bytesLeft);
sourceBuffer += _secondaryViewFrustum.fromByteArray(byteArray);
}
// desired Max Octree PPS
@ -161,9 +154,6 @@ int OctreeQuery::parseData(ReceivedMessage& message) {
memcpy(&_boundaryLevelAdjust, sourceBuffer, sizeof(_boundaryLevelAdjust));
sourceBuffer += sizeof(_boundaryLevelAdjust);
memcpy(&_cameraCenterRadius, sourceBuffer, sizeof(_cameraCenterRadius));
sourceBuffer += sizeof(_cameraCenterRadius);
// check if we have a packed JSON filter
uint16_t binaryParametersBytes;
memcpy(&binaryParametersBytes, sourceBuffer, sizeof(binaryParametersBytes));
@ -184,8 +174,3 @@ int OctreeQuery::parseData(ReceivedMessage& message) {
return sourceBuffer - startPosition;
}
glm::vec3 OctreeQuery::calculateCameraDirection() const {
glm::vec3 direction = glm::vec3(_cameraOrientation * glm::vec4(IDENTITY_FORWARD, 0.0f));
return direction;
}

View file

@ -12,16 +12,14 @@
#ifndef hifi_OctreeQuery_h
#define hifi_OctreeQuery_h
#include <inttypes.h>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <QtCore/QJsonObject>
#include <QtCore/QReadWriteLock>
#include <NodeData.h>
#include <ViewFrustum.h>
#include "OctreeConstants.h"
class OctreeQuery : public NodeData {
Q_OBJECT
@ -30,31 +28,22 @@ public:
OctreeQuery(bool randomizeConnectionID = false);
virtual ~OctreeQuery() {}
OctreeQuery(const OctreeQuery&) = delete;
OctreeQuery& operator=(const OctreeQuery&) = delete;
int getBroadcastData(unsigned char* destinationBuffer);
int parseData(ReceivedMessage& message) override;
// getters for camera details
const glm::vec3& getCameraPosition() const { return _cameraPosition; }
const glm::quat& getCameraOrientation() const { return _cameraOrientation; }
float getCameraFov() const { return _cameraFov; }
float getCameraAspectRatio() const { return _cameraAspectRatio; }
float getCameraNearClip() const { return _cameraNearClip; }
float getCameraFarClip() const { return _cameraFarClip; }
const glm::vec3& getCameraEyeOffsetPosition() const { return _cameraEyeOffsetPosition; }
float getCameraCenterRadius() const { return _cameraCenterRadius; }
bool hasMainViewFrustum() const { return _hasMainFrustum; }
void setMainViewFrustum(const ViewFrustum& viewFrustum) { _hasMainFrustum = true; _mainViewFrustum = viewFrustum; }
void clearMainViewFrustum() { _hasMainFrustum = false; }
const ViewFrustum& getMainViewFrustum() const { return _mainViewFrustum; }
glm::vec3 calculateCameraDirection() const;
bool hasSecondaryViewFrustum() const { return _hasSecondaryFrustum; }
void setSecondaryViewFrustum(const ViewFrustum& viewFrustum) { _hasSecondaryFrustum = true; _secondaryViewFrustum = viewFrustum; }
void clearSecondaryViewFrustum() { _hasSecondaryFrustum = false; }
const ViewFrustum& getSecondaryViewFrustum() const { return _secondaryViewFrustum; }
// setters for camera details
void setCameraPosition(const glm::vec3& position) { _cameraPosition = position; }
void setCameraOrientation(const glm::quat& orientation) { _cameraOrientation = orientation; }
void setCameraFov(float fov) { _cameraFov = fov; }
void setCameraAspectRatio(float aspectRatio) { _cameraAspectRatio = aspectRatio; }
void setCameraNearClip(float nearClip) { _cameraNearClip = nearClip; }
void setCameraFarClip(float farClip) { _cameraFarClip = farClip; }
void setCameraEyeOffsetPosition(const glm::vec3& eyeOffsetPosition) { _cameraEyeOffsetPosition = eyeOffsetPosition; }
void setCameraCenterRadius(float radius) { _cameraCenterRadius = radius; }
// getters/setters for JSON filter
QJsonObject getJSONParameters() { QReadLocker locker { &_jsonParametersLock }; return _jsonParameters; }
void setJSONParameters(const QJsonObject& jsonParameters)
@ -64,9 +53,6 @@ public:
int getMaxQueryPacketsPerSecond() const { return _maxQueryPPS; }
float getOctreeSizeScale() const { return _octreeElementSizeScale; }
int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; }
bool getUsesFrustum() { return _usesFrustum; }
void setUsesFrustum(bool usesFrustum) { _usesFrustum = usesFrustum; }
void incrementConnectionID() { ++_connectionID; }
@ -81,33 +67,22 @@ public slots:
void setBoundaryLevelAdjust(int boundaryLevelAdjust) { _boundaryLevelAdjust = boundaryLevelAdjust; }
protected:
// camera details for the avatar
glm::vec3 _cameraPosition { glm::vec3(0.0f) };
glm::quat _cameraOrientation { glm::quat() };
float _cameraFov;
float _cameraAspectRatio;
float _cameraNearClip;
float _cameraFarClip;
float _cameraCenterRadius;
glm::vec3 _cameraEyeOffsetPosition { glm::vec3(0.0f) };
bool _hasMainFrustum { false };
ViewFrustum _mainViewFrustum;
bool _hasSecondaryFrustum { false };
ViewFrustum _secondaryViewFrustum;
// octree server sending items
int _maxQueryPPS = DEFAULT_MAX_OCTREE_PPS;
float _octreeElementSizeScale = DEFAULT_OCTREE_SIZE_SCALE; /// used for LOD calculations
int _boundaryLevelAdjust = 0; /// used for LOD calculations
uint8_t _usesFrustum = true;
uint16_t _connectionID; // query connection ID, randomized to start, increments with each new connection to server
QJsonObject _jsonParameters;
QReadWriteLock _jsonParametersLock;
bool _hasReceivedFirstQuery { false };
private:
// privatize the copy constructor and assignment operator so they cannot be called
OctreeQuery(const OctreeQuery&);
OctreeQuery& operator= (const OctreeQuery&);
};
#endif // hifi_OctreeQuery_h

View file

@ -139,9 +139,14 @@ void OctreeQueryNode::writeToPacket(const unsigned char* buffer, unsigned int by
}
}
void OctreeQueryNode::copyCurrentViewFrustum(ViewFrustum& viewOut) const {
void OctreeQueryNode::copyCurrentMainViewFrustum(ViewFrustum& viewOut) const {
QMutexLocker viewLocker(&_viewMutex);
viewOut = _currentViewFrustum;
viewOut = _currentMainViewFrustum;
}
void OctreeQueryNode::copyCurrentSecondaryViewFrustum(ViewFrustum& viewOut) const {
QMutexLocker viewLocker(&_viewMutex);
viewOut = _currentSecondaryViewFrustum;
}
bool OctreeQueryNode::updateCurrentViewFrustum() {
@ -150,70 +155,50 @@ bool OctreeQueryNode::updateCurrentViewFrustum() {
return false;
}
if (!_usesFrustum) {
if (!_hasMainFrustum && !_hasSecondaryFrustum) {
// this client does not use a view frustum so the view frustum for this query has not changed
return false;
} else {
bool currentViewFrustumChanged = false;
ViewFrustum newestViewFrustum;
// get position and orientation details from the camera
newestViewFrustum.setPosition(getCameraPosition());
newestViewFrustum.setOrientation(getCameraOrientation());
newestViewFrustum.setCenterRadius(getCameraCenterRadius());
// Also make sure it's got the correct lens details from the camera
float originalFOV = getCameraFov();
float wideFOV = originalFOV + VIEW_FRUSTUM_FOV_OVERSEND;
if (0.0f != getCameraAspectRatio() &&
0.0f != getCameraNearClip() &&
0.0f != getCameraFarClip() &&
getCameraNearClip() != getCameraFarClip()) {
newestViewFrustum.setProjection(glm::perspective(
glm::radians(wideFOV), // hack
getCameraAspectRatio(),
getCameraNearClip(),
getCameraFarClip()));
newestViewFrustum.calculate();
}
{ // if there has been a change, then recalculate
QMutexLocker viewLocker(&_viewMutex);
if (!newestViewFrustum.isVerySimilar(_currentViewFrustum)) {
_currentViewFrustum = newestViewFrustum;
currentViewFrustumChanged = true;
}
}
// Also check for LOD changes from the client
if (_lodInitialized) {
if (_lastClientBoundaryLevelAdjust != getBoundaryLevelAdjust()) {
_lastClientBoundaryLevelAdjust = getBoundaryLevelAdjust();
_lodChanged = true;
}
if (_lastClientOctreeSizeScale != getOctreeSizeScale()) {
_lastClientOctreeSizeScale = getOctreeSizeScale();
_lodChanged = true;
}
} else {
_lodInitialized = true;
_lastClientOctreeSizeScale = getOctreeSizeScale();
_lastClientBoundaryLevelAdjust = getBoundaryLevelAdjust();
_lodChanged = false;
}
// When we first detect that the view stopped changing, we record this.
// but we don't change it back to false until we've completely sent this
// scene.
if (_viewFrustumChanging && !currentViewFrustumChanged) {
_viewFrustumJustStoppedChanging = true;
}
_viewFrustumChanging = currentViewFrustumChanged;
return currentViewFrustumChanged;
}
bool currentViewFrustumChanged = false;
{ // if there has been a change, then recalculate
QMutexLocker viewLocker(&_viewMutex);
if (_hasMainFrustum && !_mainViewFrustum.isVerySimilar(_currentMainViewFrustum)) {
_currentMainViewFrustum = _mainViewFrustum;
currentViewFrustumChanged = true;
}
if (_hasSecondaryFrustum && !_secondaryViewFrustum.isVerySimilar(_currentSecondaryViewFrustum)) {
_currentSecondaryViewFrustum = _secondaryViewFrustum;
currentViewFrustumChanged = true;
}
}
// Also check for LOD changes from the client
if (_lodInitialized) {
if (_lastClientBoundaryLevelAdjust != getBoundaryLevelAdjust()) {
_lastClientBoundaryLevelAdjust = getBoundaryLevelAdjust();
_lodChanged = true;
}
if (_lastClientOctreeSizeScale != getOctreeSizeScale()) {
_lastClientOctreeSizeScale = getOctreeSizeScale();
_lodChanged = true;
}
} else {
_lodInitialized = true;
_lastClientOctreeSizeScale = getOctreeSizeScale();
_lastClientBoundaryLevelAdjust = getBoundaryLevelAdjust();
_lodChanged = false;
}
// When we first detect that the view stopped changing, we record this.
// but we don't change it back to false until we've completely sent this
// scene.
if (_viewFrustumChanging && !currentViewFrustumChanged) {
_viewFrustumJustStoppedChanging = true;
}
_viewFrustumChanging = currentViewFrustumChanged;
return currentViewFrustumChanged;
}
void OctreeQueryNode::setViewSent(bool viewSent) {

View file

@ -49,7 +49,8 @@ public:
OctreeElementExtraEncodeData extraEncodeData;
void copyCurrentViewFrustum(ViewFrustum& viewOut) const;
void copyCurrentMainViewFrustum(ViewFrustum& viewOut) const;
void copyCurrentSecondaryViewFrustum(ViewFrustum& viewOut) const;
// These are not classic setters because they are calculating and maintaining state
// which is set asynchronously through the network receive
@ -87,9 +88,6 @@ public:
void setShouldForceFullScene(bool shouldForceFullScene) { _shouldForceFullScene = shouldForceFullScene; }
private:
OctreeQueryNode(const OctreeQueryNode &);
OctreeQueryNode& operator= (const OctreeQueryNode&);
bool _viewSent { false };
std::unique_ptr<NLPacket> _octreePacket;
bool _octreePacketWaiting;
@ -99,7 +97,8 @@ private:
quint64 _firstSuppressedPacket { usecTimestampNow() };
mutable QMutex _viewMutex { QMutex::Recursive };
ViewFrustum _currentViewFrustum;
ViewFrustum _currentMainViewFrustum;
ViewFrustum _currentSecondaryViewFrustum;
bool _viewFrustumChanging { false };
bool _viewFrustumJustStoppedChanging { true };

View file

@ -16,6 +16,7 @@
#include <glm/glm.hpp>
#include <AABox.h>
#include <AACube.h>
float calculateRenderAccuracy(const glm::vec3& position,
const AABox& bounds,
@ -73,4 +74,10 @@ float getOrthographicAccuracySize(float octreeSizeScale, int boundaryLevelAdjust
// Smallest visible element is 1cm
const float smallestSize = 0.01f;
return (smallestSize * MAX_VISIBILITY_DISTANCE_FOR_UNIT_ELEMENT) / boundaryDistanceForRenderLevel(boundaryLevelAdjust, octreeSizeScale);
}
}
bool isAngularSizeBigEnough(glm::vec3 position, const AACube& cube, float lodScaleFactor, float minDiameter) {
float distance = glm::distance(cube.calcCenter(), position) + MIN_VISIBLE_DISTANCE;
float angularDiameter = cube.getScale() / distance;
return angularDiameter > minDiameter * lodScaleFactor;
}

View file

@ -15,6 +15,7 @@
#include "OctreeConstants.h"
class AABox;
class AACube;
class QJsonDocument;
/// renderAccuracy represents a floating point "visibility" of an object based on it's view from the camera. At a simple
@ -36,4 +37,6 @@ const float SQRT_THREE = 1.73205080f;
const float MIN_ENTITY_ANGULAR_DIAMETER = MIN_ELEMENT_ANGULAR_DIAMETER * SQRT_THREE;
const float MIN_VISIBLE_DISTANCE = 0.0001f; // helps avoid divide-by-zero check
bool isAngularSizeBigEnough(glm::vec3 position, const AACube& cube, float lodScaleFactor, float minDiameter);
#endif // hifi_OctreeUtils_h

View file

@ -134,7 +134,7 @@ const char* ViewFrustum::debugPlaneName (int plane) const {
return "Unknown";
}
void ViewFrustum::fromByteArray(const QByteArray& input) {
int ViewFrustum::fromByteArray(const QByteArray& input) {
// From the wire!
glm::vec3 cameraPosition;
@ -176,6 +176,8 @@ void ViewFrustum::fromByteArray(const QByteArray& input) {
calculate();
}
return sourceBuffer - startPosition;
}

View file

@ -147,7 +147,7 @@ public:
void invalidate(); // causes all reasonable intersection tests to fail
QByteArray toByteArray();
void fromByteArray(const QByteArray& input);
int fromByteArray(const QByteArray& input);
private:
glm::mat4 _view;