Overte C++ Documentation
PrioritySortUtil.h
1 //
2 // PrioritySortUtil.h
3 //
4 // Created by Andrew Meadows on 2017-11-08
5 // Copyright 2017 High Fidelity, Inc.
6 //
7 // Distributed under the Apache License, Version 2.0.
8 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
9 //
10 #pragma once
11 #ifndef hifi_PrioritySortUtil_h
12 #define hifi_PrioritySortUtil_h
13 
14 #include <glm/glm.hpp>
15 
16 #include "NumericalConstants.h"
17 #include "shared/ConicalViewFrustum.h"
18 
19 // PrioritySortUtil is a helper for sorting 3D things relative to a ViewFrustum.
20 
21 const float OUT_OF_VIEW_PENALTY = -10.0f;
22 const float OUT_OF_VIEW_THRESHOLD = 0.5f * OUT_OF_VIEW_PENALTY;
23 
24 namespace PrioritySortUtil {
25 
26  constexpr float DEFAULT_ANGULAR_COEF { 1.0f };
27  constexpr float DEFAULT_CENTER_COEF { 0.5f };
28  constexpr float DEFAULT_AGE_COEF { 0.25f };
29 
30  class Sortable {
31  public:
32  virtual ~Sortable() = default;
33 
34  virtual glm::vec3 getPosition() const = 0;
35  virtual float getRadius() const = 0;
36  virtual uint64_t getTimestamp() const = 0;
37 
38  void setPriority(float priority) { _priority = priority; }
39  float getPriority() const { return _priority; }
40  private:
41  float _priority { 0.0f };
42  };
43 
44  template <typename T>
45  class PriorityQueue {
46  public:
47  PriorityQueue() = delete;
48  PriorityQueue(const ConicalViewFrustums& views) : _views(views), _usecCurrentTime(usecTimestampNow()) { }
49  PriorityQueue(const ConicalViewFrustums& views, float angularWeight, float centerWeight, float ageWeight)
50  : _views(views), _angularWeight(angularWeight), _centerWeight(centerWeight), _ageWeight(ageWeight)
51  , _usecCurrentTime(usecTimestampNow()) {
52  }
53 
54  void setViews(const ConicalViewFrustums& views) { _views = views; }
55 
56  void setWeights(float angularWeight, float centerWeight, float ageWeight) {
57  _angularWeight = angularWeight;
58  _centerWeight = centerWeight;
59  _ageWeight = ageWeight;
60  _usecCurrentTime = usecTimestampNow();
61  }
62 
63  size_t size() const { return _vector.size(); }
64  void push(T thing) {
65  thing.setPriority(computePriority(thing));
66  _vector.push_back(thing);
67  }
68  void reserve(size_t num) {
69  _vector.reserve(num);
70  }
71  const std::vector<T>& getSortedVector(int numToSort = 0) {
72  if (numToSort == 0 || numToSort >= (int)_vector.size()) {
73  std::sort(_vector.begin(), _vector.end(),
74  [](const T& left, const T& right) { return left.getPriority() > right.getPriority(); });
75  } else {
76  std::partial_sort(_vector.begin(), _vector.begin() + numToSort, _vector.end(),
77  [](const T& left, const T& right) { return left.getPriority() > right.getPriority(); });
78  }
79  return _vector;
80  }
81 
82  private:
83 
84  float computePriority(const T& thing) const {
85  float priority = std::numeric_limits<float>::min();
86 
87  for (const auto& view : _views) {
88  priority = std::max(priority, computePriority(view, thing));
89  }
90 
91  return priority;
92  }
93 
94  float computePriority(const ConicalViewFrustum& view, const T& thing) const {
95  // priority = weighted linear combination of multiple values:
96  // (a) angular size
97  // (b) proximity to center of view
98  // (c) time since last update
99  // where the relative "weights" are tuned to scale the contributing values into units of "priority".
100 
101  glm::vec3 position = thing.getPosition();
102  glm::vec3 offset = position - view.getPosition();
103  float distance = glm::length(offset) + 0.001f; // add 1mm to avoid divide by zero
104  const float MIN_RADIUS = 0.1f; // WORKAROUND for zero size objects (we still want them to sort by distance)
105  float radius = glm::max(thing.getRadius(), MIN_RADIUS);
106  // Other item's angle from view centre:
107  float cosineAngle = glm::dot(offset, view.getDirection()) / distance;
108  if (cosineAngle > 0.0f) {
109  cosineAngle = std::sqrt(cosineAngle);
110  }
111  float age = float((_usecCurrentTime - thing.getTimestamp()) / USECS_PER_SECOND);
112 
113  // the "age" term accumulates at the sum of all weights
114  float angularSize = radius / distance;
115  float priority = (_angularWeight * angularSize + _centerWeight * cosineAngle) * (age + 1.0f) + _ageWeight * age;
116 
117  // decrement priority of things outside keyhole
118  if (distance - radius > view.getRadius()) {
119  if (!view.intersects(offset, distance, radius)) {
120  priority += OUT_OF_VIEW_PENALTY;
121  }
122  }
123  return priority;
124  }
125 
126  ConicalViewFrustums _views;
127  std::vector<T> _vector;
128  float _angularWeight { DEFAULT_ANGULAR_COEF };
129  float _centerWeight { DEFAULT_CENTER_COEF };
130  float _ageWeight { DEFAULT_AGE_COEF };
131  quint64 _usecCurrentTime { 0 };
132  };
133 } // namespace PrioritySortUtil
134 
135  // for now we're keeping hard-coded sorted time budgets in one spot
136 const uint64_t MAX_UPDATE_RENDERABLES_TIME_BUDGET = 2000; // usec
137 const uint64_t MIN_SORTED_UPDATE_RENDERABLES_TIME_BUDGET = 1000; // usec
138 const uint64_t MAX_UPDATE_AVATARS_TIME_BUDGET = 2000; // usec
139 
140 #endif // hifi_PrioritySortUtil_h