NodeCodecQuadTreeHalfFloat.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
  2. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  3. // SPDX-License-Identifier: MIT
  4. #pragma once
  5. #include <Jolt/Core/ByteBuffer.h>
  6. #include <Jolt/Math/HalfFloat.h>
  7. #include <Jolt/AABBTree/AABBTreeBuilder.h>
  8. JPH_NAMESPACE_BEGIN
  9. class NodeCodecQuadTreeHalfFloat
  10. {
  11. public:
  12. /// Number of child nodes of this node
  13. static constexpr int NumChildrenPerNode = 4;
  14. /// Header for the tree
  15. struct Header
  16. {
  17. Float3 mRootBoundsMin;
  18. Float3 mRootBoundsMax;
  19. uint32 mRootProperties;
  20. uint8 mBlockIDBits; ///< Number of bits to address a triangle block
  21. uint8 mPadding[3] = { 0 };
  22. };
  23. /// Size of the header (an empty struct is always > 0 bytes so this needs a separate variable)
  24. static constexpr int HeaderSize = sizeof(Header);
  25. /// Stack size to use during DecodingContext::sWalkTree
  26. static constexpr int StackSize = 128;
  27. /// Node properties
  28. enum : uint32
  29. {
  30. TRIANGLE_COUNT_BITS = 4,
  31. TRIANGLE_COUNT_SHIFT = 28,
  32. TRIANGLE_COUNT_MASK = (1 << TRIANGLE_COUNT_BITS) - 1,
  33. OFFSET_BITS = 28,
  34. OFFSET_MASK = (1 << OFFSET_BITS) - 1,
  35. OFFSET_NON_SIGNIFICANT_BITS = 2,
  36. OFFSET_NON_SIGNIFICANT_MASK = (1 << OFFSET_NON_SIGNIFICANT_BITS) - 1,
  37. };
  38. /// Node structure
  39. struct Node
  40. {
  41. HalfFloat mBoundsMinX[4]; ///< 4 child bounding boxes
  42. HalfFloat mBoundsMinY[4];
  43. HalfFloat mBoundsMinZ[4];
  44. HalfFloat mBoundsMaxX[4];
  45. HalfFloat mBoundsMaxY[4];
  46. HalfFloat mBoundsMaxZ[4];
  47. uint32 mNodeProperties[4]; ///< 4 child node properties
  48. };
  49. static_assert(sizeof(Node) == 64, "Node should be 64 bytes");
  50. /// This class encodes and compresses quad tree nodes
  51. class EncodingContext
  52. {
  53. public:
  54. /// Mimics the size a call to NodeAllocate() would add to the buffer
  55. void PrepareNodeAllocate(const AABBTreeBuilder::Node *inNode, uint64 &ioBufferSize) const
  56. {
  57. // We don't emit nodes for leafs
  58. if (!inNode->HasChildren())
  59. return;
  60. // Add size of node
  61. ioBufferSize += sizeof(Node);
  62. }
  63. /// Allocate a new node for inNode.
  64. /// Algorithm can modify the order of ioChildren to indicate in which order children should be compressed
  65. /// Algorithm can enlarge the bounding boxes of the children during compression and returns these in outChildBoundsMin, outChildBoundsMax
  66. /// inNodeBoundsMin, inNodeBoundsMax is the bounding box if inNode possibly widened by compressing the parent node
  67. /// Returns size_t(-1) on error and reports the error in outError
  68. size_t NodeAllocate(const AABBTreeBuilder::Node *inNode, Vec3Arg inNodeBoundsMin, Vec3Arg inNodeBoundsMax, Array<const AABBTreeBuilder::Node *> &ioChildren, Vec3 outChildBoundsMin[NumChildrenPerNode], Vec3 outChildBoundsMax[NumChildrenPerNode], ByteBuffer &ioBuffer, const char *&outError) const
  69. {
  70. // We don't emit nodes for leafs
  71. if (!inNode->HasChildren())
  72. return ioBuffer.size();
  73. // Remember the start of the node
  74. size_t node_start = ioBuffer.size();
  75. // Fill in bounds
  76. Node *node = ioBuffer.Allocate<Node>();
  77. for (size_t i = 0; i < 4; ++i)
  78. {
  79. if (i < ioChildren.size())
  80. {
  81. const AABBTreeBuilder::Node *this_node = ioChildren[i];
  82. // Copy bounding box
  83. node->mBoundsMinX[i] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_NEG_INF>(this_node->mBounds.mMin.GetX());
  84. node->mBoundsMinY[i] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_NEG_INF>(this_node->mBounds.mMin.GetY());
  85. node->mBoundsMinZ[i] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_NEG_INF>(this_node->mBounds.mMin.GetZ());
  86. node->mBoundsMaxX[i] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_POS_INF>(this_node->mBounds.mMax.GetX());
  87. node->mBoundsMaxY[i] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_POS_INF>(this_node->mBounds.mMax.GetY());
  88. node->mBoundsMaxZ[i] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_POS_INF>(this_node->mBounds.mMax.GetZ());
  89. // Store triangle count
  90. node->mNodeProperties[i] = this_node->GetTriangleCount() << TRIANGLE_COUNT_SHIFT;
  91. if (this_node->GetTriangleCount() >= TRIANGLE_COUNT_MASK)
  92. {
  93. outError = "NodeCodecQuadTreeHalfFloat: Too many triangles";
  94. return size_t(-1);
  95. }
  96. }
  97. else
  98. {
  99. // Make this an invalid triangle node
  100. node->mNodeProperties[i] = uint32(TRIANGLE_COUNT_MASK) << TRIANGLE_COUNT_SHIFT;
  101. // Make bounding box invalid
  102. node->mBoundsMinX[i] = HALF_FLT_MAX;
  103. node->mBoundsMinY[i] = HALF_FLT_MAX;
  104. node->mBoundsMinZ[i] = HALF_FLT_MAX;
  105. node->mBoundsMaxX[i] = HALF_FLT_MAX;
  106. node->mBoundsMaxY[i] = HALF_FLT_MAX;
  107. node->mBoundsMaxZ[i] = HALF_FLT_MAX;
  108. }
  109. }
  110. // Since we don't keep track of the bounding box while descending the tree, we keep the root bounds at all levels for triangle compression
  111. for (int i = 0; i < NumChildrenPerNode; ++i)
  112. {
  113. outChildBoundsMin[i] = inNodeBoundsMin;
  114. outChildBoundsMax[i] = inNodeBoundsMax;
  115. }
  116. return node_start;
  117. }
  118. /// Once all nodes have been added, this call finalizes all nodes by patching in the offsets of the child nodes (that were added after the node itself was added)
  119. bool NodeFinalize(const AABBTreeBuilder::Node *inNode, size_t inNodeStart, uint inNumChildren, const size_t *inChildrenNodeStart, const size_t *inChildrenTrianglesStart, ByteBuffer &ioBuffer, const char *&outError)
  120. {
  121. if (!inNode->HasChildren())
  122. return true;
  123. Node *node = ioBuffer.Get<Node>(inNodeStart);
  124. for (uint i = 0; i < inNumChildren; ++i)
  125. {
  126. size_t offset;
  127. if (node->mNodeProperties[i] != 0)
  128. {
  129. // This is a triangle block
  130. offset = inChildrenTrianglesStart[i];
  131. // Store highest block with triangles so we can count the number of bits we need
  132. mHighestTriangleBlock = max(mHighestTriangleBlock, offset);
  133. }
  134. else
  135. {
  136. // This is a node block
  137. offset = inChildrenNodeStart[i];
  138. }
  139. // Store offset of next node / triangles
  140. if (offset & OFFSET_NON_SIGNIFICANT_MASK)
  141. {
  142. outError = "NodeCodecQuadTreeHalfFloat: Internal Error: Offset has non-significant bits set";
  143. return false;
  144. }
  145. offset >>= OFFSET_NON_SIGNIFICANT_BITS;
  146. if (offset > OFFSET_MASK)
  147. {
  148. outError = "NodeCodecQuadTreeHalfFloat: Offset too large. Too much data.";
  149. return false;
  150. }
  151. node->mNodeProperties[i] |= uint32(offset);
  152. }
  153. return true;
  154. }
  155. /// Once all nodes have been finalized, this will finalize the header of the nodes
  156. bool Finalize(Header *outHeader, const AABBTreeBuilder::Node *inRoot, size_t inRootNodeStart, size_t inRootTrianglesStart, const char *&outError) const
  157. {
  158. // Check if we can address the root node
  159. size_t offset = inRoot->HasChildren()? inRootNodeStart : inRootTrianglesStart;
  160. if (offset & OFFSET_NON_SIGNIFICANT_MASK)
  161. {
  162. outError = "NodeCodecQuadTreeHalfFloat: Internal Error: Offset has non-significant bits set";
  163. return false;
  164. }
  165. offset >>= OFFSET_NON_SIGNIFICANT_BITS;
  166. if (offset > OFFSET_MASK)
  167. {
  168. outError = "NodeCodecQuadTreeHalfFloat: Offset too large. Too much data.";
  169. return false;
  170. }
  171. // If the root has triangles, we need to take that offset instead since the mHighestTriangleBlock will be zero
  172. size_t highest_triangle_block = inRootTrianglesStart != size_t(-1)? inRootTrianglesStart : mHighestTriangleBlock;
  173. highest_triangle_block >>= OFFSET_NON_SIGNIFICANT_BITS;
  174. inRoot->mBounds.mMin.StoreFloat3(&outHeader->mRootBoundsMin);
  175. inRoot->mBounds.mMax.StoreFloat3(&outHeader->mRootBoundsMax);
  176. outHeader->mRootProperties = uint32(offset) + (inRoot->GetTriangleCount() << TRIANGLE_COUNT_SHIFT);
  177. outHeader->mBlockIDBits = uint8(32 - CountLeadingZeros(uint32(highest_triangle_block)));
  178. if (inRoot->GetTriangleCount() >= TRIANGLE_COUNT_MASK)
  179. {
  180. outError = "NodeCodecQuadTreeHalfFloat: Too many triangles";
  181. return false;
  182. }
  183. return true;
  184. }
  185. private:
  186. size_t mHighestTriangleBlock = 0;
  187. };
  188. /// This class decodes and decompresses quad tree nodes
  189. class DecodingContext
  190. {
  191. public:
  192. /// Get the amount of bits needed to store an ID to a triangle block
  193. inline static uint sTriangleBlockIDBits(const Header *inHeader)
  194. {
  195. return inHeader->mBlockIDBits;
  196. }
  197. /// Convert a triangle block ID to the start of the triangle buffer
  198. inline static const void * sGetTriangleBlockStart(const uint8 *inBufferStart, uint inTriangleBlockID)
  199. {
  200. return inBufferStart + (inTriangleBlockID << OFFSET_NON_SIGNIFICANT_BITS);
  201. }
  202. /// Constructor
  203. JPH_INLINE explicit DecodingContext(const Header *inHeader)
  204. {
  205. // Start with the root node on the stack
  206. mNodeStack[0] = inHeader->mRootProperties;
  207. }
  208. /// Walk the node tree calling the Visitor::VisitNodes for each node encountered and Visitor::VisitTriangles for each triangle encountered
  209. template <class TriangleContext, class Visitor>
  210. JPH_INLINE void WalkTree(const uint8 *inBufferStart, const TriangleContext &inTriangleContext, Visitor &ioVisitor)
  211. {
  212. do
  213. {
  214. // Test if node contains triangles
  215. uint32 node_properties = mNodeStack[mTop];
  216. uint32 tri_count = node_properties >> TRIANGLE_COUNT_SHIFT;
  217. if (tri_count == 0)
  218. {
  219. const Node *node = reinterpret_cast<const Node *>(inBufferStart + (node_properties << OFFSET_NON_SIGNIFICANT_BITS));
  220. // Unpack bounds
  221. UVec4 bounds_minxy = UVec4::sLoadInt4(reinterpret_cast<const uint32 *>(&node->mBoundsMinX[0]));
  222. Vec4 bounds_minx = HalfFloatConversion::ToFloat(bounds_minxy);
  223. Vec4 bounds_miny = HalfFloatConversion::ToFloat(bounds_minxy.Swizzle<SWIZZLE_Z, SWIZZLE_W, SWIZZLE_UNUSED, SWIZZLE_UNUSED>());
  224. UVec4 bounds_minzmaxx = UVec4::sLoadInt4(reinterpret_cast<const uint32 *>(&node->mBoundsMinZ[0]));
  225. Vec4 bounds_minz = HalfFloatConversion::ToFloat(bounds_minzmaxx);
  226. Vec4 bounds_maxx = HalfFloatConversion::ToFloat(bounds_minzmaxx.Swizzle<SWIZZLE_Z, SWIZZLE_W, SWIZZLE_UNUSED, SWIZZLE_UNUSED>());
  227. UVec4 bounds_maxyz = UVec4::sLoadInt4(reinterpret_cast<const uint32 *>(&node->mBoundsMaxY[0]));
  228. Vec4 bounds_maxy = HalfFloatConversion::ToFloat(bounds_maxyz);
  229. Vec4 bounds_maxz = HalfFloatConversion::ToFloat(bounds_maxyz.Swizzle<SWIZZLE_Z, SWIZZLE_W, SWIZZLE_UNUSED, SWIZZLE_UNUSED>());
  230. // Load properties for 4 children
  231. UVec4 properties = UVec4::sLoadInt4(&node->mNodeProperties[0]);
  232. // Check which sub nodes to visit
  233. int num_results = ioVisitor.VisitNodes(bounds_minx, bounds_miny, bounds_minz, bounds_maxx, bounds_maxy, bounds_maxz, properties, mTop);
  234. // Push them onto the stack
  235. JPH_ASSERT(mTop + 4 < StackSize);
  236. properties.StoreInt4(&mNodeStack[mTop]);
  237. mTop += num_results;
  238. }
  239. else if (tri_count != TRIANGLE_COUNT_MASK) // TRIANGLE_COUNT_MASK indicates a padding node, normally we shouldn't visit these nodes but when querying with a big enough box you could touch HALF_FLT_MAX (about 65K)
  240. {
  241. // Node contains triangles, do individual tests
  242. uint32 triangle_block_id = node_properties & OFFSET_MASK;
  243. const void *triangles = sGetTriangleBlockStart(inBufferStart, triangle_block_id);
  244. ioVisitor.VisitTriangles(inTriangleContext, triangles, tri_count, triangle_block_id);
  245. }
  246. // Check if we're done
  247. if (ioVisitor.ShouldAbort())
  248. break;
  249. // Fetch next node until we find one that the visitor wants to see
  250. do
  251. --mTop;
  252. while (mTop >= 0 && !ioVisitor.ShouldVisitNode(mTop));
  253. }
  254. while (mTop >= 0);
  255. }
  256. /// This can be used to have the visitor early out (ioVisitor.ShouldAbort() returns true) and later continue again (call WalkTree() again)
  257. bool IsDoneWalking() const
  258. {
  259. return mTop < 0;
  260. }
  261. private:
  262. uint32 mNodeStack[StackSize];
  263. int mTop = 0;
  264. };
  265. };
  266. JPH_NAMESPACE_END