initial commit, 4.5 stable
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
This commit is contained in:
242
thirdparty/jolt_physics/Jolt/AABBTree/AABBTreeBuilder.cpp
vendored
Normal file
242
thirdparty/jolt_physics/Jolt/AABBTree/AABBTreeBuilder.cpp
vendored
Normal file
@@ -0,0 +1,242 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <Jolt/Jolt.h>
|
||||
|
||||
#include <Jolt/AABBTree/AABBTreeBuilder.h>
|
||||
|
||||
JPH_NAMESPACE_BEGIN
|
||||
|
||||
uint AABBTreeBuilder::Node::GetMinDepth(const Array<Node> &inNodes) const
|
||||
{
|
||||
if (HasChildren())
|
||||
{
|
||||
uint left = inNodes[mChild[0]].GetMinDepth(inNodes);
|
||||
uint right = inNodes[mChild[1]].GetMinDepth(inNodes);
|
||||
return min(left, right) + 1;
|
||||
}
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint AABBTreeBuilder::Node::GetMaxDepth(const Array<Node> &inNodes) const
|
||||
{
|
||||
if (HasChildren())
|
||||
{
|
||||
uint left = inNodes[mChild[0]].GetMaxDepth(inNodes);
|
||||
uint right = inNodes[mChild[1]].GetMaxDepth(inNodes);
|
||||
return max(left, right) + 1;
|
||||
}
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint AABBTreeBuilder::Node::GetNodeCount(const Array<Node> &inNodes) const
|
||||
{
|
||||
if (HasChildren())
|
||||
return inNodes[mChild[0]].GetNodeCount(inNodes) + inNodes[mChild[1]].GetNodeCount(inNodes) + 1;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint AABBTreeBuilder::Node::GetLeafNodeCount(const Array<Node> &inNodes) const
|
||||
{
|
||||
if (HasChildren())
|
||||
return inNodes[mChild[0]].GetLeafNodeCount(inNodes) + inNodes[mChild[1]].GetLeafNodeCount(inNodes);
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint AABBTreeBuilder::Node::GetTriangleCountInTree(const Array<Node> &inNodes) const
|
||||
{
|
||||
if (HasChildren())
|
||||
return inNodes[mChild[0]].GetTriangleCountInTree(inNodes) + inNodes[mChild[1]].GetTriangleCountInTree(inNodes);
|
||||
else
|
||||
return GetTriangleCount();
|
||||
}
|
||||
|
||||
void AABBTreeBuilder::Node::GetTriangleCountPerNode(const Array<Node> &inNodes, float &outAverage, uint &outMin, uint &outMax) const
|
||||
{
|
||||
outMin = INT_MAX;
|
||||
outMax = 0;
|
||||
outAverage = 0;
|
||||
uint avg_divisor = 0;
|
||||
GetTriangleCountPerNodeInternal(inNodes, outAverage, avg_divisor, outMin, outMax);
|
||||
if (avg_divisor > 0)
|
||||
outAverage /= avg_divisor;
|
||||
}
|
||||
|
||||
float AABBTreeBuilder::Node::CalculateSAHCost(const Array<Node> &inNodes, float inCostTraversal, float inCostLeaf) const
|
||||
{
|
||||
float surface_area = mBounds.GetSurfaceArea();
|
||||
return surface_area > 0.0f? CalculateSAHCostInternal(inNodes, inCostTraversal / surface_area, inCostLeaf / surface_area) : 0.0f;
|
||||
}
|
||||
|
||||
void AABBTreeBuilder::Node::GetNChildren(const Array<Node> &inNodes, uint inN, Array<const Node*> &outChildren) const
|
||||
{
|
||||
JPH_ASSERT(outChildren.empty());
|
||||
|
||||
// Check if there is anything to expand
|
||||
if (!HasChildren())
|
||||
return;
|
||||
|
||||
// Start with the children of this node
|
||||
outChildren.push_back(&inNodes[mChild[0]]);
|
||||
outChildren.push_back(&inNodes[mChild[1]]);
|
||||
|
||||
size_t next = 0;
|
||||
bool all_triangles = true;
|
||||
while (outChildren.size() < inN)
|
||||
{
|
||||
// If we have looped over all nodes, start over with the first node again
|
||||
if (next >= outChildren.size())
|
||||
{
|
||||
// If there only triangle nodes left, we have to terminate
|
||||
if (all_triangles)
|
||||
return;
|
||||
next = 0;
|
||||
all_triangles = true;
|
||||
}
|
||||
|
||||
// Try to expand this node into its two children
|
||||
const Node *to_expand = outChildren[next];
|
||||
if (to_expand->HasChildren())
|
||||
{
|
||||
outChildren.erase(outChildren.begin() + next);
|
||||
outChildren.push_back(&inNodes[to_expand->mChild[0]]);
|
||||
outChildren.push_back(&inNodes[to_expand->mChild[1]]);
|
||||
all_triangles = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
++next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float AABBTreeBuilder::Node::CalculateSAHCostInternal(const Array<Node> &inNodes, float inCostTraversalDivSurfaceArea, float inCostLeafDivSurfaceArea) const
|
||||
{
|
||||
if (HasChildren())
|
||||
return inCostTraversalDivSurfaceArea * mBounds.GetSurfaceArea()
|
||||
+ inNodes[mChild[0]].CalculateSAHCostInternal(inNodes, inCostTraversalDivSurfaceArea, inCostLeafDivSurfaceArea)
|
||||
+ inNodes[mChild[1]].CalculateSAHCostInternal(inNodes, inCostTraversalDivSurfaceArea, inCostLeafDivSurfaceArea);
|
||||
else
|
||||
return inCostLeafDivSurfaceArea * mBounds.GetSurfaceArea() * GetTriangleCount();
|
||||
}
|
||||
|
||||
void AABBTreeBuilder::Node::GetTriangleCountPerNodeInternal(const Array<Node> &inNodes, float &outAverage, uint &outAverageDivisor, uint &outMin, uint &outMax) const
|
||||
{
|
||||
if (HasChildren())
|
||||
{
|
||||
inNodes[mChild[0]].GetTriangleCountPerNodeInternal(inNodes, outAverage, outAverageDivisor, outMin, outMax);
|
||||
inNodes[mChild[1]].GetTriangleCountPerNodeInternal(inNodes, outAverage, outAverageDivisor, outMin, outMax);
|
||||
}
|
||||
else
|
||||
{
|
||||
outAverage += GetTriangleCount();
|
||||
outAverageDivisor++;
|
||||
outMin = min(outMin, GetTriangleCount());
|
||||
outMax = max(outMax, GetTriangleCount());
|
||||
}
|
||||
}
|
||||
|
||||
AABBTreeBuilder::AABBTreeBuilder(TriangleSplitter &inSplitter, uint inMaxTrianglesPerLeaf) :
|
||||
mTriangleSplitter(inSplitter),
|
||||
mMaxTrianglesPerLeaf(inMaxTrianglesPerLeaf)
|
||||
{
|
||||
}
|
||||
|
||||
AABBTreeBuilder::Node *AABBTreeBuilder::Build(AABBTreeBuilderStats &outStats)
|
||||
{
|
||||
TriangleSplitter::Range initial = mTriangleSplitter.GetInitialRange();
|
||||
|
||||
// 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.
|
||||
// This means that at most we'll be allocating 2x the number of triangles in nodes.
|
||||
mNodes.reserve(2 * initial.Count());
|
||||
mTriangles.reserve(initial.Count());
|
||||
|
||||
// Build the tree
|
||||
Node &root = mNodes[BuildInternal(initial)];
|
||||
|
||||
// Collect stats
|
||||
float avg_triangles_per_leaf;
|
||||
uint min_triangles_per_leaf, max_triangles_per_leaf;
|
||||
root.GetTriangleCountPerNode(mNodes, avg_triangles_per_leaf, min_triangles_per_leaf, max_triangles_per_leaf);
|
||||
|
||||
mTriangleSplitter.GetStats(outStats.mSplitterStats);
|
||||
|
||||
outStats.mSAHCost = root.CalculateSAHCost(mNodes, 1.0f, 1.0f);
|
||||
outStats.mMinDepth = root.GetMinDepth(mNodes);
|
||||
outStats.mMaxDepth = root.GetMaxDepth(mNodes);
|
||||
outStats.mNodeCount = root.GetNodeCount(mNodes);
|
||||
outStats.mLeafNodeCount = root.GetLeafNodeCount(mNodes);
|
||||
outStats.mMaxTrianglesPerLeaf = mMaxTrianglesPerLeaf;
|
||||
outStats.mTreeMinTrianglesPerLeaf = min_triangles_per_leaf;
|
||||
outStats.mTreeMaxTrianglesPerLeaf = max_triangles_per_leaf;
|
||||
outStats.mTreeAvgTrianglesPerLeaf = avg_triangles_per_leaf;
|
||||
|
||||
return &root;
|
||||
}
|
||||
|
||||
uint AABBTreeBuilder::BuildInternal(const TriangleSplitter::Range &inTriangles)
|
||||
{
|
||||
// Check if there are too many triangles left
|
||||
if (inTriangles.Count() > mMaxTrianglesPerLeaf)
|
||||
{
|
||||
// Split triangles in two batches
|
||||
TriangleSplitter::Range left, right;
|
||||
if (!mTriangleSplitter.Split(inTriangles, left, right))
|
||||
{
|
||||
// When the trace below triggers:
|
||||
//
|
||||
// This code builds a tree structure to accelerate collision detection.
|
||||
// At top level it will start with all triangles in a mesh and then divides the triangles into two batches.
|
||||
// This process repeats until until the batch size is smaller than mMaxTrianglePerLeaf.
|
||||
//
|
||||
// It uses a TriangleSplitter to find a good split. When this warning triggers, the splitter was not able
|
||||
// to create a reasonable split for the triangles. This usually happens when the triangles in a batch are
|
||||
// intersecting. They could also be overlapping when projected on the 3 coordinate axis.
|
||||
//
|
||||
// To solve this issue, you could try to pass your mesh through a mesh cleaning / optimization algorithm.
|
||||
// You could also inspect the triangles that cause this issue and see if that part of the mesh can be fixed manually.
|
||||
//
|
||||
// When you do not fix this warning, the tree will be less efficient for collision detection, but it will still work.
|
||||
JPH_IF_DEBUG(Trace("AABBTreeBuilder: Doing random split for %d triangles (max per node: %u)!", (int)inTriangles.Count(), mMaxTrianglesPerLeaf);)
|
||||
int half = inTriangles.Count() / 2;
|
||||
JPH_ASSERT(half > 0);
|
||||
left = TriangleSplitter::Range(inTriangles.mBegin, inTriangles.mBegin + half);
|
||||
right = TriangleSplitter::Range(inTriangles.mBegin + half, inTriangles.mEnd);
|
||||
}
|
||||
|
||||
// Recursively build
|
||||
const uint node_index = (uint)mNodes.size();
|
||||
mNodes.push_back(Node());
|
||||
uint left_index = BuildInternal(left);
|
||||
uint right_index = BuildInternal(right);
|
||||
Node &node = mNodes[node_index];
|
||||
node.mChild[0] = left_index;
|
||||
node.mChild[1] = right_index;
|
||||
node.mBounds = mNodes[node.mChild[0]].mBounds;
|
||||
node.mBounds.Encapsulate(mNodes[node.mChild[1]].mBounds);
|
||||
return node_index;
|
||||
}
|
||||
|
||||
// Create leaf node
|
||||
const uint node_index = (uint)mNodes.size();
|
||||
mNodes.push_back(Node());
|
||||
Node &node = mNodes.back();
|
||||
node.mTrianglesBegin = (uint)mTriangles.size();
|
||||
node.mNumTriangles = inTriangles.mEnd - inTriangles.mBegin;
|
||||
const VertexList &v = mTriangleSplitter.GetVertices();
|
||||
for (uint i = inTriangles.mBegin; i < inTriangles.mEnd; ++i)
|
||||
{
|
||||
const IndexedTriangle &t = mTriangleSplitter.GetTriangle(i);
|
||||
mTriangles.push_back(t);
|
||||
node.mBounds.Encapsulate(v, t);
|
||||
}
|
||||
|
||||
return node_index;
|
||||
}
|
||||
|
||||
JPH_NAMESPACE_END
|
118
thirdparty/jolt_physics/Jolt/AABBTree/AABBTreeBuilder.h
vendored
Normal file
118
thirdparty/jolt_physics/Jolt/AABBTree/AABBTreeBuilder.h
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Jolt/TriangleSplitter/TriangleSplitter.h>
|
||||
#include <Jolt/Geometry/AABox.h>
|
||||
#include <Jolt/Core/NonCopyable.h>
|
||||
|
||||
JPH_NAMESPACE_BEGIN
|
||||
|
||||
struct AABBTreeBuilderStats
|
||||
{
|
||||
///@name Splitter stats
|
||||
TriangleSplitter::Stats mSplitterStats; ///< Stats returned by the triangle splitter algorithm
|
||||
|
||||
///@name Tree structure
|
||||
float mSAHCost = 0.0f; ///< Surface Area Heuristic cost of this tree
|
||||
int mMinDepth = 0; ///< Minimal depth of tree (number of nodes)
|
||||
int mMaxDepth = 0; ///< Maximum depth of tree (number of nodes)
|
||||
int mNodeCount = 0; ///< Number of nodes in the tree
|
||||
int mLeafNodeCount = 0; ///< Number of leaf nodes (that contain triangles)
|
||||
|
||||
///@name Configured stats
|
||||
int mMaxTrianglesPerLeaf = 0; ///< Configured max triangles per leaf
|
||||
|
||||
///@name Actual stats
|
||||
int mTreeMinTrianglesPerLeaf = 0; ///< Minimal amount of triangles in a leaf
|
||||
int mTreeMaxTrianglesPerLeaf = 0; ///< Maximal amount of triangles in a leaf
|
||||
float mTreeAvgTrianglesPerLeaf = 0.0f; ///< Average amount of triangles in leaf nodes
|
||||
};
|
||||
|
||||
/// Helper class to build an AABB tree
|
||||
class JPH_EXPORT AABBTreeBuilder
|
||||
{
|
||||
public:
|
||||
/// A node in the tree, contains the AABox for the tree and any child nodes or triangles
|
||||
class Node
|
||||
{
|
||||
public:
|
||||
JPH_OVERRIDE_NEW_DELETE
|
||||
|
||||
/// Indicates that there is no child
|
||||
static constexpr uint cInvalidNodeIndex = ~uint(0);
|
||||
|
||||
/// Get number of triangles in this node
|
||||
inline uint GetTriangleCount() const { return mNumTriangles; }
|
||||
|
||||
/// Check if this node has any children
|
||||
inline bool HasChildren() const { return mChild[0] != cInvalidNodeIndex || mChild[1] != cInvalidNodeIndex; }
|
||||
|
||||
/// Min depth of tree
|
||||
uint GetMinDepth(const Array<Node> &inNodes) const;
|
||||
|
||||
/// Max depth of tree
|
||||
uint GetMaxDepth(const Array<Node> &inNodes) const;
|
||||
|
||||
/// Number of nodes in tree
|
||||
uint GetNodeCount(const Array<Node> &inNodes) const;
|
||||
|
||||
/// Number of leaf nodes in tree
|
||||
uint GetLeafNodeCount(const Array<Node> &inNodes) const;
|
||||
|
||||
/// Get triangle count in tree
|
||||
uint GetTriangleCountInTree(const Array<Node> &inNodes) const;
|
||||
|
||||
/// Calculate min and max triangles per node
|
||||
void GetTriangleCountPerNode(const Array<Node> &inNodes, float &outAverage, uint &outMin, uint &outMax) const;
|
||||
|
||||
/// Calculate the total cost of the tree using the surface area heuristic
|
||||
float CalculateSAHCost(const Array<Node> &inNodes, float inCostTraversal, float inCostLeaf) const;
|
||||
|
||||
/// Recursively get children (breadth first) to get in total inN children (or less if there are no more)
|
||||
void GetNChildren(const Array<Node> &inNodes, uint inN, Array<const Node *> &outChildren) const;
|
||||
|
||||
/// Bounding box
|
||||
AABox mBounds;
|
||||
|
||||
/// Triangles (if no child nodes)
|
||||
uint mTrianglesBegin; // Index into mTriangles
|
||||
uint mNumTriangles = 0;
|
||||
|
||||
/// Child node indices (if no triangles)
|
||||
uint mChild[2] = { cInvalidNodeIndex, cInvalidNodeIndex };
|
||||
|
||||
private:
|
||||
friend class AABBTreeBuilder;
|
||||
|
||||
/// Recursive helper function to calculate cost of the tree
|
||||
float CalculateSAHCostInternal(const Array<Node> &inNodes, float inCostTraversalDivSurfaceArea, float inCostLeafDivSurfaceArea) const;
|
||||
|
||||
/// Recursive helper function to calculate min and max triangles per node
|
||||
void GetTriangleCountPerNodeInternal(const Array<Node> &inNodes, float &outAverage, uint &outAverageDivisor, uint &outMin, uint &outMax) const;
|
||||
};
|
||||
|
||||
/// Constructor
|
||||
AABBTreeBuilder(TriangleSplitter &inSplitter, uint inMaxTrianglesPerLeaf = 16);
|
||||
|
||||
/// Recursively build tree, returns the root node of the tree
|
||||
Node * Build(AABBTreeBuilderStats &outStats);
|
||||
|
||||
/// Get all nodes
|
||||
const Array<Node> & GetNodes() const { return mNodes; }
|
||||
|
||||
/// Get all triangles
|
||||
const Array<IndexedTriangle> &GetTriangles() const { return mTriangles; }
|
||||
|
||||
private:
|
||||
uint BuildInternal(const TriangleSplitter::Range &inTriangles);
|
||||
|
||||
TriangleSplitter & mTriangleSplitter;
|
||||
const uint mMaxTrianglesPerLeaf;
|
||||
Array<Node> mNodes;
|
||||
Array<IndexedTriangle> mTriangles;
|
||||
};
|
||||
|
||||
JPH_NAMESPACE_END
|
296
thirdparty/jolt_physics/Jolt/AABBTree/AABBTreeToBuffer.h
vendored
Normal file
296
thirdparty/jolt_physics/Jolt/AABBTree/AABBTreeToBuffer.h
vendored
Normal file
@@ -0,0 +1,296 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Jolt/AABBTree/AABBTreeBuilder.h>
|
||||
#include <Jolt/Core/ByteBuffer.h>
|
||||
#include <Jolt/Geometry/IndexedTriangle.h>
|
||||
|
||||
JPH_NAMESPACE_BEGIN
|
||||
|
||||
/// Conversion algorithm that converts an AABB tree to an optimized binary buffer
|
||||
template <class TriangleCodec, class NodeCodec>
|
||||
class AABBTreeToBuffer
|
||||
{
|
||||
public:
|
||||
/// Header for the tree
|
||||
using NodeHeader = typename NodeCodec::Header;
|
||||
|
||||
/// Size in bytes of the header of the tree
|
||||
static const int HeaderSize = NodeCodec::HeaderSize;
|
||||
|
||||
/// Maximum number of children per node in the tree
|
||||
static const int NumChildrenPerNode = NodeCodec::NumChildrenPerNode;
|
||||
|
||||
/// Header for the triangles
|
||||
using TriangleHeader = typename TriangleCodec::TriangleHeader;
|
||||
|
||||
/// Size in bytes of the header for the triangles
|
||||
static const int TriangleHeaderSize = TriangleCodec::TriangleHeaderSize;
|
||||
|
||||
/// Convert AABB tree. Returns false if failed.
|
||||
bool Convert(const Array<IndexedTriangle> &inTriangles, const Array<AABBTreeBuilder::Node> &inNodes, const VertexList &inVertices, const AABBTreeBuilder::Node *inRoot, bool inStoreUserData, const char *&outError)
|
||||
{
|
||||
typename NodeCodec::EncodingContext node_ctx;
|
||||
typename TriangleCodec::EncodingContext tri_ctx(inVertices);
|
||||
|
||||
// Child nodes out of loop so we don't constantly realloc it
|
||||
Array<const AABBTreeBuilder::Node *> child_nodes;
|
||||
child_nodes.reserve(NumChildrenPerNode);
|
||||
|
||||
// First calculate how big the tree is going to be.
|
||||
// Since the tree can be huge for very large meshes, we don't want
|
||||
// to reallocate the buffer as it may cause out of memory situations.
|
||||
// This loop mimics the construction loop below.
|
||||
uint64 total_size = HeaderSize + TriangleHeaderSize;
|
||||
size_t node_count = 1; // Start with root node
|
||||
size_t to_process_max_size = 1; // Track size of queues so we can do a single reserve below
|
||||
size_t to_process_triangles_max_size = 0;
|
||||
{ // A scope to free the memory associated with to_estimate and to_estimate_triangles
|
||||
Array<const AABBTreeBuilder::Node *> to_estimate;
|
||||
Array<const AABBTreeBuilder::Node *> to_estimate_triangles;
|
||||
to_estimate.push_back(inRoot);
|
||||
for (;;)
|
||||
{
|
||||
while (!to_estimate.empty())
|
||||
{
|
||||
// Get the next node to process
|
||||
const AABBTreeBuilder::Node *node = to_estimate.back();
|
||||
to_estimate.pop_back();
|
||||
|
||||
// Update total size
|
||||
node_ctx.PrepareNodeAllocate(node, total_size);
|
||||
|
||||
if (node->HasChildren())
|
||||
{
|
||||
// Collect the first NumChildrenPerNode sub-nodes in the tree
|
||||
child_nodes.clear(); // Won't free the memory
|
||||
node->GetNChildren(inNodes, NumChildrenPerNode, child_nodes);
|
||||
|
||||
// Increment the number of nodes we're going to store
|
||||
node_count += child_nodes.size();
|
||||
|
||||
// Insert in reverse order so we estimate left child first when taking nodes from the back
|
||||
for (int idx = int(child_nodes.size()) - 1; idx >= 0; --idx)
|
||||
{
|
||||
// Store triangles in separate list so we process them last
|
||||
const AABBTreeBuilder::Node *child = child_nodes[idx];
|
||||
if (child->HasChildren())
|
||||
{
|
||||
to_estimate.push_back(child);
|
||||
to_process_max_size = max(to_estimate.size(), to_process_max_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
to_estimate_triangles.push_back(child);
|
||||
to_process_triangles_max_size = max(to_estimate_triangles.size(), to_process_triangles_max_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update total size
|
||||
tri_ctx.PreparePack(&inTriangles[node->mTrianglesBegin], node->mNumTriangles, inStoreUserData, total_size);
|
||||
}
|
||||
}
|
||||
|
||||
// If we've got triangles to estimate, loop again with just the triangles
|
||||
if (to_estimate_triangles.empty())
|
||||
break;
|
||||
else
|
||||
to_estimate.swap(to_estimate_triangles);
|
||||
}
|
||||
}
|
||||
|
||||
// Finalize the prepare stage for the triangle context
|
||||
tri_ctx.FinalizePreparePack(total_size);
|
||||
|
||||
// Reserve the buffer
|
||||
if (size_t(total_size) != total_size)
|
||||
{
|
||||
outError = "AABBTreeToBuffer: Out of memory!";
|
||||
return false;
|
||||
}
|
||||
mTree.reserve(size_t(total_size));
|
||||
|
||||
// Add headers
|
||||
NodeHeader *header = HeaderSize > 0? mTree.Allocate<NodeHeader>() : nullptr;
|
||||
TriangleHeader *triangle_header = TriangleHeaderSize > 0? mTree.Allocate<TriangleHeader>() : nullptr;
|
||||
|
||||
struct NodeData
|
||||
{
|
||||
const AABBTreeBuilder::Node * mNode = nullptr; // Node that this entry belongs to
|
||||
Vec3 mNodeBoundsMin; // Quantized node bounds
|
||||
Vec3 mNodeBoundsMax;
|
||||
size_t mNodeStart = size_t(-1); // Start of node in mTree
|
||||
size_t mTriangleStart = size_t(-1); // Start of the triangle data in mTree
|
||||
size_t mChildNodeStart[NumChildrenPerNode]; // Start of the children of the node in mTree
|
||||
size_t mChildTrianglesStart[NumChildrenPerNode]; // Start of the triangle data in mTree
|
||||
size_t * mParentChildNodeStart = nullptr; // Where to store mNodeStart (to patch mChildNodeStart of my parent)
|
||||
size_t * mParentTrianglesStart = nullptr; // Where to store mTriangleStart (to patch mChildTrianglesStart of my parent)
|
||||
uint mNumChildren = 0; // Number of children
|
||||
};
|
||||
|
||||
Array<NodeData *> to_process;
|
||||
to_process.reserve(to_process_max_size);
|
||||
Array<NodeData *> to_process_triangles;
|
||||
to_process_triangles.reserve(to_process_triangles_max_size);
|
||||
Array<NodeData> node_list;
|
||||
node_list.reserve(node_count); // Needed to ensure that array is not reallocated, so we can keep pointers in the array
|
||||
|
||||
NodeData root;
|
||||
root.mNode = inRoot;
|
||||
root.mNodeBoundsMin = inRoot->mBounds.mMin;
|
||||
root.mNodeBoundsMax = inRoot->mBounds.mMax;
|
||||
node_list.push_back(root);
|
||||
to_process.push_back(&node_list.back());
|
||||
|
||||
for (;;)
|
||||
{
|
||||
while (!to_process.empty())
|
||||
{
|
||||
// Get the next node to process
|
||||
NodeData *node_data = to_process.back();
|
||||
to_process.pop_back();
|
||||
|
||||
// Due to quantization box could have become bigger, not smaller
|
||||
JPH_ASSERT(AABox(node_data->mNodeBoundsMin, node_data->mNodeBoundsMax).Contains(node_data->mNode->mBounds), "AABBTreeToBuffer: Bounding box became smaller!");
|
||||
|
||||
// Collect the first NumChildrenPerNode sub-nodes in the tree
|
||||
child_nodes.clear(); // Won't free the memory
|
||||
node_data->mNode->GetNChildren(inNodes, NumChildrenPerNode, child_nodes);
|
||||
node_data->mNumChildren = (uint)child_nodes.size();
|
||||
|
||||
// Fill in default child bounds
|
||||
Vec3 child_bounds_min[NumChildrenPerNode], child_bounds_max[NumChildrenPerNode];
|
||||
for (size_t i = 0; i < NumChildrenPerNode; ++i)
|
||||
if (i < child_nodes.size())
|
||||
{
|
||||
child_bounds_min[i] = child_nodes[i]->mBounds.mMin;
|
||||
child_bounds_max[i] = child_nodes[i]->mBounds.mMax;
|
||||
}
|
||||
else
|
||||
{
|
||||
child_bounds_min[i] = Vec3::sZero();
|
||||
child_bounds_max[i] = Vec3::sZero();
|
||||
}
|
||||
|
||||
// Start a new node
|
||||
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);
|
||||
if (node_data->mNodeStart == size_t(-1))
|
||||
return false;
|
||||
|
||||
if (node_data->mNode->HasChildren())
|
||||
{
|
||||
// Insert in reverse order so we process left child first when taking nodes from the back
|
||||
for (int idx = int(child_nodes.size()) - 1; idx >= 0; --idx)
|
||||
{
|
||||
const AABBTreeBuilder::Node *child_node = child_nodes[idx];
|
||||
|
||||
// Due to quantization box could have become bigger, not smaller
|
||||
JPH_ASSERT(AABox(child_bounds_min[idx], child_bounds_max[idx]).Contains(child_node->mBounds), "AABBTreeToBuffer: Bounding box became smaller!");
|
||||
|
||||
// Add child to list of nodes to be processed
|
||||
NodeData child;
|
||||
child.mNode = child_node;
|
||||
child.mNodeBoundsMin = child_bounds_min[idx];
|
||||
child.mNodeBoundsMax = child_bounds_max[idx];
|
||||
child.mParentChildNodeStart = &node_data->mChildNodeStart[idx];
|
||||
child.mParentTrianglesStart = &node_data->mChildTrianglesStart[idx];
|
||||
node_list.push_back(child);
|
||||
|
||||
// Store triangles in separate list so we process them last
|
||||
if (child_node->HasChildren())
|
||||
to_process.push_back(&node_list.back());
|
||||
else
|
||||
to_process_triangles.push_back(&node_list.back());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add triangles
|
||||
node_data->mTriangleStart = tri_ctx.Pack(&inTriangles[node_data->mNode->mTrianglesBegin], node_data->mNode->mNumTriangles, inStoreUserData, mTree, outError);
|
||||
if (node_data->mTriangleStart == size_t(-1))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Patch offset into parent
|
||||
if (node_data->mParentChildNodeStart != nullptr)
|
||||
{
|
||||
*node_data->mParentChildNodeStart = node_data->mNodeStart;
|
||||
*node_data->mParentTrianglesStart = node_data->mTriangleStart;
|
||||
}
|
||||
}
|
||||
|
||||
// If we've got triangles to process, loop again with just the triangles
|
||||
if (to_process_triangles.empty())
|
||||
break;
|
||||
else
|
||||
to_process.swap(to_process_triangles);
|
||||
}
|
||||
|
||||
// Assert that our reservation was correct (we don't know if we swapped the arrays or not)
|
||||
JPH_ASSERT(to_process_max_size == to_process.capacity() || to_process_triangles_max_size == to_process.capacity());
|
||||
JPH_ASSERT(to_process_max_size == to_process_triangles.capacity() || to_process_triangles_max_size == to_process_triangles.capacity());
|
||||
|
||||
// Finalize all nodes
|
||||
for (NodeData &n : node_list)
|
||||
if (!node_ctx.NodeFinalize(n.mNode, n.mNodeStart, n.mNumChildren, n.mChildNodeStart, n.mChildTrianglesStart, mTree, outError))
|
||||
return false;
|
||||
|
||||
// Finalize the triangles
|
||||
tri_ctx.Finalize(inVertices, triangle_header, mTree);
|
||||
|
||||
// Validate that our reservations were correct
|
||||
if (node_count != node_list.size())
|
||||
{
|
||||
outError = "Internal Error: Node memory estimate was incorrect, memory corruption!";
|
||||
return false;
|
||||
}
|
||||
if (total_size != mTree.size())
|
||||
{
|
||||
outError = "Internal Error: Tree memory estimate was incorrect, memory corruption!";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Finalize the nodes
|
||||
return node_ctx.Finalize(header, inRoot, node_list[0].mNodeStart, node_list[0].mTriangleStart, outError);
|
||||
}
|
||||
|
||||
/// Get resulting data
|
||||
inline const ByteBuffer & GetBuffer() const
|
||||
{
|
||||
return mTree;
|
||||
}
|
||||
|
||||
/// Get resulting data
|
||||
inline ByteBuffer & GetBuffer()
|
||||
{
|
||||
return mTree;
|
||||
}
|
||||
|
||||
/// Get header for tree
|
||||
inline const NodeHeader * GetNodeHeader() const
|
||||
{
|
||||
return mTree.Get<NodeHeader>(0);
|
||||
}
|
||||
|
||||
/// Get header for triangles
|
||||
inline const TriangleHeader * GetTriangleHeader() const
|
||||
{
|
||||
return mTree.Get<TriangleHeader>(HeaderSize);
|
||||
}
|
||||
|
||||
/// Get root of resulting tree
|
||||
inline const void * GetRoot() const
|
||||
{
|
||||
return mTree.Get<void>(HeaderSize + TriangleHeaderSize);
|
||||
}
|
||||
|
||||
private:
|
||||
ByteBuffer mTree; ///< Resulting tree structure
|
||||
};
|
||||
|
||||
JPH_NAMESPACE_END
|
323
thirdparty/jolt_physics/Jolt/AABBTree/NodeCodec/NodeCodecQuadTreeHalfFloat.h
vendored
Normal file
323
thirdparty/jolt_physics/Jolt/AABBTree/NodeCodec/NodeCodecQuadTreeHalfFloat.h
vendored
Normal file
@@ -0,0 +1,323 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Jolt/Core/ByteBuffer.h>
|
||||
#include <Jolt/Math/HalfFloat.h>
|
||||
#include <Jolt/AABBTree/AABBTreeBuilder.h>
|
||||
|
||||
JPH_NAMESPACE_BEGIN
|
||||
|
||||
class NodeCodecQuadTreeHalfFloat
|
||||
{
|
||||
public:
|
||||
/// Number of child nodes of this node
|
||||
static constexpr int NumChildrenPerNode = 4;
|
||||
|
||||
/// Header for the tree
|
||||
struct Header
|
||||
{
|
||||
Float3 mRootBoundsMin;
|
||||
Float3 mRootBoundsMax;
|
||||
uint32 mRootProperties;
|
||||
uint8 mBlockIDBits; ///< Number of bits to address a triangle block
|
||||
uint8 mPadding[3] = { 0 };
|
||||
};
|
||||
|
||||
/// Size of the header (an empty struct is always > 0 bytes so this needs a separate variable)
|
||||
static constexpr int HeaderSize = sizeof(Header);
|
||||
|
||||
/// Stack size to use during DecodingContext::sWalkTree
|
||||
static constexpr int StackSize = 128;
|
||||
|
||||
/// Node properties
|
||||
enum : uint32
|
||||
{
|
||||
TRIANGLE_COUNT_BITS = 4,
|
||||
TRIANGLE_COUNT_SHIFT = 28,
|
||||
TRIANGLE_COUNT_MASK = (1 << TRIANGLE_COUNT_BITS) - 1,
|
||||
OFFSET_BITS = 28,
|
||||
OFFSET_MASK = (1 << OFFSET_BITS) - 1,
|
||||
OFFSET_NON_SIGNIFICANT_BITS = 2,
|
||||
OFFSET_NON_SIGNIFICANT_MASK = (1 << OFFSET_NON_SIGNIFICANT_BITS) - 1,
|
||||
};
|
||||
|
||||
/// Node structure
|
||||
struct Node
|
||||
{
|
||||
HalfFloat mBoundsMinX[4]; ///< 4 child bounding boxes
|
||||
HalfFloat mBoundsMinY[4];
|
||||
HalfFloat mBoundsMinZ[4];
|
||||
HalfFloat mBoundsMaxX[4];
|
||||
HalfFloat mBoundsMaxY[4];
|
||||
HalfFloat mBoundsMaxZ[4];
|
||||
uint32 mNodeProperties[4]; ///< 4 child node properties
|
||||
};
|
||||
|
||||
static_assert(sizeof(Node) == 64, "Node should be 64 bytes");
|
||||
|
||||
/// This class encodes and compresses quad tree nodes
|
||||
class EncodingContext
|
||||
{
|
||||
public:
|
||||
/// Mimics the size a call to NodeAllocate() would add to the buffer
|
||||
void PrepareNodeAllocate(const AABBTreeBuilder::Node *inNode, uint64 &ioBufferSize) const
|
||||
{
|
||||
// We don't emit nodes for leafs
|
||||
if (!inNode->HasChildren())
|
||||
return;
|
||||
|
||||
// Add size of node
|
||||
ioBufferSize += sizeof(Node);
|
||||
}
|
||||
|
||||
/// Allocate a new node for inNode.
|
||||
/// Algorithm can modify the order of ioChildren to indicate in which order children should be compressed
|
||||
/// Algorithm can enlarge the bounding boxes of the children during compression and returns these in outChildBoundsMin, outChildBoundsMax
|
||||
/// inNodeBoundsMin, inNodeBoundsMax is the bounding box if inNode possibly widened by compressing the parent node
|
||||
/// Returns size_t(-1) on error and reports the error in outError
|
||||
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
|
||||
{
|
||||
// We don't emit nodes for leafs
|
||||
if (!inNode->HasChildren())
|
||||
return ioBuffer.size();
|
||||
|
||||
// Remember the start of the node
|
||||
size_t node_start = ioBuffer.size();
|
||||
|
||||
// Fill in bounds
|
||||
Node *node = ioBuffer.Allocate<Node>();
|
||||
|
||||
for (size_t i = 0; i < 4; ++i)
|
||||
{
|
||||
if (i < ioChildren.size())
|
||||
{
|
||||
const AABBTreeBuilder::Node *this_node = ioChildren[i];
|
||||
|
||||
// Copy bounding box
|
||||
node->mBoundsMinX[i] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_NEG_INF>(this_node->mBounds.mMin.GetX());
|
||||
node->mBoundsMinY[i] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_NEG_INF>(this_node->mBounds.mMin.GetY());
|
||||
node->mBoundsMinZ[i] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_NEG_INF>(this_node->mBounds.mMin.GetZ());
|
||||
node->mBoundsMaxX[i] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_POS_INF>(this_node->mBounds.mMax.GetX());
|
||||
node->mBoundsMaxY[i] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_POS_INF>(this_node->mBounds.mMax.GetY());
|
||||
node->mBoundsMaxZ[i] = HalfFloatConversion::FromFloat<HalfFloatConversion::ROUND_TO_POS_INF>(this_node->mBounds.mMax.GetZ());
|
||||
|
||||
// Store triangle count
|
||||
node->mNodeProperties[i] = this_node->GetTriangleCount() << TRIANGLE_COUNT_SHIFT;
|
||||
if (this_node->GetTriangleCount() >= TRIANGLE_COUNT_MASK)
|
||||
{
|
||||
outError = "NodeCodecQuadTreeHalfFloat: Too many triangles";
|
||||
return size_t(-1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Make this an invalid triangle node
|
||||
node->mNodeProperties[i] = uint32(TRIANGLE_COUNT_MASK) << TRIANGLE_COUNT_SHIFT;
|
||||
|
||||
// Make bounding box invalid
|
||||
node->mBoundsMinX[i] = HALF_FLT_MAX;
|
||||
node->mBoundsMinY[i] = HALF_FLT_MAX;
|
||||
node->mBoundsMinZ[i] = HALF_FLT_MAX;
|
||||
node->mBoundsMaxX[i] = HALF_FLT_MAX;
|
||||
node->mBoundsMaxY[i] = HALF_FLT_MAX;
|
||||
node->mBoundsMaxZ[i] = HALF_FLT_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
for (int i = 0; i < NumChildrenPerNode; ++i)
|
||||
{
|
||||
outChildBoundsMin[i] = inNodeBoundsMin;
|
||||
outChildBoundsMax[i] = inNodeBoundsMax;
|
||||
}
|
||||
|
||||
return node_start;
|
||||
}
|
||||
|
||||
/// 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)
|
||||
bool NodeFinalize(const AABBTreeBuilder::Node *inNode, size_t inNodeStart, uint inNumChildren, const size_t *inChildrenNodeStart, const size_t *inChildrenTrianglesStart, ByteBuffer &ioBuffer, const char *&outError)
|
||||
{
|
||||
if (!inNode->HasChildren())
|
||||
return true;
|
||||
|
||||
Node *node = ioBuffer.Get<Node>(inNodeStart);
|
||||
for (uint i = 0; i < inNumChildren; ++i)
|
||||
{
|
||||
size_t offset;
|
||||
if (node->mNodeProperties[i] != 0)
|
||||
{
|
||||
// This is a triangle block
|
||||
offset = inChildrenTrianglesStart[i];
|
||||
|
||||
// Store highest block with triangles so we can count the number of bits we need
|
||||
mHighestTriangleBlock = max(mHighestTriangleBlock, offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is a node block
|
||||
offset = inChildrenNodeStart[i];
|
||||
}
|
||||
|
||||
// Store offset of next node / triangles
|
||||
if (offset & OFFSET_NON_SIGNIFICANT_MASK)
|
||||
{
|
||||
outError = "NodeCodecQuadTreeHalfFloat: Internal Error: Offset has non-significant bits set";
|
||||
return false;
|
||||
}
|
||||
offset >>= OFFSET_NON_SIGNIFICANT_BITS;
|
||||
if (offset > OFFSET_MASK)
|
||||
{
|
||||
outError = "NodeCodecQuadTreeHalfFloat: Offset too large. Too much data.";
|
||||
return false;
|
||||
}
|
||||
node->mNodeProperties[i] |= uint32(offset);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Once all nodes have been finalized, this will finalize the header of the nodes
|
||||
bool Finalize(Header *outHeader, const AABBTreeBuilder::Node *inRoot, size_t inRootNodeStart, size_t inRootTrianglesStart, const char *&outError) const
|
||||
{
|
||||
// Check if we can address the root node
|
||||
size_t offset = inRoot->HasChildren()? inRootNodeStart : inRootTrianglesStart;
|
||||
if (offset & OFFSET_NON_SIGNIFICANT_MASK)
|
||||
{
|
||||
outError = "NodeCodecQuadTreeHalfFloat: Internal Error: Offset has non-significant bits set";
|
||||
return false;
|
||||
}
|
||||
offset >>= OFFSET_NON_SIGNIFICANT_BITS;
|
||||
if (offset > OFFSET_MASK)
|
||||
{
|
||||
outError = "NodeCodecQuadTreeHalfFloat: Offset too large. Too much data.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the root has triangles, we need to take that offset instead since the mHighestTriangleBlock will be zero
|
||||
size_t highest_triangle_block = inRootTrianglesStart != size_t(-1)? inRootTrianglesStart : mHighestTriangleBlock;
|
||||
highest_triangle_block >>= OFFSET_NON_SIGNIFICANT_BITS;
|
||||
|
||||
inRoot->mBounds.mMin.StoreFloat3(&outHeader->mRootBoundsMin);
|
||||
inRoot->mBounds.mMax.StoreFloat3(&outHeader->mRootBoundsMax);
|
||||
outHeader->mRootProperties = uint32(offset) + (inRoot->GetTriangleCount() << TRIANGLE_COUNT_SHIFT);
|
||||
outHeader->mBlockIDBits = uint8(32 - CountLeadingZeros(uint32(highest_triangle_block)));
|
||||
if (inRoot->GetTriangleCount() >= TRIANGLE_COUNT_MASK)
|
||||
{
|
||||
outError = "NodeCodecQuadTreeHalfFloat: Too many triangles";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t mHighestTriangleBlock = 0;
|
||||
};
|
||||
|
||||
/// This class decodes and decompresses quad tree nodes
|
||||
class DecodingContext
|
||||
{
|
||||
public:
|
||||
/// Get the amount of bits needed to store an ID to a triangle block
|
||||
inline static uint sTriangleBlockIDBits(const Header *inHeader)
|
||||
{
|
||||
return inHeader->mBlockIDBits;
|
||||
}
|
||||
|
||||
/// Convert a triangle block ID to the start of the triangle buffer
|
||||
inline static const void * sGetTriangleBlockStart(const uint8 *inBufferStart, uint inTriangleBlockID)
|
||||
{
|
||||
return inBufferStart + (inTriangleBlockID << OFFSET_NON_SIGNIFICANT_BITS);
|
||||
}
|
||||
|
||||
/// Constructor
|
||||
JPH_INLINE explicit DecodingContext(const Header *inHeader)
|
||||
{
|
||||
// Start with the root node on the stack
|
||||
mNodeStack[0] = inHeader->mRootProperties;
|
||||
}
|
||||
|
||||
/// Walk the node tree calling the Visitor::VisitNodes for each node encountered and Visitor::VisitTriangles for each triangle encountered
|
||||
template <class TriangleContext, class Visitor>
|
||||
JPH_INLINE void WalkTree(const uint8 *inBufferStart, const TriangleContext &inTriangleContext, Visitor &ioVisitor)
|
||||
{
|
||||
do
|
||||
{
|
||||
// Test if node contains triangles
|
||||
uint32 node_properties = mNodeStack[mTop];
|
||||
uint32 tri_count = node_properties >> TRIANGLE_COUNT_SHIFT;
|
||||
if (tri_count == 0)
|
||||
{
|
||||
const Node *node = reinterpret_cast<const Node *>(inBufferStart + (node_properties << OFFSET_NON_SIGNIFICANT_BITS));
|
||||
|
||||
// Unpack bounds
|
||||
#ifdef JPH_CPU_BIG_ENDIAN
|
||||
Vec4 bounds_minx = HalfFloatConversion::ToFloat(UVec4(node->mBoundsMinX[0] + (node->mBoundsMinX[1] << 16), node->mBoundsMinX[2] + (node->mBoundsMinX[3] << 16), 0, 0));
|
||||
Vec4 bounds_miny = HalfFloatConversion::ToFloat(UVec4(node->mBoundsMinY[0] + (node->mBoundsMinY[1] << 16), node->mBoundsMinY[2] + (node->mBoundsMinY[3] << 16), 0, 0));
|
||||
Vec4 bounds_minz = HalfFloatConversion::ToFloat(UVec4(node->mBoundsMinZ[0] + (node->mBoundsMinZ[1] << 16), node->mBoundsMinZ[2] + (node->mBoundsMinZ[3] << 16), 0, 0));
|
||||
|
||||
Vec4 bounds_maxx = HalfFloatConversion::ToFloat(UVec4(node->mBoundsMaxX[0] + (node->mBoundsMaxX[1] << 16), node->mBoundsMaxX[2] + (node->mBoundsMaxX[3] << 16), 0, 0));
|
||||
Vec4 bounds_maxy = HalfFloatConversion::ToFloat(UVec4(node->mBoundsMaxY[0] + (node->mBoundsMaxY[1] << 16), node->mBoundsMaxY[2] + (node->mBoundsMaxY[3] << 16), 0, 0));
|
||||
Vec4 bounds_maxz = HalfFloatConversion::ToFloat(UVec4(node->mBoundsMaxZ[0] + (node->mBoundsMaxZ[1] << 16), node->mBoundsMaxZ[2] + (node->mBoundsMaxZ[3] << 16), 0, 0));
|
||||
#else
|
||||
UVec4 bounds_minxy = UVec4::sLoadInt4(reinterpret_cast<const uint32 *>(&node->mBoundsMinX[0]));
|
||||
Vec4 bounds_minx = HalfFloatConversion::ToFloat(bounds_minxy);
|
||||
Vec4 bounds_miny = HalfFloatConversion::ToFloat(bounds_minxy.Swizzle<SWIZZLE_Z, SWIZZLE_W, SWIZZLE_UNUSED, SWIZZLE_UNUSED>());
|
||||
|
||||
UVec4 bounds_minzmaxx = UVec4::sLoadInt4(reinterpret_cast<const uint32 *>(&node->mBoundsMinZ[0]));
|
||||
Vec4 bounds_minz = HalfFloatConversion::ToFloat(bounds_minzmaxx);
|
||||
Vec4 bounds_maxx = HalfFloatConversion::ToFloat(bounds_minzmaxx.Swizzle<SWIZZLE_Z, SWIZZLE_W, SWIZZLE_UNUSED, SWIZZLE_UNUSED>());
|
||||
|
||||
UVec4 bounds_maxyz = UVec4::sLoadInt4(reinterpret_cast<const uint32 *>(&node->mBoundsMaxY[0]));
|
||||
Vec4 bounds_maxy = HalfFloatConversion::ToFloat(bounds_maxyz);
|
||||
Vec4 bounds_maxz = HalfFloatConversion::ToFloat(bounds_maxyz.Swizzle<SWIZZLE_Z, SWIZZLE_W, SWIZZLE_UNUSED, SWIZZLE_UNUSED>());
|
||||
#endif
|
||||
|
||||
// Load properties for 4 children
|
||||
UVec4 properties = UVec4::sLoadInt4(&node->mNodeProperties[0]);
|
||||
|
||||
// Check which sub nodes to visit
|
||||
int num_results = ioVisitor.VisitNodes(bounds_minx, bounds_miny, bounds_minz, bounds_maxx, bounds_maxy, bounds_maxz, properties, mTop);
|
||||
|
||||
// Push them onto the stack
|
||||
JPH_ASSERT(mTop + 4 < StackSize);
|
||||
properties.StoreInt4(&mNodeStack[mTop]);
|
||||
mTop += num_results;
|
||||
}
|
||||
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)
|
||||
{
|
||||
// Node contains triangles, do individual tests
|
||||
uint32 triangle_block_id = node_properties & OFFSET_MASK;
|
||||
const void *triangles = sGetTriangleBlockStart(inBufferStart, triangle_block_id);
|
||||
|
||||
ioVisitor.VisitTriangles(inTriangleContext, triangles, tri_count, triangle_block_id);
|
||||
}
|
||||
|
||||
// Check if we're done
|
||||
if (ioVisitor.ShouldAbort())
|
||||
break;
|
||||
|
||||
// Fetch next node until we find one that the visitor wants to see
|
||||
do
|
||||
--mTop;
|
||||
while (mTop >= 0 && !ioVisitor.ShouldVisitNode(mTop));
|
||||
}
|
||||
while (mTop >= 0);
|
||||
}
|
||||
|
||||
/// This can be used to have the visitor early out (ioVisitor.ShouldAbort() returns true) and later continue again (call WalkTree() again)
|
||||
bool IsDoneWalking() const
|
||||
{
|
||||
return mTop < 0;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32 mNodeStack[StackSize];
|
||||
int mTop = 0;
|
||||
};
|
||||
};
|
||||
|
||||
JPH_NAMESPACE_END
|
555
thirdparty/jolt_physics/Jolt/AABBTree/TriangleCodec/TriangleCodecIndexed8BitPackSOA4Flags.h
vendored
Normal file
555
thirdparty/jolt_physics/Jolt/AABBTree/TriangleCodec/TriangleCodecIndexed8BitPackSOA4Flags.h
vendored
Normal file
@@ -0,0 +1,555 @@
|
||||
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||||
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Jolt/Geometry/RayTriangle.h>
|
||||
|
||||
JPH_NAMESPACE_BEGIN
|
||||
|
||||
/// Store vertices in 64 bits and indices in 8 bits + 8 bit of flags per triangle like this:
|
||||
///
|
||||
/// TriangleBlockHeader,
|
||||
/// TriangleBlock (4 triangles and their flags in 16 bytes),
|
||||
/// TriangleBlock...
|
||||
/// [Optional] UserData (4 bytes per triangle)
|
||||
///
|
||||
/// Vertices are stored:
|
||||
///
|
||||
/// VertexData (1 vertex in 64 bits),
|
||||
/// VertexData...
|
||||
///
|
||||
/// They're compressed relative to the bounding box as provided by the node codec.
|
||||
class TriangleCodecIndexed8BitPackSOA4Flags
|
||||
{
|
||||
public:
|
||||
class TriangleHeader
|
||||
{
|
||||
public:
|
||||
Float3 mOffset; ///< Offset of all vertices
|
||||
Float3 mScale; ///< Scale of all vertices, vertex_position = mOffset + mScale * compressed_vertex_position
|
||||
};
|
||||
|
||||
/// Size of the header (an empty struct is always > 0 bytes so this needs a separate variable)
|
||||
static constexpr int TriangleHeaderSize = sizeof(TriangleHeader);
|
||||
|
||||
/// If this codec could return a different offset than the current buffer size when calling Pack()
|
||||
static constexpr bool ChangesOffsetOnPack = false;
|
||||
|
||||
/// Amount of bits per component
|
||||
enum EComponentData : uint32
|
||||
{
|
||||
COMPONENT_BITS = 21,
|
||||
COMPONENT_MASK = (1 << COMPONENT_BITS) - 1,
|
||||
};
|
||||
|
||||
/// Packed X and Y coordinate
|
||||
enum EVertexXY : uint32
|
||||
{
|
||||
COMPONENT_X = 0,
|
||||
COMPONENT_Y1 = COMPONENT_BITS,
|
||||
COMPONENT_Y1_BITS = 32 - COMPONENT_BITS,
|
||||
};
|
||||
|
||||
/// Packed Z and Y coordinate
|
||||
enum EVertexZY : uint32
|
||||
{
|
||||
COMPONENT_Z = 0,
|
||||
COMPONENT_Y2 = COMPONENT_BITS,
|
||||
COMPONENT_Y2_BITS = 31 - COMPONENT_BITS,
|
||||
};
|
||||
|
||||
/// A single packed vertex
|
||||
struct VertexData
|
||||
{
|
||||
uint32 mVertexXY;
|
||||
uint32 mVertexZY;
|
||||
};
|
||||
|
||||
static_assert(sizeof(VertexData) == 8, "Compiler added padding");
|
||||
|
||||
/// A block of 4 triangles
|
||||
struct TriangleBlock
|
||||
{
|
||||
uint8 mIndices[3][4]; ///< 8 bit indices to triangle vertices for 4 triangles in the form mIndices[vertex][triangle] where vertex in [0, 2] and triangle in [0, 3]
|
||||
uint8 mFlags[4]; ///< Triangle flags (could contain material and active edges)
|
||||
};
|
||||
|
||||
static_assert(sizeof(TriangleBlock) == 16, "Compiler added padding");
|
||||
|
||||
enum ETriangleBlockHeaderFlags : uint32
|
||||
{
|
||||
OFFSET_TO_VERTICES_BITS = 29, ///< Offset from current block to start of vertices in bytes
|
||||
OFFSET_TO_VERTICES_MASK = (1 << OFFSET_TO_VERTICES_BITS) - 1,
|
||||
OFFSET_NON_SIGNIFICANT_BITS = 2, ///< The offset from the current block to the start of the vertices must be a multiple of 4 bytes
|
||||
OFFSET_NON_SIGNIFICANT_MASK = (1 << OFFSET_NON_SIGNIFICANT_BITS) - 1,
|
||||
OFFSET_TO_USERDATA_BITS = 3, ///< When user data is stored, this is the number of blocks to skip to get to the user data (0 = no user data)
|
||||
OFFSET_TO_USERDATA_MASK = (1 << OFFSET_TO_USERDATA_BITS) - 1,
|
||||
};
|
||||
|
||||
/// A triangle header, will be followed by one or more TriangleBlocks
|
||||
struct TriangleBlockHeader
|
||||
{
|
||||
const VertexData * GetVertexData() const { return reinterpret_cast<const VertexData *>(reinterpret_cast<const uint8 *>(this) + ((mFlags & OFFSET_TO_VERTICES_MASK) << OFFSET_NON_SIGNIFICANT_BITS)); }
|
||||
const TriangleBlock * GetTriangleBlock() const { return reinterpret_cast<const TriangleBlock *>(reinterpret_cast<const uint8 *>(this) + sizeof(TriangleBlockHeader)); }
|
||||
const uint32 * GetUserData() const { uint32 offset = mFlags >> OFFSET_TO_VERTICES_BITS; return offset == 0? nullptr : reinterpret_cast<const uint32 *>(GetTriangleBlock() + offset); }
|
||||
|
||||
uint32 mFlags;
|
||||
};
|
||||
|
||||
static_assert(sizeof(TriangleBlockHeader) == 4, "Compiler added padding");
|
||||
|
||||
/// This class is used to validate that the triangle data will not be degenerate after compression
|
||||
class ValidationContext
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
ValidationContext(const IndexedTriangleList &inTriangles, const VertexList &inVertices) :
|
||||
mVertices(inVertices)
|
||||
{
|
||||
// Only used the referenced triangles, just like EncodingContext::Finalize does
|
||||
for (const IndexedTriangle &i : inTriangles)
|
||||
for (uint32 idx : i.mIdx)
|
||||
mBounds.Encapsulate(Vec3(inVertices[idx]));
|
||||
}
|
||||
|
||||
/// Test if a triangle will be degenerate after quantization
|
||||
bool IsDegenerate(const IndexedTriangle &inTriangle) const
|
||||
{
|
||||
// Quantize the triangle in the same way as EncodingContext::Finalize does
|
||||
UVec4 quantized_vertex[3];
|
||||
Vec3 compress_scale = Vec3::sReplicate(COMPONENT_MASK) / Vec3::sMax(mBounds.GetSize(), Vec3::sReplicate(1.0e-20f));
|
||||
for (int i = 0; i < 3; ++i)
|
||||
quantized_vertex[i] = ((Vec3(mVertices[inTriangle.mIdx[i]]) - mBounds.mMin) * compress_scale + Vec3::sReplicate(0.5f)).ToInt();
|
||||
return quantized_vertex[0] == quantized_vertex[1] || quantized_vertex[1] == quantized_vertex[2] || quantized_vertex[0] == quantized_vertex[2];
|
||||
}
|
||||
|
||||
private:
|
||||
const VertexList & mVertices;
|
||||
AABox mBounds;
|
||||
};
|
||||
|
||||
/// This class is used to encode and compress triangle data into a byte buffer
|
||||
class EncodingContext
|
||||
{
|
||||
public:
|
||||
/// Indicates a vertex hasn't been seen yet in the triangle list
|
||||
static constexpr uint32 cNotFound = 0xffffffff;
|
||||
|
||||
/// Construct the encoding context
|
||||
explicit EncodingContext(const VertexList &inVertices) :
|
||||
mVertexMap(inVertices.size(), cNotFound)
|
||||
{
|
||||
}
|
||||
|
||||
/// Mimics the size a call to Pack() would add to the buffer
|
||||
void PreparePack(const IndexedTriangle *inTriangles, uint inNumTriangles, bool inStoreUserData, uint64 &ioBufferSize)
|
||||
{
|
||||
// Add triangle block header
|
||||
ioBufferSize += sizeof(TriangleBlockHeader);
|
||||
|
||||
// Compute first vertex that this batch will use (ensuring there's enough room if none of the vertices are shared)
|
||||
uint start_vertex = Clamp((int)mVertexCount - 256 + (int)inNumTriangles * 3, 0, (int)mVertexCount);
|
||||
|
||||
// Pack vertices
|
||||
uint padded_triangle_count = AlignUp(inNumTriangles, 4);
|
||||
for (uint t = 0; t < padded_triangle_count; t += 4)
|
||||
{
|
||||
// Add triangle block header
|
||||
ioBufferSize += sizeof(TriangleBlock);
|
||||
|
||||
for (uint vertex_nr = 0; vertex_nr < 3; ++vertex_nr)
|
||||
for (uint block_tri_idx = 0; block_tri_idx < 4; ++block_tri_idx)
|
||||
{
|
||||
// Fetch vertex index. Create degenerate triangles for padding triangles.
|
||||
bool triangle_available = t + block_tri_idx < inNumTriangles;
|
||||
uint32 src_vertex_index = triangle_available? inTriangles[t + block_tri_idx].mIdx[vertex_nr] : inTriangles[inNumTriangles - 1].mIdx[0];
|
||||
|
||||
// Check if we've seen this vertex before and if it is in the range that we can encode
|
||||
uint32 &vertex_index = mVertexMap[src_vertex_index];
|
||||
if (vertex_index == cNotFound || vertex_index < start_vertex)
|
||||
{
|
||||
// Add vertex
|
||||
vertex_index = mVertexCount;
|
||||
mVertexCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add user data
|
||||
if (inStoreUserData)
|
||||
ioBufferSize += inNumTriangles * sizeof(uint32);
|
||||
}
|
||||
|
||||
/// Mimics the size the Finalize() call would add to ioBufferSize
|
||||
void FinalizePreparePack(uint64 &ioBufferSize)
|
||||
{
|
||||
// Remember where the vertices are going to start in the output buffer
|
||||
JPH_ASSERT(IsAligned(ioBufferSize, 4));
|
||||
mVerticesStartIdx = size_t(ioBufferSize);
|
||||
|
||||
// Add vertices to buffer
|
||||
ioBufferSize += uint64(mVertexCount) * sizeof(VertexData);
|
||||
|
||||
// Reserve the amount of memory we need for the vertices
|
||||
mVertices.reserve(mVertexCount);
|
||||
|
||||
// Set vertex map back to 'not found'
|
||||
for (uint32 &v : mVertexMap)
|
||||
v = cNotFound;
|
||||
}
|
||||
|
||||
/// Pack the triangles in inContainer to ioBuffer. This stores the mMaterialIndex of a triangle in the 8 bit flags.
|
||||
/// Returns size_t(-1) on error.
|
||||
size_t Pack(const IndexedTriangle *inTriangles, uint inNumTriangles, bool inStoreUserData, ByteBuffer &ioBuffer, const char *&outError)
|
||||
{
|
||||
JPH_ASSERT(inNumTriangles > 0);
|
||||
|
||||
// Determine position of triangles start
|
||||
size_t triangle_block_start = ioBuffer.size();
|
||||
|
||||
// Allocate triangle block header
|
||||
TriangleBlockHeader *header = ioBuffer.Allocate<TriangleBlockHeader>();
|
||||
|
||||
// Compute first vertex that this batch will use (ensuring there's enough room if none of the vertices are shared)
|
||||
uint start_vertex = Clamp((int)mVertices.size() - 256 + (int)inNumTriangles * 3, 0, (int)mVertices.size());
|
||||
|
||||
// Store the start vertex offset relative to TriangleBlockHeader
|
||||
size_t offset_to_vertices = mVerticesStartIdx - triangle_block_start + size_t(start_vertex) * sizeof(VertexData);
|
||||
if (offset_to_vertices & OFFSET_NON_SIGNIFICANT_MASK)
|
||||
{
|
||||
outError = "TriangleCodecIndexed8BitPackSOA4Flags: Internal Error: Offset has non-significant bits set";
|
||||
return size_t(-1);
|
||||
}
|
||||
offset_to_vertices >>= OFFSET_NON_SIGNIFICANT_BITS;
|
||||
if (offset_to_vertices > OFFSET_TO_VERTICES_MASK)
|
||||
{
|
||||
outError = "TriangleCodecIndexed8BitPackSOA4Flags: Offset to vertices doesn't fit. Too much data.";
|
||||
return size_t(-1);
|
||||
}
|
||||
header->mFlags = uint32(offset_to_vertices);
|
||||
|
||||
// When we store user data we need to store the offset to the user data in TriangleBlocks
|
||||
uint padded_triangle_count = AlignUp(inNumTriangles, 4);
|
||||
if (inStoreUserData)
|
||||
{
|
||||
uint32 num_blocks = padded_triangle_count >> 2;
|
||||
JPH_ASSERT(num_blocks <= OFFSET_TO_USERDATA_MASK);
|
||||
header->mFlags |= num_blocks << OFFSET_TO_VERTICES_BITS;
|
||||
}
|
||||
|
||||
// Pack vertices
|
||||
for (uint t = 0; t < padded_triangle_count; t += 4)
|
||||
{
|
||||
TriangleBlock *block = ioBuffer.Allocate<TriangleBlock>();
|
||||
for (uint vertex_nr = 0; vertex_nr < 3; ++vertex_nr)
|
||||
for (uint block_tri_idx = 0; block_tri_idx < 4; ++block_tri_idx)
|
||||
{
|
||||
// Fetch vertex index. Create degenerate triangles for padding triangles.
|
||||
bool triangle_available = t + block_tri_idx < inNumTriangles;
|
||||
uint32 src_vertex_index = triangle_available? inTriangles[t + block_tri_idx].mIdx[vertex_nr] : inTriangles[inNumTriangles - 1].mIdx[0];
|
||||
|
||||
// Check if we've seen this vertex before and if it is in the range that we can encode
|
||||
uint32 &vertex_index = mVertexMap[src_vertex_index];
|
||||
if (vertex_index == cNotFound || vertex_index < start_vertex)
|
||||
{
|
||||
// Add vertex
|
||||
vertex_index = (uint32)mVertices.size();
|
||||
mVertices.push_back(src_vertex_index);
|
||||
}
|
||||
|
||||
// Store vertex index
|
||||
uint32 vertex_offset = vertex_index - start_vertex;
|
||||
if (vertex_offset > 0xff)
|
||||
{
|
||||
outError = "TriangleCodecIndexed8BitPackSOA4Flags: Offset doesn't fit in 8 bit";
|
||||
return size_t(-1);
|
||||
}
|
||||
block->mIndices[vertex_nr][block_tri_idx] = (uint8)vertex_offset;
|
||||
|
||||
// Store flags
|
||||
uint32 flags = triangle_available? inTriangles[t + block_tri_idx].mMaterialIndex : 0;
|
||||
if (flags > 0xff)
|
||||
{
|
||||
outError = "TriangleCodecIndexed8BitPackSOA4Flags: Material index doesn't fit in 8 bit";
|
||||
return size_t(-1);
|
||||
}
|
||||
block->mFlags[block_tri_idx] = (uint8)flags;
|
||||
}
|
||||
}
|
||||
|
||||
// Store user data
|
||||
if (inStoreUserData)
|
||||
{
|
||||
uint32 *user_data = ioBuffer.Allocate<uint32>(inNumTriangles);
|
||||
for (uint t = 0; t < inNumTriangles; ++t)
|
||||
user_data[t] = inTriangles[t].mUserData;
|
||||
}
|
||||
|
||||
return triangle_block_start;
|
||||
}
|
||||
|
||||
/// After all triangles have been packed, this finalizes the header and triangle buffer
|
||||
void Finalize(const VertexList &inVertices, TriangleHeader *ioHeader, ByteBuffer &ioBuffer) const
|
||||
{
|
||||
// Assert that our reservations were correct
|
||||
JPH_ASSERT(mVertices.size() == mVertexCount);
|
||||
JPH_ASSERT(ioBuffer.size() == mVerticesStartIdx);
|
||||
|
||||
// Check if anything to do
|
||||
if (mVertices.empty())
|
||||
return;
|
||||
|
||||
// Calculate bounding box
|
||||
AABox bounds;
|
||||
for (uint32 v : mVertices)
|
||||
bounds.Encapsulate(Vec3(inVertices[v]));
|
||||
|
||||
// Compress vertices
|
||||
VertexData *vertices = ioBuffer.Allocate<VertexData>(mVertices.size());
|
||||
Vec3 compress_scale = Vec3::sReplicate(COMPONENT_MASK) / Vec3::sMax(bounds.GetSize(), Vec3::sReplicate(1.0e-20f));
|
||||
for (uint32 v : mVertices)
|
||||
{
|
||||
UVec4 c = ((Vec3(inVertices[v]) - bounds.mMin) * compress_scale + Vec3::sReplicate(0.5f)).ToInt();
|
||||
JPH_ASSERT(c.GetX() <= COMPONENT_MASK);
|
||||
JPH_ASSERT(c.GetY() <= COMPONENT_MASK);
|
||||
JPH_ASSERT(c.GetZ() <= COMPONENT_MASK);
|
||||
vertices->mVertexXY = c.GetX() + (c.GetY() << COMPONENT_Y1);
|
||||
vertices->mVertexZY = c.GetZ() + ((c.GetY() >> COMPONENT_Y1_BITS) << COMPONENT_Y2);
|
||||
++vertices;
|
||||
}
|
||||
|
||||
// Store decompression information
|
||||
bounds.mMin.StoreFloat3(&ioHeader->mOffset);
|
||||
(bounds.GetSize() / Vec3::sReplicate(COMPONENT_MASK)).StoreFloat3(&ioHeader->mScale);
|
||||
}
|
||||
|
||||
private:
|
||||
using VertexMap = Array<uint32>;
|
||||
|
||||
uint32 mVertexCount = 0; ///< Number of vertices calculated during PreparePack
|
||||
size_t mVerticesStartIdx = 0; ///< Start of the vertices in the output buffer, calculated during PreparePack
|
||||
Array<uint32> mVertices; ///< Output vertices as an index into the original vertex list (inVertices), sorted according to occurrence
|
||||
VertexMap mVertexMap; ///< Maps from the original mesh vertex index (inVertices) to the index in our output vertices (mVertices)
|
||||
};
|
||||
|
||||
/// This class is used to decode and decompress triangle data packed by the EncodingContext
|
||||
class DecodingContext
|
||||
{
|
||||
private:
|
||||
/// Private helper function to unpack the 1 vertex of 4 triangles (outX contains the x coordinate of triangle 0 .. 3 etc.)
|
||||
JPH_INLINE void Unpack(const VertexData *inVertices, UVec4Arg inIndex, Vec4 &outX, Vec4 &outY, Vec4 &outZ) const
|
||||
{
|
||||
// Get compressed data
|
||||
UVec4 c1 = UVec4::sGatherInt4<8>(&inVertices->mVertexXY, inIndex);
|
||||
UVec4 c2 = UVec4::sGatherInt4<8>(&inVertices->mVertexZY, inIndex);
|
||||
|
||||
// Unpack the x y and z component
|
||||
UVec4 xc = UVec4::sAnd(c1, UVec4::sReplicate(COMPONENT_MASK));
|
||||
UVec4 yc = UVec4::sOr(c1.LogicalShiftRight<COMPONENT_Y1>(), c2.LogicalShiftRight<COMPONENT_Y2>().LogicalShiftLeft<COMPONENT_Y1_BITS>());
|
||||
UVec4 zc = UVec4::sAnd(c2, UVec4::sReplicate(COMPONENT_MASK));
|
||||
|
||||
// Convert to float
|
||||
outX = Vec4::sFusedMultiplyAdd(xc.ToFloat(), mScaleX, mOffsetX);
|
||||
outY = Vec4::sFusedMultiplyAdd(yc.ToFloat(), mScaleY, mOffsetY);
|
||||
outZ = Vec4::sFusedMultiplyAdd(zc.ToFloat(), mScaleZ, mOffsetZ);
|
||||
}
|
||||
|
||||
/// Private helper function to unpack 4 triangles from a triangle block
|
||||
JPH_INLINE void Unpack(const TriangleBlock *inBlock, const VertexData *inVertices, Vec4 &outX1, Vec4 &outY1, Vec4 &outZ1, Vec4 &outX2, Vec4 &outY2, Vec4 &outZ2, Vec4 &outX3, Vec4 &outY3, Vec4 &outZ3) const
|
||||
{
|
||||
// Get the indices for the three vertices (reads 4 bytes extra, but these are the flags so that's ok)
|
||||
UVec4 indices = UVec4::sLoadInt4(reinterpret_cast<const uint32 *>(&inBlock->mIndices[0]));
|
||||
UVec4 iv1 = indices.Expand4Byte0();
|
||||
UVec4 iv2 = indices.Expand4Byte4();
|
||||
UVec4 iv3 = indices.Expand4Byte8();
|
||||
|
||||
#ifdef JPH_CPU_BIG_ENDIAN
|
||||
// On big endian systems we need to reverse the bytes
|
||||
iv1 = iv1.Swizzle<SWIZZLE_W, SWIZZLE_Z, SWIZZLE_Y, SWIZZLE_X>();
|
||||
iv2 = iv2.Swizzle<SWIZZLE_W, SWIZZLE_Z, SWIZZLE_Y, SWIZZLE_X>();
|
||||
iv3 = iv3.Swizzle<SWIZZLE_W, SWIZZLE_Z, SWIZZLE_Y, SWIZZLE_X>();
|
||||
#endif
|
||||
|
||||
// Decompress the triangle data
|
||||
Unpack(inVertices, iv1, outX1, outY1, outZ1);
|
||||
Unpack(inVertices, iv2, outX2, outY2, outZ2);
|
||||
Unpack(inVertices, iv3, outX3, outY3, outZ3);
|
||||
}
|
||||
|
||||
public:
|
||||
JPH_INLINE explicit DecodingContext(const TriangleHeader *inHeader) :
|
||||
mOffsetX(Vec4::sReplicate(inHeader->mOffset.x)),
|
||||
mOffsetY(Vec4::sReplicate(inHeader->mOffset.y)),
|
||||
mOffsetZ(Vec4::sReplicate(inHeader->mOffset.z)),
|
||||
mScaleX(Vec4::sReplicate(inHeader->mScale.x)),
|
||||
mScaleY(Vec4::sReplicate(inHeader->mScale.y)),
|
||||
mScaleZ(Vec4::sReplicate(inHeader->mScale.z))
|
||||
{
|
||||
}
|
||||
|
||||
/// Unpacks triangles in the format t1v1,t1v2,t1v3, t2v1,t2v2,t2v3, ...
|
||||
JPH_INLINE void Unpack(const void *inTriangleStart, uint32 inNumTriangles, Vec3 *outTriangles) const
|
||||
{
|
||||
JPH_ASSERT(inNumTriangles > 0);
|
||||
const TriangleBlockHeader *header = reinterpret_cast<const TriangleBlockHeader *>(inTriangleStart);
|
||||
const VertexData *vertices = header->GetVertexData();
|
||||
const TriangleBlock *t = header->GetTriangleBlock();
|
||||
const TriangleBlock *end = t + ((inNumTriangles + 3) >> 2);
|
||||
|
||||
int triangles_left = inNumTriangles;
|
||||
|
||||
do
|
||||
{
|
||||
// Unpack the vertices for 4 triangles
|
||||
Vec4 v1x, v1y, v1z, v2x, v2y, v2z, v3x, v3y, v3z;
|
||||
Unpack(t, vertices, v1x, v1y, v1z, v2x, v2y, v2z, v3x, v3y, v3z);
|
||||
|
||||
// Transpose it so we get normal vectors
|
||||
Mat44 v1 = Mat44(v1x, v1y, v1z, Vec4::sZero()).Transposed();
|
||||
Mat44 v2 = Mat44(v2x, v2y, v2z, Vec4::sZero()).Transposed();
|
||||
Mat44 v3 = Mat44(v3x, v3y, v3z, Vec4::sZero()).Transposed();
|
||||
|
||||
// Store triangle data
|
||||
for (int i = 0; i < 4 && triangles_left > 0; ++i, --triangles_left)
|
||||
{
|
||||
*outTriangles++ = v1.GetColumn3(i);
|
||||
*outTriangles++ = v2.GetColumn3(i);
|
||||
*outTriangles++ = v3.GetColumn3(i);
|
||||
}
|
||||
|
||||
++t;
|
||||
}
|
||||
while (t < end);
|
||||
}
|
||||
|
||||
/// Tests a ray against the packed triangles
|
||||
JPH_INLINE float TestRay(Vec3Arg inRayOrigin, Vec3Arg inRayDirection, const void *inTriangleStart, uint32 inNumTriangles, float inClosest, uint32 &outClosestTriangleIndex) const
|
||||
{
|
||||
JPH_ASSERT(inNumTriangles > 0);
|
||||
const TriangleBlockHeader *header = reinterpret_cast<const TriangleBlockHeader *>(inTriangleStart);
|
||||
const VertexData *vertices = header->GetVertexData();
|
||||
const TriangleBlock *t = header->GetTriangleBlock();
|
||||
const TriangleBlock *end = t + ((inNumTriangles + 3) >> 2);
|
||||
|
||||
Vec4 closest = Vec4::sReplicate(inClosest);
|
||||
UVec4 closest_triangle_idx = UVec4::sZero();
|
||||
|
||||
UVec4 start_triangle_idx = UVec4::sZero();
|
||||
do
|
||||
{
|
||||
// Unpack the vertices for 4 triangles
|
||||
Vec4 v1x, v1y, v1z, v2x, v2y, v2z, v3x, v3y, v3z;
|
||||
Unpack(t, vertices, v1x, v1y, v1z, v2x, v2y, v2z, v3x, v3y, v3z);
|
||||
|
||||
// Perform ray vs triangle test
|
||||
Vec4 distance = RayTriangle4(inRayOrigin, inRayDirection, v1x, v1y, v1z, v2x, v2y, v2z, v3x, v3y, v3z);
|
||||
|
||||
// Update closest with the smaller values
|
||||
UVec4 smaller = Vec4::sLess(distance, closest);
|
||||
closest = Vec4::sSelect(closest, distance, smaller);
|
||||
|
||||
// Update triangle index with the smallest values
|
||||
UVec4 triangle_idx = start_triangle_idx + UVec4(0, 1, 2, 3);
|
||||
closest_triangle_idx = UVec4::sSelect(closest_triangle_idx, triangle_idx, smaller);
|
||||
|
||||
// Next block
|
||||
++t;
|
||||
start_triangle_idx += UVec4::sReplicate(4);
|
||||
}
|
||||
while (t < end);
|
||||
|
||||
// Get the smallest component
|
||||
Vec4::sSort4(closest, closest_triangle_idx);
|
||||
outClosestTriangleIndex = closest_triangle_idx.GetX();
|
||||
return closest.GetX();
|
||||
}
|
||||
|
||||
/// Decode a single triangle
|
||||
inline void GetTriangle(const void *inTriangleStart, uint32 inTriangleIdx, Vec3 &outV1, Vec3 &outV2, Vec3 &outV3) const
|
||||
{
|
||||
const TriangleBlockHeader *header = reinterpret_cast<const TriangleBlockHeader *>(inTriangleStart);
|
||||
const VertexData *vertices = header->GetVertexData();
|
||||
const TriangleBlock *block = header->GetTriangleBlock() + (inTriangleIdx >> 2);
|
||||
uint32 block_triangle_idx = inTriangleIdx & 0b11;
|
||||
|
||||
// Get the 3 vertices
|
||||
const VertexData &v1 = vertices[block->mIndices[0][block_triangle_idx]];
|
||||
const VertexData &v2 = vertices[block->mIndices[1][block_triangle_idx]];
|
||||
const VertexData &v3 = vertices[block->mIndices[2][block_triangle_idx]];
|
||||
|
||||
// Pack the vertices
|
||||
UVec4 c1(v1.mVertexXY, v2.mVertexXY, v3.mVertexXY, 0);
|
||||
UVec4 c2(v1.mVertexZY, v2.mVertexZY, v3.mVertexZY, 0);
|
||||
|
||||
// Unpack the x y and z component
|
||||
UVec4 xc = UVec4::sAnd(c1, UVec4::sReplicate(COMPONENT_MASK));
|
||||
UVec4 yc = UVec4::sOr(c1.LogicalShiftRight<COMPONENT_Y1>(), c2.LogicalShiftRight<COMPONENT_Y2>().LogicalShiftLeft<COMPONENT_Y1_BITS>());
|
||||
UVec4 zc = UVec4::sAnd(c2, UVec4::sReplicate(COMPONENT_MASK));
|
||||
|
||||
// Convert to float
|
||||
Vec4 vx = Vec4::sFusedMultiplyAdd(xc.ToFloat(), mScaleX, mOffsetX);
|
||||
Vec4 vy = Vec4::sFusedMultiplyAdd(yc.ToFloat(), mScaleY, mOffsetY);
|
||||
Vec4 vz = Vec4::sFusedMultiplyAdd(zc.ToFloat(), mScaleZ, mOffsetZ);
|
||||
|
||||
// Transpose it so we get normal vectors
|
||||
Mat44 trans = Mat44(vx, vy, vz, Vec4::sZero()).Transposed();
|
||||
outV1 = trans.GetAxisX();
|
||||
outV2 = trans.GetAxisY();
|
||||
outV3 = trans.GetAxisZ();
|
||||
}
|
||||
|
||||
/// Get user data for a triangle
|
||||
JPH_INLINE uint32 GetUserData(const void *inTriangleStart, uint32 inTriangleIdx) const
|
||||
{
|
||||
const TriangleBlockHeader *header = reinterpret_cast<const TriangleBlockHeader *>(inTriangleStart);
|
||||
const uint32 *user_data = header->GetUserData();
|
||||
return user_data != nullptr? user_data[inTriangleIdx] : 0;
|
||||
}
|
||||
|
||||
/// Get flags for entire triangle block
|
||||
JPH_INLINE static void sGetFlags(const void *inTriangleStart, uint32 inNumTriangles, uint8 *outTriangleFlags)
|
||||
{
|
||||
JPH_ASSERT(inNumTriangles > 0);
|
||||
const TriangleBlockHeader *header = reinterpret_cast<const TriangleBlockHeader *>(inTriangleStart);
|
||||
const TriangleBlock *t = header->GetTriangleBlock();
|
||||
const TriangleBlock *end = t + ((inNumTriangles + 3) >> 2);
|
||||
|
||||
int triangles_left = inNumTriangles;
|
||||
do
|
||||
{
|
||||
for (int i = 0; i < 4 && triangles_left > 0; ++i, --triangles_left)
|
||||
*outTriangleFlags++ = t->mFlags[i];
|
||||
|
||||
++t;
|
||||
}
|
||||
while (t < end);
|
||||
}
|
||||
|
||||
/// Get flags for a particular triangle
|
||||
JPH_INLINE static uint8 sGetFlags(const void *inTriangleStart, int inTriangleIndex)
|
||||
{
|
||||
const TriangleBlockHeader *header = reinterpret_cast<const TriangleBlockHeader *>(inTriangleStart);
|
||||
const TriangleBlock *first_block = header->GetTriangleBlock();
|
||||
return first_block[inTriangleIndex >> 2].mFlags[inTriangleIndex & 0b11];
|
||||
}
|
||||
|
||||
/// Unpacks triangles and flags, convenience function
|
||||
JPH_INLINE void Unpack(const void *inTriangleStart, uint32 inNumTriangles, Vec3 *outTriangles, uint8 *outTriangleFlags) const
|
||||
{
|
||||
Unpack(inTriangleStart, inNumTriangles, outTriangles);
|
||||
sGetFlags(inTriangleStart, inNumTriangles, outTriangleFlags);
|
||||
}
|
||||
|
||||
private:
|
||||
Vec4 mOffsetX;
|
||||
Vec4 mOffsetY;
|
||||
Vec4 mOffsetZ;
|
||||
Vec4 mScaleX;
|
||||
Vec4 mScaleY;
|
||||
Vec4 mScaleZ;
|
||||
};
|
||||
};
|
||||
|
||||
JPH_NAMESPACE_END
|
Reference in New Issue
Block a user