Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/manifold/src/collider.h
9902 views
1
// Copyright 2021 The Manifold Authors.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
#pragma once
16
#include "manifold/common.h"
17
#include "parallel.h"
18
#include "utils.h"
19
#include "vec.h"
20
21
#ifdef _MSC_VER
22
#include <intrin.h>
23
#endif
24
25
#if (MANIFOLD_PAR == 1)
26
#include <tbb/combinable.h>
27
#endif
28
29
namespace manifold {
30
31
namespace collider_internal {
32
// Adjustable parameters
33
constexpr int kInitialLength = 128;
34
constexpr int kLengthMultiple = 4;
35
constexpr int kSequentialThreshold = 512;
36
// Fundamental constants
37
constexpr int kRoot = 1;
38
39
#ifdef _MSC_VER
40
41
#ifndef _WINDEF_
42
using DWORD = unsigned long;
43
#endif
44
45
uint32_t inline ctz(uint32_t value) {
46
DWORD trailing_zero = 0;
47
48
if (_BitScanForward(&trailing_zero, value)) {
49
return trailing_zero;
50
} else {
51
// This is undefined, I better choose 32 than 0
52
return 32;
53
}
54
}
55
56
uint32_t inline clz(uint32_t value) {
57
DWORD leading_zero = 0;
58
59
if (_BitScanReverse(&leading_zero, value)) {
60
return 31 - leading_zero;
61
} else {
62
// Same remarks as above
63
return 32;
64
}
65
}
66
#endif
67
68
constexpr inline bool IsLeaf(int node) { return node % 2 == 0; }
69
constexpr inline bool IsInternal(int node) { return node % 2 == 1; }
70
constexpr inline int Node2Internal(int node) { return (node - 1) / 2; }
71
constexpr inline int Internal2Node(int internal) { return internal * 2 + 1; }
72
constexpr inline int Node2Leaf(int node) { return node / 2; }
73
constexpr inline int Leaf2Node(int leaf) { return leaf * 2; }
74
75
struct CreateRadixTree {
76
VecView<int> nodeParent_;
77
VecView<std::pair<int, int>> internalChildren_;
78
const VecView<const uint32_t> leafMorton_;
79
80
int PrefixLength(uint32_t a, uint32_t b) const {
81
// count-leading-zeros is used to find the number of identical highest-order
82
// bits
83
#ifdef _MSC_VER
84
// return __lzcnt(a ^ b);
85
return clz(a ^ b);
86
#else
87
return __builtin_clz(a ^ b);
88
#endif
89
}
90
91
int PrefixLength(int i, int j) const {
92
if (j < 0 || j >= static_cast<int>(leafMorton_.size())) {
93
return -1;
94
} else {
95
int out;
96
if (leafMorton_[i] == leafMorton_[j])
97
// use index to disambiguate
98
out = 32 +
99
PrefixLength(static_cast<uint32_t>(i), static_cast<uint32_t>(j));
100
else
101
out = PrefixLength(leafMorton_[i], leafMorton_[j]);
102
return out;
103
}
104
}
105
106
int RangeEnd(int i) const {
107
// Determine direction of range (+1 or -1)
108
int dir = PrefixLength(i, i + 1) - PrefixLength(i, i - 1);
109
dir = (dir > 0) - (dir < 0);
110
// Compute conservative range length with exponential increase
111
int commonPrefix = PrefixLength(i, i - dir);
112
int max_length = kInitialLength;
113
while (PrefixLength(i, i + dir * max_length) > commonPrefix)
114
max_length *= kLengthMultiple;
115
// Compute precise range length with binary search
116
int length = 0;
117
for (int step = max_length / 2; step > 0; step /= 2) {
118
if (PrefixLength(i, i + dir * (length + step)) > commonPrefix)
119
length += step;
120
}
121
return i + dir * length;
122
}
123
124
int FindSplit(int first, int last) const {
125
int commonPrefix = PrefixLength(first, last);
126
// Find the furthest object that shares more than commonPrefix bits with the
127
// first one, using binary search.
128
int split = first;
129
int step = last - first;
130
do {
131
step = (step + 1) >> 1; // divide by 2, rounding up
132
int newSplit = split + step;
133
if (newSplit < last) {
134
int splitPrefix = PrefixLength(first, newSplit);
135
if (splitPrefix > commonPrefix) split = newSplit;
136
}
137
} while (step > 1);
138
return split;
139
}
140
141
void operator()(int internal) {
142
int first = internal;
143
// Find the range of objects with a common prefix
144
int last = RangeEnd(first);
145
if (first > last) std::swap(first, last);
146
// Determine where the next-highest difference occurs
147
int split = FindSplit(first, last);
148
int child1 = split == first ? Leaf2Node(split) : Internal2Node(split);
149
++split;
150
int child2 = split == last ? Leaf2Node(split) : Internal2Node(split);
151
// Record parent_child relationships.
152
internalChildren_[internal].first = child1;
153
internalChildren_[internal].second = child2;
154
int node = Internal2Node(internal);
155
nodeParent_[child1] = node;
156
nodeParent_[child2] = node;
157
}
158
};
159
160
template <typename F, const bool selfCollision, typename Recorder>
161
struct FindCollision {
162
F& f;
163
VecView<const Box> nodeBBox_;
164
VecView<const std::pair<int, int>> internalChildren_;
165
Recorder& recorder;
166
167
using Local = typename Recorder::Local;
168
169
inline int RecordCollision(int node, const int queryIdx, Local& local) {
170
bool overlaps = nodeBBox_[node].DoesOverlap(f(queryIdx));
171
if (overlaps && IsLeaf(node)) {
172
int leafIdx = Node2Leaf(node);
173
if (!selfCollision || leafIdx != queryIdx) {
174
recorder.record(queryIdx, leafIdx, local);
175
}
176
}
177
return overlaps && IsInternal(node); // Should traverse into node
178
}
179
180
void operator()(const int queryIdx) {
181
// stack cannot overflow because radix tree has max depth 30 (Morton code) +
182
// 32 (index).
183
int stack[64];
184
int top = -1;
185
// Depth-first search
186
int node = kRoot;
187
Local& local = recorder.local();
188
while (1) {
189
int internal = Node2Internal(node);
190
int child1 = internalChildren_[internal].first;
191
int child2 = internalChildren_[internal].second;
192
193
int traverse1 = RecordCollision(child1, queryIdx, local);
194
int traverse2 = RecordCollision(child2, queryIdx, local);
195
196
if (!traverse1 && !traverse2) {
197
if (top < 0) break; // done
198
node = stack[top--]; // get a saved node
199
} else {
200
node = traverse1 ? child1 : child2; // go here next
201
if (traverse1 && traverse2) {
202
stack[++top] = child2; // save the other for later
203
}
204
}
205
}
206
}
207
};
208
209
struct BuildInternalBoxes {
210
VecView<Box> nodeBBox_;
211
VecView<int> counter_;
212
const VecView<int> nodeParent_;
213
const VecView<std::pair<int, int>> internalChildren_;
214
215
void operator()(int leaf) {
216
int node = Leaf2Node(leaf);
217
do {
218
node = nodeParent_[node];
219
int internal = Node2Internal(node);
220
if (AtomicAdd(counter_[internal], 1) == 0) return;
221
nodeBBox_[node] = nodeBBox_[internalChildren_[internal].first].Union(
222
nodeBBox_[internalChildren_[internal].second]);
223
} while (node != kRoot);
224
}
225
};
226
227
struct TransformBox {
228
const mat3x4 transform;
229
void operator()(Box& box) { box = box.Transform(transform); }
230
};
231
232
constexpr inline uint32_t SpreadBits3(uint32_t v) {
233
v = 0xFF0000FFu & (v * 0x00010001u);
234
v = 0x0F00F00Fu & (v * 0x00000101u);
235
v = 0xC30C30C3u & (v * 0x00000011u);
236
v = 0x49249249u & (v * 0x00000005u);
237
return v;
238
}
239
} // namespace collider_internal
240
241
template <typename F>
242
struct SimpleRecorder {
243
using Local = F;
244
F& f;
245
246
inline void record(int queryIdx, int leafIdx, F& f) const {
247
f(queryIdx, leafIdx);
248
}
249
Local& local() { return f; }
250
};
251
252
template <typename F>
253
inline SimpleRecorder<F> MakeSimpleRecorder(F& f) {
254
return SimpleRecorder<F>{f};
255
}
256
257
/** @ingroup Private */
258
class Collider {
259
public:
260
Collider() {};
261
262
Collider(const VecView<const Box>& leafBB,
263
const VecView<const uint32_t>& leafMorton) {
264
ZoneScoped;
265
DEBUG_ASSERT(leafBB.size() == leafMorton.size(), userErr,
266
"vectors must be the same length");
267
int num_nodes = 2 * leafBB.size() - 1;
268
// assign and allocate members
269
nodeBBox_.resize_nofill(num_nodes);
270
nodeParent_.resize(num_nodes, -1);
271
internalChildren_.resize(leafBB.size() - 1, std::make_pair(-1, -1));
272
// organize tree
273
for_each_n(autoPolicy(NumInternal(), 1e4), countAt(0), NumInternal(),
274
collider_internal::CreateRadixTree(
275
{nodeParent_, internalChildren_, leafMorton}));
276
UpdateBoxes(leafBB);
277
}
278
279
bool Transform(mat3x4 transform) {
280
ZoneScoped;
281
bool axisAligned = true;
282
for (int row : {0, 1, 2}) {
283
int count = 0;
284
for (int col : {0, 1, 2}) {
285
if (transform[col][row] == 0.0) ++count;
286
}
287
if (count != 2) axisAligned = false;
288
}
289
if (axisAligned) {
290
for_each(autoPolicy(nodeBBox_.size(), 1e5), nodeBBox_.begin(),
291
nodeBBox_.end(),
292
[transform](Box& box) { box = box.Transform(transform); });
293
}
294
return axisAligned;
295
}
296
297
void UpdateBoxes(const VecView<const Box>& leafBB) {
298
ZoneScoped;
299
DEBUG_ASSERT(leafBB.size() == NumLeaves(), userErr,
300
"must have the same number of updated boxes as original");
301
// copy in leaf node Boxes
302
auto leaves = StridedRange(nodeBBox_.begin(), nodeBBox_.end(), 2);
303
copy(leafBB.cbegin(), leafBB.cend(), leaves.begin());
304
// create global counters
305
Vec<int> counter(NumInternal(), 0);
306
// kernel over leaves to save internal Boxes
307
for_each_n(autoPolicy(NumInternal(), 1e3), countAt(0), NumLeaves(),
308
collider_internal::BuildInternalBoxes(
309
{nodeBBox_, counter, nodeParent_, internalChildren_}));
310
}
311
312
// This function iterates over queriesIn and calls recorder.record(queryIdx,
313
// leafIdx, local) for each collision it found.
314
// If selfCollisionl is true, it will skip the case where queryIdx == leafIdx.
315
// The recorder should provide a local() method that returns a Recorder::Local
316
// type, representing thread local storage. By default, recorder.record can
317
// run in parallel and the thread local storage can be combined at the end.
318
// If parallel is false, the function will run in sequential mode.
319
//
320
// If thread local storage is not needed, use SimpleRecorder.
321
template <const bool selfCollision = false, typename T, typename Recorder>
322
void Collisions(const VecView<const T>& queriesIn, Recorder& recorder,
323
bool parallel = true) const {
324
ZoneScoped;
325
using collider_internal::FindCollision;
326
if (internalChildren_.empty()) return;
327
auto f = [queriesIn](const int i) { return queriesIn[i]; };
328
for_each_n(parallel ? autoPolicy(queriesIn.size(),
329
collider_internal::kSequentialThreshold)
330
: ExecutionPolicy::Seq,
331
countAt(0), queriesIn.size(),
332
FindCollision<decltype(f), selfCollision, Recorder>{
333
f, nodeBBox_, internalChildren_, recorder});
334
}
335
336
template <const bool selfCollision = false, typename F, typename Recorder>
337
void Collisions(F f, int n, Recorder& recorder, bool parallel = true) const {
338
ZoneScoped;
339
using collider_internal::FindCollision;
340
if (internalChildren_.empty()) return;
341
for_each_n(parallel ? autoPolicy(n, collider_internal::kSequentialThreshold)
342
: ExecutionPolicy::Seq,
343
countAt(0), n,
344
FindCollision<decltype(f), selfCollision, Recorder>{
345
f, nodeBBox_, internalChildren_, recorder});
346
}
347
348
static uint32_t MortonCode(vec3 position, Box bBox) {
349
using collider_internal::SpreadBits3;
350
vec3 xyz = (position - bBox.min) / (bBox.max - bBox.min);
351
xyz = la::min(vec3(1023.0), la::max(vec3(0.0), 1024.0 * xyz));
352
uint32_t x = SpreadBits3(static_cast<uint32_t>(xyz.x));
353
uint32_t y = SpreadBits3(static_cast<uint32_t>(xyz.y));
354
uint32_t z = SpreadBits3(static_cast<uint32_t>(xyz.z));
355
return x * 4 + y * 2 + z;
356
}
357
358
private:
359
Vec<Box> nodeBBox_;
360
Vec<int> nodeParent_;
361
// even nodes are leaves, odd nodes are internal, root is 1
362
Vec<std::pair<int, int>> internalChildren_;
363
364
size_t NumInternal() const { return internalChildren_.size(); };
365
size_t NumLeaves() const {
366
return internalChildren_.empty() ? 0 : (NumInternal() + 1);
367
};
368
};
369
370
} // namespace manifold
371
372