Overte C++ Documentation
SpatialTree.h
1 //
2 // SpatialTree.h
3 // render/src/render
4 //
5 // Created by Sam Gateau on 1/25/16.
6 // Copyright 2014 High Fidelity, Inc.
7 //
8 // Distributed under the Apache License, Version 2.0.
9 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
10 //
11 
12 #ifndef hifi_render_Octree_h
13 #define hifi_render_Octree_h
14 
15 #include <vector>
16 #include <memory>
17 #include <cassert>
18 #include <array>
19 #include <functional>
20 #include <limits>
21 #include <glm/glm.hpp>
22 #include <glm/gtx/bit.hpp>
23 #include <AABox.h>
24 
25 // maybe we could avoid the Item inclusion here for the OCtree class?
26 #include "Item.h"
27 
28 namespace render {
29 
30  class Brick {
31  public:
32  std::vector<ItemID> items;
33  std::vector<ItemID> subcellItems;
34 
35  void free() {};
36  };
37 
38  class Octree {
39  public:
40 
41  using LinkStorage = int8_t;
42  enum Link : LinkStorage {
43 
44  Octant_L_B_N = 0,
45  Octant_R_B_N = 1,
46  Octant_L_T_N = 2,
47  Octant_R_T_N = 3,
48  Octant_L_B_F = 4,
49  Octant_R_B_F = 5,
50  Octant_L_T_F = 6,
51  Octant_R_T_F = 7,
52 
53  NUM_OCTANTS,
54 
55  Parent = NUM_OCTANTS,
56  BrickLink = Parent + 1,
57 
58  NUM_LINKS = BrickLink + 1,
59 
60 
61  XAxis = 0x01,
62  YAxis = 0x02,
63  ZAxis = 0x04,
64 
65  NoLink = 0x0F,
66  };
67 
68  using Octant = Link;
69 
70 
71  // Depth, Dim, Volume
72  // {0, 1, 1}
73  // {1, 2, 8}
74  // {2, 4, 64}
75  // {3, 8, 512}
76  // {4, 16, 4096}
77  // {5, 32, 32768}
78  // {6, 64, 262144}
79  // {7, 128, 2097152}
80  // {8, 256, 16777216}
81  // {9, 512, 134217728}
82  // {10, 1024, 1073741824}
83  // {11, 2048, 8589934592}
84  // {12, 4096, 68719476736}
85  // {13, 8192, 549755813888}
86  // {14, 16384, 4398046511104}
87  // {15, 32768, 35184372088832}
88  // {16, 65536, 281474976710656}
89 
90  // Max depth is 15 => 32Km root down to 1m cells
91  using Depth = int8_t;
92  static const Depth ROOT_DEPTH { 0 };
93  static const Depth MAX_DEPTH { 15 };
94  static const Depth METRIC_COORD_DEPTH { 15 };
95  static const float INV_DEPTH_DIM[Octree::MAX_DEPTH + 1];
96 
97  static int getDepthDimension(Depth depth) { return 1 << depth; }
98  static float getDepthDimensionf(Depth depth) { return (float) getDepthDimension(depth); }
99  static float getInvDepthDimension(Depth depth) { return INV_DEPTH_DIM[depth]; }
100  static float getCoordSubcellWidth(Depth depth) { return (1.7320f * getInvDepthDimension(depth) * 0.5f); }
101 
102  // Need 16bits integer coordinates on each axes: 32768 cell positions
103  using Coord = int16_t;
104  using Coord3 = glm::i16vec3;
105  using Coord4 = glm::i16vec4;
106  using Coord3f = glm::vec3;
107  using Coord4f = glm::vec4;
108 
109  static Coord depthBitmask(Depth depth) { return Coord(1 << (MAX_DEPTH - depth)); }
110 
111  static Depth coordToDepth(Coord length) {
112  Depth depth = MAX_DEPTH;
113  while (length) {
114  length >>= 1;
115  depth--;
116  }
117  return depth;
118  }
119 
120  class Location {
121  void assertValid() {
122  assert((pos.x >= 0) && (pos.y >= 0) && (pos.z >= 0));
123  assert((pos.x < (1 << depth)) && (pos.y < (1 << depth)) && (pos.z < (1 << depth)));
124  }
125  public:
126  using vector = std::vector< Location >;
127 
128  Location() {}
129  Location(const Coord3& xyz, Depth d) : pos(xyz), depth(d) { assertValid(); }
130  Location(Depth d) : pos(0), depth(d) { assertValid(); }
131 
132  Coord3 pos { 0 };
133  uint8_t spare { 0 };
134  Depth depth { ROOT_DEPTH };
135 
136  Coord3f getCenter() const {
137  Coord3f center = (Coord3f(pos) + Coord3f(0.5f)) * Coord3f(Octree::getInvDepthDimension(depth));
138  return center;
139  }
140 
141  bool operator== (const Location& other) const { return pos == other.pos && depth == other.depth; }
142 
143  // Eval the octant of this cell relative to its parent
144  Octant octant() const { return Octant((pos.x & 1) | ((pos.y & 1) << 1) | ((pos.z & 1) << 2)); }
145  Coord3 octantAxes(Link octant) const { return Coord3((Coord)bool(octant & XAxis), (Coord)bool(octant & YAxis), (Coord)bool(octant & ZAxis)); }
146 
147  // Get the Parent cell Location of this cell
148  Location parent() const { return Location{ (pos >> Coord3(1)), Depth(depth <= 0 ? 0 : depth - 1) }; }
149  Location child(Link octant) const { return Location{ (pos << Coord3(1)) | octantAxes(octant), Depth(depth + 1) }; }
150 
151 
152  // Eval the list of locations (the path) from root to the destination.
153  // the root cell is included
154  static vector pathTo(const Location& destination);
155 
156  // Eval the location best fitting the specified range
157  static Location evalFromRange(const Coord3& minCoord, const Coord3& maxCoord, Depth rangeDepth = MAX_DEPTH);
158 
159 
160  // Eval the intersection test against a frustum
161  enum Intersection {
162  Outside = 0,
163  Intersect,
164  Inside,
165  };
166  static Intersection intersectCell(const Location& cell, const Coord4f frustum[6]);
167  };
168  using Locations = Location::vector;
169 
170  // Cell or Brick Indexing
171  using Index = ItemCell; // int32_t
172  static const Index INVALID_CELL = -1;
173  static const Index ROOT_CELL = 0;
174 
175  // With a maximum of INT_MAX(2 ^ 31) cells in our octree
176  static const Index MAXIMUM_INDEX = INT_MAX; // For fun, test setting this to 100 and this should still works with only the allocated maximum number of cells
177 
178  using Indices = std::vector<Index>;
179 
180  // the cell description
181  class Cell {
182  public:
183 
184  enum BitFlags : uint8_t {
185  HasChildren = 0x01,
186  BrickFilled = 0x02,
187  };
188 
189  void free() { _location = Location(); for (auto& link : _links) { link = INVALID_CELL; } }
190 
191  const Location& getlocation() const { return _location; }
192 
193  Index parent() const { return _links[Parent]; }
194  bool hasParent() const { return parent() != INVALID_CELL; }
195 
196  Index child(Link octant) const { return _links[octant]; }
197  bool hasChild(Link octant) const { return child(octant) != INVALID_CELL; }
198  bool hasChildren() const { return (_location.spare & HasChildren); }
199  void setChild(Link octant, Index child) {
200  _links[octant] = child;
201  if (child != INVALID_CELL) {
202  _location.spare |= HasChildren;
203  } else {
204  if (!checkHasChildren()) {
205  _location.spare &= ~HasChildren;
206  }
207  }
208  }
209 
210  Index brick() const { return _links[BrickLink]; }
211  bool hasBrick() const { return _links[BrickLink] != INVALID_CELL; }
212  void setBrick(Index brick) { _links[BrickLink] = brick; }
213 
214  void setBrickFilled() { _location.spare |= BrickFilled; }
215  void setBrickEmpty() { _location.spare &= ~BrickFilled; }
216  bool isBrickEmpty() const { return !(_location.spare & BrickFilled); }
217 
218  Cell() :
219  _links({ { INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL } })
220  {}
221 
222  Cell(Index parent, Location loc) :
223  _links({ { INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL, parent, INVALID_CELL } }),
224  _location(loc)
225  {}
226 
227  private:
228  std::array<Index, NUM_LINKS> _links;
229  Location _location;
230 
231  bool checkHasChildren() const {
232  for (LinkStorage octant = Octant_L_B_N; octant < NUM_OCTANTS; octant++) {
233  if (hasChild((Link)octant)) {
234  return true;
235  }
236  }
237  return false;
238  }
239  };
240  using Cells = std::vector< Cell >;
241 
242  using Bricks = std::vector< Brick >;
243 
244  bool checkCellIndex(Index index) const { return (index >= 0) && (index < (Index) _cells.size()); }
245  bool checkBrickIndex(Index index) const { return ((index >= 0) && (index < (Index) _bricks.size())); }
246 
247  Octree() {};
248 
249  // Clean a cell branch starting from the leave:
250  // Check that the cell brick is empty, if so free it else stop
251  // Check that the cell has no children, if so free itself else stop
252  // Apply the same logic to the parent cell
253  void cleanCellBranch(Index index);
254 
255  // Indexing/Allocating the cells as the tree gets populated
256  // Return the cell Index/Indices at the specified location/path, allocate all the cells on the path from the root if needed
257  Indices indexCellPath(const Locations& path);
258  Index indexCell(const Location& loc) { return indexCellPath(Location::pathTo(loc)).back(); }
259 
260  // Same as indexCellPath except that NO cells are allocated, only the COncrete cells previously allocated
261  // the returned indices stops at the last existing cell on the requested path.
262  Indices indexConcreteCellPath(const Locations& path) const;
263 
264  // Get the cell location from the CellID
265  Location getCellLocation(Index cellID) const {
266  if (checkCellIndex(cellID)) {
267  return getConcreteCell(cellID).getlocation();
268  }
269  return Location();
270  }
271 
272  // Reach a concrete cell
273  const Cell& getConcreteCell(Index index) const {
274  assert(checkCellIndex(index));
275  return _cells[index];
276  }
277 
278  // Let s talk about the Cell Bricks now
279  using CellBrickAccessor = std::function<void(Cell& cell, Brick& brick, Index brickIdx)>;
280 
281  // acces a cell (must be concrete), then call the brick accessor if the brick exists ( or is just created if authorized to)
282  // This returns the Brick index
283  Index accessCellBrick(Index cellID, const CellBrickAccessor& accessor, bool createBrick = true);
284 
285  const Brick& getConcreteBrick(Index index) const {
286  assert(checkBrickIndex(index));
287  return _bricks[index];
288  }
289 
290  // Cell Selection and traversal
291 
292  class CellSelection {
293  public:
294  Indices insideCells;
295  Indices insideBricks;
296  Indices partialCells;
297  Indices partialBricks;
298 
299  Indices& cells(bool inside) { return (inside ? insideCells : partialCells); }
300  Indices& bricks(bool inside) { return (inside ? insideBricks : partialBricks); }
301 
302  size_t size() const { return insideBricks.size() + partialBricks.size(); }
303 
304  void clear() {
305  insideCells.clear();
306  insideBricks.clear();
307  partialCells.clear();
308  partialBricks.clear();
309  }
310  };
311 
312  class FrustumSelector {
313  public:
314  Coord4f frustum[6];
315 
316  virtual ~FrustumSelector() {}
317  virtual float testThreshold(const Coord3f& point, float size) const = 0;
318  };
319 
320  class PerspectiveSelector : public FrustumSelector {
321  public:
322  Coord3f eyePos;
323  float angle;
324  float squareTanAlpha;
325 
326  void setAngle(float a);
327  float testThreshold(const Coord3f& point, float size) const override;
328  };
329 
330  class OrthographicSelector : public FrustumSelector {
331  public:
332  float squareMinSize;
333 
334  void setSize(float a) { squareMinSize = a * a; }
335  float testThreshold(const Coord3f& point, float size) const override;
336  };
337 
338  int select(CellSelection& selection, const FrustumSelector& selector) const;
339  int selectTraverse(Index cellID, CellSelection& selection, const FrustumSelector& selector) const;
340  int selectBranch(Index cellID, CellSelection& selection, const FrustumSelector& selector) const;
341  int selectCellBrick(Index cellID, CellSelection& selection, bool inside) const;
342 
343 
344  int getNumAllocatedCells() const { return (int)_cells.size(); }
345  int getNumFreeCells() const { return (int)_freeCells.size(); }
346 
347  protected:
348  Index allocateCell(Index parent, const Location& location);
349  void freeCell(Index index);
350 
351  Index allocateBrick();
352  void freeBrick(Index index);
353 
354  Cell& editCell(Index index) {
355  assert(checkCellIndex(index));
356  return _cells[index];
357  }
358 
359 
360  // Octree members
361  Cells _cells = Cells(1, Cell()); // start with only the Cell root
362  Bricks _bricks;
363  Indices _freeCells; // stack of free cells to be reused for allocation
364  Indices _freeBricks; // stack of free bricks to be reused for allocation
365  };
366 }
367 
368 // CLose the namespace here before including the Item in the picture, maybe Octre could stay pure of it
369 namespace render {
370 
371  // An octree of Items organizing them efficiently for culling
372  // The octree only cares about the bound & the key of an item to store it a the right cell location
373  class ItemSpatialTree : public Octree {
374  float _size{ 32768.0f };
375  float _invSize { 1.0f / _size };
376  glm::vec3 _origin { -16384.0f };
377 
378  void init(glm::vec3 origin, float size) {
379  _size = size;
380  _invSize = 1.0f / _size;
381  _origin = origin;
382  }
383  public:
384  // THe overall size and origin of the tree are defined at creation
385  ItemSpatialTree(glm::vec3 origin, float size) { init(origin, size); }
386 
387  float getSize() const { return _size; }
388  const glm::vec3& getOrigin() const { return _origin; }
389 
390  float getCellWidth(Depth depth) const { return _size * getInvDepthDimension(depth); }
391  float getInvCellWidth(Depth depth) const { return getDepthDimensionf(depth) * _invSize; }
392 
393  float getCellHalfDiagonalSquare(Depth depth) const {
394  float cellHalfWidth = 0.5f * getCellWidth(depth);
395  return 3.0f * cellHalfWidth * cellHalfWidth;
396  }
397 
398  glm::vec3 evalPos(const Coord3& coord, Depth depth = Octree::METRIC_COORD_DEPTH) const {
399  return getOrigin() + glm::vec3(coord) * getCellWidth(depth);
400  }
401  glm::vec3 evalPos(const Coord3& coord, float cellWidth) const {
402  return getOrigin() + glm::vec3(coord) * cellWidth;
403  }
404 
405 
406  // Clamp a 3D relative position to make sure it is in the valid range space of the octree
407  glm::vec3 clampRelPosToTreeRange(const glm::vec3& pos) const {
408  const float EPSILON = 0.0001f;
409  return glm::vec3(
410  std::min(std::max(pos.x, 0.0f), _size - EPSILON),
411  std::min(std::max(pos.y, 0.0f), _size - EPSILON),
412  std::min(std::max(pos.z, 0.0f), _size - EPSILON));
413  }
414 
415  // Eval an integer cell coordinate (at the specified deepth) from a given 3d position
416  // If the 3D position is out of the octree volume, then the position is clamped
417  // so the integer coordinate is meaningfull
418  Coord3 evalCoord(const glm::vec3& pos, Depth depth = Octree::METRIC_COORD_DEPTH) const {
419  auto npos = clampRelPosToTreeRange((pos - getOrigin()));
420  return Coord3(npos * getInvCellWidth(depth)); // Truncate fractional part
421  }
422 
423  // Eval a real cell coordinate (at the specified deepth) from a given 3d position
424  // Position is NOT clamped to the boundaries of the octree so beware of conversion to a Coord3!
425  Coord3f evalCoordf(const glm::vec3& pos, Depth depth = Octree::METRIC_COORD_DEPTH) const {
426  auto npos = (pos - getOrigin());
427  return Coord3f(npos * getInvCellWidth(depth));
428  }
429 
430  // Bound to Location
431  AABox evalBound(const Location& loc) const {
432  float cellWidth = getCellWidth(loc.depth);
433  return AABox(evalPos(loc.pos, cellWidth), cellWidth);
434  }
435 
436  // Eval the cell location for a given arbitrary Bound,
437  // if the Bound crosses any of the Octree planes then the root cell is returned
438  Location evalLocation(const AABox& bound, Coord3f& minCoordf, Coord3f& maxCoordf) const;
439  Locations evalLocations(const ItemBounds& bounds) const;
440 
441  // Managing itemsInserting items in cells
442  // Cells need to have been allocated first calling indexCell
443  Index insertItem(Index cellIdx, const ItemKey& key, const ItemID& item);
444  bool updateItem(Index cellIdx, const ItemKey& oldKey, const ItemKey& key, const ItemID& item);
445  bool removeItem(Index cellIdx, const ItemKey& key, const ItemID& item);
446 
447  Index resetItem(Index oldCell, const ItemKey& oldKey, const AABox& bound, const ItemID& item, ItemKey& newKey);
448 
449  // Selection and traverse
450  int selectCells(CellSelection& selection, const ViewFrustum& frustum, float threshold) const;
451 
452  class ItemSelection {
453  public:
454  CellSelection cellSelection;
455  ItemIDs insideItems;
456  ItemIDs insideSubcellItems;
457  ItemIDs partialItems;
458  ItemIDs partialSubcellItems;
459 
460  ItemIDs& items(bool inside) { return (inside ? insideItems : partialItems); }
461  ItemIDs& subcellItems(bool inside) { return (inside ? insideSubcellItems : partialSubcellItems); }
462 
463  size_t insideNumItems() const { return insideItems.size() + insideSubcellItems.size(); }
464  size_t partialNumItems() const { return partialItems.size() + partialSubcellItems.size(); }
465  size_t numItems() const { return insideNumItems() + partialNumItems(); }
466 
467  void clear() {
468  cellSelection.clear();
469  insideItems.clear();
470  insideSubcellItems.clear();
471  partialItems.clear();
472  partialSubcellItems.clear();
473  }
474  };
475 
476  int selectCellItems(ItemSelection& selection, const ItemFilter& filter, const ViewFrustum& frustum,
477  float threshold) const;
478  };
479 }
480 
481 #endif // hifi_render_Octree_h