Path: blob/master/thirdparty/jolt_physics/Jolt/AABBTree/AABBTreeBuilder.cpp
9906 views
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)1// SPDX-FileCopyrightText: 2021 Jorrit Rouwe2// SPDX-License-Identifier: MIT34#include <Jolt/Jolt.h>56#include <Jolt/AABBTree/AABBTreeBuilder.h>78JPH_NAMESPACE_BEGIN910uint AABBTreeBuilder::Node::GetMinDepth(const Array<Node> &inNodes) const11{12if (HasChildren())13{14uint left = inNodes[mChild[0]].GetMinDepth(inNodes);15uint right = inNodes[mChild[1]].GetMinDepth(inNodes);16return min(left, right) + 1;17}18else19return 1;20}2122uint AABBTreeBuilder::Node::GetMaxDepth(const Array<Node> &inNodes) const23{24if (HasChildren())25{26uint left = inNodes[mChild[0]].GetMaxDepth(inNodes);27uint right = inNodes[mChild[1]].GetMaxDepth(inNodes);28return max(left, right) + 1;29}30else31return 1;32}3334uint AABBTreeBuilder::Node::GetNodeCount(const Array<Node> &inNodes) const35{36if (HasChildren())37return inNodes[mChild[0]].GetNodeCount(inNodes) + inNodes[mChild[1]].GetNodeCount(inNodes) + 1;38else39return 1;40}4142uint AABBTreeBuilder::Node::GetLeafNodeCount(const Array<Node> &inNodes) const43{44if (HasChildren())45return inNodes[mChild[0]].GetLeafNodeCount(inNodes) + inNodes[mChild[1]].GetLeafNodeCount(inNodes);46else47return 1;48}4950uint AABBTreeBuilder::Node::GetTriangleCountInTree(const Array<Node> &inNodes) const51{52if (HasChildren())53return inNodes[mChild[0]].GetTriangleCountInTree(inNodes) + inNodes[mChild[1]].GetTriangleCountInTree(inNodes);54else55return GetTriangleCount();56}5758void AABBTreeBuilder::Node::GetTriangleCountPerNode(const Array<Node> &inNodes, float &outAverage, uint &outMin, uint &outMax) const59{60outMin = INT_MAX;61outMax = 0;62outAverage = 0;63uint avg_divisor = 0;64GetTriangleCountPerNodeInternal(inNodes, outAverage, avg_divisor, outMin, outMax);65if (avg_divisor > 0)66outAverage /= avg_divisor;67}6869float AABBTreeBuilder::Node::CalculateSAHCost(const Array<Node> &inNodes, float inCostTraversal, float inCostLeaf) const70{71float surface_area = mBounds.GetSurfaceArea();72return surface_area > 0.0f? CalculateSAHCostInternal(inNodes, inCostTraversal / surface_area, inCostLeaf / surface_area) : 0.0f;73}7475void AABBTreeBuilder::Node::GetNChildren(const Array<Node> &inNodes, uint inN, Array<const Node*> &outChildren) const76{77JPH_ASSERT(outChildren.empty());7879// Check if there is anything to expand80if (!HasChildren())81return;8283// Start with the children of this node84outChildren.push_back(&inNodes[mChild[0]]);85outChildren.push_back(&inNodes[mChild[1]]);8687size_t next = 0;88bool all_triangles = true;89while (outChildren.size() < inN)90{91// If we have looped over all nodes, start over with the first node again92if (next >= outChildren.size())93{94// If there only triangle nodes left, we have to terminate95if (all_triangles)96return;97next = 0;98all_triangles = true;99}100101// Try to expand this node into its two children102const Node *to_expand = outChildren[next];103if (to_expand->HasChildren())104{105outChildren.erase(outChildren.begin() + next);106outChildren.push_back(&inNodes[to_expand->mChild[0]]);107outChildren.push_back(&inNodes[to_expand->mChild[1]]);108all_triangles = false;109}110else111{112++next;113}114}115}116117float AABBTreeBuilder::Node::CalculateSAHCostInternal(const Array<Node> &inNodes, float inCostTraversalDivSurfaceArea, float inCostLeafDivSurfaceArea) const118{119if (HasChildren())120return inCostTraversalDivSurfaceArea * mBounds.GetSurfaceArea()121+ inNodes[mChild[0]].CalculateSAHCostInternal(inNodes, inCostTraversalDivSurfaceArea, inCostLeafDivSurfaceArea)122+ inNodes[mChild[1]].CalculateSAHCostInternal(inNodes, inCostTraversalDivSurfaceArea, inCostLeafDivSurfaceArea);123else124return inCostLeafDivSurfaceArea * mBounds.GetSurfaceArea() * GetTriangleCount();125}126127void AABBTreeBuilder::Node::GetTriangleCountPerNodeInternal(const Array<Node> &inNodes, float &outAverage, uint &outAverageDivisor, uint &outMin, uint &outMax) const128{129if (HasChildren())130{131inNodes[mChild[0]].GetTriangleCountPerNodeInternal(inNodes, outAverage, outAverageDivisor, outMin, outMax);132inNodes[mChild[1]].GetTriangleCountPerNodeInternal(inNodes, outAverage, outAverageDivisor, outMin, outMax);133}134else135{136outAverage += GetTriangleCount();137outAverageDivisor++;138outMin = min(outMin, GetTriangleCount());139outMax = max(outMax, GetTriangleCount());140}141}142143AABBTreeBuilder::AABBTreeBuilder(TriangleSplitter &inSplitter, uint inMaxTrianglesPerLeaf) :144mTriangleSplitter(inSplitter),145mMaxTrianglesPerLeaf(inMaxTrianglesPerLeaf)146{147}148149AABBTreeBuilder::Node *AABBTreeBuilder::Build(AABBTreeBuilderStats &outStats)150{151TriangleSplitter::Range initial = mTriangleSplitter.GetInitialRange();152153// Worst case for number of nodes: 1 leaf node per triangle. At each level above, the number of nodes is half that of the level below.154// This means that at most we'll be allocating 2x the number of triangles in nodes.155mNodes.reserve(2 * initial.Count());156mTriangles.reserve(initial.Count());157158// Build the tree159Node &root = mNodes[BuildInternal(initial)];160161// Collect stats162float avg_triangles_per_leaf;163uint min_triangles_per_leaf, max_triangles_per_leaf;164root.GetTriangleCountPerNode(mNodes, avg_triangles_per_leaf, min_triangles_per_leaf, max_triangles_per_leaf);165166mTriangleSplitter.GetStats(outStats.mSplitterStats);167168outStats.mSAHCost = root.CalculateSAHCost(mNodes, 1.0f, 1.0f);169outStats.mMinDepth = root.GetMinDepth(mNodes);170outStats.mMaxDepth = root.GetMaxDepth(mNodes);171outStats.mNodeCount = root.GetNodeCount(mNodes);172outStats.mLeafNodeCount = root.GetLeafNodeCount(mNodes);173outStats.mMaxTrianglesPerLeaf = mMaxTrianglesPerLeaf;174outStats.mTreeMinTrianglesPerLeaf = min_triangles_per_leaf;175outStats.mTreeMaxTrianglesPerLeaf = max_triangles_per_leaf;176outStats.mTreeAvgTrianglesPerLeaf = avg_triangles_per_leaf;177178return &root;179}180181uint AABBTreeBuilder::BuildInternal(const TriangleSplitter::Range &inTriangles)182{183// Check if there are too many triangles left184if (inTriangles.Count() > mMaxTrianglesPerLeaf)185{186// Split triangles in two batches187TriangleSplitter::Range left, right;188if (!mTriangleSplitter.Split(inTriangles, left, right))189{190// When the trace below triggers:191//192// This code builds a tree structure to accelerate collision detection.193// At top level it will start with all triangles in a mesh and then divides the triangles into two batches.194// This process repeats until until the batch size is smaller than mMaxTrianglePerLeaf.195//196// It uses a TriangleSplitter to find a good split. When this warning triggers, the splitter was not able197// to create a reasonable split for the triangles. This usually happens when the triangles in a batch are198// intersecting. They could also be overlapping when projected on the 3 coordinate axis.199//200// To solve this issue, you could try to pass your mesh through a mesh cleaning / optimization algorithm.201// You could also inspect the triangles that cause this issue and see if that part of the mesh can be fixed manually.202//203// When you do not fix this warning, the tree will be less efficient for collision detection, but it will still work.204JPH_IF_DEBUG(Trace("AABBTreeBuilder: Doing random split for %d triangles (max per node: %u)!", (int)inTriangles.Count(), mMaxTrianglesPerLeaf);)205int half = inTriangles.Count() / 2;206JPH_ASSERT(half > 0);207left = TriangleSplitter::Range(inTriangles.mBegin, inTriangles.mBegin + half);208right = TriangleSplitter::Range(inTriangles.mBegin + half, inTriangles.mEnd);209}210211// Recursively build212const uint node_index = (uint)mNodes.size();213mNodes.push_back(Node());214uint left_index = BuildInternal(left);215uint right_index = BuildInternal(right);216Node &node = mNodes[node_index];217node.mChild[0] = left_index;218node.mChild[1] = right_index;219node.mBounds = mNodes[node.mChild[0]].mBounds;220node.mBounds.Encapsulate(mNodes[node.mChild[1]].mBounds);221return node_index;222}223224// Create leaf node225const uint node_index = (uint)mNodes.size();226mNodes.push_back(Node());227Node &node = mNodes.back();228node.mTrianglesBegin = (uint)mTriangles.size();229node.mNumTriangles = inTriangles.mEnd - inTriangles.mBegin;230const VertexList &v = mTriangleSplitter.GetVertices();231for (uint i = inTriangles.mBegin; i < inTriangles.mEnd; ++i)232{233const IndexedTriangle &t = mTriangleSplitter.GetTriangle(i);234mTriangles.push_back(t);235node.mBounds.Encapsulate(v, t);236}237238return node_index;239}240241JPH_NAMESPACE_END242243244