Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/manifold/src/sort.cpp
9903 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
#include <atomic>
16
#include <set>
17
18
#include "disjoint_sets.h"
19
#include "impl.h"
20
#include "parallel.h"
21
#include "shared.h"
22
23
namespace {
24
using namespace manifold;
25
26
constexpr uint32_t kNoCode = 0xFFFFFFFFu;
27
28
uint32_t MortonCode(vec3 position, Box bBox) {
29
// Unreferenced vertices are marked NaN, and this will sort them to the end
30
// (the Morton code only uses the first 30 of 32 bits).
31
if (std::isnan(position.x)) return kNoCode;
32
33
return Collider::MortonCode(position, bBox);
34
}
35
36
struct ReindexFace {
37
VecView<Halfedge> halfedge;
38
VecView<vec4> halfedgeTangent;
39
VecView<const Halfedge> oldHalfedge;
40
VecView<const vec4> oldHalfedgeTangent;
41
VecView<const int> faceNew2Old;
42
VecView<const int> faceOld2New;
43
44
void operator()(int newFace) {
45
const int oldFace = faceNew2Old[newFace];
46
for (const int i : {0, 1, 2}) {
47
const int oldEdge = 3 * oldFace + i;
48
Halfedge edge = oldHalfedge[oldEdge];
49
const int pairedFace = edge.pairedHalfedge / 3;
50
const int offset = edge.pairedHalfedge - 3 * pairedFace;
51
edge.pairedHalfedge = 3 * faceOld2New[pairedFace] + offset;
52
const int newEdge = 3 * newFace + i;
53
halfedge[newEdge] = edge;
54
if (!oldHalfedgeTangent.empty()) {
55
halfedgeTangent[newEdge] = oldHalfedgeTangent[oldEdge];
56
}
57
}
58
}
59
};
60
61
template <typename Precision, typename I>
62
bool MergeMeshGLP(MeshGLP<Precision, I>& mesh) {
63
ZoneScoped;
64
std::multiset<std::pair<int, int>> openEdges;
65
66
std::vector<int> merge(mesh.NumVert());
67
std::iota(merge.begin(), merge.end(), 0);
68
for (size_t i = 0; i < mesh.mergeFromVert.size(); ++i) {
69
merge[mesh.mergeFromVert[i]] = mesh.mergeToVert[i];
70
}
71
72
const auto numVert = mesh.NumVert();
73
const auto numTri = mesh.NumTri();
74
const int next[3] = {1, 2, 0};
75
for (size_t tri = 0; tri < numTri; ++tri) {
76
for (int i : {0, 1, 2}) {
77
auto edge = std::make_pair(merge[mesh.triVerts[3 * tri + next[i]]],
78
merge[mesh.triVerts[3 * tri + i]]);
79
auto it = openEdges.find(edge);
80
if (it == openEdges.end()) {
81
std::swap(edge.first, edge.second);
82
openEdges.insert(edge);
83
} else {
84
openEdges.erase(it);
85
}
86
}
87
}
88
if (openEdges.empty()) {
89
return false;
90
}
91
92
const auto numOpenVert = openEdges.size();
93
Vec<int> openVerts(numOpenVert);
94
int i = 0;
95
for (const auto& edge : openEdges) {
96
const int vert = edge.first;
97
openVerts[i++] = vert;
98
}
99
100
Vec<Precision> vertPropD(mesh.vertProperties);
101
Box bBox;
102
for (const int i : {0, 1, 2}) {
103
auto iPos =
104
StridedRange(vertPropD.begin() + i, vertPropD.end(), mesh.numProp);
105
auto minMax = manifold::transform_reduce(
106
iPos.begin(), iPos.end(),
107
std::make_pair(std::numeric_limits<double>::infinity(),
108
-std::numeric_limits<double>::infinity()),
109
[](auto a, auto b) {
110
return std::make_pair(std::min(a.first, b.first),
111
std::max(a.second, b.second));
112
},
113
[](double f) { return std::make_pair(f, f); });
114
bBox.min[i] = minMax.first;
115
bBox.max[i] = minMax.second;
116
}
117
118
const double tolerance = std::max(static_cast<double>(mesh.tolerance),
119
(std::is_same<Precision, float>::value
120
? std::numeric_limits<float>::epsilon()
121
: kPrecision) *
122
bBox.Scale());
123
124
auto policy = autoPolicy(numOpenVert, 1e5);
125
Vec<Box> vertBox(numOpenVert);
126
Vec<uint32_t> vertMorton(numOpenVert);
127
128
for_each_n(policy, countAt(0), numOpenVert,
129
[&vertMorton, &vertBox, &openVerts, &bBox, &mesh,
130
tolerance](const int i) {
131
int vert = openVerts[i];
132
133
const vec3 center(mesh.vertProperties[mesh.numProp * vert],
134
mesh.vertProperties[mesh.numProp * vert + 1],
135
mesh.vertProperties[mesh.numProp * vert + 2]);
136
137
vertBox[i].min = center - tolerance / 2.0;
138
vertBox[i].max = center + tolerance / 2.0;
139
140
vertMorton[i] = MortonCode(center, bBox);
141
});
142
143
Vec<int> vertNew2Old(numOpenVert);
144
sequence(vertNew2Old.begin(), vertNew2Old.end());
145
146
stable_sort(vertNew2Old.begin(), vertNew2Old.end(),
147
[&vertMorton](const int& a, const int& b) {
148
return vertMorton[a] < vertMorton[b];
149
});
150
151
Permute(vertMorton, vertNew2Old);
152
Permute(vertBox, vertNew2Old);
153
Permute(openVerts, vertNew2Old);
154
155
Collider collider(vertBox, vertMorton);
156
DisjointSets uf(numVert);
157
158
auto f = [&uf, &openVerts](int a, int b) {
159
return uf.unite(openVerts[a], openVerts[b]);
160
};
161
auto recorder = MakeSimpleRecorder(f);
162
collider.Collisions<true>(vertBox.cview(), recorder, false);
163
164
for (size_t i = 0; i < mesh.mergeFromVert.size(); ++i) {
165
uf.unite(static_cast<int>(mesh.mergeFromVert[i]),
166
static_cast<int>(mesh.mergeToVert[i]));
167
}
168
169
mesh.mergeToVert.clear();
170
mesh.mergeFromVert.clear();
171
for (size_t v = 0; v < numVert; ++v) {
172
const size_t mergeTo = uf.find(v);
173
if (mergeTo != v) {
174
mesh.mergeFromVert.push_back(v);
175
mesh.mergeToVert.push_back(mergeTo);
176
}
177
}
178
179
return true;
180
}
181
} // namespace
182
183
namespace manifold {
184
185
/**
186
* Once halfedge_ has been filled in, this function can be called to create the
187
* rest of the internal data structures. This function also removes the verts
188
* and halfedges flagged for removal (NaN verts and -1 halfedges).
189
*/
190
void Manifold::Impl::Finish() {
191
if (halfedge_.size() == 0) return;
192
193
CalculateBBox();
194
SetEpsilon(epsilon_);
195
if (!bBox_.IsFinite()) {
196
// Decimated out of existence - early out.
197
MakeEmpty(Error::NoError);
198
return;
199
}
200
201
SortVerts();
202
Vec<Box> faceBox;
203
Vec<uint32_t> faceMorton;
204
GetFaceBoxMorton(faceBox, faceMorton);
205
SortFaces(faceBox, faceMorton);
206
if (halfedge_.size() == 0) return;
207
CompactProps();
208
209
DEBUG_ASSERT(halfedge_.size() % 6 == 0, topologyErr,
210
"Not an even number of faces after sorting faces!");
211
212
#ifdef MANIFOLD_DEBUG
213
if (ManifoldParams().intermediateChecks) {
214
auto MaxOrMinus = [](int a, int b) {
215
return std::min(a, b) < 0 ? -1 : std::max(a, b);
216
};
217
int face = 0;
218
Halfedge extrema = {0, 0, 0};
219
for (size_t i = 0; i < halfedge_.size(); i++) {
220
Halfedge e = halfedge_[i];
221
if (!e.IsForward()) std::swap(e.startVert, e.endVert);
222
extrema.startVert = std::min(extrema.startVert, e.startVert);
223
extrema.endVert = std::min(extrema.endVert, e.endVert);
224
extrema.pairedHalfedge =
225
MaxOrMinus(extrema.pairedHalfedge, e.pairedHalfedge);
226
face = MaxOrMinus(face, i / 3);
227
}
228
DEBUG_ASSERT(extrema.startVert >= 0, topologyErr,
229
"Vertex index is negative!");
230
DEBUG_ASSERT(extrema.endVert < static_cast<int>(NumVert()), topologyErr,
231
"Vertex index exceeds number of verts!");
232
DEBUG_ASSERT(extrema.pairedHalfedge >= 0, topologyErr,
233
"Halfedge index is negative!");
234
DEBUG_ASSERT(extrema.pairedHalfedge < 2 * static_cast<int>(NumEdge()),
235
topologyErr, "Halfedge index exceeds number of halfedges!");
236
DEBUG_ASSERT(face >= 0, topologyErr, "Face index is negative!");
237
DEBUG_ASSERT(face < static_cast<int>(NumTri()), topologyErr,
238
"Face index exceeds number of faces!");
239
}
240
#endif
241
242
DEBUG_ASSERT(meshRelation_.triRef.size() == NumTri() ||
243
meshRelation_.triRef.size() == 0,
244
logicErr, "Mesh Relation doesn't fit!");
245
DEBUG_ASSERT(faceNormal_.size() == NumTri() || faceNormal_.size() == 0,
246
logicErr,
247
"faceNormal size = " + std::to_string(faceNormal_.size()) +
248
", NumTri = " + std::to_string(NumTri()));
249
CalculateNormals();
250
collider_ = Collider(faceBox, faceMorton);
251
252
DEBUG_ASSERT(Is2Manifold(), logicErr, "mesh is not 2-manifold!");
253
}
254
255
/**
256
* Sorts the vertices according to their Morton code.
257
*/
258
void Manifold::Impl::SortVerts() {
259
ZoneScoped;
260
const auto numVert = NumVert();
261
Vec<uint32_t> vertMorton(numVert);
262
auto policy = autoPolicy(numVert, 1e5);
263
for_each_n(policy, countAt(0), numVert, [this, &vertMorton](const int vert) {
264
vertMorton[vert] = MortonCode(vertPos_[vert], bBox_);
265
});
266
267
Vec<int> vertNew2Old(numVert);
268
sequence(vertNew2Old.begin(), vertNew2Old.end());
269
270
stable_sort(vertNew2Old.begin(), vertNew2Old.end(),
271
[&vertMorton](const int& a, const int& b) {
272
return vertMorton[a] < vertMorton[b];
273
});
274
275
ReindexVerts(vertNew2Old, numVert);
276
277
// Verts were flagged for removal with NaNs and assigned kNoCode to sort
278
// them to the end, which allows them to be removed.
279
const auto newNumVert =
280
std::lower_bound(vertNew2Old.begin(), vertNew2Old.end(), kNoCode,
281
[&vertMorton](const int vert, const uint32_t val) {
282
return vertMorton[vert] < val;
283
}) -
284
vertNew2Old.begin();
285
286
vertNew2Old.resize(newNumVert);
287
Permute(vertPos_, vertNew2Old);
288
289
if (vertNormal_.size() == numVert) {
290
Permute(vertNormal_, vertNew2Old);
291
}
292
}
293
294
/**
295
* Updates the halfedges to point to new vert indices based on a mapping,
296
* vertNew2Old. This may be a subset, so the total number of original verts is
297
* also given.
298
*/
299
void Manifold::Impl::ReindexVerts(const Vec<int>& vertNew2Old,
300
size_t oldNumVert) {
301
ZoneScoped;
302
Vec<int> vertOld2New(oldNumVert);
303
scatter(countAt(0), countAt(static_cast<int>(NumVert())), vertNew2Old.begin(),
304
vertOld2New.begin());
305
const bool hasProp = NumProp() > 0;
306
for_each(autoPolicy(oldNumVert, 1e5), halfedge_.begin(), halfedge_.end(),
307
[&vertOld2New, hasProp](Halfedge& edge) {
308
if (edge.startVert < 0) return;
309
edge.startVert = vertOld2New[edge.startVert];
310
edge.endVert = vertOld2New[edge.endVert];
311
if (!hasProp) {
312
edge.propVert = edge.startVert;
313
}
314
});
315
}
316
317
/**
318
* Removes unreferenced property verts and reindexes propVerts.
319
*/
320
void Manifold::Impl::CompactProps() {
321
ZoneScoped;
322
if (numProp_ == 0) return;
323
324
const int numProp = NumProp();
325
const auto numVerts = properties_.size() / numProp;
326
Vec<int> keep(numVerts, 0);
327
auto policy = autoPolicy(numVerts, 1e5);
328
329
for_each(policy, halfedge_.cbegin(), halfedge_.cend(), [&keep](Halfedge h) {
330
reinterpret_cast<std::atomic<int>*>(&keep[h.propVert])
331
->store(1, std::memory_order_relaxed);
332
});
333
Vec<int> propOld2New(numVerts + 1, 0);
334
inclusive_scan(keep.begin(), keep.end(), propOld2New.begin() + 1);
335
336
Vec<double> oldProp = properties_;
337
const int numVertsNew = propOld2New[numVerts];
338
auto& properties = properties_;
339
properties.resize_nofill(numProp * numVertsNew);
340
for_each_n(
341
policy, countAt(0), numVerts,
342
[&properties, &oldProp, &propOld2New, &keep, &numProp](const int oldIdx) {
343
if (keep[oldIdx] == 0) return;
344
for (int p = 0; p < numProp; ++p) {
345
properties[propOld2New[oldIdx] * numProp + p] =
346
oldProp[oldIdx * numProp + p];
347
}
348
});
349
for_each(policy, halfedge_.begin(), halfedge_.end(),
350
[&propOld2New](Halfedge& edge) {
351
edge.propVert = propOld2New[edge.propVert];
352
});
353
}
354
355
/**
356
* Fills the faceBox and faceMorton input with the bounding boxes and Morton
357
* codes of the faces, respectively. The Morton code is based on the center of
358
* the bounding box.
359
*/
360
void Manifold::Impl::GetFaceBoxMorton(Vec<Box>& faceBox,
361
Vec<uint32_t>& faceMorton) const {
362
ZoneScoped;
363
// faceBox should be initialized
364
faceBox.resize(NumTri(), Box());
365
faceMorton.resize_nofill(NumTri());
366
for_each_n(autoPolicy(NumTri(), 1e5), countAt(0), NumTri(),
367
[this, &faceBox, &faceMorton](const int face) {
368
// Removed tris are marked by all halfedges having pairedHalfedge
369
// = -1, and this will sort them to the end (the Morton code only
370
// uses the first 30 of 32 bits).
371
if (halfedge_[3 * face].pairedHalfedge < 0) {
372
faceMorton[face] = kNoCode;
373
return;
374
}
375
376
vec3 center(0.0);
377
378
for (const int i : {0, 1, 2}) {
379
const vec3 pos = vertPos_[halfedge_[3 * face + i].startVert];
380
center += pos;
381
faceBox[face].Union(pos);
382
}
383
center /= 3;
384
385
faceMorton[face] = MortonCode(center, bBox_);
386
});
387
}
388
389
/**
390
* Sorts the faces of this manifold according to their input Morton code. The
391
* bounding box and Morton code arrays are also sorted accordingly.
392
*/
393
void Manifold::Impl::SortFaces(Vec<Box>& faceBox, Vec<uint32_t>& faceMorton) {
394
ZoneScoped;
395
Vec<int> faceNew2Old(NumTri());
396
sequence(faceNew2Old.begin(), faceNew2Old.end());
397
398
stable_sort(faceNew2Old.begin(), faceNew2Old.end(),
399
[&faceMorton](const int& a, const int& b) {
400
return faceMorton[a] < faceMorton[b];
401
});
402
403
// Tris were flagged for removal with pairedHalfedge = -1 and assigned kNoCode
404
// to sort them to the end, which allows them to be removed.
405
const int newNumTri =
406
std::lower_bound(faceNew2Old.begin(), faceNew2Old.end(), kNoCode,
407
[&faceMorton](const int face, const uint32_t val) {
408
return faceMorton[face] < val;
409
}) -
410
faceNew2Old.begin();
411
faceNew2Old.resize(newNumTri);
412
413
Permute(faceMorton, faceNew2Old);
414
Permute(faceBox, faceNew2Old);
415
GatherFaces(faceNew2Old);
416
}
417
418
/**
419
* Creates the halfedge_ vector for this manifold by copying a set of faces from
420
* another manifold, given by oldHalfedge. Input faceNew2Old defines the old
421
* faces to gather into this.
422
*/
423
void Manifold::Impl::GatherFaces(const Vec<int>& faceNew2Old) {
424
ZoneScoped;
425
const auto numTri = faceNew2Old.size();
426
if (meshRelation_.triRef.size() == NumTri())
427
Permute(meshRelation_.triRef, faceNew2Old);
428
if (faceNormal_.size() == NumTri()) Permute(faceNormal_, faceNew2Old);
429
430
Vec<Halfedge> oldHalfedge(std::move(halfedge_));
431
Vec<vec4> oldHalfedgeTangent(std::move(halfedgeTangent_));
432
Vec<int> faceOld2New(oldHalfedge.size() / 3);
433
auto policy = autoPolicy(numTri, 1e5);
434
scatter(countAt(0_uz), countAt(numTri), faceNew2Old.begin(),
435
faceOld2New.begin());
436
437
halfedge_.resize_nofill(3 * numTri);
438
if (oldHalfedgeTangent.size() != 0)
439
halfedgeTangent_.resize_nofill(3 * numTri);
440
for_each_n(policy, countAt(0), numTri,
441
ReindexFace({halfedge_, halfedgeTangent_, oldHalfedge,
442
oldHalfedgeTangent, faceNew2Old, faceOld2New}));
443
}
444
445
void Manifold::Impl::GatherFaces(const Impl& old, const Vec<int>& faceNew2Old) {
446
ZoneScoped;
447
const auto numTri = faceNew2Old.size();
448
449
meshRelation_.triRef.resize_nofill(numTri);
450
gather(faceNew2Old.begin(), faceNew2Old.end(),
451
old.meshRelation_.triRef.begin(), meshRelation_.triRef.begin());
452
453
for (const auto& pair : old.meshRelation_.meshIDtransform) {
454
meshRelation_.meshIDtransform[pair.first] = pair.second;
455
}
456
457
if (old.NumProp() > 0) {
458
numProp_ = old.numProp_;
459
properties_ = old.properties_;
460
}
461
462
if (old.faceNormal_.size() == old.NumTri()) {
463
faceNormal_.resize_nofill(numTri);
464
gather(faceNew2Old.begin(), faceNew2Old.end(), old.faceNormal_.begin(),
465
faceNormal_.begin());
466
}
467
468
Vec<int> faceOld2New(old.NumTri());
469
scatter(countAt(0_uz), countAt(numTri), faceNew2Old.begin(),
470
faceOld2New.begin());
471
472
halfedge_.resize_nofill(3 * numTri);
473
if (old.halfedgeTangent_.size() != 0)
474
halfedgeTangent_.resize_nofill(3 * numTri);
475
for_each_n(autoPolicy(numTri, 1e5), countAt(0), numTri,
476
ReindexFace({halfedge_, halfedgeTangent_, old.halfedge_,
477
old.halfedgeTangent_, faceNew2Old, faceOld2New}));
478
}
479
480
/**
481
* Updates the mergeFromVert and mergeToVert vectors in order to create a
482
* manifold solid. If the MeshGL is already manifold, no change will occur and
483
* the function will return false. Otherwise, this will merge verts along open
484
* edges within tolerance (the maximum of the MeshGL tolerance and the
485
* baseline bounding-box tolerance), keeping any from the existing merge
486
* vectors, and return true.
487
*
488
* There is no guarantee the result will be manifold - this is a best-effort
489
* helper function designed primarily to aid in the case where a manifold
490
* multi-material MeshGL was produced, but its merge vectors were lost due to
491
* a round-trip through a file format. Constructing a Manifold from the result
492
* will report an error status if it is not manifold.
493
*/
494
template <>
495
bool MeshGL::Merge() {
496
return MergeMeshGLP(*this);
497
}
498
499
template <>
500
bool MeshGL64::Merge() {
501
return MergeMeshGLP(*this);
502
}
503
} // namespace manifold
504
505