AABBTreeToBuffer.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #pragma once
  4. #include <Jolt/AABBTree/AABBTreeBuilder.h>
  5. #include <Jolt/Core/ByteBuffer.h>
  6. #include <Jolt/Geometry/IndexedTriangle.h>
  7. JPH_SUPPRESS_WARNINGS_STD_BEGIN
  8. #include <deque>
  9. JPH_SUPPRESS_WARNINGS_STD_END
  10. JPH_NAMESPACE_BEGIN
  11. /// How the tree should be converted
  12. enum class EAABBTreeToBufferConvertMode
  13. {
  14. DepthFirst, ///< Arrange the nodes depth first, put the triangles right after the leaf nodes (so interleaving them with nodes)
  15. DepthFirstTrianglesLast, ///< Arrange the nodes depth first and put all triangles blocks after the last node block
  16. BreadthFirst, ///< Arrange the nodes breadth first, put the triangles right after the leaf nodes (so interleaving them with nodes)
  17. BreadthFirstTrianglesLast, ///< Arrange the nodes breadth first and put all triangles blocks after the last node block
  18. };
  19. /// Convert mode to string
  20. inline string ConvertToString(EAABBTreeToBufferConvertMode inConvertMode)
  21. {
  22. switch (inConvertMode)
  23. {
  24. case EAABBTreeToBufferConvertMode::DepthFirst: return "DepthFirst";
  25. case EAABBTreeToBufferConvertMode::DepthFirstTrianglesLast: return "DepthFirstTrianglesLast";
  26. case EAABBTreeToBufferConvertMode::BreadthFirst: return "BreadthFirst";
  27. case EAABBTreeToBufferConvertMode::BreadthFirstTrianglesLast: return "BreadthFirstTrianglesLast";
  28. }
  29. JPH_ASSERT(false);
  30. return "Invalid";
  31. }
  32. /// Struct that holds statistics about the AABB tree that was built
  33. struct AABBTreeToBufferStats
  34. {
  35. uint mTotalSize = 0; ///< Total size of the built tree in bytes
  36. uint mNodesSize = 0; ///< Total size of all nodes in the tree in bytes
  37. uint mTrianglesSize = 0; ///< Total size of all triangles in the tree in bytes
  38. float mBytesPerTriangle = 0.0f; ///< Average number of bytes per triangle (includes all tree overhead)
  39. string mTriangleCodecName; ///< Name of the codec that was used to build the tree
  40. float mVerticesPerTriangle = 0.0f; ///< How many vertices a triangle on average has
  41. };
  42. /// Conversion algorithm that converts an AABB tree to an optimized binary buffer
  43. template <class TriangleCodec, class NodeCodec>
  44. class AABBTreeToBuffer
  45. {
  46. public:
  47. /// Header for the tree
  48. using NodeHeader = typename NodeCodec::Header;
  49. /// Size in bytes of the header of the tree
  50. static const int HeaderSize = NodeCodec::HeaderSize;
  51. /// Maximum number of children per node in the tree
  52. static const int NumChildrenPerNode = NodeCodec::NumChildrenPerNode;
  53. /// Header for the triangles
  54. using TriangleHeader = typename TriangleCodec::TriangleHeader;
  55. /// Size in bytes of the header for the triangles
  56. static const int TriangleHeaderSize = TriangleCodec::TriangleHeaderSize;
  57. /// Convert AABB tree. Returns false if failed.
  58. bool Convert(const VertexList &inVertices, const AABBTreeBuilder::Node *inRoot, AABBTreeToBufferStats &outStats, string &outError, EAABBTreeToBufferConvertMode inConvertMode = EAABBTreeToBufferConvertMode::DepthFirst)
  59. {
  60. const typename NodeCodec::EncodingContext node_ctx;
  61. typename TriangleCodec::EncodingContext tri_ctx;
  62. // Estimate the amount of memory required
  63. uint tri_count = inRoot->GetTriangleCountInTree();
  64. uint node_count = inRoot->GetNodeCount();
  65. uint nodes_size = node_ctx.GetPessimisticMemoryEstimate(node_count);
  66. uint total_size = HeaderSize + TriangleHeaderSize + nodes_size + tri_ctx.GetPessimisticMemoryEstimate(tri_count);
  67. mTree.reserve(total_size);
  68. // Reset counters
  69. mNodesSize = 0;
  70. // Add headers
  71. NodeHeader *header = HeaderSize > 0? mTree.Allocate<NodeHeader>() : nullptr;
  72. TriangleHeader *triangle_header = TriangleHeaderSize > 0? mTree.Allocate<TriangleHeader>() : nullptr;
  73. struct NodeData
  74. {
  75. const AABBTreeBuilder::Node * mNode = nullptr; // Node that this entry belongs to
  76. Vec3 mNodeBoundsMin; // Quantized node bounds
  77. Vec3 mNodeBoundsMax;
  78. uint mNodeStart = uint(-1); // Start of node in mTree
  79. uint mTriangleStart = uint(-1); // Start of the triangle data in mTree
  80. uint mNumChildren = 0; // Number of children
  81. uint mChildNodeStart[NumChildrenPerNode]; // Start of the children of the node in mTree
  82. uint mChildTrianglesStart[NumChildrenPerNode]; // Start of the triangle data in mTree
  83. uint * mParentChildNodeStart = nullptr; // Where to store mNodeStart (to patch mChildNodeStart of my parent)
  84. uint * mParentTrianglesStart = nullptr; // Where to store mTriangleStart (to patch mChildTrianglesStart of my parent)
  85. };
  86. deque<NodeData *> to_process;
  87. deque<NodeData *> to_process_triangles;
  88. vector<NodeData> node_list;
  89. node_list.reserve(node_count); // Needed to ensure that array is not reallocated, so we can keep pointers in the array
  90. NodeData root;
  91. root.mNode = inRoot;
  92. root.mNodeBoundsMin = inRoot->mBounds.mMin;
  93. root.mNodeBoundsMax = inRoot->mBounds.mMax;
  94. node_list.push_back(root);
  95. to_process.push_back(&node_list.back());
  96. // Child nodes out of loop so we don't constantly realloc it
  97. vector<const AABBTreeBuilder::Node *> child_nodes;
  98. child_nodes.reserve(NumChildrenPerNode);
  99. for (;;)
  100. {
  101. while (!to_process.empty())
  102. {
  103. // Get the next node to process
  104. NodeData *node_data = nullptr;
  105. switch (inConvertMode)
  106. {
  107. case EAABBTreeToBufferConvertMode::DepthFirst:
  108. case EAABBTreeToBufferConvertMode::DepthFirstTrianglesLast:
  109. node_data = to_process.back();
  110. to_process.pop_back();
  111. break;
  112. case EAABBTreeToBufferConvertMode::BreadthFirst:
  113. case EAABBTreeToBufferConvertMode::BreadthFirstTrianglesLast:
  114. node_data = to_process.front();
  115. to_process.pop_front();
  116. break;
  117. default:
  118. JPH_ASSERT(false);
  119. break;
  120. }
  121. // Due to quantization box could have become bigger, not smaller
  122. JPH_ASSERT(AABox(node_data->mNodeBoundsMin, node_data->mNodeBoundsMax).Contains(node_data->mNode->mBounds), "AABBTreeToBuffer: Bounding box became smaller!");
  123. // Collect the first NumChildrenPerNode sub-nodes in the tree
  124. child_nodes.clear(); // Won't free the memory
  125. node_data->mNode->GetNChildren(NumChildrenPerNode, child_nodes);
  126. node_data->mNumChildren = (uint)child_nodes.size();
  127. // Fill in default child bounds
  128. Vec3 child_bounds_min[NumChildrenPerNode], child_bounds_max[NumChildrenPerNode];
  129. for (size_t i = 0; i < NumChildrenPerNode; ++i)
  130. if (i < child_nodes.size())
  131. {
  132. child_bounds_min[i] = child_nodes[i]->mBounds.mMin;
  133. child_bounds_max[i] = child_nodes[i]->mBounds.mMax;
  134. }
  135. else
  136. {
  137. child_bounds_min[i] = Vec3::sZero();
  138. child_bounds_max[i] = Vec3::sZero();
  139. }
  140. // Start a new node
  141. uint old_size = (uint)mTree.size();
  142. node_data->mNodeStart = node_ctx.NodeAllocate(node_data->mNode, node_data->mNodeBoundsMin, node_data->mNodeBoundsMax, child_nodes, child_bounds_min, child_bounds_max, mTree, outError);
  143. if (node_data->mNodeStart == uint(-1))
  144. return false;
  145. mNodesSize += (uint)mTree.size() - old_size;
  146. if (node_data->mNode->HasChildren())
  147. {
  148. for (size_t i = 0; i < child_nodes.size(); ++i)
  149. {
  150. // Depth first: Insert in reverse order so we process left child first when taking nodes from the back
  151. size_t idx = (inConvertMode == EAABBTreeToBufferConvertMode::DepthFirst || inConvertMode == EAABBTreeToBufferConvertMode::DepthFirstTrianglesLast)?
  152. child_nodes.size() - 1 - i : i;
  153. // Due to quantization box could have become bigger, not smaller
  154. JPH_ASSERT(AABox(child_bounds_min[idx], child_bounds_max[idx]).Contains(child_nodes[idx]->mBounds), "AABBTreeToBuffer: Bounding box became smaller!");
  155. // Add child to list of nodes to be processed
  156. NodeData child;
  157. child.mNode = child_nodes[idx];
  158. child.mNodeBoundsMin = child_bounds_min[idx];
  159. child.mNodeBoundsMax = child_bounds_max[idx];
  160. child.mParentChildNodeStart = &node_data->mChildNodeStart[idx];
  161. child.mParentTrianglesStart = &node_data->mChildTrianglesStart[idx];
  162. NodeData *old = &node_list[0];
  163. node_list.push_back(child);
  164. if (old != &node_list[0])
  165. {
  166. outError = "Internal Error: Array reallocated, memory corruption!";
  167. return false;
  168. }
  169. switch (inConvertMode)
  170. {
  171. case EAABBTreeToBufferConvertMode::DepthFirst:
  172. case EAABBTreeToBufferConvertMode::BreadthFirst:
  173. to_process.push_back(&node_list.back());
  174. break;
  175. case EAABBTreeToBufferConvertMode::DepthFirstTrianglesLast:
  176. case EAABBTreeToBufferConvertMode::BreadthFirstTrianglesLast:
  177. if (node_list.back().mNode->HasChildren())
  178. to_process.push_back(&node_list.back());
  179. else
  180. to_process_triangles.push_back(&node_list.back());
  181. break;
  182. }
  183. }
  184. }
  185. else
  186. {
  187. // Add triangles
  188. node_data->mTriangleStart = tri_ctx.Pack(inVertices, node_data->mNode->mTriangles, mTree, outError);
  189. if (node_data->mTriangleStart == uint(-1))
  190. return false;
  191. }
  192. // Patch offset into parent
  193. if (node_data->mParentChildNodeStart != nullptr)
  194. {
  195. *node_data->mParentChildNodeStart = node_data->mNodeStart;
  196. *node_data->mParentTrianglesStart = node_data->mTriangleStart;
  197. }
  198. }
  199. // If we've got triangles to process, loop again with just the triangles
  200. if (to_process_triangles.empty())
  201. break;
  202. else
  203. to_process.swap(to_process_triangles);
  204. }
  205. // Finalize all nodes
  206. for (NodeData &n : node_list)
  207. if (!node_ctx.NodeFinalize(n.mNode, n.mNodeStart, n.mNumChildren, n.mChildNodeStart, n.mChildTrianglesStart, mTree, outError))
  208. return false;
  209. // Finalize the triangles
  210. tri_ctx.Finalize(triangle_header, mTree);
  211. // Get stats
  212. tri_ctx.GetStats(outStats.mTriangleCodecName, outStats.mVerticesPerTriangle);
  213. // Validate that we reserved enough memory
  214. if (nodes_size < mNodesSize)
  215. {
  216. outError = "Internal Error: Not enough memory reserved for nodes!";
  217. return false;
  218. }
  219. if (total_size < (uint)mTree.size())
  220. {
  221. outError = "Internal Error: Not enough memory reserved for triangles!";
  222. return false;
  223. }
  224. // Finalize the nodes
  225. if (!node_ctx.Finalize(header, inRoot, node_list[0].mNodeStart, node_list[0].mTriangleStart, outError))
  226. return false;
  227. // Shrink the tree, this will invalidate the header and triangle_header variables
  228. mTree.shrink_to_fit();
  229. // Output stats
  230. outStats.mTotalSize = (uint)mTree.size();
  231. outStats.mNodesSize = mNodesSize;
  232. outStats.mTrianglesSize = (uint)mTree.size() - mNodesSize;
  233. outStats.mBytesPerTriangle = (float)mTree.size() / tri_count;
  234. return true;
  235. }
  236. /// Get resulting data
  237. inline const ByteBuffer & GetBuffer() const
  238. {
  239. return mTree;
  240. }
  241. /// Get resulting data
  242. inline ByteBuffer & GetBuffer()
  243. {
  244. return mTree;
  245. }
  246. /// Get header for tree
  247. inline const NodeHeader * GetNodeHeader() const
  248. {
  249. return mTree.Get<NodeHeader>(0);
  250. }
  251. /// Get header for triangles
  252. inline const TriangleHeader * GetTriangleHeader() const
  253. {
  254. return mTree.Get<TriangleHeader>(HeaderSize);
  255. }
  256. /// Get root of resulting tree
  257. inline const void * GetRoot() const
  258. {
  259. return mTree.Get<void>(HeaderSize + TriangleHeaderSize);
  260. }
  261. private:
  262. ByteBuffer mTree; ///< Resulting tree structure
  263. uint mNodesSize; ///< Size in bytes of the nodes in the buffer
  264. };
  265. JPH_NAMESPACE_END