mirror of
https://github.com/JulianGro/overte.git
synced 2025-05-07 12:39:55 +02: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) {
|
if (forceRemove) {
|
||||||
priority = PrioritizedEntity::FORCE_REMOVE;
|
priority = PrioritizedEntity::FORCE_REMOVE;
|
||||||
} else {
|
} else {
|
||||||
bool success = false;
|
const auto& view = _traversal.getCurrentView();
|
||||||
AACube cube = entity->getQueryAACube(success);
|
priority = view.computePriority(entity);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (priority != PrioritizedEntity::DO_NOT_SEND) {
|
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
|
// (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 "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) {
|
switch (type) {
|
||||||
case DiffTraversal::First:
|
case DiffTraversal::First:
|
||||||
|
@ -251,25 +238,8 @@ void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, En
|
||||||
if (_sendQueue.contains(entity.get())) {
|
if (_sendQueue.contains(entity.get())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
float priority = PrioritizedEntity::DO_NOT_SEND;
|
const auto& view = _traversal.getCurrentView();
|
||||||
|
float priority = view.computePriority(entity);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (priority != PrioritizedEntity::DO_NOT_SEND) {
|
if (priority != PrioritizedEntity::DO_NOT_SEND) {
|
||||||
_sendQueue.emplace(entity, priority);
|
_sendQueue.emplace(entity, priority);
|
||||||
|
@ -288,21 +258,11 @@ void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, En
|
||||||
}
|
}
|
||||||
float priority = PrioritizedEntity::DO_NOT_SEND;
|
float priority = PrioritizedEntity::DO_NOT_SEND;
|
||||||
|
|
||||||
|
|
||||||
auto knownTimestamp = _knownState.find(entity.get());
|
auto knownTimestamp = _knownState.find(entity.get());
|
||||||
if (knownTimestamp == _knownState.end()) {
|
if (knownTimestamp == _knownState.end()) {
|
||||||
bool success = false;
|
const auto& view = _traversal.getCurrentView();
|
||||||
AACube cube = entity->getQueryAACube(success);
|
priority = view.computePriority(entity);
|
||||||
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;
|
|
||||||
}
|
|
||||||
} else if (entity->getLastEdited() > knownTimestamp->second ||
|
} else if (entity->getLastEdited() > knownTimestamp->second ||
|
||||||
entity->getLastChangedOnServer() > knownTimestamp->second) {
|
entity->getLastChangedOnServer() > knownTimestamp->second) {
|
||||||
// it is known and it changed --> put it on the queue with any priority
|
// 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;
|
priority = PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (priority != PrioritizedEntity::DO_NOT_SEND) {
|
if (priority != PrioritizedEntity::DO_NOT_SEND) {
|
||||||
_sendQueue.emplace(entity, priority);
|
_sendQueue.emplace(entity, priority);
|
||||||
}
|
}
|
||||||
|
@ -328,21 +287,11 @@ void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, En
|
||||||
}
|
}
|
||||||
float priority = PrioritizedEntity::DO_NOT_SEND;
|
float priority = PrioritizedEntity::DO_NOT_SEND;
|
||||||
|
|
||||||
|
|
||||||
auto knownTimestamp = _knownState.find(entity.get());
|
auto knownTimestamp = _knownState.find(entity.get());
|
||||||
if (knownTimestamp == _knownState.end()) {
|
if (knownTimestamp == _knownState.end()) {
|
||||||
bool success = false;
|
const auto& view = _traversal.getCurrentView();
|
||||||
AACube cube = entity->getQueryAACube(success);
|
priority = view.computePriority(entity);
|
||||||
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;
|
|
||||||
}
|
|
||||||
} else if (entity->getLastEdited() > knownTimestamp->second ||
|
} else if (entity->getLastEdited() > knownTimestamp->second ||
|
||||||
entity->getLastChangedOnServer() > knownTimestamp->second) {
|
entity->getLastChangedOnServer() > knownTimestamp->second) {
|
||||||
// it is known and it changed --> put it on the queue with any priority
|
// 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;
|
priority = PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (priority != PrioritizedEntity::DO_NOT_SEND) {
|
if (priority != PrioritizedEntity::DO_NOT_SEND) {
|
||||||
_sendQueue.emplace(entity, priority);
|
_sendQueue.emplace(entity, priority);
|
||||||
}
|
}
|
||||||
|
@ -463,14 +411,13 @@ bool EntityTreeSendThread::traverseTreeAndBuildNextPacketPayload(EncodeBitstream
|
||||||
void EntityTreeSendThread::editingEntityPointer(const EntityItemPointer& entity) {
|
void EntityTreeSendThread::editingEntityPointer(const EntityItemPointer& entity) {
|
||||||
if (entity) {
|
if (entity) {
|
||||||
if (!_sendQueue.contains(entity.get()) && _knownState.find(entity.get()) != _knownState.end()) {
|
if (!_sendQueue.contains(entity.get()) && _knownState.find(entity.get()) != _knownState.end()) {
|
||||||
bool success = false;
|
const auto& view = _traversal.getCurrentView();
|
||||||
AACube cube = entity->getQueryAACube(success);
|
float priority = view.computePriority(entity);
|
||||||
if (success) {
|
|
||||||
// We can force a removal from _knownState if the current view is used and entity is out of view
|
// 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)) {
|
if (priority == PrioritizedEntity::DO_NOT_SEND) {
|
||||||
_sendQueue.emplace(entity, PrioritizedEntity::FORCE_REMOVE, true);
|
_sendQueue.emplace(entity, PrioritizedEntity::FORCE_REMOVE, true);
|
||||||
}
|
} else if (priority == PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY) {
|
||||||
} else {
|
|
||||||
_sendQueue.emplace(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY, true);
|
_sendQueue.emplace(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,8 +17,9 @@
|
||||||
#include "../octree/OctreeSendThread.h"
|
#include "../octree/OctreeSendThread.h"
|
||||||
|
|
||||||
#include <DiffTraversal.h>
|
#include <DiffTraversal.h>
|
||||||
|
#include <EntityPriorityQueue.h>
|
||||||
|
#include <shared/ConicalViewFrustum.h>
|
||||||
|
|
||||||
#include "EntityPriorityQueue.h"
|
|
||||||
|
|
||||||
class EntityNodeData;
|
class EntityNodeData;
|
||||||
class EntityItem;
|
class EntityItem;
|
||||||
|
@ -51,7 +52,6 @@ private:
|
||||||
DiffTraversal _traversal;
|
DiffTraversal _traversal;
|
||||||
EntityPriorityQueue _sendQueue;
|
EntityPriorityQueue _sendQueue;
|
||||||
std::unordered_map<EntityItem*, uint64_t> _knownState;
|
std::unordered_map<EntityItem*, uint64_t> _knownState;
|
||||||
ConicalView _conicalView; // cached optimized view for fast priority calculations
|
|
||||||
|
|
||||||
// packet construction stuff
|
// packet construction stuff
|
||||||
EntityTreeElementExtraEncodeDataPointer _extraEncodeData { new EntityTreeElementExtraEncodeData() };
|
EntityTreeElementExtraEncodeDataPointer _extraEncodeData { new EntityTreeElementExtraEncodeData() };
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
|
|
||||||
#include <OctreeUtils.h>
|
#include <OctreeUtils.h>
|
||||||
|
|
||||||
|
#include "EntityPriorityQueue.h"
|
||||||
|
|
||||||
DiffTraversal::Waypoint::Waypoint(EntityTreeElementPointer& element) : _nextIndex(0) {
|
DiffTraversal::Waypoint::Waypoint(EntityTreeElementPointer& element) : _nextIndex(0) {
|
||||||
assert(element);
|
assert(element);
|
||||||
_weakElement = element;
|
_weakElement = element;
|
||||||
|
@ -35,19 +37,9 @@ void DiffTraversal::Waypoint::getNextVisibleElementFirstTime(DiffTraversal::Visi
|
||||||
while (_nextIndex < NUMBER_OF_CHILDREN) {
|
while (_nextIndex < NUMBER_OF_CHILDREN) {
|
||||||
EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex);
|
EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex);
|
||||||
++_nextIndex;
|
++_nextIndex;
|
||||||
if (nextElement) {
|
if (nextElement && view.shouldTraverseElement(*nextElement)) {
|
||||||
const auto& cube = nextElement->getAACube();
|
next.element = nextElement;
|
||||||
if (!view.usesViewFrustums()) {
|
return;
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,7 +55,6 @@ void DiffTraversal::Waypoint::getNextVisibleElementRepeat(
|
||||||
EntityTreeElementPointer element = _weakElement.lock();
|
EntityTreeElementPointer element = _weakElement.lock();
|
||||||
if (element->getLastChangedContent() > lastTime) {
|
if (element->getLastChangedContent() > lastTime) {
|
||||||
next.element = element;
|
next.element = element;
|
||||||
next.intersection = ViewFrustum::INTERSECT;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,30 +64,17 @@ void DiffTraversal::Waypoint::getNextVisibleElementRepeat(
|
||||||
while (_nextIndex < NUMBER_OF_CHILDREN) {
|
while (_nextIndex < NUMBER_OF_CHILDREN) {
|
||||||
EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex);
|
EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex);
|
||||||
++_nextIndex;
|
++_nextIndex;
|
||||||
if (nextElement && nextElement->getLastChanged() > lastTime) {
|
if (nextElement &&
|
||||||
if (!view.usesViewFrustums()) {
|
nextElement->getLastChanged() > lastTime &&
|
||||||
// No LOD truncation if we aren't using the view frustum
|
view.shouldTraverseElement(*nextElement)) {
|
||||||
next.element = nextElement;
|
|
||||||
next.intersection = ViewFrustum::INSIDE;
|
next.element = nextElement;
|
||||||
return;
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
next.element.reset();
|
next.element.reset();
|
||||||
next.intersection = ViewFrustum::OUTSIDE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DiffTraversal::Waypoint::getNextVisibleElementDifferential(DiffTraversal::VisibleElement& next,
|
void DiffTraversal::Waypoint::getNextVisibleElementDifferential(DiffTraversal::VisibleElement& next,
|
||||||
|
@ -106,7 +84,6 @@ void DiffTraversal::Waypoint::getNextVisibleElementDifferential(DiffTraversal::V
|
||||||
++_nextIndex;
|
++_nextIndex;
|
||||||
EntityTreeElementPointer element = _weakElement.lock();
|
EntityTreeElementPointer element = _weakElement.lock();
|
||||||
next.element = element;
|
next.element = element;
|
||||||
next.intersection = ViewFrustum::INTERSECT;
|
|
||||||
return;
|
return;
|
||||||
} else if (_nextIndex < NUMBER_OF_CHILDREN) {
|
} else if (_nextIndex < NUMBER_OF_CHILDREN) {
|
||||||
EntityTreeElementPointer element = _weakElement.lock();
|
EntityTreeElementPointer element = _weakElement.lock();
|
||||||
|
@ -114,74 +91,14 @@ void DiffTraversal::Waypoint::getNextVisibleElementDifferential(DiffTraversal::V
|
||||||
while (_nextIndex < NUMBER_OF_CHILDREN) {
|
while (_nextIndex < NUMBER_OF_CHILDREN) {
|
||||||
EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex);
|
EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex);
|
||||||
++_nextIndex;
|
++_nextIndex;
|
||||||
if (nextElement) {
|
if (nextElement && view.shouldTraverseElement(*nextElement)) {
|
||||||
// check for LOD truncation
|
next.element = nextElement;
|
||||||
const auto& cube = nextElement->getAACube();
|
return;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
next.element.reset();
|
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 {
|
bool DiffTraversal::View::usesViewFrustums() const {
|
||||||
|
@ -204,6 +121,73 @@ bool DiffTraversal::View::isVerySimilar(const View& view) const {
|
||||||
return true;
|
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() {
|
DiffTraversal::DiffTraversal() {
|
||||||
const int32_t MIN_PATH_DEPTH = 16;
|
const int32_t MIN_PATH_DEPTH = 16;
|
||||||
_path.reserve(MIN_PATH_DEPTH);
|
_path.reserve(MIN_PATH_DEPTH);
|
||||||
|
@ -262,7 +246,6 @@ DiffTraversal::Type DiffTraversal::prepareNewTraversal(const DiffTraversal::View
|
||||||
void DiffTraversal::getNextVisibleElement(DiffTraversal::VisibleElement& next) {
|
void DiffTraversal::getNextVisibleElement(DiffTraversal::VisibleElement& next) {
|
||||||
if (_path.empty()) {
|
if (_path.empty()) {
|
||||||
next.element.reset();
|
next.element.reset();
|
||||||
next.intersection = ViewFrustum::OUTSIDE;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_getNextVisibleElementCallback(next);
|
_getNextVisibleElementCallback(next);
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
#ifndef hifi_DiffTraversal_h
|
#ifndef hifi_DiffTraversal_h
|
||||||
#define hifi_DiffTraversal_h
|
#define hifi_DiffTraversal_h
|
||||||
|
|
||||||
#include <ViewFrustum.h>
|
#include <shared/ConicalViewFrustum.h>
|
||||||
|
|
||||||
#include "EntityTreeElement.h"
|
#include "EntityTreeElement.h"
|
||||||
|
|
||||||
|
@ -24,19 +24,18 @@ public:
|
||||||
class VisibleElement {
|
class VisibleElement {
|
||||||
public:
|
public:
|
||||||
EntityTreeElementPointer element;
|
EntityTreeElementPointer element;
|
||||||
ViewFrustum::intersection intersection { ViewFrustum::OUTSIDE };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// View is a struct with a ViewFrustum and LOD parameters
|
// View is a struct with a ViewFrustum and LOD parameters
|
||||||
class View {
|
class View {
|
||||||
public:
|
public:
|
||||||
bool isBigEnough(const AACube& cube, float minDiameter = MIN_ENTITY_ANGULAR_DIAMETER) const;
|
|
||||||
bool intersects(const AACube& cube) const;
|
|
||||||
bool usesViewFrustums() const;
|
bool usesViewFrustums() const;
|
||||||
bool isVerySimilar(const View& view) 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 };
|
uint64_t startTime { 0 };
|
||||||
float lodScaleFactor { 1.0f };
|
float lodScaleFactor { 1.0f };
|
||||||
};
|
};
|
||||||
|
@ -65,9 +64,6 @@ public:
|
||||||
Type prepareNewTraversal(const DiffTraversal::View& view, EntityTreeElementPointer root);
|
Type prepareNewTraversal(const DiffTraversal::View& view, EntityTreeElementPointer root);
|
||||||
|
|
||||||
const View& getCurrentView() const { return _currentView; }
|
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; }
|
uint64_t getStartOfCompletedTraversal() const { return _completedView.startTime; }
|
||||||
bool finished() const { return _path.empty(); }
|
bool finished() const { return _path.empty(); }
|
||||||
|
|
|
@ -15,44 +15,14 @@
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
|
||||||
#include <AACube.h>
|
#include "EntityItem.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;
|
|
||||||
};
|
|
||||||
|
|
||||||
// PrioritizedEntity is a placeholder in a sorted queue.
|
// PrioritizedEntity is a placeholder in a sorted queue.
|
||||||
class PrioritizedEntity {
|
class PrioritizedEntity {
|
||||||
public:
|
public:
|
||||||
static const float DO_NOT_SEND;
|
static constexpr float DO_NOT_SEND { -1.0e-6f };
|
||||||
static const float FORCE_REMOVE;
|
static constexpr float FORCE_REMOVE { -1.0e-5f };
|
||||||
static const float WHEN_IN_DOUBT_PRIORITY;
|
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) {}
|
PrioritizedEntity(EntityItemPointer entity, float priority, bool forceRemove = false) : _weakEntity(entity), _rawEntityPointer(entity.get()), _priority(priority), _forceRemove(forceRemove) {}
|
||||||
EntityItemPointer getEntity() const { return _weakEntity.lock(); }
|
EntityItemPointer getEntity() const { return _weakEntity.lock(); }
|
|
@ -222,6 +222,12 @@ int unpackOrientationQuatFromSixBytes(const unsigned char* buffer, glm::quat& qu
|
||||||
return 6;
|
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
|
// Safe version of glm::eulerAngles; uses the factorization method described in David Eberly's
|
||||||
// http://www.geometrictools.com/Documentation/EulerAngles.pdf (via Clyde,
|
// 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 packFloatVec3ToSignedTwoByteFixed(unsigned char* destBuffer, const glm::vec3& srcVector, int radix);
|
||||||
int unpackFloatVec3FromSignedTwoByteFixed(const unsigned char* sourceBuffer, glm::vec3& destination, 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
|
/// \return vec3 with euler angles in radians
|
||||||
glm::vec3 safeEulerAngles(const glm::quat& q);
|
glm::vec3 safeEulerAngles(const glm::quat& q);
|
||||||
|
|
||||||
|
|
|
@ -338,13 +338,6 @@ bool ViewFrustum::boxIntersectsKeyhole(const AABox& box) const {
|
||||||
return true;
|
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.
|
// TODO: the slop and relative error should be passed in by argument rather than hard-coded.
|
||||||
bool ViewFrustum::isVerySimilar(const ViewFrustum& other) const {
|
bool ViewFrustum::isVerySimilar(const ViewFrustum& other) const {
|
||||||
const float MIN_POSITION_SLOP_SQUARED = 25.0f; // 5 meters squared
|
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