Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/manifold/src/face_op.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 <unordered_set>
16
17
#include "impl.h"
18
#include "manifold/common.h"
19
#include "manifold/polygon.h"
20
#include "parallel.h"
21
#include "shared.h"
22
23
#if (MANIFOLD_PAR == 1) && __has_include(<tbb/concurrent_map.h>)
24
#include <tbb/tbb.h>
25
#define TBB_PREVIEW_CONCURRENT_ORDERED_CONTAINERS 1
26
#include <tbb/concurrent_map.h>
27
#endif
28
29
namespace {
30
using namespace manifold;
31
32
/**
33
* Returns an assembled set of vertex index loops of the input list of
34
* Halfedges, where each vert must be referenced the same number of times as a
35
* startVert and endVert. If startHalfedgeIdx is given, instead of putting
36
* vertex indices into the returned polygons structure, it will use the halfedge
37
* indices instead.
38
*/
39
std::vector<std::vector<int>> AssembleHalfedges(VecView<Halfedge>::IterC start,
40
VecView<Halfedge>::IterC end,
41
const int startHalfedgeIdx) {
42
std::multimap<int, int> vert_edge;
43
for (auto edge = start; edge != end; ++edge) {
44
vert_edge.emplace(
45
std::make_pair(edge->startVert, static_cast<int>(edge - start)));
46
}
47
48
std::vector<std::vector<int>> polys;
49
int startEdge = 0;
50
int thisEdge = startEdge;
51
while (1) {
52
if (thisEdge == startEdge) {
53
if (vert_edge.empty()) break;
54
startEdge = vert_edge.begin()->second;
55
thisEdge = startEdge;
56
polys.push_back({});
57
}
58
polys.back().push_back(startHalfedgeIdx + thisEdge);
59
const auto result = vert_edge.find((start + thisEdge)->endVert);
60
DEBUG_ASSERT(result != vert_edge.end(), topologyErr, "non-manifold edge");
61
thisEdge = result->second;
62
vert_edge.erase(result);
63
}
64
return polys;
65
}
66
67
/**
68
* Add the vertex position projection to the indexed polygons.
69
*/
70
PolygonsIdx ProjectPolygons(const std::vector<std::vector<int>>& polys,
71
const Vec<Halfedge>& halfedge,
72
const Vec<vec3>& vertPos, mat2x3 projection) {
73
PolygonsIdx polygons;
74
for (const auto& poly : polys) {
75
polygons.push_back({});
76
for (const auto& edge : poly) {
77
polygons.back().push_back(
78
{projection * vertPos[halfedge[edge].startVert], edge});
79
} // for vert
80
} // for poly
81
return polygons;
82
}
83
} // namespace
84
85
namespace manifold {
86
87
using GeneralTriangulation = std::function<std::vector<ivec3>(int)>;
88
using AddTriangle = std::function<void(int, ivec3, vec3, TriRef)>;
89
90
/**
91
* Triangulates the faces. In this case, the halfedge_ vector is not yet a set
92
* of triangles as required by this data structure, but is instead a set of
93
* general faces with the input faceEdge vector having length of the number of
94
* faces + 1. The values are indicies into the halfedge_ vector for the first
95
* edge of each face, with the final value being the length of the halfedge_
96
* vector itself. Upon return, halfedge_ has been lengthened and properly
97
* represents the mesh as a set of triangles as usual. In this process the
98
* faceNormal_ values are retained, repeated as necessary.
99
*/
100
void Manifold::Impl::Face2Tri(const Vec<int>& faceEdge,
101
const Vec<TriRef>& halfedgeRef,
102
bool allowConvex) {
103
ZoneScoped;
104
Vec<ivec3> triVerts;
105
Vec<vec3> triNormal;
106
Vec<ivec3> triProp;
107
Vec<TriRef>& triRef = meshRelation_.triRef;
108
triRef.clear();
109
auto processFace = [&](GeneralTriangulation general, AddTriangle addTri,
110
int face) {
111
const int firstEdge = faceEdge[face];
112
const int lastEdge = faceEdge[face + 1];
113
const int numEdge = lastEdge - firstEdge;
114
if (numEdge == 0) return;
115
DEBUG_ASSERT(numEdge >= 3, topologyErr, "face has less than three edges.");
116
const vec3 normal = faceNormal_[face];
117
118
if (numEdge == 3) { // Single triangle
119
ivec3 triEdge(firstEdge, firstEdge + 1, firstEdge + 2);
120
ivec3 tri(halfedge_[firstEdge].startVert,
121
halfedge_[firstEdge + 1].startVert,
122
halfedge_[firstEdge + 2].startVert);
123
ivec3 ends(halfedge_[firstEdge].endVert, halfedge_[firstEdge + 1].endVert,
124
halfedge_[firstEdge + 2].endVert);
125
if (ends[0] == tri[2]) {
126
std::swap(triEdge[1], triEdge[2]);
127
std::swap(tri[1], tri[2]);
128
std::swap(ends[1], ends[2]);
129
}
130
DEBUG_ASSERT(ends[0] == tri[1] && ends[1] == tri[2] && ends[2] == tri[0],
131
topologyErr, "These 3 edges do not form a triangle!");
132
133
addTri(face, triEdge, normal, halfedgeRef[firstEdge]);
134
} else if (numEdge == 4) { // Pair of triangles
135
const mat2x3 projection = GetAxisAlignedProjection(normal);
136
auto triCCW = [&projection, this](const ivec3 tri) {
137
return CCW(projection * this->vertPos_[halfedge_[tri[0]].startVert],
138
projection * this->vertPos_[halfedge_[tri[1]].startVert],
139
projection * this->vertPos_[halfedge_[tri[2]].startVert],
140
epsilon_) >= 0;
141
};
142
143
std::vector<int> quad = AssembleHalfedges(
144
halfedge_.cbegin() + faceEdge[face],
145
halfedge_.cbegin() + faceEdge[face + 1], faceEdge[face])[0];
146
147
const la::mat<int, 3, 2> tris[2] = {
148
{{quad[0], quad[1], quad[2]}, {quad[0], quad[2], quad[3]}},
149
{{quad[1], quad[2], quad[3]}, {quad[0], quad[1], quad[3]}}};
150
151
int choice = 0;
152
153
if (!(triCCW(tris[0][0]) && triCCW(tris[0][1]))) {
154
choice = 1;
155
} else if (triCCW(tris[1][0]) && triCCW(tris[1][1])) {
156
vec3 diag0 = vertPos_[halfedge_[quad[0]].startVert] -
157
vertPos_[halfedge_[quad[2]].startVert];
158
vec3 diag1 = vertPos_[halfedge_[quad[1]].startVert] -
159
vertPos_[halfedge_[quad[3]].startVert];
160
if (la::length2(diag0) > la::length2(diag1)) {
161
choice = 1;
162
}
163
}
164
165
for (const auto& tri : tris[choice]) {
166
addTri(face, tri, normal, halfedgeRef[firstEdge]);
167
}
168
} else { // General triangulation
169
for (const auto& tri : general(face)) {
170
addTri(face, tri, normal, halfedgeRef[firstEdge]);
171
}
172
}
173
};
174
auto generalTriangulation = [&](int face) {
175
const vec3 normal = faceNormal_[face];
176
const mat2x3 projection = GetAxisAlignedProjection(normal);
177
const PolygonsIdx polys = ProjectPolygons(
178
AssembleHalfedges(halfedge_.cbegin() + faceEdge[face],
179
halfedge_.cbegin() + faceEdge[face + 1],
180
faceEdge[face]),
181
halfedge_, vertPos_, projection);
182
return TriangulateIdx(polys, epsilon_, allowConvex);
183
};
184
#if (MANIFOLD_PAR == 1) && __has_include(<tbb/tbb.h>)
185
tbb::task_group group;
186
// map from face to triangle
187
tbb::concurrent_unordered_map<int, std::vector<ivec3>> results;
188
Vec<size_t> triCount(faceEdge.size());
189
triCount.back() = 0;
190
// precompute number of triangles per face, and launch async tasks to
191
// triangulate complex faces
192
for_each(autoPolicy(faceEdge.size(), 1e5), countAt(0_uz),
193
countAt(faceEdge.size() - 1), [&](size_t face) {
194
triCount[face] = faceEdge[face + 1] - faceEdge[face] - 2;
195
DEBUG_ASSERT(triCount[face] >= 1, topologyErr,
196
"face has less than three edges.");
197
if (triCount[face] > 2)
198
group.run([&, face] {
199
std::vector<ivec3> newTris = generalTriangulation(face);
200
triCount[face] = newTris.size();
201
results[face] = std::move(newTris);
202
});
203
});
204
group.wait();
205
// prefix sum computation (assign unique index to each face) and preallocation
206
exclusive_scan(triCount.begin(), triCount.end(), triCount.begin(), 0_uz);
207
triVerts.resize(triCount.back());
208
triProp.resize(triCount.back());
209
triNormal.resize(triCount.back());
210
triRef.resize(triCount.back());
211
212
auto processFace2 = std::bind(
213
processFace, [&](size_t face) { return std::move(results[face]); },
214
[&](size_t face, ivec3 tri, vec3 normal, TriRef r) {
215
for (const int i : {0, 1, 2}) {
216
triVerts[triCount[face]][i] = halfedge_[tri[i]].startVert;
217
triProp[triCount[face]][i] = halfedge_[tri[i]].propVert;
218
}
219
triNormal[triCount[face]] = normal;
220
triRef[triCount[face]] = r;
221
triCount[face]++;
222
},
223
std::placeholders::_1);
224
// set triangles in parallel
225
for_each(autoPolicy(faceEdge.size(), 1e4), countAt(0_uz),
226
countAt(faceEdge.size() - 1), processFace2);
227
#else
228
triVerts.reserve(faceEdge.size());
229
triNormal.reserve(faceEdge.size());
230
triRef.reserve(faceEdge.size());
231
auto processFace2 = std::bind(
232
processFace, generalTriangulation,
233
[&](size_t, ivec3 tri, vec3 normal, TriRef r) {
234
ivec3 verts;
235
ivec3 props;
236
for (const int i : {0, 1, 2}) {
237
verts[i] = halfedge_[tri[i]].startVert;
238
props[i] = halfedge_[tri[i]].propVert;
239
}
240
triVerts.push_back(verts);
241
triProp.push_back(props);
242
triNormal.push_back(normal);
243
triRef.push_back(r);
244
},
245
std::placeholders::_1);
246
for (size_t face = 0; face < faceEdge.size() - 1; ++face) {
247
processFace2(face);
248
}
249
#endif
250
251
faceNormal_ = std::move(triNormal);
252
CreateHalfedges(triProp, triVerts);
253
}
254
255
Polygons Manifold::Impl::Slice(double height) const {
256
Box plane = bBox_;
257
plane.min.z = plane.max.z = height;
258
Vec<Box> query;
259
query.push_back(plane);
260
261
std::unordered_set<int> tris;
262
auto recordCollision = [&](int, int tri) {
263
double min = std::numeric_limits<double>::infinity();
264
double max = -std::numeric_limits<double>::infinity();
265
for (const int j : {0, 1, 2}) {
266
const double z = vertPos_[halfedge_[3 * tri + j].startVert].z;
267
min = std::min(min, z);
268
max = std::max(max, z);
269
}
270
271
if (min <= height && max > height) {
272
tris.insert(tri);
273
}
274
};
275
276
auto recorder = MakeSimpleRecorder(recordCollision);
277
collider_.Collisions<false>(query.cview(), recorder, false);
278
279
Polygons polys;
280
while (!tris.empty()) {
281
const int startTri = *tris.begin();
282
SimplePolygon poly;
283
284
int k = 0;
285
for (const int j : {0, 1, 2}) {
286
if (vertPos_[halfedge_[3 * startTri + j].startVert].z > height &&
287
vertPos_[halfedge_[3 * startTri + Next3(j)].startVert].z <= height) {
288
k = Next3(j);
289
break;
290
}
291
}
292
293
int tri = startTri;
294
do {
295
tris.erase(tris.find(tri));
296
if (vertPos_[halfedge_[3 * tri + k].endVert].z <= height) {
297
k = Next3(k);
298
}
299
300
Halfedge up = halfedge_[3 * tri + k];
301
const vec3 below = vertPos_[up.startVert];
302
const vec3 above = vertPos_[up.endVert];
303
const double a = (height - below.z) / (above.z - below.z);
304
poly.push_back(vec2(la::lerp(below, above, a)));
305
306
const int pair = up.pairedHalfedge;
307
tri = pair / 3;
308
k = Next3(pair % 3);
309
} while (tri != startTri);
310
311
polys.push_back(poly);
312
}
313
314
return polys;
315
}
316
317
Polygons Manifold::Impl::Project() const {
318
const mat2x3 projection = GetAxisAlignedProjection({0, 0, 1});
319
Vec<Halfedge> cusps(NumEdge());
320
cusps.resize(
321
copy_if(
322
halfedge_.cbegin(), halfedge_.cend(), cusps.begin(),
323
[&](Halfedge edge) {
324
return faceNormal_[halfedge_[edge.pairedHalfedge].pairedHalfedge /
325
3]
326
.z >= 0 &&
327
faceNormal_[edge.pairedHalfedge / 3].z < 0;
328
}) -
329
cusps.begin());
330
331
PolygonsIdx polysIndexed =
332
ProjectPolygons(AssembleHalfedges(cusps.cbegin(), cusps.cend(), 0), cusps,
333
vertPos_, projection);
334
335
Polygons polys;
336
for (const auto& poly : polysIndexed) {
337
SimplePolygon simple;
338
for (const PolyVert& polyVert : poly) {
339
simple.push_back(polyVert.pos);
340
}
341
polys.push_back(simple);
342
}
343
344
return polys;
345
}
346
} // namespace manifold
347
348