mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
WIP Only use conical frustums on the server
This commit is contained in:
parent
21396287a4
commit
d774dc2060
11 changed files with 306 additions and 296 deletions
|
@ -1,76 +0,0 @@
|
|||
//
|
||||
// EntityPriorityQueue.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 "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 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();
|
||||
_direction = viewFrustum.getDirection();
|
||||
|
||||
// We cache the sin and cos of the half angle of the cone that bounds the frustum.
|
||||
// (the math here is left as an exercise for the reader)
|
||||
float A = viewFrustum.getAspectRatio();
|
||||
float t = tanf(0.5f * viewFrustum.getFieldOfView());
|
||||
_cosAngle = 1.0f / sqrtf(1.0f + (A * A + 1.0f) * (t * t));
|
||||
_sinAngle = sqrtf(1.0f - _cosAngle * _cosAngle);
|
||||
|
||||
_radius = viewFrustum.getCenterRadius();
|
||||
}
|
||||
|
||||
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
|
||||
if (d < _radius + r) {
|
||||
return r;
|
||||
}
|
||||
// We check the angle between the center of the cube and the _direction of the view.
|
||||
// If it is less than the sum of the half-angle from center of cone to outer edge plus
|
||||
// the half apparent angle of the bounding sphere then it is in view.
|
||||
//
|
||||
// The math here is left as an exercise for the reader with the following hints:
|
||||
// (1) We actually check the dot product of the cube's local position rather than the angle and
|
||||
// (2) we take advantage of this trig identity: cos(A+B) = cos(A)*cos(B) - sin(A)*sin(B)
|
||||
if (glm::dot(p, _direction) > sqrtf(d * d - r * r) * _cosAngle - r * _sinAngle) {
|
||||
const float AVOID_DIVIDE_BY_ZERO = 0.001f;
|
||||
return r / (d + AVOID_DIVIDE_BY_ZERO);
|
||||
}
|
||||
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 {
|
||||
if (_conicalViewFrustums.empty()) {
|
||||
return PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY;
|
||||
}
|
||||
|
||||
float priority = PrioritizedEntity::DO_NOT_SEND;
|
||||
|
||||
for (const auto& view : _conicalViewFrustums) {
|
||||
priority = std::max(priority, view.computePriority(cube));
|
||||
}
|
||||
|
||||
return priority;
|
||||
}
|
|
@ -141,16 +141,8 @@ void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, O
|
|||
if (forceRemove) {
|
||||
priority = PrioritizedEntity::FORCE_REMOVE;
|
||||
} else {
|
||||
bool success = false;
|
||||
AACube cube = entity->getQueryAACube(success);
|
||||
if (success) {
|
||||
const auto& view = _traversal.getCurrentView();
|
||||
if (view.intersects(cube) && view.isBigEnough(cube)) {
|
||||
priority = _conicalView.computePriority(cube);
|
||||
}
|
||||
} else {
|
||||
priority = PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY;
|
||||
}
|
||||
const auto& view = _traversal.getCurrentView();
|
||||
priority = view.computePriority(entity);
|
||||
}
|
||||
|
||||
if (priority != PrioritizedEntity::DO_NOT_SEND) {
|
||||
|
@ -235,11 +227,6 @@ void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, En
|
|||
// (3) Differential = view has changed --> find what has changed or in new view but not old
|
||||
//
|
||||
// The "scanCallback" we provide to the traversal depends on the type:
|
||||
//
|
||||
// The _conicalView is updated here as a cached view approximation used by the lambdas for efficient
|
||||
// computation of entity sorting priorities.
|
||||
//
|
||||
_conicalView.set(_traversal.getCurrentView());
|
||||
|
||||
switch (type) {
|
||||
case DiffTraversal::First:
|
||||
|
@ -251,25 +238,8 @@ void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, En
|
|||
if (_sendQueue.contains(entity.get())) {
|
||||
return;
|
||||
}
|
||||
float priority = PrioritizedEntity::DO_NOT_SEND;
|
||||
|
||||
|
||||
bool success = false;
|
||||
AACube cube = entity->getQueryAACube(success);
|
||||
if (success) {
|
||||
const auto& view = _traversal.getCurrentView();
|
||||
// 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.
|
||||
if ((next.intersection == ViewFrustum::INSIDE || view.intersects(cube)) &&
|
||||
view.isBigEnough(cube)) {
|
||||
priority = _conicalView.computePriority(cube);
|
||||
}
|
||||
} else {
|
||||
priority = PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY;
|
||||
}
|
||||
|
||||
const auto& view = _traversal.getCurrentView();
|
||||
float priority = view.computePriority(entity);
|
||||
|
||||
if (priority != PrioritizedEntity::DO_NOT_SEND) {
|
||||
_sendQueue.emplace(entity, priority);
|
||||
|
@ -288,21 +258,11 @@ void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, En
|
|||
}
|
||||
float priority = PrioritizedEntity::DO_NOT_SEND;
|
||||
|
||||
|
||||
auto knownTimestamp = _knownState.find(entity.get());
|
||||
if (knownTimestamp == _knownState.end()) {
|
||||
bool success = false;
|
||||
AACube cube = entity->getQueryAACube(success);
|
||||
if (success) {
|
||||
const auto& view = _traversal.getCurrentView();
|
||||
// See the DiffTraversal::First case for an explanation of the "entity is too small" check
|
||||
if ((next.intersection == ViewFrustum::INSIDE || view.intersects(cube)) &&
|
||||
view.isBigEnough(cube)) {
|
||||
priority = _conicalView.computePriority(cube);
|
||||
}
|
||||
} else {
|
||||
priority = PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY;
|
||||
}
|
||||
const auto& view = _traversal.getCurrentView();
|
||||
priority = view.computePriority(entity);
|
||||
|
||||
} else if (entity->getLastEdited() > knownTimestamp->second ||
|
||||
entity->getLastChangedOnServer() > knownTimestamp->second) {
|
||||
// it is known and it changed --> put it on the queue with any priority
|
||||
|
@ -310,7 +270,6 @@ void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, En
|
|||
priority = PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY;
|
||||
}
|
||||
|
||||
|
||||
if (priority != PrioritizedEntity::DO_NOT_SEND) {
|
||||
_sendQueue.emplace(entity, priority);
|
||||
}
|
||||
|
@ -328,21 +287,11 @@ void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, En
|
|||
}
|
||||
float priority = PrioritizedEntity::DO_NOT_SEND;
|
||||
|
||||
|
||||
auto knownTimestamp = _knownState.find(entity.get());
|
||||
if (knownTimestamp == _knownState.end()) {
|
||||
bool success = false;
|
||||
AACube cube = entity->getQueryAACube(success);
|
||||
if (success) {
|
||||
const auto& view = _traversal.getCurrentView();
|
||||
// See the DiffTraversal::First case for an explanation of the "entity is too small" check
|
||||
if ((next.intersection == ViewFrustum::INSIDE || view.intersects(cube)) &&
|
||||
view.isBigEnough(cube)) {
|
||||
priority = _conicalView.computePriority(cube);
|
||||
}
|
||||
} else {
|
||||
priority = PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY;
|
||||
}
|
||||
const auto& view = _traversal.getCurrentView();
|
||||
priority = view.computePriority(entity);
|
||||
|
||||
} else if (entity->getLastEdited() > knownTimestamp->second ||
|
||||
entity->getLastChangedOnServer() > knownTimestamp->second) {
|
||||
// it is known and it changed --> put it on the queue with any priority
|
||||
|
@ -350,7 +299,6 @@ void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, En
|
|||
priority = PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY;
|
||||
}
|
||||
|
||||
|
||||
if (priority != PrioritizedEntity::DO_NOT_SEND) {
|
||||
_sendQueue.emplace(entity, priority);
|
||||
}
|
||||
|
@ -463,14 +411,13 @@ bool EntityTreeSendThread::traverseTreeAndBuildNextPacketPayload(EncodeBitstream
|
|||
void EntityTreeSendThread::editingEntityPointer(const EntityItemPointer& entity) {
|
||||
if (entity) {
|
||||
if (!_sendQueue.contains(entity.get()) && _knownState.find(entity.get()) != _knownState.end()) {
|
||||
bool success = false;
|
||||
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().intersects(cube)) {
|
||||
_sendQueue.emplace(entity, PrioritizedEntity::FORCE_REMOVE, true);
|
||||
}
|
||||
} else {
|
||||
const auto& view = _traversal.getCurrentView();
|
||||
float priority = view.computePriority(entity);
|
||||
|
||||
// We can force a removal from _knownState if the current view is used and entity is out of view
|
||||
if (priority == PrioritizedEntity::DO_NOT_SEND) {
|
||||
_sendQueue.emplace(entity, PrioritizedEntity::FORCE_REMOVE, true);
|
||||
} else if (priority == PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY) {
|
||||
_sendQueue.emplace(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY, true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,8 +17,9 @@
|
|||
#include "../octree/OctreeSendThread.h"
|
||||
|
||||
#include <DiffTraversal.h>
|
||||
#include <EntityPriorityQueue.h>
|
||||
#include <shared/ConicalViewFrustum.h>
|
||||
|
||||
#include "EntityPriorityQueue.h"
|
||||
|
||||
class EntityNodeData;
|
||||
class EntityItem;
|
||||
|
@ -51,7 +52,6 @@ private:
|
|||
DiffTraversal _traversal;
|
||||
EntityPriorityQueue _sendQueue;
|
||||
std::unordered_map<EntityItem*, uint64_t> _knownState;
|
||||
ConicalView _conicalView; // cached optimized view for fast priority calculations
|
||||
|
||||
// packet construction stuff
|
||||
EntityTreeElementExtraEncodeDataPointer _extraEncodeData { new EntityTreeElementExtraEncodeData() };
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
|
||||
#include <OctreeUtils.h>
|
||||
|
||||
#include "EntityPriorityQueue.h"
|
||||
|
||||
DiffTraversal::Waypoint::Waypoint(EntityTreeElementPointer& element) : _nextIndex(0) {
|
||||
assert(element);
|
||||
_weakElement = element;
|
||||
|
@ -35,19 +37,9 @@ void DiffTraversal::Waypoint::getNextVisibleElementFirstTime(DiffTraversal::Visi
|
|||
while (_nextIndex < NUMBER_OF_CHILDREN) {
|
||||
EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex);
|
||||
++_nextIndex;
|
||||
if (nextElement) {
|
||||
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.intersects(cube)) {
|
||||
// check for LOD truncation
|
||||
if (view.isBigEnough(cube, MIN_ELEMENT_ANGULAR_DIAMETER)) {
|
||||
next.element = nextElement;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (nextElement && view.shouldTraverseElement(*nextElement)) {
|
||||
next.element = nextElement;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -63,7 +55,6 @@ void DiffTraversal::Waypoint::getNextVisibleElementRepeat(
|
|||
EntityTreeElementPointer element = _weakElement.lock();
|
||||
if (element->getLastChangedContent() > lastTime) {
|
||||
next.element = element;
|
||||
next.intersection = ViewFrustum::INTERSECT;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -73,30 +64,17 @@ void DiffTraversal::Waypoint::getNextVisibleElementRepeat(
|
|||
while (_nextIndex < NUMBER_OF_CHILDREN) {
|
||||
EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex);
|
||||
++_nextIndex;
|
||||
if (nextElement && nextElement->getLastChanged() > lastTime) {
|
||||
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
|
||||
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;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nextElement &&
|
||||
nextElement->getLastChanged() > lastTime &&
|
||||
view.shouldTraverseElement(*nextElement)) {
|
||||
|
||||
next.element = nextElement;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
next.element.reset();
|
||||
next.intersection = ViewFrustum::OUTSIDE;
|
||||
}
|
||||
|
||||
void DiffTraversal::Waypoint::getNextVisibleElementDifferential(DiffTraversal::VisibleElement& next,
|
||||
|
@ -106,7 +84,6 @@ void DiffTraversal::Waypoint::getNextVisibleElementDifferential(DiffTraversal::V
|
|||
++_nextIndex;
|
||||
EntityTreeElementPointer element = _weakElement.lock();
|
||||
next.element = element;
|
||||
next.intersection = ViewFrustum::INTERSECT;
|
||||
return;
|
||||
} else if (_nextIndex < NUMBER_OF_CHILDREN) {
|
||||
EntityTreeElementPointer element = _weakElement.lock();
|
||||
|
@ -114,74 +91,14 @@ void DiffTraversal::Waypoint::getNextVisibleElementDifferential(DiffTraversal::V
|
|||
while (_nextIndex < NUMBER_OF_CHILDREN) {
|
||||
EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex);
|
||||
++_nextIndex;
|
||||
if (nextElement) {
|
||||
// check for LOD truncation
|
||||
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;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (nextElement && view.shouldTraverseElement(*nextElement)) {
|
||||
next.element = nextElement;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
next.element.reset();
|
||||
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;
|
||||
}
|
||||
|
||||
bool isBigEnough = std::any_of(std::begin(viewFrustums), std::end(viewFrustums),
|
||||
[&](const ViewFrustum& viewFrustum) {
|
||||
return isAngularSizeBigEnough(viewFrustum.getPosition(), cube, lodScaleFactor, minDiameter);
|
||||
});
|
||||
|
||||
return isBigEnough;
|
||||
}
|
||||
|
||||
bool DiffTraversal::View::intersects(const AACube& cube) const {
|
||||
if (viewFrustums.empty()) {
|
||||
// Everything intersects when not using view frustums
|
||||
return true;
|
||||
}
|
||||
|
||||
bool intersects = std::any_of(std::begin(viewFrustums), std::end(viewFrustums),
|
||||
[&](const ViewFrustum& viewFrustum) {
|
||||
return viewFrustum.cubeIntersectsKeyhole(cube);
|
||||
});
|
||||
|
||||
return intersects;
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -204,6 +121,73 @@ bool DiffTraversal::View::isVerySimilar(const View& view) const {
|
|||
return true;
|
||||
}
|
||||
|
||||
float DiffTraversal::View::computePriority(const EntityItemPointer& entity) const {
|
||||
if (!entity) {
|
||||
return PrioritizedEntity::DO_NOT_SEND;
|
||||
}
|
||||
|
||||
if (!usesViewFrustums()) {
|
||||
return PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
auto cube = entity->getQueryAACube(success);
|
||||
if (!success) {
|
||||
return PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY;
|
||||
}
|
||||
|
||||
auto center = cube.calcCenter(); // center of bounding sphere
|
||||
auto radius = 0.5f * SQRT_THREE * cube.getScale(); // radius of bounding sphere
|
||||
|
||||
auto priority = PrioritizedEntity::DO_NOT_SEND;
|
||||
|
||||
for (const auto& frustum : viewFrustums) {
|
||||
auto position = center - frustum.getPosition(); // position of bounding sphere in view-frame
|
||||
float distance = glm::length(position); // distance to center of bounding sphere
|
||||
|
||||
// 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 angularSize = frustum.getAngularSize(distance, radius);
|
||||
if (angularSize > lodScaleFactor * MIN_ENTITY_ANGULAR_DIAMETER &&
|
||||
frustum.intersects(position, distance, radius)) {
|
||||
|
||||
// use the angular size as priority
|
||||
// we compute the max priority for all frustums
|
||||
priority = std::max(priority, angularSize);
|
||||
}
|
||||
}
|
||||
|
||||
return priority;
|
||||
}
|
||||
|
||||
bool DiffTraversal::View::shouldTraverseElement(const EntityTreeElement& element) const {
|
||||
if (!usesViewFrustums()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto& cube = element.getAACube();
|
||||
|
||||
auto center = cube.calcCenter(); // center of bounding sphere
|
||||
auto radius = 0.5f * SQRT_THREE * cube.getScale(); // radius of bounding sphere
|
||||
|
||||
|
||||
return any_of(begin(viewFrustums), end(viewFrustums), [&](const ConicalViewFrustum& frustum) {
|
||||
auto position = center - frustum.getPosition(); // position of bounding sphere in view-frame
|
||||
float distance = glm::length(position); // distance to center of bounding sphere
|
||||
|
||||
// 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 angularSize = frustum.getAngularSize(distance, radius);
|
||||
|
||||
return angularSize > lodScaleFactor * MIN_ELEMENT_ANGULAR_DIAMETER &&
|
||||
frustum.intersects(position, distance, radius);
|
||||
});
|
||||
}
|
||||
|
||||
DiffTraversal::DiffTraversal() {
|
||||
const int32_t MIN_PATH_DEPTH = 16;
|
||||
_path.reserve(MIN_PATH_DEPTH);
|
||||
|
@ -262,7 +246,6 @@ DiffTraversal::Type DiffTraversal::prepareNewTraversal(const DiffTraversal::View
|
|||
void DiffTraversal::getNextVisibleElement(DiffTraversal::VisibleElement& next) {
|
||||
if (_path.empty()) {
|
||||
next.element.reset();
|
||||
next.intersection = ViewFrustum::OUTSIDE;
|
||||
return;
|
||||
}
|
||||
_getNextVisibleElementCallback(next);
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#ifndef hifi_DiffTraversal_h
|
||||
#define hifi_DiffTraversal_h
|
||||
|
||||
#include <ViewFrustum.h>
|
||||
#include <shared/ConicalViewFrustum.h>
|
||||
|
||||
#include "EntityTreeElement.h"
|
||||
|
||||
|
@ -24,19 +24,18 @@ public:
|
|||
class VisibleElement {
|
||||
public:
|
||||
EntityTreeElementPointer element;
|
||||
ViewFrustum::intersection intersection { ViewFrustum::OUTSIDE };
|
||||
};
|
||||
|
||||
// View is a struct with a ViewFrustum and LOD parameters
|
||||
class View {
|
||||
public:
|
||||
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;
|
||||
|
||||
ViewFrustums viewFrustums;
|
||||
bool shouldTraverseElement(const EntityTreeElement& element) const;
|
||||
float computePriority(const EntityItemPointer& entity) const;
|
||||
|
||||
ConicalViewFrustums viewFrustums;
|
||||
uint64_t startTime { 0 };
|
||||
float lodScaleFactor { 1.0f };
|
||||
};
|
||||
|
@ -65,9 +64,6 @@ public:
|
|||
Type prepareNewTraversal(const DiffTraversal::View& view, EntityTreeElementPointer root);
|
||||
|
||||
const View& getCurrentView() const { return _currentView; }
|
||||
const View& getCompletedView() const { return _completedView; }
|
||||
|
||||
bool doesCurrentUseViewFrustum() const { return _currentView.usesViewFrustums(); }
|
||||
|
||||
uint64_t getStartOfCompletedTraversal() const { return _completedView.startTime; }
|
||||
bool finished() const { return _path.empty(); }
|
||||
|
|
|
@ -15,44 +15,14 @@
|
|||
#include <queue>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <AACube.h>
|
||||
#include <DiffTraversal.h>
|
||||
#include <EntityTreeElement.h>
|
||||
|
||||
const float SQRT_TWO_OVER_TWO = 0.7071067811865f;
|
||||
const float DEFAULT_VIEW_RADIUS = 10.0f;
|
||||
|
||||
// ConicalViewFrustum is an approximation of a ViewFrustum for fast calculation of sort priority.
|
||||
class ConicalViewFrustum {
|
||||
public:
|
||||
ConicalViewFrustum() {}
|
||||
ConicalViewFrustum(const ViewFrustum& viewFrustum) { set(viewFrustum); }
|
||||
void set(const ViewFrustum& viewFrustum);
|
||||
float computePriority(const AACube& cube) const;
|
||||
private:
|
||||
glm::vec3 _position { 0.0f, 0.0f, 0.0f };
|
||||
glm::vec3 _direction { 0.0f, 0.0f, 1.0f };
|
||||
float _sinAngle { SQRT_TWO_OVER_TWO };
|
||||
float _cosAngle { SQRT_TWO_OVER_TWO };
|
||||
float _radius { DEFAULT_VIEW_RADIUS };
|
||||
};
|
||||
|
||||
// Simple wrapper around a set of conical view frustums
|
||||
class ConicalView {
|
||||
public:
|
||||
ConicalView() {}
|
||||
void set(const DiffTraversal::View& view);
|
||||
float computePriority(const AACube& cube) const;
|
||||
private:
|
||||
std::vector<ConicalViewFrustum> _conicalViewFrustums;
|
||||
};
|
||||
#include "EntityItem.h"
|
||||
|
||||
// PrioritizedEntity is a placeholder in a sorted queue.
|
||||
class PrioritizedEntity {
|
||||
public:
|
||||
static const float DO_NOT_SEND;
|
||||
static const float FORCE_REMOVE;
|
||||
static const float WHEN_IN_DOUBT_PRIORITY;
|
||||
static constexpr float DO_NOT_SEND { -1.0e-6f };
|
||||
static constexpr float FORCE_REMOVE { -1.0e-5f };
|
||||
static constexpr float WHEN_IN_DOUBT_PRIORITY { 1.0f };
|
||||
|
||||
PrioritizedEntity(EntityItemPointer entity, float priority, bool forceRemove = false) : _weakEntity(entity), _rawEntityPointer(entity.get()), _priority(priority), _forceRemove(forceRemove) {}
|
||||
EntityItemPointer getEntity() const { return _weakEntity.lock(); }
|
|
@ -222,6 +222,12 @@ int unpackOrientationQuatFromSixBytes(const unsigned char* buffer, glm::quat& qu
|
|||
return 6;
|
||||
}
|
||||
|
||||
bool closeEnough(float a, float b, float relativeError) {
|
||||
assert(relativeError >= 0.0f);
|
||||
// NOTE: we add EPSILON to the denominator so we can avoid checking for division by zero.
|
||||
// This method works fine when: fabsf(a + b) >> EPSILON
|
||||
return fabsf(a - b) / (0.5f * fabsf(a + b) + EPSILON) < relativeError;
|
||||
}
|
||||
|
||||
// Safe version of glm::eulerAngles; uses the factorization method described in David Eberly's
|
||||
// http://www.geometrictools.com/Documentation/EulerAngles.pdf (via Clyde,
|
||||
|
|
|
@ -137,6 +137,8 @@ int unpackFloatScalarFromSignedTwoByteFixed(const int16_t* byteFixedPointer, flo
|
|||
int packFloatVec3ToSignedTwoByteFixed(unsigned char* destBuffer, const glm::vec3& srcVector, int radix);
|
||||
int unpackFloatVec3FromSignedTwoByteFixed(const unsigned char* sourceBuffer, glm::vec3& destination, int radix);
|
||||
|
||||
bool closeEnough(float a, float b, float relativeError);
|
||||
|
||||
/// \return vec3 with euler angles in radians
|
||||
glm::vec3 safeEulerAngles(const glm::quat& q);
|
||||
|
||||
|
|
|
@ -338,13 +338,6 @@ bool ViewFrustum::boxIntersectsKeyhole(const AABox& box) const {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool closeEnough(float a, float b, float relativeError) {
|
||||
assert(relativeError >= 0.0f);
|
||||
// NOTE: we add EPSILON to the denominator so we can avoid checking for division by zero.
|
||||
// This method works fine when: fabsf(a + b) >> EPSILON
|
||||
return fabsf(a - b) / (0.5f * fabsf(a + b) + EPSILON) < relativeError;
|
||||
}
|
||||
|
||||
// TODO: the slop and relative error should be passed in by argument rather than hard-coded.
|
||||
bool ViewFrustum::isVerySimilar(const ViewFrustum& other) const {
|
||||
const float MIN_POSITION_SLOP_SQUARED = 25.0f; // 5 meters squared
|
||||
|
|
125
libraries/shared/src/shared/ConicalViewFrustum.cpp
Normal file
125
libraries/shared/src/shared/ConicalViewFrustum.cpp
Normal file
|
@ -0,0 +1,125 @@
|
|||
//
|
||||
// ConicalViewFrustum.cpp
|
||||
// libraries/shared/src/shared
|
||||
//
|
||||
// Created by Clement Brisset 4/26/18
|
||||
// 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 "ConicalViewFrustum.h"
|
||||
|
||||
#include "../ViewFrustum.h"
|
||||
|
||||
void ConicalViewFrustum::set(const ViewFrustum& viewFrustum) {
|
||||
// The ConicalViewFrustum 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();
|
||||
_direction = viewFrustum.getDirection();
|
||||
_radius = viewFrustum.getCenterRadius();
|
||||
_farClip = viewFrustum.getFarClip();
|
||||
|
||||
// Considering the rectangle intersection the frustum and the perpendicular plane 1 unit
|
||||
// away from the frustum's origin
|
||||
// We are looking for the angle between the ray that goes through the center of the rectangle
|
||||
// and the ray that goes through one of the corners of the rectangle
|
||||
// (Both rays coming from the frustum's origin)
|
||||
// This angle will let us construct a cone in which the frustum is inscribed
|
||||
// Let's define:
|
||||
// A = aspect ratio = width / height
|
||||
// fov = vertical field of view
|
||||
// y = half height of the rectangle
|
||||
// x = half width of the rectangle
|
||||
// r = half diagonal of the rectangle
|
||||
// then, we have:
|
||||
// y / 1 = tan(fov / 2)
|
||||
// x = A * y = A * tan(fov / 2)
|
||||
// r^2 = x^2 + y^2 = (A^2 + 1) * tan^2(fov / 2)
|
||||
// r / 1 = tan(angle) = sqrt((A^2 + 1) * tan^2(fov / 2))
|
||||
// angle = atan(sqrt((A^2 + 1) * tan^2(fov / 2)))
|
||||
float A = viewFrustum.getAspectRatio();
|
||||
float t = tanf(0.5f * viewFrustum.getFieldOfView());
|
||||
|
||||
auto tan2Angle = (A * A + 1.0f) * (t * t);
|
||||
_angle = atanf(sqrt(tan2Angle));
|
||||
|
||||
calculate();
|
||||
}
|
||||
|
||||
void ConicalViewFrustum::calculate() {
|
||||
_cosAngle = cosf(_angle);
|
||||
_sinAngle = sqrtf(1.0f - _cosAngle * _cosAngle);
|
||||
}
|
||||
|
||||
bool ConicalViewFrustum::isVerySimilar(const ConicalViewFrustum& other) const {
|
||||
const float MIN_POSITION_SLOP_SQUARED = 25.0f; // 5 meters squared
|
||||
const float MIN_ANGLE_BETWEEN = 0.174533f; // radian angle between 2 vectors 10 degrees apart
|
||||
const float MIN_RELATIVE_ERROR = 0.01f; // 1%
|
||||
|
||||
return glm::distance2(_position, other._position) < MIN_POSITION_SLOP_SQUARED &&
|
||||
angleBetween(_direction, other._direction) > MIN_ANGLE_BETWEEN &&
|
||||
closeEnough(_angle, other._angle, MIN_RELATIVE_ERROR) &&
|
||||
closeEnough(_farClip, other._farClip, MIN_RELATIVE_ERROR) &&
|
||||
closeEnough(_radius, other._radius, MIN_RELATIVE_ERROR);
|
||||
}
|
||||
|
||||
bool ConicalViewFrustum::intersects(const glm::vec3& position, float distance, float radius) const {
|
||||
if (distance < _radius + radius) {
|
||||
// Inside keyhole radius
|
||||
return true;
|
||||
}
|
||||
if (distance > _farClip + radius) {
|
||||
// Past far clip
|
||||
return false;
|
||||
}
|
||||
|
||||
// We check the angle between the center of the cube and the _direction of the view.
|
||||
// If it is less than the sum of the half-angle from center of cone to outer edge plus
|
||||
// the half apparent angle of the bounding sphere then it is in view.
|
||||
//
|
||||
// The math here is left as an exercise for the reader with the following hints:
|
||||
// (1) We actually check the dot product of the cube's local position rather than the angle and
|
||||
// (2) we take advantage of this trig identity: cos(A+B) = cos(A)*cos(B) - sin(A)*sin(B)
|
||||
return glm::dot(position, _direction) >
|
||||
sqrtf(distance * distance - radius * radius) * _cosAngle - radius * _sinAngle;
|
||||
}
|
||||
|
||||
bool ConicalViewFrustum::getAngularSize(float distance, float radius) const {
|
||||
const float AVOID_DIVIDE_BY_ZERO = 0.001f;
|
||||
float angularSize = radius / (distance + AVOID_DIVIDE_BY_ZERO);
|
||||
return angularSize;
|
||||
}
|
||||
|
||||
int ConicalViewFrustum::serialize(unsigned char* destinationBuffer) const {
|
||||
const unsigned char* startPosition = destinationBuffer;
|
||||
|
||||
memcpy(destinationBuffer, &_position, sizeof(_position));
|
||||
destinationBuffer += sizeof(_position);
|
||||
memcpy(destinationBuffer, &_direction, sizeof(_direction));
|
||||
destinationBuffer += sizeof(_direction);
|
||||
destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _angle);
|
||||
destinationBuffer += packClipValueToTwoByte(destinationBuffer, _farClip);
|
||||
memcpy(destinationBuffer, &_radius, sizeof(_radius));
|
||||
destinationBuffer += sizeof(_radius);
|
||||
|
||||
return destinationBuffer - startPosition;
|
||||
}
|
||||
|
||||
int ConicalViewFrustum::deserialize(const unsigned char* sourceBuffer) {
|
||||
const unsigned char* startPosition = sourceBuffer;
|
||||
|
||||
memcpy(&_position, sourceBuffer, sizeof(_position));
|
||||
sourceBuffer += sizeof(_position);
|
||||
memcpy(&_direction, sourceBuffer, sizeof(_direction));
|
||||
sourceBuffer += sizeof(_direction);
|
||||
sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*)sourceBuffer, &_angle);
|
||||
sourceBuffer += unpackClipValueFromTwoByte(sourceBuffer, _farClip);
|
||||
memcpy(&_radius, sourceBuffer, sizeof(_radius));
|
||||
sourceBuffer += sizeof(_radius);
|
||||
|
||||
calculate();
|
||||
|
||||
return sourceBuffer - startPosition;
|
||||
}
|
64
libraries/shared/src/shared/ConicalViewFrustum.h
Normal file
64
libraries/shared/src/shared/ConicalViewFrustum.h
Normal file
|
@ -0,0 +1,64 @@
|
|||
//
|
||||
// ConicalViewFrustum.h
|
||||
// libraries/shared/src/shared
|
||||
//
|
||||
// Created by Clement Brisset 4/26/18
|
||||
// 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
|
||||
//
|
||||
|
||||
#ifndef hifi_ConicalViewFrustum_h
|
||||
#define hifi_ConicalViewFrustum_h
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
class AACube;
|
||||
class ViewFrustum;
|
||||
using ViewFrustums = std::vector<ViewFrustum>;
|
||||
|
||||
const float SQRT_TWO_OVER_TWO = 0.7071067811865f;
|
||||
const float DEFAULT_VIEW_ANGLE = 1.0f;
|
||||
const float DEFAULT_VIEW_RADIUS = 10.0f;
|
||||
const float DEFAULT_VIEW_FAR_CLIP = 100.0f;
|
||||
|
||||
// ConicalViewFrustum is an approximation of a ViewFrustum for fast calculation of sort priority.
|
||||
class ConicalViewFrustum {
|
||||
public:
|
||||
ConicalViewFrustum() = default;
|
||||
ConicalViewFrustum(const ViewFrustum& viewFrustum) { set(viewFrustum); }
|
||||
|
||||
void set(const ViewFrustum& viewFrustum);
|
||||
void calculate();
|
||||
|
||||
const glm::vec3& getPosition() const { return _position; }
|
||||
const glm::vec3& getDirection() const { return _direction; }
|
||||
float getAngle() const { return _angle; }
|
||||
float getRadius() const { return _radius; }
|
||||
float getFarClip() const { return _farClip; }
|
||||
|
||||
bool isVerySimilar(const ConicalViewFrustum& other) const;
|
||||
|
||||
bool intersects(const glm::vec3& position, float distance, float radius) const;
|
||||
bool getAngularSize(float distance, float radius) const;
|
||||
|
||||
int serialize(unsigned char* destinationBuffer) const;
|
||||
int deserialize(const unsigned char* sourceBuffer);
|
||||
|
||||
private:
|
||||
glm::vec3 _position { 0.0f, 0.0f, 0.0f };
|
||||
glm::vec3 _direction { 0.0f, 0.0f, 1.0f };
|
||||
float _angle { DEFAULT_VIEW_ANGLE };
|
||||
float _radius { DEFAULT_VIEW_RADIUS };
|
||||
float _farClip { DEFAULT_VIEW_FAR_CLIP };
|
||||
|
||||
float _sinAngle { SQRT_TWO_OVER_TWO };
|
||||
float _cosAngle { SQRT_TWO_OVER_TWO };
|
||||
};
|
||||
using ConicalViewFrustums = std::vector<ConicalViewFrustum>;
|
||||
|
||||
|
||||
#endif /* hifi_ConicalViewFrustum_h */
|
Loading…
Reference in a new issue