2
0

NodeCodecQuadTreeHalfFloat.h 11 KB

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