Octree.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. 
  2. #ifndef DFPSR_OCTREE
  3. #define DFPSR_OCTREE
  4. #include "../../DFPSR/includeFramework.h"
  5. #include <memory>
  6. namespace dsr {
  7. enum class LeafAction {
  8. None,
  9. Erase // Erase the leaf as soon as possible
  10. };
  11. // Used for recursively selecting parts of the tree to enter
  12. // bool(const IVector3D& minBound, const IVector3D& maxBound)
  13. using OcTreeFilter = std::function<bool(const IVector3D&, const IVector3D&)>;
  14. // Used to read data from the tree
  15. // void(T& content, const IVector3D origin, const IVector3D minBound, const IVector3D maxBound)
  16. template <typename T>
  17. using OcTreeLeafOperation = std::function<LeafAction(T&, const IVector3D, const IVector3D, const IVector3D)>;
  18. // A leaf
  19. template <typename T>
  20. class OctreeLeaf {
  21. public:
  22. T content;
  23. IVector3D origin, minBound, maxBound;
  24. public:
  25. OctreeLeaf(const T& content, const IVector3D origin, const IVector3D minBound, const IVector3D maxBound)
  26. : content(content), origin(origin), minBound(minBound), maxBound(maxBound) {}
  27. public:
  28. LeafAction find(const OcTreeFilter& boundFilter, const OcTreeLeafOperation<T>& leafOperation) {
  29. if (boundFilter(this->minBound, this->maxBound)) {
  30. return leafOperation(this->content, this->origin, this->minBound, this->maxBound);
  31. } else {
  32. return LeafAction::None;
  33. }
  34. }
  35. };
  36. static const uint32_t Octree_MaskX = 1u;
  37. static const uint32_t Octree_MaskY = 2u;
  38. static const uint32_t Octree_MaskZ = 4u;
  39. inline int Octree_getBranchIndex(bool pX, bool pY, bool pZ) {
  40. return (int)((pX ? Octree_MaskX : 0u) | (pY ? Octree_MaskY : 0u) | (pZ ? Octree_MaskZ : 0u));
  41. }
  42. struct IBox3D {
  43. IVector3D min, max;
  44. IBox3D(const IVector3D& min, const IVector3D& max) : min(min), max(max) {}
  45. };
  46. inline IBox3D splitBound(const IBox3D& parent, int branchIndex) {
  47. assert(branchIndex >= 0 && branchIndex < 8);
  48. IVector3D size = (parent.max - parent.min) / 2;
  49. assert(size.x > 0 && size.y > 0 && size.z > 0);
  50. IVector3D minBound = parent.min;
  51. if ((uint32_t)branchIndex & Octree_MaskX) { minBound.x += size.x; }
  52. if ((uint32_t)branchIndex & Octree_MaskY) { minBound.y += size.y; }
  53. if ((uint32_t)branchIndex & Octree_MaskZ) { minBound.z += size.z; }
  54. IVector3D maxBound = minBound + size;
  55. return IBox3D(minBound, maxBound);
  56. }
  57. // A branch
  58. template <typename T>
  59. class OctreeNode {
  60. public:
  61. // The ownership telling if a leaf of origin belongs here
  62. IVector3D minOwnedBound, maxOwnedBound;
  63. // The combined bounding box of all children, which may exceed the owned bound by the largest leaf radius measured from the origin
  64. IVector3D minLeafBound, maxLeafBound;
  65. // When divided, any added leaves will try to be inserted into child nodes when possible
  66. // Leaves that are too large may stay at the parent node
  67. bool divided = false;
  68. // One optional child node for each of the 8 sections in the octree
  69. std::shared_ptr<OctreeNode<T>> childNodes[8]; // Used for branching when getting too crowded with direct leaves
  70. // The leaves that have not yet been assigned to a specific child node
  71. List<OctreeLeaf<T>> leaves;
  72. public:
  73. // Copy constructor
  74. OctreeNode(const OctreeNode<T>& original)
  75. : minOwnedBound(original.minOwnedBound),
  76. maxOwnedBound(original.maxOwnedBound),
  77. minLeafBound(original.minLeafBound),
  78. maxLeafBound(original.maxLeafBound),
  79. divided(original.divided),
  80. leaves(original.leaves) {
  81. for (int n = 0; n < 8; n++) {
  82. this->childNodes[n] = original.childNodes[n];
  83. }
  84. }
  85. // Create from first leaf
  86. OctreeNode(const OctreeLeaf<T>& firstLeaf, const IVector3D& minOwnedBound, const IVector3D& maxOwnedBound)
  87. : minOwnedBound(minOwnedBound), maxOwnedBound(maxOwnedBound), minLeafBound(firstLeaf.minBound), maxLeafBound(firstLeaf.maxBound), divided(false) {
  88. this->leaves.push(firstLeaf);
  89. }
  90. // Create from first child node
  91. OctreeNode(const OctreeNode<T>& firstBranch, int firstBranchIndex, const IVector3D& minOwnedBound, const IVector3D& maxOwnedBound)
  92. : minOwnedBound(minOwnedBound), maxOwnedBound(maxOwnedBound), minLeafBound(firstBranch.minLeafBound), maxLeafBound(firstBranch.maxLeafBound), divided(true) {
  93. this->childNodes[firstBranchIndex] = std::make_shared<OctreeNode<T>>(firstBranch);
  94. }
  95. public:
  96. bool insideLeafBound(const IVector3D& origin) {
  97. return origin.x >= this->minLeafBound.x && origin.y >= this->minLeafBound.y && origin.z >= this->minLeafBound.z
  98. && origin.x <= this->maxLeafBound.x && origin.y <= this->maxLeafBound.y && origin.z <= this->maxLeafBound.z;
  99. }
  100. bool insideOwnedBound(const IVector3D& origin) {
  101. return origin.x >= this->minOwnedBound.x && origin.y >= this->minOwnedBound.y && origin.z >= this->minOwnedBound.z
  102. && origin.x <= this->maxOwnedBound.x && origin.y <= this->maxOwnedBound.y && origin.z <= this->maxOwnedBound.z;
  103. }
  104. // Get the branch index closest to the origin
  105. int getInnerBranchIndex() {
  106. return Octree_getBranchIndex(
  107. this->minOwnedBound.x + this->maxOwnedBound.x < 0,
  108. this->minOwnedBound.y + this->maxOwnedBound.y < 0,
  109. this->minOwnedBound.z + this->maxOwnedBound.z < 0
  110. );
  111. }
  112. // Returns true iff the given leaf is allowed to create a new branch
  113. bool mayBranch(const OctreeLeaf<T>& leaf) {
  114. IVector3D leafDimensions = leaf.maxBound - leaf.minBound;
  115. IVector3D maxDimensions = (this->maxOwnedBound - this->minOwnedBound) / 4;
  116. return this->divided == true
  117. && leafDimensions.x <= maxDimensions.x
  118. && leafDimensions.y <= maxDimensions.y
  119. && leafDimensions.z <= maxDimensions.z;
  120. }
  121. void insert(const OctreeLeaf<T>& leaf) {
  122. // Update leaf bounds along each passed node along the way
  123. if (leaf.minBound.x < this->minLeafBound.x) { this->minLeafBound.x = leaf.minBound.x; }
  124. if (leaf.minBound.y < this->minLeafBound.y) { this->minLeafBound.y = leaf.minBound.y; }
  125. if (leaf.minBound.z < this->minLeafBound.z) { this->minLeafBound.z = leaf.minBound.z; }
  126. if (leaf.maxBound.x > this->maxLeafBound.x) { this->maxLeafBound.x = leaf.maxBound.x; }
  127. if (leaf.maxBound.y > this->maxLeafBound.y) { this->maxLeafBound.y = leaf.maxBound.y; }
  128. if (leaf.maxBound.z > this->maxLeafBound.z) { this->maxLeafBound.z = leaf.maxBound.z; }
  129. // Make sure that the origin is inside of the owned bound by creating new parents until the point is covered
  130. while (!this->insideOwnedBound(leaf.origin)) {
  131. if (this->minOwnedBound.x < -100000000 || this->maxOwnedBound.x > 100000000) {
  132. throwError("Cannot expand (", this->minOwnedBound, ")..(", this->maxOwnedBound, ") to include ", leaf.origin, "! The origin must be given to the correct side of the octree's root.\n");
  133. }
  134. // Create a new parent node containing the old one
  135. OctreeNode newParent = OctreeNode(*this, this->getInnerBranchIndex(), this->minOwnedBound * 2, this->maxOwnedBound * 2);
  136. // Replace the content of this node with the new parent so that pointers to the old node leads to it
  137. *this = newParent;
  138. }
  139. // Try inserting into any child node
  140. for (int n = 0; n < 8; n++) {
  141. if (this->childNodes[n].get() != nullptr && this->childNodes[n]->insideOwnedBound(leaf.origin)) {
  142. this->childNodes[n]->insert(leaf);
  143. return; // Avoid inserting into multiple nodes
  144. }
  145. }
  146. // If there's no matching branch that can contain it, check if a new branch should be created for it
  147. if (this->mayBranch(leaf)) {
  148. // Create a new branch for the leaf
  149. IVector3D middle = (this->minOwnedBound + this->maxOwnedBound) / 2;
  150. int newBranchIndex = Octree_getBranchIndex(leaf.origin.x >= middle.x, leaf.origin.y >= middle.y, leaf.origin.z >= middle.z);
  151. assert(this->childNodes[newBranchIndex].get() == nullptr);
  152. IBox3D childRegion = splitBound(IBox3D(this->minOwnedBound, this->maxOwnedBound), newBranchIndex);
  153. this->childNodes[newBranchIndex] = std::make_shared<OctreeNode<T>>(leaf, childRegion.min, childRegion.max);
  154. } else {
  155. // Add the leaf
  156. this->leaves.push(leaf);
  157. // Split when the direct leaves are too many
  158. if (this->leaves.length() > 64) {
  159. // Split the node into branches
  160. this->divided = true;
  161. List<OctreeLeaf<T>> oldLeaves = this->leaves;
  162. this->leaves.clear();
  163. for (int l = 0; l < oldLeaves.length(); l++) {
  164. this->insert(oldLeaves[l]);
  165. }
  166. }
  167. }
  168. }
  169. void find(const OcTreeFilter& boundFilter, const OcTreeLeafOperation<T>& leafOperation) {
  170. if (boundFilter(this->minLeafBound, this->maxLeafBound)) {
  171. // Looping backwards over leaves allow erasing without stepping into recently moved elements
  172. for (int l = this->leaves.length() - 1; l >= 0; l--) {
  173. if (this->leaves[l].find(boundFilter, leafOperation) == LeafAction::Erase) {
  174. this->leaves.remove(l);
  175. }
  176. }
  177. // Search in the other branches recursively
  178. for (int n = 0; n < 8; n++) {
  179. if (this->childNodes[n].get() != nullptr) {
  180. this->childNodes[n]->find(boundFilter, leafOperation);
  181. }
  182. }
  183. }
  184. }
  185. };
  186. // The root
  187. template <typename T>
  188. class Octree {
  189. private:
  190. // One start node for each direction to simplify expansion
  191. std::shared_ptr<OctreeNode<T>> childNodes[8];
  192. // Settings
  193. int initialSize; // Should be around the average total world size to create the most balanced trees
  194. public:
  195. explicit Octree(int initialSize)
  196. : initialSize(initialSize) {}
  197. public:
  198. // Precondition: minBound <= origin <= maxBound
  199. void insert(const T& leaf, const IVector3D& origin, const IVector3D& minBound, const IVector3D& maxBound) {
  200. int sideIndex = Octree_getBranchIndex(origin.x >= 0, origin.y >= 0, origin.z >= 0);
  201. if (this->childNodes[sideIndex].get() == nullptr) {
  202. // Calculate minimum required size
  203. int requiredSize = std::max(abs(origin.x), std::max(abs(origin.y), abs(origin.z)));
  204. // Calculate final cube size to be stored directly inside of the root
  205. int size = this->initialSize;
  206. while(size < requiredSize) {
  207. size *= 2;
  208. }
  209. IVector3D minOwnedBound = IVector3D(
  210. origin.x < 0 ? -size : 0,
  211. origin.y < 0 ? -size : 0,
  212. origin.z < 0 ? -size : 0
  213. );
  214. IVector3D maxOwnedBound = IVector3D(
  215. origin.x < 0 ? 0 : size,
  216. origin.y < 0 ? 0 : size,
  217. origin.z < 0 ? 0 : size
  218. );
  219. this->childNodes[sideIndex] = std::make_shared<OctreeNode<T>>(OctreeLeaf<T>(leaf, origin, minBound, maxBound), minOwnedBound, maxOwnedBound);
  220. } else {
  221. this->childNodes[sideIndex]->insert(OctreeLeaf<T>(leaf, origin, minBound, maxBound));
  222. }
  223. }
  224. // Find leaves using a custom filter
  225. void map(const OcTreeFilter& boundFilter, const OcTreeLeafOperation<T>& leafOperation) {
  226. for (int sideIndex = 0; sideIndex < 8; sideIndex++) {
  227. if (this->childNodes[sideIndex].get() != nullptr) {
  228. this->childNodes[sideIndex]->find(boundFilter, leafOperation);
  229. }
  230. }
  231. }
  232. // Find leaves using an axis aligned search box
  233. // Each leaf who's bounding box is touching the search box will be given as argument to the leafOperation callback
  234. void map(const IVector3D& searchMinBound, const IVector3D& searchMaxBound, const OcTreeLeafOperation<T>& leafOperation) {
  235. this->map([searchMinBound, searchMaxBound](const IVector3D& minBound, const IVector3D& maxBound){
  236. return searchMaxBound.x >= minBound.x && searchMinBound.x <= maxBound.x
  237. && searchMaxBound.y >= minBound.y && searchMinBound.y <= maxBound.y
  238. && searchMaxBound.z >= minBound.z && searchMinBound.z <= maxBound.z;
  239. }, leafOperation);
  240. }
  241. };
  242. }
  243. #endif