Octree.h 11 KB

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