Path: blob/master/thirdparty/jolt_physics/Jolt/AABBTree/AABBTreeToBuffer.h
9906 views
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)1// SPDX-FileCopyrightText: 2021 Jorrit Rouwe2// SPDX-License-Identifier: MIT34#pragma once56#include <Jolt/AABBTree/AABBTreeBuilder.h>7#include <Jolt/Core/ByteBuffer.h>8#include <Jolt/Geometry/IndexedTriangle.h>910JPH_NAMESPACE_BEGIN1112/// Conversion algorithm that converts an AABB tree to an optimized binary buffer13template <class TriangleCodec, class NodeCodec>14class AABBTreeToBuffer15{16public:17/// Header for the tree18using NodeHeader = typename NodeCodec::Header;1920/// Size in bytes of the header of the tree21static const int HeaderSize = NodeCodec::HeaderSize;2223/// Maximum number of children per node in the tree24static const int NumChildrenPerNode = NodeCodec::NumChildrenPerNode;2526/// Header for the triangles27using TriangleHeader = typename TriangleCodec::TriangleHeader;2829/// Size in bytes of the header for the triangles30static const int TriangleHeaderSize = TriangleCodec::TriangleHeaderSize;3132/// Convert AABB tree. Returns false if failed.33bool Convert(const Array<IndexedTriangle> &inTriangles, const Array<AABBTreeBuilder::Node> &inNodes, const VertexList &inVertices, const AABBTreeBuilder::Node *inRoot, bool inStoreUserData, const char *&outError)34{35typename NodeCodec::EncodingContext node_ctx;36typename TriangleCodec::EncodingContext tri_ctx(inVertices);3738// Child nodes out of loop so we don't constantly realloc it39Array<const AABBTreeBuilder::Node *> child_nodes;40child_nodes.reserve(NumChildrenPerNode);4142// First calculate how big the tree is going to be.43// Since the tree can be huge for very large meshes, we don't want44// to reallocate the buffer as it may cause out of memory situations.45// This loop mimics the construction loop below.46uint64 total_size = HeaderSize + TriangleHeaderSize;47size_t node_count = 1; // Start with root node48size_t to_process_max_size = 1; // Track size of queues so we can do a single reserve below49size_t to_process_triangles_max_size = 0;50{ // A scope to free the memory associated with to_estimate and to_estimate_triangles51Array<const AABBTreeBuilder::Node *> to_estimate;52Array<const AABBTreeBuilder::Node *> to_estimate_triangles;53to_estimate.push_back(inRoot);54for (;;)55{56while (!to_estimate.empty())57{58// Get the next node to process59const AABBTreeBuilder::Node *node = to_estimate.back();60to_estimate.pop_back();6162// Update total size63node_ctx.PrepareNodeAllocate(node, total_size);6465if (node->HasChildren())66{67// Collect the first NumChildrenPerNode sub-nodes in the tree68child_nodes.clear(); // Won't free the memory69node->GetNChildren(inNodes, NumChildrenPerNode, child_nodes);7071// Increment the number of nodes we're going to store72node_count += child_nodes.size();7374// Insert in reverse order so we estimate left child first when taking nodes from the back75for (int idx = int(child_nodes.size()) - 1; idx >= 0; --idx)76{77// Store triangles in separate list so we process them last78const AABBTreeBuilder::Node *child = child_nodes[idx];79if (child->HasChildren())80{81to_estimate.push_back(child);82to_process_max_size = max(to_estimate.size(), to_process_max_size);83}84else85{86to_estimate_triangles.push_back(child);87to_process_triangles_max_size = max(to_estimate_triangles.size(), to_process_triangles_max_size);88}89}90}91else92{93// Update total size94tri_ctx.PreparePack(&inTriangles[node->mTrianglesBegin], node->mNumTriangles, inStoreUserData, total_size);95}96}9798// If we've got triangles to estimate, loop again with just the triangles99if (to_estimate_triangles.empty())100break;101else102to_estimate.swap(to_estimate_triangles);103}104}105106// Finalize the prepare stage for the triangle context107tri_ctx.FinalizePreparePack(total_size);108109// Reserve the buffer110if (size_t(total_size) != total_size)111{112outError = "AABBTreeToBuffer: Out of memory!";113return false;114}115mTree.reserve(size_t(total_size));116117// Add headers118NodeHeader *header = HeaderSize > 0? mTree.Allocate<NodeHeader>() : nullptr;119TriangleHeader *triangle_header = TriangleHeaderSize > 0? mTree.Allocate<TriangleHeader>() : nullptr;120121struct NodeData122{123const AABBTreeBuilder::Node * mNode = nullptr; // Node that this entry belongs to124Vec3 mNodeBoundsMin; // Quantized node bounds125Vec3 mNodeBoundsMax;126size_t mNodeStart = size_t(-1); // Start of node in mTree127size_t mTriangleStart = size_t(-1); // Start of the triangle data in mTree128size_t mChildNodeStart[NumChildrenPerNode]; // Start of the children of the node in mTree129size_t mChildTrianglesStart[NumChildrenPerNode]; // Start of the triangle data in mTree130size_t * mParentChildNodeStart = nullptr; // Where to store mNodeStart (to patch mChildNodeStart of my parent)131size_t * mParentTrianglesStart = nullptr; // Where to store mTriangleStart (to patch mChildTrianglesStart of my parent)132uint mNumChildren = 0; // Number of children133};134135Array<NodeData *> to_process;136to_process.reserve(to_process_max_size);137Array<NodeData *> to_process_triangles;138to_process_triangles.reserve(to_process_triangles_max_size);139Array<NodeData> node_list;140node_list.reserve(node_count); // Needed to ensure that array is not reallocated, so we can keep pointers in the array141142NodeData root;143root.mNode = inRoot;144root.mNodeBoundsMin = inRoot->mBounds.mMin;145root.mNodeBoundsMax = inRoot->mBounds.mMax;146node_list.push_back(root);147to_process.push_back(&node_list.back());148149for (;;)150{151while (!to_process.empty())152{153// Get the next node to process154NodeData *node_data = to_process.back();155to_process.pop_back();156157// Due to quantization box could have become bigger, not smaller158JPH_ASSERT(AABox(node_data->mNodeBoundsMin, node_data->mNodeBoundsMax).Contains(node_data->mNode->mBounds), "AABBTreeToBuffer: Bounding box became smaller!");159160// Collect the first NumChildrenPerNode sub-nodes in the tree161child_nodes.clear(); // Won't free the memory162node_data->mNode->GetNChildren(inNodes, NumChildrenPerNode, child_nodes);163node_data->mNumChildren = (uint)child_nodes.size();164165// Fill in default child bounds166Vec3 child_bounds_min[NumChildrenPerNode], child_bounds_max[NumChildrenPerNode];167for (size_t i = 0; i < NumChildrenPerNode; ++i)168if (i < child_nodes.size())169{170child_bounds_min[i] = child_nodes[i]->mBounds.mMin;171child_bounds_max[i] = child_nodes[i]->mBounds.mMax;172}173else174{175child_bounds_min[i] = Vec3::sZero();176child_bounds_max[i] = Vec3::sZero();177}178179// Start a new node180node_data->mNodeStart = node_ctx.NodeAllocate(node_data->mNode, node_data->mNodeBoundsMin, node_data->mNodeBoundsMax, child_nodes, child_bounds_min, child_bounds_max, mTree, outError);181if (node_data->mNodeStart == size_t(-1))182return false;183184if (node_data->mNode->HasChildren())185{186// Insert in reverse order so we process left child first when taking nodes from the back187for (int idx = int(child_nodes.size()) - 1; idx >= 0; --idx)188{189const AABBTreeBuilder::Node *child_node = child_nodes[idx];190191// Due to quantization box could have become bigger, not smaller192JPH_ASSERT(AABox(child_bounds_min[idx], child_bounds_max[idx]).Contains(child_node->mBounds), "AABBTreeToBuffer: Bounding box became smaller!");193194// Add child to list of nodes to be processed195NodeData child;196child.mNode = child_node;197child.mNodeBoundsMin = child_bounds_min[idx];198child.mNodeBoundsMax = child_bounds_max[idx];199child.mParentChildNodeStart = &node_data->mChildNodeStart[idx];200child.mParentTrianglesStart = &node_data->mChildTrianglesStart[idx];201node_list.push_back(child);202203// Store triangles in separate list so we process them last204if (child_node->HasChildren())205to_process.push_back(&node_list.back());206else207to_process_triangles.push_back(&node_list.back());208}209}210else211{212// Add triangles213node_data->mTriangleStart = tri_ctx.Pack(&inTriangles[node_data->mNode->mTrianglesBegin], node_data->mNode->mNumTriangles, inStoreUserData, mTree, outError);214if (node_data->mTriangleStart == size_t(-1))215return false;216}217218// Patch offset into parent219if (node_data->mParentChildNodeStart != nullptr)220{221*node_data->mParentChildNodeStart = node_data->mNodeStart;222*node_data->mParentTrianglesStart = node_data->mTriangleStart;223}224}225226// If we've got triangles to process, loop again with just the triangles227if (to_process_triangles.empty())228break;229else230to_process.swap(to_process_triangles);231}232233// Assert that our reservation was correct (we don't know if we swapped the arrays or not)234JPH_ASSERT(to_process_max_size == to_process.capacity() || to_process_triangles_max_size == to_process.capacity());235JPH_ASSERT(to_process_max_size == to_process_triangles.capacity() || to_process_triangles_max_size == to_process_triangles.capacity());236237// Finalize all nodes238for (NodeData &n : node_list)239if (!node_ctx.NodeFinalize(n.mNode, n.mNodeStart, n.mNumChildren, n.mChildNodeStart, n.mChildTrianglesStart, mTree, outError))240return false;241242// Finalize the triangles243tri_ctx.Finalize(inVertices, triangle_header, mTree);244245// Validate that our reservations were correct246if (node_count != node_list.size())247{248outError = "Internal Error: Node memory estimate was incorrect, memory corruption!";249return false;250}251if (total_size != mTree.size())252{253outError = "Internal Error: Tree memory estimate was incorrect, memory corruption!";254return false;255}256257// Finalize the nodes258return node_ctx.Finalize(header, inRoot, node_list[0].mNodeStart, node_list[0].mTriangleStart, outError);259}260261/// Get resulting data262inline const ByteBuffer & GetBuffer() const263{264return mTree;265}266267/// Get resulting data268inline ByteBuffer & GetBuffer()269{270return mTree;271}272273/// Get header for tree274inline const NodeHeader * GetNodeHeader() const275{276return mTree.Get<NodeHeader>(0);277}278279/// Get header for triangles280inline const TriangleHeader * GetTriangleHeader() const281{282return mTree.Get<TriangleHeader>(HeaderSize);283}284285/// Get root of resulting tree286inline const void * GetRoot() const287{288return mTree.Get<void>(HeaderSize + TriangleHeaderSize);289}290291private:292ByteBuffer mTree; ///< Resulting tree structure293};294295JPH_NAMESPACE_END296297298