12 #ifndef hifi_render_Octree_h
13 #define hifi_render_Octree_h
21 #include <glm/glm.hpp>
22 #include <glm/gtx/bit.hpp>
32 std::vector<ItemID> items;
33 std::vector<ItemID> subcellItems;
41 using LinkStorage = int8_t;
42 enum Link : LinkStorage {
56 BrickLink = Parent + 1,
58 NUM_LINKS = BrickLink + 1,
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];
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); }
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;
109 static Coord depthBitmask(Depth depth) {
return Coord(1 << (MAX_DEPTH - depth)); }
111 static Depth coordToDepth(Coord length) {
112 Depth depth = MAX_DEPTH;
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)));
126 using vector = std::vector< Location >;
129 Location(
const Coord3& xyz, Depth d) : pos(xyz), depth(d) { assertValid(); }
130 Location(Depth d) : pos(0), depth(d) { assertValid(); }
134 Depth depth { ROOT_DEPTH };
136 Coord3f getCenter()
const {
137 Coord3f center = (Coord3f(pos) + Coord3f(0.5f)) * Coord3f(Octree::getInvDepthDimension(depth));
141 bool operator== (
const Location& other)
const {
return pos == other.pos && depth == other.depth; }
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)); }
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) }; }
154 static vector pathTo(
const Location& destination);
157 static Location evalFromRange(
const Coord3& minCoord,
const Coord3& maxCoord, Depth rangeDepth = MAX_DEPTH);
166 static Intersection intersectCell(
const Location& cell,
const Coord4f frustum[6]);
168 using Locations = Location::vector;
171 using Index = ItemCell;
172 static const Index INVALID_CELL = -1;
173 static const Index ROOT_CELL = 0;
176 static const Index MAXIMUM_INDEX = INT_MAX;
178 using Indices = std::vector<Index>;
184 enum BitFlags : uint8_t {
189 void free() { _location = Location();
for (
auto& link : _links) { link = INVALID_CELL; } }
191 const Location& getlocation()
const {
return _location; }
193 Index parent()
const {
return _links[Parent]; }
194 bool hasParent()
const {
return parent() != INVALID_CELL; }
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;
204 if (!checkHasChildren()) {
205 _location.spare &= ~HasChildren;
210 Index brick()
const {
return _links[BrickLink]; }
211 bool hasBrick()
const {
return _links[BrickLink] != INVALID_CELL; }
212 void setBrick(Index brick) { _links[BrickLink] = brick; }
214 void setBrickFilled() { _location.spare |= BrickFilled; }
215 void setBrickEmpty() { _location.spare &= ~BrickFilled; }
216 bool isBrickEmpty()
const {
return !(_location.spare & BrickFilled); }
219 _links({ { INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL, INVALID_CELL } })
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 } }),
228 std::array<Index, NUM_LINKS> _links;
231 bool checkHasChildren()
const {
232 for (LinkStorage octant = Octant_L_B_N; octant < NUM_OCTANTS; octant++) {
233 if (hasChild((Link)octant)) {
240 using Cells = std::vector< Cell >;
242 using Bricks = std::vector< Brick >;
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())); }
253 void cleanCellBranch(Index index);
257 Indices indexCellPath(
const Locations& path);
258 Index indexCell(
const Location& loc) {
return indexCellPath(Location::pathTo(loc)).back(); }
262 Indices indexConcreteCellPath(
const Locations& path)
const;
265 Location getCellLocation(Index cellID)
const {
266 if (checkCellIndex(cellID)) {
267 return getConcreteCell(cellID).getlocation();
273 const Cell& getConcreteCell(Index index)
const {
274 assert(checkCellIndex(index));
275 return _cells[index];
279 using CellBrickAccessor = std::function<void(Cell& cell, Brick& brick, Index brickIdx)>;
283 Index accessCellBrick(Index cellID,
const CellBrickAccessor& accessor,
bool createBrick =
true);
285 const Brick& getConcreteBrick(Index index)
const {
286 assert(checkBrickIndex(index));
287 return _bricks[index];
292 class CellSelection {
295 Indices insideBricks;
296 Indices partialCells;
297 Indices partialBricks;
299 Indices& cells(
bool inside) {
return (inside ? insideCells : partialCells); }
300 Indices& bricks(
bool inside) {
return (inside ? insideBricks : partialBricks); }
302 size_t size()
const {
return insideBricks.size() + partialBricks.size(); }
306 insideBricks.clear();
307 partialCells.clear();
308 partialBricks.clear();
312 class FrustumSelector {
316 virtual ~FrustumSelector() {}
317 virtual float testThreshold(
const Coord3f& point,
float size)
const = 0;
320 class PerspectiveSelector :
public FrustumSelector {
324 float squareTanAlpha;
326 void setAngle(
float a);
327 float testThreshold(
const Coord3f& point,
float size)
const override;
330 class OrthographicSelector :
public FrustumSelector {
334 void setSize(
float a) { squareMinSize = a * a; }
335 float testThreshold(
const Coord3f& point,
float size)
const override;
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;
344 int getNumAllocatedCells()
const {
return (
int)_cells.size(); }
345 int getNumFreeCells()
const {
return (
int)_freeCells.size(); }
348 Index allocateCell(Index parent,
const Location& location);
349 void freeCell(Index index);
351 Index allocateBrick();
352 void freeBrick(Index index);
354 Cell& editCell(Index index) {
355 assert(checkCellIndex(index));
356 return _cells[index];
361 Cells _cells = Cells(1, Cell());
373 class ItemSpatialTree :
public Octree {
374 float _size{ 32768.0f };
375 float _invSize { 1.0f / _size };
376 glm::vec3 _origin { -16384.0f };
378 void init(glm::vec3 origin,
float size) {
380 _invSize = 1.0f / _size;
385 ItemSpatialTree(glm::vec3 origin,
float size) { init(origin, size); }
387 float getSize()
const {
return _size; }
388 const glm::vec3& getOrigin()
const {
return _origin; }
390 float getCellWidth(Depth depth)
const {
return _size * getInvDepthDimension(depth); }
391 float getInvCellWidth(Depth depth)
const {
return getDepthDimensionf(depth) * _invSize; }
393 float getCellHalfDiagonalSquare(Depth depth)
const {
394 float cellHalfWidth = 0.5f * getCellWidth(depth);
395 return 3.0f * cellHalfWidth * cellHalfWidth;
398 glm::vec3 evalPos(
const Coord3& coord, Depth depth = Octree::METRIC_COORD_DEPTH)
const {
399 return getOrigin() + glm::vec3(coord) * getCellWidth(depth);
401 glm::vec3 evalPos(
const Coord3& coord,
float cellWidth)
const {
402 return getOrigin() + glm::vec3(coord) * cellWidth;
407 glm::vec3 clampRelPosToTreeRange(
const glm::vec3& pos)
const {
408 const float EPSILON = 0.0001f;
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));
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));
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));
431 AABox evalBound(
const Location& loc)
const {
432 float cellWidth = getCellWidth(loc.depth);
433 return AABox(evalPos(loc.pos, cellWidth), cellWidth);
438 Location evalLocation(
const AABox& bound, Coord3f& minCoordf, Coord3f& maxCoordf)
const;
439 Locations evalLocations(
const ItemBounds& bounds)
const;
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);
447 Index resetItem(Index oldCell,
const ItemKey& oldKey,
const AABox& bound,
const ItemID& item, ItemKey& newKey);
450 int selectCells(CellSelection& selection,
const ViewFrustum& frustum,
float threshold)
const;
452 class ItemSelection {
454 CellSelection cellSelection;
456 ItemIDs insideSubcellItems;
457 ItemIDs partialItems;
458 ItemIDs partialSubcellItems;
460 ItemIDs& items(
bool inside) {
return (inside ? insideItems : partialItems); }
461 ItemIDs& subcellItems(
bool inside) {
return (inside ? insideSubcellItems : partialSubcellItems); }
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(); }
468 cellSelection.clear();
470 insideSubcellItems.clear();
471 partialItems.clear();
472 partialSubcellItems.clear();
476 int selectCellItems(ItemSelection& selection,
const ItemFilter& filter,
const ViewFrustum& frustum,
477 float threshold)
const;