Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/manifold/src/impl.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 "impl.h"
16
17
#include <algorithm>
18
#include <atomic>
19
#include <cstring>
20
#include <map>
21
#include <optional>
22
23
#include "csg_tree.h"
24
#include "disjoint_sets.h"
25
#include "hashtable.h"
26
#include "manifold/optional_assert.h"
27
#include "mesh_fixes.h"
28
#include "parallel.h"
29
#include "shared.h"
30
#include "svd.h"
31
32
namespace {
33
using namespace manifold;
34
35
/**
36
* Returns arc cosine of 𝑥.
37
*
38
* @return value in range [0,M_PI]
39
* @return NAN if 𝑥 ∈ {NAN,+INFINITY,-INFINITY}
40
* @return NAN if 𝑥 ∉ [-1,1]
41
*/
42
double sun_acos(double x) {
43
/*
44
* Origin of acos function: FreeBSD /usr/src/lib/msun/src/e_acos.c
45
* Changed the use of union to memcpy to avoid undefined behavior.
46
* ====================================================
47
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
48
*
49
* Developed at SunSoft, a Sun Microsystems, Inc. business.
50
* Permission to use, copy, modify, and distribute this
51
* software is freely granted, provided that this notice
52
* is preserved.
53
* ====================================================
54
*/
55
constexpr double pio2_hi =
56
1.57079632679489655800e+00; /* 0x3FF921FB, 0x54442D18 */
57
constexpr double pio2_lo =
58
6.12323399573676603587e-17; /* 0x3C91A626, 0x33145C07 */
59
constexpr double pS0 =
60
1.66666666666666657415e-01; /* 0x3FC55555, 0x55555555 */
61
constexpr double pS1 =
62
-3.25565818622400915405e-01; /* 0xBFD4D612, 0x03EB6F7D */
63
constexpr double pS2 =
64
2.01212532134862925881e-01; /* 0x3FC9C155, 0x0E884455 */
65
constexpr double pS3 =
66
-4.00555345006794114027e-02; /* 0xBFA48228, 0xB5688F3B */
67
constexpr double pS4 =
68
7.91534994289814532176e-04; /* 0x3F49EFE0, 0x7501B288 */
69
constexpr double pS5 =
70
3.47933107596021167570e-05; /* 0x3F023DE1, 0x0DFDF709 */
71
constexpr double qS1 =
72
-2.40339491173441421878e+00; /* 0xC0033A27, 0x1C8A2D4B */
73
constexpr double qS2 =
74
2.02094576023350569471e+00; /* 0x40002AE5, 0x9C598AC8 */
75
constexpr double qS3 =
76
-6.88283971605453293030e-01; /* 0xBFE6066C, 0x1B8D0159 */
77
constexpr double qS4 =
78
7.70381505559019352791e-02; /* 0x3FB3B8C5, 0xB12E9282 */
79
auto R = [=](double z) {
80
double p, q;
81
p = z * (pS0 + z * (pS1 + z * (pS2 + z * (pS3 + z * (pS4 + z * pS5)))));
82
q = 1.0 + z * (qS1 + z * (qS2 + z * (qS3 + z * qS4)));
83
return p / q;
84
};
85
double z, w, s, c, df;
86
uint64_t xx;
87
uint32_t hx, lx, ix;
88
memcpy(&xx, &x, sizeof(xx));
89
hx = xx >> 32;
90
ix = hx & 0x7fffffff;
91
/* |x| >= 1 or nan */
92
if (ix >= 0x3ff00000) {
93
lx = xx;
94
if (((ix - 0x3ff00000) | lx) == 0) {
95
/* acos(1)=0, acos(-1)=pi */
96
if (hx >> 31) return 2 * pio2_hi + 0x1p-120f;
97
return 0;
98
}
99
return 0 / (x - x);
100
}
101
/* |x| < 0.5 */
102
if (ix < 0x3fe00000) {
103
if (ix <= 0x3c600000) /* |x| < 2**-57 */
104
return pio2_hi + 0x1p-120f;
105
return pio2_hi - (x - (pio2_lo - x * R(x * x)));
106
}
107
/* x < -0.5 */
108
if (hx >> 31) {
109
z = (1.0 + x) * 0.5;
110
s = sqrt(z);
111
w = R(z) * s - pio2_lo;
112
return 2 * (pio2_hi - (s + w));
113
}
114
/* x > 0.5 */
115
z = (1.0 - x) * 0.5;
116
s = sqrt(z);
117
memcpy(&xx, &s, sizeof(xx));
118
xx &= 0xffffffff00000000;
119
memcpy(&df, &xx, sizeof(xx));
120
c = (z - df * df) / (s + df);
121
w = R(z) * s + c;
122
return 2 * (df + w);
123
}
124
125
struct Transform4x3 {
126
const mat3x4 transform;
127
128
vec3 operator()(vec3 position) { return transform * vec4(position, 1.0); }
129
};
130
131
struct UpdateMeshID {
132
const HashTableD<uint32_t> meshIDold2new;
133
134
void operator()(TriRef& ref) { ref.meshID = meshIDold2new[ref.meshID]; }
135
};
136
137
int GetLabels(std::vector<int>& components,
138
const Vec<std::pair<int, int>>& edges, int numNodes) {
139
DisjointSets uf(numNodes);
140
for (auto edge : edges) {
141
if (edge.first == -1 || edge.second == -1) continue;
142
uf.unite(edge.first, edge.second);
143
}
144
145
return uf.connectedComponents(components);
146
}
147
} // namespace
148
149
namespace manifold {
150
151
#if (MANIFOLD_PAR == 1)
152
tbb::task_arena gc_arena(1, 1, tbb::task_arena::priority::low);
153
#endif
154
155
std::atomic<uint32_t> Manifold::Impl::meshIDCounter_(1);
156
157
uint32_t Manifold::Impl::ReserveIDs(uint32_t n) {
158
return Manifold::Impl::meshIDCounter_.fetch_add(n, std::memory_order_relaxed);
159
}
160
161
/**
162
* Create either a unit tetrahedron, cube or octahedron. The cube is in the
163
* first octant, while the others are symmetric about the origin.
164
*/
165
Manifold::Impl::Impl(Shape shape, const mat3x4 m) {
166
std::vector<vec3> vertPos;
167
std::vector<ivec3> triVerts;
168
switch (shape) {
169
case Shape::Tetrahedron:
170
vertPos = {{-1.0, -1.0, 1.0},
171
{-1.0, 1.0, -1.0},
172
{1.0, -1.0, -1.0},
173
{1.0, 1.0, 1.0}};
174
triVerts = {{2, 0, 1}, {0, 3, 1}, {2, 3, 0}, {3, 2, 1}};
175
break;
176
case Shape::Cube:
177
vertPos = {{0.0, 0.0, 0.0}, //
178
{0.0, 0.0, 1.0}, //
179
{0.0, 1.0, 0.0}, //
180
{0.0, 1.0, 1.0}, //
181
{1.0, 0.0, 0.0}, //
182
{1.0, 0.0, 1.0}, //
183
{1.0, 1.0, 0.0}, //
184
{1.0, 1.0, 1.0}};
185
triVerts = {{1, 0, 4}, {2, 4, 0}, //
186
{1, 3, 0}, {3, 1, 5}, //
187
{3, 2, 0}, {3, 7, 2}, //
188
{5, 4, 6}, {5, 1, 4}, //
189
{6, 4, 2}, {7, 6, 2}, //
190
{7, 3, 5}, {7, 5, 6}};
191
break;
192
case Shape::Octahedron:
193
vertPos = {{1.0, 0.0, 0.0}, //
194
{-1.0, 0.0, 0.0}, //
195
{0.0, 1.0, 0.0}, //
196
{0.0, -1.0, 0.0}, //
197
{0.0, 0.0, 1.0}, //
198
{0.0, 0.0, -1.0}};
199
triVerts = {{0, 2, 4}, {1, 5, 3}, //
200
{2, 1, 4}, {3, 5, 0}, //
201
{1, 3, 4}, {0, 5, 2}, //
202
{3, 0, 4}, {2, 5, 1}};
203
break;
204
}
205
vertPos_ = vertPos;
206
for (auto& v : vertPos_) v = m * vec4(v, 1.0);
207
CreateHalfedges(triVerts);
208
Finish();
209
InitializeOriginal();
210
MarkCoplanar();
211
}
212
213
void Manifold::Impl::RemoveUnreferencedVerts() {
214
ZoneScoped;
215
const int numVert = NumVert();
216
Vec<int> keep(numVert, 0);
217
auto policy = autoPolicy(numVert, 1e5);
218
for_each(policy, halfedge_.cbegin(), halfedge_.cend(), [&keep](Halfedge h) {
219
if (h.startVert >= 0) {
220
reinterpret_cast<std::atomic<int>*>(&keep[h.startVert])
221
->store(1, std::memory_order_relaxed);
222
}
223
});
224
225
for_each_n(policy, countAt(0), numVert, [&keep, this](int v) {
226
if (keep[v] == 0) {
227
vertPos_[v] = vec3(NAN);
228
}
229
});
230
}
231
232
void Manifold::Impl::InitializeOriginal(bool keepFaceID) {
233
const int meshID = ReserveIDs(1);
234
meshRelation_.originalID = meshID;
235
auto& triRef = meshRelation_.triRef;
236
triRef.resize_nofill(NumTri());
237
for_each_n(autoPolicy(NumTri(), 1e5), countAt(0), NumTri(),
238
[meshID, keepFaceID, &triRef](const int tri) {
239
triRef[tri] = {meshID, meshID, -1,
240
keepFaceID ? triRef[tri].coplanarID : tri};
241
});
242
meshRelation_.meshIDtransform.clear();
243
meshRelation_.meshIDtransform[meshID] = {meshID};
244
}
245
246
void Manifold::Impl::MarkCoplanar() {
247
ZoneScoped;
248
const int numTri = NumTri();
249
struct TriPriority {
250
double area2;
251
int tri;
252
};
253
Vec<TriPriority> triPriority(numTri);
254
for_each_n(autoPolicy(numTri), countAt(0), numTri,
255
[&triPriority, this](int tri) {
256
meshRelation_.triRef[tri].coplanarID = -1;
257
if (halfedge_[3 * tri].startVert < 0) {
258
triPriority[tri] = {0, tri};
259
return;
260
}
261
const vec3 v = vertPos_[halfedge_[3 * tri].startVert];
262
triPriority[tri] = {
263
length2(cross(vertPos_[halfedge_[3 * tri].endVert] - v,
264
vertPos_[halfedge_[3 * tri + 1].endVert] - v)),
265
tri};
266
});
267
268
stable_sort(triPriority.begin(), triPriority.end(),
269
[](auto a, auto b) { return a.area2 > b.area2; });
270
271
Vec<int> interiorHalfedges;
272
for (const auto tp : triPriority) {
273
if (meshRelation_.triRef[tp.tri].coplanarID >= 0) continue;
274
275
meshRelation_.triRef[tp.tri].coplanarID = tp.tri;
276
if (halfedge_[3 * tp.tri].startVert < 0) continue;
277
const vec3 base = vertPos_[halfedge_[3 * tp.tri].startVert];
278
const vec3 normal = faceNormal_[tp.tri];
279
interiorHalfedges.resize(3);
280
interiorHalfedges[0] = 3 * tp.tri;
281
interiorHalfedges[1] = 3 * tp.tri + 1;
282
interiorHalfedges[2] = 3 * tp.tri + 2;
283
while (!interiorHalfedges.empty()) {
284
const int h =
285
NextHalfedge(halfedge_[interiorHalfedges.back()].pairedHalfedge);
286
interiorHalfedges.pop_back();
287
if (meshRelation_.triRef[h / 3].coplanarID >= 0) continue;
288
289
const vec3 v = vertPos_[halfedge_[h].endVert];
290
if (std::abs(dot(v - base, normal)) < tolerance_) {
291
meshRelation_.triRef[h / 3].coplanarID = tp.tri;
292
293
if (interiorHalfedges.empty() ||
294
h != halfedge_[interiorHalfedges.back()].pairedHalfedge) {
295
interiorHalfedges.push_back(h);
296
} else {
297
interiorHalfedges.pop_back();
298
}
299
const int hNext = NextHalfedge(h);
300
interiorHalfedges.push_back(hNext);
301
}
302
}
303
}
304
}
305
306
/**
307
* Dereference duplicate property vertices if they are exactly floating-point
308
* equal. These unreferenced properties are then removed by CompactProps.
309
*/
310
void Manifold::Impl::DedupePropVerts() {
311
ZoneScoped;
312
const size_t numProp = NumProp();
313
if (numProp == 0) return;
314
315
Vec<std::pair<int, int>> vert2vert(halfedge_.size(), {-1, -1});
316
for_each_n(autoPolicy(halfedge_.size(), 1e4), countAt(0), halfedge_.size(),
317
[&vert2vert, numProp, this](const int edgeIdx) {
318
const Halfedge edge = halfedge_[edgeIdx];
319
if (edge.pairedHalfedge < 0) return;
320
const int edgeFace = edgeIdx / 3;
321
const int pairFace = edge.pairedHalfedge / 3;
322
323
if (meshRelation_.triRef[edgeFace].meshID !=
324
meshRelation_.triRef[pairFace].meshID)
325
return;
326
327
const int prop0 = halfedge_[edgeIdx].propVert;
328
const int prop1 =
329
halfedge_[NextHalfedge(edge.pairedHalfedge)].propVert;
330
bool propEqual = true;
331
for (size_t p = 0; p < numProp; ++p) {
332
if (properties_[numProp * prop0 + p] !=
333
properties_[numProp * prop1 + p]) {
334
propEqual = false;
335
break;
336
}
337
}
338
if (propEqual) {
339
vert2vert[edgeIdx] = std::make_pair(prop0, prop1);
340
}
341
});
342
343
std::vector<int> vertLabels;
344
const size_t numPropVert = NumPropVert();
345
const int numLabels = GetLabels(vertLabels, vert2vert, numPropVert);
346
347
std::vector<int> label2vert(numLabels);
348
for (size_t v = 0; v < numPropVert; ++v) label2vert[vertLabels[v]] = v;
349
for (Halfedge& edge : halfedge_)
350
edge.propVert = label2vert[vertLabels[edge.propVert]];
351
}
352
353
constexpr int kRemovedHalfedge = -2;
354
355
struct HalfedgePairData {
356
int largeVert;
357
int tri;
358
int edgeIndex;
359
360
bool operator<(const HalfedgePairData& other) const {
361
return largeVert < other.largeVert ||
362
(largeVert == other.largeVert && tri < other.tri);
363
}
364
};
365
366
template <bool useProp, typename F>
367
struct PrepHalfedges {
368
VecView<Halfedge> halfedges;
369
const VecView<ivec3> triProp;
370
const VecView<ivec3> triVert;
371
F& f;
372
373
void operator()(const int tri) {
374
const ivec3& props = triProp[tri];
375
for (const int i : {0, 1, 2}) {
376
const int j = Next3(i);
377
const int k = Next3(j);
378
const int e = 3 * tri + i;
379
const int v0 = useProp ? props[i] : triVert[tri][i];
380
const int v1 = useProp ? props[j] : triVert[tri][j];
381
DEBUG_ASSERT(v0 != v1, logicErr, "topological degeneracy");
382
halfedges[e] = {v0, v1, -1, props[i]};
383
f(e, v0, v1);
384
}
385
}
386
};
387
388
/**
389
* Create the halfedge_ data structure from a list of triangles. If the optional
390
* prop2vert array is missing, it's assumed these triangles are are pointing to
391
* both vert and propVert indices. If prop2vert is present, the triangles are
392
* assumed to be pointing to propVert indices only. The prop2vert array is used
393
* to map the propVert indices to vert indices.
394
*/
395
void Manifold::Impl::CreateHalfedges(const Vec<ivec3>& triProp,
396
const Vec<ivec3>& triVert) {
397
ZoneScoped;
398
const size_t numTri = triProp.size();
399
const int numHalfedge = 3 * numTri;
400
// drop the old value first to avoid copy
401
halfedge_.clear(true);
402
halfedge_.resize_nofill(numHalfedge);
403
auto policy = autoPolicy(numTri, 1e5);
404
405
int vertCount = static_cast<int>(vertPos_.size());
406
Vec<int> ids(numHalfedge);
407
{
408
ZoneScopedN("PrepHalfedges");
409
if (vertCount < (1 << 18)) {
410
// For small vertex count, it is faster to just do sorting
411
Vec<uint64_t> edge(numHalfedge);
412
auto setEdge = [&edge](int e, int v0, int v1) {
413
edge[e] = static_cast<uint64_t>(v0 < v1 ? 1 : 0) << 63 |
414
(static_cast<uint64_t>(std::min(v0, v1))) << 32 |
415
static_cast<uint64_t>(std::max(v0, v1));
416
};
417
if (triVert.empty()) {
418
for_each_n(policy, countAt(0), numTri,
419
PrepHalfedges<true, decltype(setEdge)>{halfedge_, triProp,
420
triVert, setEdge});
421
} else {
422
for_each_n(policy, countAt(0), numTri,
423
PrepHalfedges<false, decltype(setEdge)>{halfedge_, triProp,
424
triVert, setEdge});
425
}
426
sequence(ids.begin(), ids.end());
427
stable_sort(ids.begin(), ids.end(), [&edge](const int& a, const int& b) {
428
return edge[a] < edge[b];
429
});
430
} else {
431
// For larger vertex count, we separate the ids into slices for halfedges
432
// with the same smaller vertex.
433
// We first copy them there (as HalfedgePairData), and then do sorting
434
// locally for each slice.
435
// This helps with memory locality, and is faster for larger meshes.
436
Vec<HalfedgePairData> entries(numHalfedge);
437
Vec<int> offsets(vertCount * 2, 0);
438
auto setOffset = [&offsets, vertCount](int _e, int v0, int v1) {
439
const int offset = v0 > v1 ? 0 : vertCount;
440
AtomicAdd(offsets[std::min(v0, v1) + offset], 1);
441
};
442
if (triVert.empty()) {
443
for_each_n(policy, countAt(0), numTri,
444
PrepHalfedges<true, decltype(setOffset)>{
445
halfedge_, triProp, triVert, setOffset});
446
} else {
447
for_each_n(policy, countAt(0), numTri,
448
PrepHalfedges<false, decltype(setOffset)>{
449
halfedge_, triProp, triVert, setOffset});
450
}
451
exclusive_scan(offsets.begin(), offsets.end(), offsets.begin());
452
for_each_n(policy, countAt(0), numTri,
453
[this, &offsets, &entries, vertCount](const int tri) {
454
for (const int i : {0, 1, 2}) {
455
const int e = 3 * tri + i;
456
const int v0 = halfedge_[e].startVert;
457
const int v1 = halfedge_[e].endVert;
458
const int offset = v0 > v1 ? 0 : vertCount;
459
const int start = std::min(v0, v1);
460
const int index = AtomicAdd(offsets[start + offset], 1);
461
entries[index] = {std::max(v0, v1), tri, e};
462
}
463
});
464
for_each_n(policy, countAt(0), offsets.size(), [&](const int v) {
465
int start = v == 0 ? 0 : offsets[v - 1];
466
int end = offsets[v];
467
for (int i = start; i < end; ++i) ids[i] = i;
468
std::sort(ids.begin() + start, ids.begin() + end,
469
[&entries](int a, int b) { return entries[a] < entries[b]; });
470
for (int i = start; i < end; ++i) ids[i] = entries[ids[i]].edgeIndex;
471
});
472
}
473
}
474
475
// Mark opposed triangles for removal - this may strand unreferenced verts
476
// which are removed later by RemoveUnreferencedVerts() and Finish().
477
const int numEdge = numHalfedge / 2;
478
479
const auto body = [&](int i, int consecutiveStart, int segmentEnd) {
480
const int pair0 = ids[i];
481
Halfedge& h0 = halfedge_[pair0];
482
int k = consecutiveStart + numEdge;
483
while (1) {
484
const int pair1 = ids[k];
485
Halfedge& h1 = halfedge_[pair1];
486
if (h0.startVert != h1.endVert || h0.endVert != h1.startVert) break;
487
if (halfedge_[NextHalfedge(pair0)].endVert ==
488
halfedge_[NextHalfedge(pair1)].endVert) {
489
h0.pairedHalfedge = h1.pairedHalfedge = kRemovedHalfedge;
490
// Reorder so that remaining edges pair up
491
if (k != i + numEdge) std::swap(ids[i + numEdge], ids[k]);
492
break;
493
}
494
++k;
495
if (k >= segmentEnd + numEdge) break;
496
}
497
if (i + 1 == segmentEnd) return consecutiveStart;
498
Halfedge& h1 = halfedge_[ids[i + 1]];
499
if (h1.startVert == h0.startVert && h1.endVert == h0.endVert)
500
return consecutiveStart;
501
return i + 1;
502
};
503
504
#if MANIFOLD_PAR == 1
505
Vec<std::pair<int, int>> ranges;
506
const int increment = std::min(
507
std::max(numEdge / tbb::this_task_arena::max_concurrency() / 2, 1024),
508
numEdge);
509
const auto duplicated = [&](int a, int b) {
510
const Halfedge& h0 = halfedge_[ids[a]];
511
const Halfedge& h1 = halfedge_[ids[b]];
512
return h0.startVert == h1.startVert && h0.endVert == h1.endVert;
513
};
514
int end = 0;
515
while (end < numEdge) {
516
const int start = end;
517
end = std::min(end + increment, numEdge);
518
// make sure duplicated halfedges are in the same partition
519
while (end < numEdge && duplicated(end - 1, end)) end++;
520
ranges.push_back(std::make_pair(start, end));
521
}
522
for_each(ExecutionPolicy::Par, ranges.begin(), ranges.end(),
523
[&](const std::pair<int, int>& range) {
524
const auto [start, end] = range;
525
int consecutiveStart = start;
526
for (int i = start; i < end; ++i)
527
consecutiveStart = body(i, consecutiveStart, end);
528
});
529
#else
530
int consecutiveStart = 0;
531
for (int i = 0; i < numEdge; ++i)
532
consecutiveStart = body(i, consecutiveStart, numEdge);
533
#endif
534
for_each_n(policy, countAt(0), numEdge, [this, &ids, numEdge](int i) {
535
const int pair0 = ids[i];
536
const int pair1 = ids[i + numEdge];
537
if (halfedge_[pair0].pairedHalfedge != kRemovedHalfedge) {
538
halfedge_[pair0].pairedHalfedge = pair1;
539
halfedge_[pair1].pairedHalfedge = pair0;
540
} else {
541
halfedge_[pair0] = halfedge_[pair1] = {-1, -1, -1};
542
}
543
});
544
}
545
546
/**
547
* Does a full recalculation of the face bounding boxes, including updating
548
* the collider, but does not resort the faces.
549
*/
550
void Manifold::Impl::Update() {
551
CalculateBBox();
552
Vec<Box> faceBox;
553
Vec<uint32_t> faceMorton;
554
GetFaceBoxMorton(faceBox, faceMorton);
555
collider_.UpdateBoxes(faceBox);
556
}
557
558
void Manifold::Impl::MakeEmpty(Error status) {
559
bBox_ = Box();
560
vertPos_.clear();
561
halfedge_.clear();
562
vertNormal_.clear();
563
faceNormal_.clear();
564
halfedgeTangent_.clear();
565
meshRelation_ = MeshRelationD();
566
status_ = status;
567
}
568
569
void Manifold::Impl::Warp(std::function<void(vec3&)> warpFunc) {
570
WarpBatch([&warpFunc](VecView<vec3> vecs) {
571
for_each(ExecutionPolicy::Seq, vecs.begin(), vecs.end(), warpFunc);
572
});
573
}
574
575
void Manifold::Impl::WarpBatch(std::function<void(VecView<vec3>)> warpFunc) {
576
warpFunc(vertPos_.view());
577
CalculateBBox();
578
if (!IsFinite()) {
579
MakeEmpty(Error::NonFiniteVertex);
580
return;
581
}
582
Update();
583
faceNormal_.clear(); // force recalculation of triNormal
584
SetEpsilon();
585
Finish();
586
MarkCoplanar();
587
meshRelation_.originalID = -1;
588
}
589
590
Manifold::Impl Manifold::Impl::Transform(const mat3x4& transform_) const {
591
ZoneScoped;
592
if (transform_ == mat3x4(la::identity)) return *this;
593
auto policy = autoPolicy(NumVert());
594
Impl result;
595
if (status_ != Manifold::Error::NoError) {
596
result.status_ = status_;
597
return result;
598
}
599
if (!all(la::isfinite(transform_))) {
600
result.MakeEmpty(Error::NonFiniteVertex);
601
return result;
602
}
603
result.collider_ = collider_;
604
result.meshRelation_ = meshRelation_;
605
result.epsilon_ = epsilon_;
606
result.tolerance_ = tolerance_;
607
result.numProp_ = numProp_;
608
result.properties_ = properties_;
609
result.bBox_ = bBox_;
610
result.halfedge_ = halfedge_;
611
result.halfedgeTangent_.resize(halfedgeTangent_.size());
612
613
result.meshRelation_.originalID = -1;
614
for (auto& m : result.meshRelation_.meshIDtransform) {
615
m.second.transform = transform_ * Mat4(m.second.transform);
616
}
617
618
result.vertPos_.resize(NumVert());
619
result.faceNormal_.resize(faceNormal_.size());
620
result.vertNormal_.resize(vertNormal_.size());
621
transform(vertPos_.begin(), vertPos_.end(), result.vertPos_.begin(),
622
Transform4x3({transform_}));
623
624
mat3 normalTransform = NormalTransform(transform_);
625
transform(faceNormal_.begin(), faceNormal_.end(), result.faceNormal_.begin(),
626
TransformNormals({normalTransform}));
627
transform(vertNormal_.begin(), vertNormal_.end(), result.vertNormal_.begin(),
628
TransformNormals({normalTransform}));
629
630
const bool invert = la::determinant(mat3(transform_)) < 0;
631
632
if (halfedgeTangent_.size() > 0) {
633
for_each_n(policy, countAt(0), halfedgeTangent_.size(),
634
TransformTangents({result.halfedgeTangent_, 0, mat3(transform_),
635
invert, halfedgeTangent_, halfedge_}));
636
}
637
638
if (invert) {
639
for_each_n(policy, countAt(0), result.NumTri(),
640
FlipTris({result.halfedge_}));
641
}
642
643
// This optimization does a cheap collider update if the transform is
644
// axis-aligned.
645
if (!result.collider_.Transform(transform_)) result.Update();
646
647
result.CalculateBBox();
648
// Scale epsilon by the norm of the 3x3 portion of the transform.
649
result.epsilon_ *= SpectralNorm(mat3(transform_));
650
// Maximum of inherited epsilon loss and translational epsilon loss.
651
result.SetEpsilon(result.epsilon_);
652
return result;
653
}
654
655
/**
656
* Sets epsilon based on the bounding box, and limits its minimum value
657
* by the optional input.
658
*/
659
void Manifold::Impl::SetEpsilon(double minEpsilon, bool useSingle) {
660
epsilon_ = MaxEpsilon(minEpsilon, bBox_);
661
double minTol = epsilon_;
662
if (useSingle)
663
minTol =
664
std::max(minTol, std::numeric_limits<float>::epsilon() * bBox_.Scale());
665
tolerance_ = std::max(tolerance_, minTol);
666
}
667
668
/**
669
* If face normals are already present, this function uses them to compute
670
* vertex normals (angle-weighted pseudo-normals); otherwise it also computes
671
* the face normals. Face normals are only calculated when needed because
672
* nearly degenerate faces will accrue rounding error, while the Boolean can
673
* retain their original normal, which is more accurate and can help with
674
* merging coplanar faces.
675
*
676
* If the face normals have been invalidated by an operation like Warp(),
677
* ensure you do faceNormal_.resize(0) before calling this function to force
678
* recalculation.
679
*/
680
void Manifold::Impl::CalculateNormals() {
681
ZoneScoped;
682
vertNormal_.resize(NumVert());
683
auto policy = autoPolicy(NumTri());
684
685
std::vector<std::atomic<int>> vertHalfedgeMap(NumVert());
686
for_each_n(policy, countAt(0), NumVert(), [&](const size_t vert) {
687
vertHalfedgeMap[vert] = std::numeric_limits<int>::max();
688
});
689
690
auto atomicMin = [&vertHalfedgeMap](int value, int vert) {
691
if (vert < 0) return;
692
int old = std::numeric_limits<int>::max();
693
while (!vertHalfedgeMap[vert].compare_exchange_strong(old, value))
694
if (old < value) break;
695
};
696
if (faceNormal_.size() != NumTri()) {
697
faceNormal_.resize(NumTri());
698
for_each_n(policy, countAt(0), NumTri(), [&](const int face) {
699
vec3& triNormal = faceNormal_[face];
700
if (halfedge_[3 * face].startVert < 0) {
701
triNormal = vec3(0, 0, 1);
702
return;
703
}
704
705
ivec3 triVerts;
706
for (int i : {0, 1, 2}) {
707
int v = halfedge_[3 * face + i].startVert;
708
triVerts[i] = v;
709
atomicMin(3 * face + i, v);
710
}
711
712
vec3 edge[3];
713
for (int i : {0, 1, 2}) {
714
const int j = (i + 1) % 3;
715
edge[i] = la::normalize(vertPos_[triVerts[j]] - vertPos_[triVerts[i]]);
716
}
717
triNormal = la::normalize(la::cross(edge[0], edge[1]));
718
if (std::isnan(triNormal.x)) triNormal = vec3(0, 0, 1);
719
});
720
} else {
721
for_each_n(policy, countAt(0), halfedge_.size(),
722
[&](const int i) { atomicMin(i, halfedge_[i].startVert); });
723
}
724
725
for_each_n(policy, countAt(0), NumVert(), [&](const size_t vert) {
726
int firstEdge = vertHalfedgeMap[vert].load();
727
// not referenced
728
if (firstEdge == std::numeric_limits<int>::max()) {
729
vertNormal_[vert] = vec3(0.0);
730
return;
731
}
732
vec3 normal = vec3(0.0);
733
ForVert(firstEdge, [&](int edge) {
734
ivec3 triVerts = {halfedge_[edge].startVert, halfedge_[edge].endVert,
735
halfedge_[NextHalfedge(edge)].endVert};
736
vec3 currEdge =
737
la::normalize(vertPos_[triVerts[1]] - vertPos_[triVerts[0]]);
738
vec3 prevEdge =
739
la::normalize(vertPos_[triVerts[0]] - vertPos_[triVerts[2]]);
740
741
// if it is not finite, this means that the triangle is degenerate, and we
742
// should just exclude it from the normal calculation...
743
if (!la::isfinite(currEdge[0]) || !la::isfinite(prevEdge[0])) return;
744
double dot = -la::dot(prevEdge, currEdge);
745
double phi = dot >= 1 ? 0 : (dot <= -1 ? kPi : sun_acos(dot));
746
normal += phi * faceNormal_[edge / 3];
747
});
748
vertNormal_[vert] = SafeNormalize(normal);
749
});
750
}
751
752
/**
753
* Remaps all the contained meshIDs to new unique values to represent new
754
* instances of these meshes.
755
*/
756
void Manifold::Impl::IncrementMeshIDs() {
757
HashTable<uint32_t> meshIDold2new(meshRelation_.meshIDtransform.size() * 2);
758
// Update keys of the transform map
759
std::map<int, Relation> oldTransforms;
760
std::swap(meshRelation_.meshIDtransform, oldTransforms);
761
const int numMeshIDs = oldTransforms.size();
762
int nextMeshID = ReserveIDs(numMeshIDs);
763
for (const auto& pair : oldTransforms) {
764
meshIDold2new.D().Insert(pair.first, nextMeshID);
765
meshRelation_.meshIDtransform[nextMeshID++] = pair.second;
766
}
767
768
const size_t numTri = NumTri();
769
for_each_n(autoPolicy(numTri, 1e5), meshRelation_.triRef.begin(), numTri,
770
UpdateMeshID({meshIDold2new.D()}));
771
}
772
773
#ifdef MANIFOLD_DEBUG
774
/**
775
* Debugging output using high precision OBJ files with specialized comments
776
*/
777
std::ostream& operator<<(std::ostream& stream, const Manifold::Impl& impl) {
778
stream << std::setprecision(19); // for double precision
779
stream << std::fixed; // for uniformity in output numbers
780
stream << "# ======= begin mesh ======" << std::endl;
781
stream << "# tolerance = " << impl.tolerance_ << std::endl;
782
stream << "# epsilon = " << impl.epsilon_ << std::endl;
783
// TODO: Mesh relation, vertex normal and face normal
784
for (const vec3& v : impl.vertPos_)
785
stream << "v " << v.x << " " << v.y << " " << v.z << std::endl;
786
std::vector<ivec3> triangles;
787
triangles.reserve(impl.halfedge_.size() / 3);
788
for (size_t i = 0; i < impl.halfedge_.size(); i += 3)
789
triangles.emplace_back(impl.halfedge_[i].startVert + 1,
790
impl.halfedge_[i + 1].startVert + 1,
791
impl.halfedge_[i + 2].startVert + 1);
792
sort(triangles.begin(), triangles.end());
793
for (const auto& tri : triangles)
794
stream << "f " << tri.x << " " << tri.y << " " << tri.z << std::endl;
795
stream << "# ======== end mesh =======" << std::endl;
796
return stream;
797
}
798
799
/**
800
* Import a mesh from a Wavefront OBJ file that was exported with Write. This
801
* function is the counterpart to Write and should be used with it. This
802
* function is not guaranteed to be able to import OBJ files not written by the
803
* Write function.
804
*/
805
Manifold Manifold::ReadOBJ(std::istream& stream) {
806
if (!stream.good()) return Invalid();
807
808
MeshGL64 mesh;
809
std::optional<double> epsilon;
810
stream >> std::setprecision(19);
811
while (true) {
812
char c = stream.get();
813
if (stream.eof()) break;
814
switch (c) {
815
case '#': {
816
char c = stream.get();
817
if (c == ' ') {
818
constexpr int SIZE = 10;
819
std::array<char, SIZE> tmp;
820
stream.get(tmp.data(), SIZE, '\n');
821
if (strncmp(tmp.data(), "tolerance", SIZE) == 0) {
822
// skip 3 letters
823
for (int _ : {0, 1, 2}) stream.get();
824
stream >> mesh.tolerance;
825
} else if (strncmp(tmp.data(), "epsilon =", SIZE) == 0) {
826
double tmp;
827
stream >> tmp;
828
epsilon = {tmp};
829
} else {
830
// add it back because it is not what we want
831
int end = 0;
832
while (end < SIZE && tmp[end] != 0) end++;
833
while (--end > -1) stream.putback(tmp[end]);
834
}
835
c = stream.get();
836
}
837
// just skip the remaining comment
838
while (c != '\n' && !stream.eof()) {
839
c = stream.get();
840
}
841
break;
842
}
843
case 'v':
844
for (int _ : {0, 1, 2}) {
845
double x;
846
stream >> x;
847
mesh.vertProperties.push_back(x);
848
}
849
break;
850
case 'f':
851
for (int _ : {0, 1, 2}) {
852
uint64_t x;
853
stream >> x;
854
mesh.triVerts.push_back(x - 1);
855
}
856
break;
857
case '\r':
858
case '\n':
859
break;
860
default:
861
DEBUG_ASSERT(false, userErr, "unexpected character in Manifold import");
862
}
863
}
864
auto m = std::make_shared<Manifold::Impl>(mesh);
865
if (epsilon) m->SetEpsilon(*epsilon);
866
return Manifold(m);
867
}
868
869
/**
870
* Export the mesh to a Wavefront OBJ file in a way that preserves the full
871
* 64-bit precision of the vertex positions, as well as storing metadata such as
872
* the tolerance and epsilon. Useful for debugging and testing. Files written
873
* by WriteOBJ should be read back in with ReadOBJ.
874
*/
875
bool Manifold::WriteOBJ(std::ostream& stream) const {
876
if (!stream.good()) return false;
877
stream << *this->GetCsgLeafNode().GetImpl();
878
return true;
879
}
880
#endif
881
882
} // namespace manifold
883
884