Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/jolt_physics/Jolt/AABBTree/AABBTreeToBuffer.h
9906 views
1
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
2
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
3
// SPDX-License-Identifier: MIT
4
5
#pragma once
6
7
#include <Jolt/AABBTree/AABBTreeBuilder.h>
8
#include <Jolt/Core/ByteBuffer.h>
9
#include <Jolt/Geometry/IndexedTriangle.h>
10
11
JPH_NAMESPACE_BEGIN
12
13
/// Conversion algorithm that converts an AABB tree to an optimized binary buffer
14
template <class TriangleCodec, class NodeCodec>
15
class AABBTreeToBuffer
16
{
17
public:
18
/// Header for the tree
19
using NodeHeader = typename NodeCodec::Header;
20
21
/// Size in bytes of the header of the tree
22
static const int HeaderSize = NodeCodec::HeaderSize;
23
24
/// Maximum number of children per node in the tree
25
static const int NumChildrenPerNode = NodeCodec::NumChildrenPerNode;
26
27
/// Header for the triangles
28
using TriangleHeader = typename TriangleCodec::TriangleHeader;
29
30
/// Size in bytes of the header for the triangles
31
static const int TriangleHeaderSize = TriangleCodec::TriangleHeaderSize;
32
33
/// Convert AABB tree. Returns false if failed.
34
bool Convert(const Array<IndexedTriangle> &inTriangles, const Array<AABBTreeBuilder::Node> &inNodes, const VertexList &inVertices, const AABBTreeBuilder::Node *inRoot, bool inStoreUserData, const char *&outError)
35
{
36
typename NodeCodec::EncodingContext node_ctx;
37
typename TriangleCodec::EncodingContext tri_ctx(inVertices);
38
39
// Child nodes out of loop so we don't constantly realloc it
40
Array<const AABBTreeBuilder::Node *> child_nodes;
41
child_nodes.reserve(NumChildrenPerNode);
42
43
// First calculate how big the tree is going to be.
44
// Since the tree can be huge for very large meshes, we don't want
45
// to reallocate the buffer as it may cause out of memory situations.
46
// This loop mimics the construction loop below.
47
uint64 total_size = HeaderSize + TriangleHeaderSize;
48
size_t node_count = 1; // Start with root node
49
size_t to_process_max_size = 1; // Track size of queues so we can do a single reserve below
50
size_t to_process_triangles_max_size = 0;
51
{ // A scope to free the memory associated with to_estimate and to_estimate_triangles
52
Array<const AABBTreeBuilder::Node *> to_estimate;
53
Array<const AABBTreeBuilder::Node *> to_estimate_triangles;
54
to_estimate.push_back(inRoot);
55
for (;;)
56
{
57
while (!to_estimate.empty())
58
{
59
// Get the next node to process
60
const AABBTreeBuilder::Node *node = to_estimate.back();
61
to_estimate.pop_back();
62
63
// Update total size
64
node_ctx.PrepareNodeAllocate(node, total_size);
65
66
if (node->HasChildren())
67
{
68
// Collect the first NumChildrenPerNode sub-nodes in the tree
69
child_nodes.clear(); // Won't free the memory
70
node->GetNChildren(inNodes, NumChildrenPerNode, child_nodes);
71
72
// Increment the number of nodes we're going to store
73
node_count += child_nodes.size();
74
75
// Insert in reverse order so we estimate left child first when taking nodes from the back
76
for (int idx = int(child_nodes.size()) - 1; idx >= 0; --idx)
77
{
78
// Store triangles in separate list so we process them last
79
const AABBTreeBuilder::Node *child = child_nodes[idx];
80
if (child->HasChildren())
81
{
82
to_estimate.push_back(child);
83
to_process_max_size = max(to_estimate.size(), to_process_max_size);
84
}
85
else
86
{
87
to_estimate_triangles.push_back(child);
88
to_process_triangles_max_size = max(to_estimate_triangles.size(), to_process_triangles_max_size);
89
}
90
}
91
}
92
else
93
{
94
// Update total size
95
tri_ctx.PreparePack(&inTriangles[node->mTrianglesBegin], node->mNumTriangles, inStoreUserData, total_size);
96
}
97
}
98
99
// If we've got triangles to estimate, loop again with just the triangles
100
if (to_estimate_triangles.empty())
101
break;
102
else
103
to_estimate.swap(to_estimate_triangles);
104
}
105
}
106
107
// Finalize the prepare stage for the triangle context
108
tri_ctx.FinalizePreparePack(total_size);
109
110
// Reserve the buffer
111
if (size_t(total_size) != total_size)
112
{
113
outError = "AABBTreeToBuffer: Out of memory!";
114
return false;
115
}
116
mTree.reserve(size_t(total_size));
117
118
// Add headers
119
NodeHeader *header = HeaderSize > 0? mTree.Allocate<NodeHeader>() : nullptr;
120
TriangleHeader *triangle_header = TriangleHeaderSize > 0? mTree.Allocate<TriangleHeader>() : nullptr;
121
122
struct NodeData
123
{
124
const AABBTreeBuilder::Node * mNode = nullptr; // Node that this entry belongs to
125
Vec3 mNodeBoundsMin; // Quantized node bounds
126
Vec3 mNodeBoundsMax;
127
size_t mNodeStart = size_t(-1); // Start of node in mTree
128
size_t mTriangleStart = size_t(-1); // Start of the triangle data in mTree
129
size_t mChildNodeStart[NumChildrenPerNode]; // Start of the children of the node in mTree
130
size_t mChildTrianglesStart[NumChildrenPerNode]; // Start of the triangle data in mTree
131
size_t * mParentChildNodeStart = nullptr; // Where to store mNodeStart (to patch mChildNodeStart of my parent)
132
size_t * mParentTrianglesStart = nullptr; // Where to store mTriangleStart (to patch mChildTrianglesStart of my parent)
133
uint mNumChildren = 0; // Number of children
134
};
135
136
Array<NodeData *> to_process;
137
to_process.reserve(to_process_max_size);
138
Array<NodeData *> to_process_triangles;
139
to_process_triangles.reserve(to_process_triangles_max_size);
140
Array<NodeData> node_list;
141
node_list.reserve(node_count); // Needed to ensure that array is not reallocated, so we can keep pointers in the array
142
143
NodeData root;
144
root.mNode = inRoot;
145
root.mNodeBoundsMin = inRoot->mBounds.mMin;
146
root.mNodeBoundsMax = inRoot->mBounds.mMax;
147
node_list.push_back(root);
148
to_process.push_back(&node_list.back());
149
150
for (;;)
151
{
152
while (!to_process.empty())
153
{
154
// Get the next node to process
155
NodeData *node_data = to_process.back();
156
to_process.pop_back();
157
158
// Due to quantization box could have become bigger, not smaller
159
JPH_ASSERT(AABox(node_data->mNodeBoundsMin, node_data->mNodeBoundsMax).Contains(node_data->mNode->mBounds), "AABBTreeToBuffer: Bounding box became smaller!");
160
161
// Collect the first NumChildrenPerNode sub-nodes in the tree
162
child_nodes.clear(); // Won't free the memory
163
node_data->mNode->GetNChildren(inNodes, NumChildrenPerNode, child_nodes);
164
node_data->mNumChildren = (uint)child_nodes.size();
165
166
// Fill in default child bounds
167
Vec3 child_bounds_min[NumChildrenPerNode], child_bounds_max[NumChildrenPerNode];
168
for (size_t i = 0; i < NumChildrenPerNode; ++i)
169
if (i < child_nodes.size())
170
{
171
child_bounds_min[i] = child_nodes[i]->mBounds.mMin;
172
child_bounds_max[i] = child_nodes[i]->mBounds.mMax;
173
}
174
else
175
{
176
child_bounds_min[i] = Vec3::sZero();
177
child_bounds_max[i] = Vec3::sZero();
178
}
179
180
// Start a new node
181
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);
182
if (node_data->mNodeStart == size_t(-1))
183
return false;
184
185
if (node_data->mNode->HasChildren())
186
{
187
// Insert in reverse order so we process left child first when taking nodes from the back
188
for (int idx = int(child_nodes.size()) - 1; idx >= 0; --idx)
189
{
190
const AABBTreeBuilder::Node *child_node = child_nodes[idx];
191
192
// Due to quantization box could have become bigger, not smaller
193
JPH_ASSERT(AABox(child_bounds_min[idx], child_bounds_max[idx]).Contains(child_node->mBounds), "AABBTreeToBuffer: Bounding box became smaller!");
194
195
// Add child to list of nodes to be processed
196
NodeData child;
197
child.mNode = child_node;
198
child.mNodeBoundsMin = child_bounds_min[idx];
199
child.mNodeBoundsMax = child_bounds_max[idx];
200
child.mParentChildNodeStart = &node_data->mChildNodeStart[idx];
201
child.mParentTrianglesStart = &node_data->mChildTrianglesStart[idx];
202
node_list.push_back(child);
203
204
// Store triangles in separate list so we process them last
205
if (child_node->HasChildren())
206
to_process.push_back(&node_list.back());
207
else
208
to_process_triangles.push_back(&node_list.back());
209
}
210
}
211
else
212
{
213
// Add triangles
214
node_data->mTriangleStart = tri_ctx.Pack(&inTriangles[node_data->mNode->mTrianglesBegin], node_data->mNode->mNumTriangles, inStoreUserData, mTree, outError);
215
if (node_data->mTriangleStart == size_t(-1))
216
return false;
217
}
218
219
// Patch offset into parent
220
if (node_data->mParentChildNodeStart != nullptr)
221
{
222
*node_data->mParentChildNodeStart = node_data->mNodeStart;
223
*node_data->mParentTrianglesStart = node_data->mTriangleStart;
224
}
225
}
226
227
// If we've got triangles to process, loop again with just the triangles
228
if (to_process_triangles.empty())
229
break;
230
else
231
to_process.swap(to_process_triangles);
232
}
233
234
// Assert that our reservation was correct (we don't know if we swapped the arrays or not)
235
JPH_ASSERT(to_process_max_size == to_process.capacity() || to_process_triangles_max_size == to_process.capacity());
236
JPH_ASSERT(to_process_max_size == to_process_triangles.capacity() || to_process_triangles_max_size == to_process_triangles.capacity());
237
238
// Finalize all nodes
239
for (NodeData &n : node_list)
240
if (!node_ctx.NodeFinalize(n.mNode, n.mNodeStart, n.mNumChildren, n.mChildNodeStart, n.mChildTrianglesStart, mTree, outError))
241
return false;
242
243
// Finalize the triangles
244
tri_ctx.Finalize(inVertices, triangle_header, mTree);
245
246
// Validate that our reservations were correct
247
if (node_count != node_list.size())
248
{
249
outError = "Internal Error: Node memory estimate was incorrect, memory corruption!";
250
return false;
251
}
252
if (total_size != mTree.size())
253
{
254
outError = "Internal Error: Tree memory estimate was incorrect, memory corruption!";
255
return false;
256
}
257
258
// Finalize the nodes
259
return node_ctx.Finalize(header, inRoot, node_list[0].mNodeStart, node_list[0].mTriangleStart, outError);
260
}
261
262
/// Get resulting data
263
inline const ByteBuffer & GetBuffer() const
264
{
265
return mTree;
266
}
267
268
/// Get resulting data
269
inline ByteBuffer & GetBuffer()
270
{
271
return mTree;
272
}
273
274
/// Get header for tree
275
inline const NodeHeader * GetNodeHeader() const
276
{
277
return mTree.Get<NodeHeader>(0);
278
}
279
280
/// Get header for triangles
281
inline const TriangleHeader * GetTriangleHeader() const
282
{
283
return mTree.Get<TriangleHeader>(HeaderSize);
284
}
285
286
/// Get root of resulting tree
287
inline const void * GetRoot() const
288
{
289
return mTree.Get<void>(HeaderSize + TriangleHeaderSize);
290
}
291
292
private:
293
ByteBuffer mTree; ///< Resulting tree structure
294
};
295
296
JPH_NAMESPACE_END
297
298