/src/draco/src/draco/mesh/corner_table.cc
Line | Count | Source |
1 | | // Copyright 2016 The Draco 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 "draco/mesh/corner_table.h" |
16 | | |
17 | | #include <limits> |
18 | | #include <memory> |
19 | | |
20 | | #include "draco/attributes/geometry_indices.h" |
21 | | #include "draco/mesh/corner_table_iterators.h" |
22 | | |
23 | | namespace draco { |
24 | | |
25 | | CornerTable::CornerTable() |
26 | 27.8k | : num_original_vertices_(0), |
27 | 27.8k | num_degenerated_faces_(0), |
28 | 27.8k | num_isolated_vertices_(0), |
29 | 27.8k | valence_cache_(*this) {} |
30 | | |
31 | | std::unique_ptr<CornerTable> CornerTable::Create( |
32 | 0 | const IndexTypeVector<FaceIndex, FaceType> &faces) { |
33 | 0 | std::unique_ptr<CornerTable> ct(new CornerTable()); |
34 | 0 | if (!ct->Init(faces)) { |
35 | 0 | return nullptr; |
36 | 0 | } |
37 | 0 | return ct; |
38 | 0 | } |
39 | | |
40 | 0 | bool CornerTable::Init(const IndexTypeVector<FaceIndex, FaceType> &faces) { |
41 | 0 | valence_cache_.ClearValenceCache(); |
42 | 0 | valence_cache_.ClearValenceCacheInaccurate(); |
43 | 0 | corner_to_vertex_map_.resize(faces.size() * 3); |
44 | 0 | for (FaceIndex fi(0); fi < static_cast<uint32_t>(faces.size()); ++fi) { |
45 | 0 | for (int i = 0; i < 3; ++i) { |
46 | 0 | corner_to_vertex_map_[FirstCorner(fi) + i] = faces[fi][i]; |
47 | 0 | } |
48 | 0 | } |
49 | 0 | int num_vertices = -1; |
50 | 0 | if (!ComputeOppositeCorners(&num_vertices)) { |
51 | 0 | return false; |
52 | 0 | } |
53 | 0 | if (!BreakNonManifoldEdges()) { |
54 | 0 | return false; |
55 | 0 | } |
56 | 0 | if (!ComputeVertexCorners(num_vertices)) { |
57 | 0 | return false; |
58 | 0 | } |
59 | 0 | return true; |
60 | 0 | } |
61 | | |
62 | 0 | bool CornerTable::Reset(int num_faces) { |
63 | 0 | return Reset(num_faces, num_faces * 3); |
64 | 0 | } |
65 | | |
66 | 27.8k | bool CornerTable::Reset(int num_faces, int num_vertices) { |
67 | 27.8k | if (num_faces < 0 || num_vertices < 0) { |
68 | 5 | return false; |
69 | 5 | } |
70 | 27.8k | const unsigned int num_faces_unsigned = num_faces; |
71 | 27.8k | if (num_faces_unsigned > |
72 | 27.8k | std::numeric_limits<CornerIndex::ValueType>::max() / 3) { |
73 | 0 | return false; |
74 | 0 | } |
75 | 27.8k | corner_to_vertex_map_.assign(num_faces_unsigned * 3, kInvalidVertexIndex); |
76 | 27.8k | opposite_corners_.assign(num_faces_unsigned * 3, kInvalidCornerIndex); |
77 | 27.8k | vertex_corners_.reserve(num_vertices); |
78 | 27.8k | valence_cache_.ClearValenceCache(); |
79 | 27.8k | valence_cache_.ClearValenceCacheInaccurate(); |
80 | 27.8k | return true; |
81 | 27.8k | } |
82 | | |
83 | 0 | bool CornerTable::ComputeOppositeCorners(int *num_vertices) { |
84 | 0 | DRACO_DCHECK(GetValenceCache().IsCacheEmpty()); |
85 | 0 | if (num_vertices == nullptr) { |
86 | 0 | return false; |
87 | 0 | } |
88 | 0 | opposite_corners_.resize(num_corners(), kInvalidCornerIndex); |
89 | | |
90 | | // Out implementation for finding opposite corners is based on keeping track |
91 | | // of outgoing half-edges for each vertex of the mesh. Half-edges (defined by |
92 | | // their opposite corners) are processed one by one and whenever a new |
93 | | // half-edge (corner) is processed, we check whether the sink vertex of |
94 | | // this half-edge contains its sibling half-edge. If yes, we connect them and |
95 | | // remove the sibling half-edge from the sink vertex, otherwise we add the new |
96 | | // half-edge to its source vertex. |
97 | | |
98 | | // First compute the number of outgoing half-edges (corners) attached to each |
99 | | // vertex. |
100 | 0 | std::vector<int> num_corners_on_vertices; |
101 | 0 | num_corners_on_vertices.reserve(num_corners()); |
102 | 0 | for (CornerIndex c(0); c < num_corners(); ++c) { |
103 | 0 | const VertexIndex v1 = Vertex(c); |
104 | 0 | if (v1.value() >= static_cast<int>(num_corners_on_vertices.size())) { |
105 | 0 | num_corners_on_vertices.resize(v1.value() + 1, 0); |
106 | 0 | } |
107 | | // For each corner there is always exactly one outgoing half-edge attached |
108 | | // to its vertex. |
109 | 0 | num_corners_on_vertices[v1.value()]++; |
110 | 0 | } |
111 | | |
112 | | // Create a storage for half-edges on each vertex. We store all half-edges in |
113 | | // one array, where each entry is identified by the half-edge's sink vertex id |
114 | | // and the associated half-edge corner id (corner opposite to the half-edge). |
115 | | // Each vertex will be assigned storage for up to |
116 | | // |num_corners_on_vertices[vert_id]| half-edges. Unused half-edges are marked |
117 | | // with |sink_vert| == kInvalidVertexIndex. |
118 | 0 | struct VertexEdgePair { |
119 | 0 | VertexEdgePair() |
120 | 0 | : sink_vert(kInvalidVertexIndex), edge_corner(kInvalidCornerIndex) {} |
121 | 0 | VertexIndex sink_vert; |
122 | 0 | CornerIndex edge_corner; |
123 | 0 | }; |
124 | 0 | std::vector<VertexEdgePair> vertex_edges(num_corners(), VertexEdgePair()); |
125 | | |
126 | | // For each vertex compute the offset (location where the first half-edge |
127 | | // entry of a given vertex is going to be stored). This way each vertex is |
128 | | // guaranteed to have a non-overlapping storage with respect to the other |
129 | | // vertices. |
130 | 0 | std::vector<int> vertex_offset(num_corners_on_vertices.size()); |
131 | 0 | int offset = 0; |
132 | 0 | for (size_t i = 0; i < num_corners_on_vertices.size(); ++i) { |
133 | 0 | vertex_offset[i] = offset; |
134 | 0 | offset += num_corners_on_vertices[i]; |
135 | 0 | } |
136 | | |
137 | | // Now go over the all half-edges (using their opposite corners) and either |
138 | | // insert them to the |vertex_edge| array or connect them with existing |
139 | | // half-edges. |
140 | 0 | for (CornerIndex c(0); c < num_corners(); ++c) { |
141 | 0 | const VertexIndex tip_v = Vertex(c); |
142 | 0 | const VertexIndex source_v = Vertex(Next(c)); |
143 | 0 | const VertexIndex sink_v = Vertex(Previous(c)); |
144 | |
|
145 | 0 | const FaceIndex face_index = Face(c); |
146 | 0 | if (c == FirstCorner(face_index)) { |
147 | | // Check whether the face is degenerated, if so ignore it. |
148 | 0 | const VertexIndex v0 = Vertex(c); |
149 | 0 | if (v0 == source_v || v0 == sink_v || source_v == sink_v) { |
150 | 0 | ++num_degenerated_faces_; |
151 | 0 | c += 2; // Ignore the next two corners of the same face. |
152 | 0 | continue; |
153 | 0 | } |
154 | 0 | } |
155 | | |
156 | 0 | CornerIndex opposite_c(kInvalidCornerIndex); |
157 | | // The maximum number of half-edges attached to the sink vertex. |
158 | 0 | const int num_corners_on_vert = num_corners_on_vertices[sink_v.value()]; |
159 | | // Where to look for the first half-edge on the sink vertex. |
160 | 0 | offset = vertex_offset[sink_v.value()]; |
161 | 0 | for (int i = 0; i < num_corners_on_vert; ++i, ++offset) { |
162 | 0 | const VertexIndex other_v = vertex_edges[offset].sink_vert; |
163 | 0 | if (other_v == kInvalidVertexIndex) { |
164 | 0 | break; // No matching half-edge found on the sink vertex. |
165 | 0 | } |
166 | 0 | if (other_v == source_v) { |
167 | 0 | if (tip_v == Vertex(vertex_edges[offset].edge_corner)) { |
168 | 0 | continue; // Don't connect mirrored faces. |
169 | 0 | } |
170 | | // A matching half-edge was found on the sink vertex. Mark the |
171 | | // half-edge's opposite corner. |
172 | 0 | opposite_c = vertex_edges[offset].edge_corner; |
173 | | // Remove the half-edge from the sink vertex. We remap all subsequent |
174 | | // half-edges one slot down. |
175 | | // TODO(ostava): This can be optimized a little bit, by remapping only |
176 | | // the half-edge on the last valid slot into the deleted half-edge's |
177 | | // slot. |
178 | 0 | for (int j = i + 1; j < num_corners_on_vert; ++j, ++offset) { |
179 | 0 | vertex_edges[offset] = vertex_edges[offset + 1]; |
180 | 0 | if (vertex_edges[offset].sink_vert == kInvalidVertexIndex) { |
181 | 0 | break; // Unused half-edge reached. |
182 | 0 | } |
183 | 0 | } |
184 | | // Mark the last entry as unused. |
185 | 0 | vertex_edges[offset].sink_vert = kInvalidVertexIndex; |
186 | 0 | break; |
187 | 0 | } |
188 | 0 | } |
189 | 0 | if (opposite_c == kInvalidCornerIndex) { |
190 | | // No opposite corner found. Insert the new edge |
191 | 0 | const int num_corners_on_source_vert = |
192 | 0 | num_corners_on_vertices[source_v.value()]; |
193 | 0 | offset = vertex_offset[source_v.value()]; |
194 | 0 | for (int i = 0; i < num_corners_on_source_vert; ++i, ++offset) { |
195 | | // Find the first unused half-edge slot on the source vertex. |
196 | 0 | if (vertex_edges[offset].sink_vert == kInvalidVertexIndex) { |
197 | 0 | vertex_edges[offset].sink_vert = sink_v; |
198 | 0 | vertex_edges[offset].edge_corner = c; |
199 | 0 | break; |
200 | 0 | } |
201 | 0 | } |
202 | 0 | } else { |
203 | | // Opposite corner found. |
204 | 0 | opposite_corners_[c] = opposite_c; |
205 | 0 | opposite_corners_[opposite_c] = c; |
206 | 0 | } |
207 | 0 | } |
208 | 0 | *num_vertices = static_cast<int>(num_corners_on_vertices.size()); |
209 | 0 | return true; |
210 | 0 | } |
211 | | |
212 | 0 | bool CornerTable::BreakNonManifoldEdges() { |
213 | | // This function detects and breaks non-manifold edges that are caused by |
214 | | // folds in 1-ring neighborhood around a vertex. Non-manifold edges can occur |
215 | | // when the 1-ring surface around a vertex self-intersects in a common edge. |
216 | | // For example imagine a surface around a pivot vertex 0, where the 1-ring |
217 | | // is defined by vertices |1, 2, 3, 1, 4|. The surface passes edge <0, 1> |
218 | | // twice which would result in a non-manifold edge that needs to be broken. |
219 | | // For now all faces connected to these non-manifold edges are disconnected |
220 | | // resulting in open boundaries on the mesh. New vertices will be created |
221 | | // automatically for each new disjoint patch in the ComputeVertexCorners() |
222 | | // method. |
223 | | // Note that all other non-manifold edges are implicitly handled by the |
224 | | // function ComputeVertexCorners() that automatically creates new vertices |
225 | | // on disjoint 1-ring surface patches. |
226 | |
|
227 | 0 | std::vector<bool> visited_corners(num_corners(), false); |
228 | 0 | std::vector<std::pair<VertexIndex, CornerIndex>> sink_vertices; |
229 | 0 | bool mesh_connectivity_updated = false; |
230 | 0 | do { |
231 | 0 | mesh_connectivity_updated = false; |
232 | 0 | for (CornerIndex c(0); c < num_corners(); ++c) { |
233 | 0 | if (visited_corners[c.value()]) { |
234 | 0 | continue; |
235 | 0 | } |
236 | 0 | sink_vertices.clear(); |
237 | | |
238 | | // First swing all the way to find the left-most corner connected to the |
239 | | // corner's vertex. |
240 | 0 | CornerIndex first_c = c; |
241 | 0 | CornerIndex current_c = c; |
242 | 0 | CornerIndex next_c; |
243 | 0 | while (next_c = SwingLeft(current_c), |
244 | 0 | next_c != first_c && next_c != kInvalidCornerIndex && |
245 | 0 | !visited_corners[next_c.value()]) { |
246 | 0 | current_c = next_c; |
247 | 0 | } |
248 | |
|
249 | 0 | first_c = current_c; |
250 | | |
251 | | // Swing right from the first corner and check if all visited edges |
252 | | // are unique. |
253 | 0 | do { |
254 | 0 | visited_corners[current_c.value()] = true; |
255 | | // Each new edge is defined by the pivot vertex (that is the same for |
256 | | // all faces) and by the sink vertex (that is the |next| vertex from the |
257 | | // currently processed pivot corner. I.e., each edge is uniquely defined |
258 | | // by the sink vertex index. |
259 | 0 | const CornerIndex sink_c = Next(current_c); |
260 | 0 | const VertexIndex sink_v = corner_to_vertex_map_[sink_c]; |
261 | | |
262 | | // Corner that defines the edge on the face. |
263 | 0 | const CornerIndex edge_corner = Previous(current_c); |
264 | 0 | bool vertex_connectivity_updated = false; |
265 | | // Go over all processed edges (sink vertices). If the current sink |
266 | | // vertex has been already encountered before it may indicate a |
267 | | // non-manifold edge that needs to be broken. |
268 | 0 | for (auto &&attached_sink_vertex : sink_vertices) { |
269 | 0 | if (attached_sink_vertex.first == sink_v) { |
270 | | // Sink vertex has been already processed. |
271 | 0 | const CornerIndex other_edge_corner = attached_sink_vertex.second; |
272 | 0 | const CornerIndex opp_edge_corner = Opposite(edge_corner); |
273 | |
|
274 | 0 | if (opp_edge_corner == other_edge_corner) { |
275 | | // We are closing the loop so no need to change the connectivity. |
276 | 0 | continue; |
277 | 0 | } |
278 | | |
279 | | // Break the connectivity on the non-manifold edge. |
280 | | // TODO(ostava): It may be possible to reconnect the faces in a way |
281 | | // that the final surface would be manifold. |
282 | 0 | const CornerIndex opp_other_edge_corner = |
283 | 0 | Opposite(other_edge_corner); |
284 | 0 | if (opp_edge_corner != kInvalidCornerIndex) { |
285 | 0 | SetOppositeCorner(opp_edge_corner, kInvalidCornerIndex); |
286 | 0 | } |
287 | 0 | if (opp_other_edge_corner != kInvalidCornerIndex) { |
288 | 0 | SetOppositeCorner(opp_other_edge_corner, kInvalidCornerIndex); |
289 | 0 | } |
290 | |
|
291 | 0 | SetOppositeCorner(edge_corner, kInvalidCornerIndex); |
292 | 0 | SetOppositeCorner(other_edge_corner, kInvalidCornerIndex); |
293 | |
|
294 | 0 | vertex_connectivity_updated = true; |
295 | 0 | break; |
296 | 0 | } |
297 | 0 | } |
298 | 0 | if (vertex_connectivity_updated) { |
299 | | // Because of the updated connectivity, not all corners connected to |
300 | | // this vertex have been processed and we need to go over them again. |
301 | | // TODO(ostava): This can be optimized as we don't really need to |
302 | | // iterate over all corners. |
303 | 0 | mesh_connectivity_updated = true; |
304 | 0 | break; |
305 | 0 | } |
306 | | // Insert new sink vertex information <sink vertex index, edge corner>. |
307 | 0 | std::pair<VertexIndex, CornerIndex> new_sink_vert; |
308 | 0 | new_sink_vert.first = corner_to_vertex_map_[Previous(current_c)]; |
309 | 0 | new_sink_vert.second = sink_c; |
310 | 0 | sink_vertices.push_back(new_sink_vert); |
311 | |
|
312 | 0 | current_c = SwingRight(current_c); |
313 | 0 | } while (current_c != first_c && current_c != kInvalidCornerIndex); |
314 | 0 | } |
315 | 0 | } while (mesh_connectivity_updated); |
316 | 0 | return true; |
317 | 0 | } |
318 | | |
319 | 0 | bool CornerTable::ComputeVertexCorners(int num_vertices) { |
320 | 0 | DRACO_DCHECK(GetValenceCache().IsCacheEmpty()); |
321 | 0 | num_original_vertices_ = num_vertices; |
322 | 0 | vertex_corners_.resize(num_vertices, kInvalidCornerIndex); |
323 | | // Arrays for marking visited vertices and corners that allow us to detect |
324 | | // non-manifold vertices. |
325 | 0 | std::vector<bool> visited_vertices(num_vertices, false); |
326 | 0 | std::vector<bool> visited_corners(num_corners(), false); |
327 | |
|
328 | 0 | for (FaceIndex f(0); f < num_faces(); ++f) { |
329 | 0 | const CornerIndex first_face_corner = FirstCorner(f); |
330 | | // Check whether the face is degenerated. If so ignore it. |
331 | 0 | if (IsDegenerated(f)) { |
332 | 0 | continue; |
333 | 0 | } |
334 | | |
335 | 0 | for (int k = 0; k < 3; ++k) { |
336 | 0 | const CornerIndex c = first_face_corner + k; |
337 | 0 | if (visited_corners[c.value()]) { |
338 | 0 | continue; |
339 | 0 | } |
340 | 0 | VertexIndex v = corner_to_vertex_map_[c]; |
341 | | // Note that one vertex maps to many corners, but we just keep track |
342 | | // of the vertex which has a boundary on the left if the vertex lies on |
343 | | // the boundary. This means that all the related corners can be accessed |
344 | | // by iterating over the SwingRight() operator. |
345 | | // In case of a vertex inside the mesh, the choice is arbitrary. |
346 | 0 | bool is_non_manifold_vertex = false; |
347 | 0 | if (visited_vertices[v.value()]) { |
348 | | // A visited vertex of an unvisited corner found. Must be a non-manifold |
349 | | // vertex. |
350 | | // Create a new vertex for it. |
351 | 0 | vertex_corners_.push_back(kInvalidCornerIndex); |
352 | 0 | non_manifold_vertex_parents_.push_back(v); |
353 | 0 | visited_vertices.push_back(false); |
354 | 0 | v = VertexIndex(num_vertices++); |
355 | 0 | is_non_manifold_vertex = true; |
356 | 0 | } |
357 | | // Mark the vertex as visited. |
358 | 0 | visited_vertices[v.value()] = true; |
359 | | |
360 | | // First swing all the way to the left and mark all corners on the way. |
361 | 0 | CornerIndex act_c(c); |
362 | 0 | while (act_c != kInvalidCornerIndex) { |
363 | 0 | visited_corners[act_c.value()] = true; |
364 | | // Vertex will eventually point to the left most corner. |
365 | 0 | vertex_corners_[v] = act_c; |
366 | 0 | if (is_non_manifold_vertex) { |
367 | | // Update vertex index in the corresponding face. |
368 | 0 | corner_to_vertex_map_[act_c] = v; |
369 | 0 | } |
370 | 0 | act_c = SwingLeft(act_c); |
371 | 0 | if (act_c == c) { |
372 | 0 | break; // Full circle reached. |
373 | 0 | } |
374 | 0 | } |
375 | 0 | if (act_c == kInvalidCornerIndex) { |
376 | | // If we have reached an open boundary we need to swing right from the |
377 | | // initial corner to mark all corners in the opposite direction. |
378 | 0 | act_c = SwingRight(c); |
379 | 0 | while (act_c != kInvalidCornerIndex) { |
380 | 0 | visited_corners[act_c.value()] = true; |
381 | 0 | if (is_non_manifold_vertex) { |
382 | | // Update vertex index in the corresponding face. |
383 | 0 | corner_to_vertex_map_[act_c] = v; |
384 | 0 | } |
385 | 0 | act_c = SwingRight(act_c); |
386 | 0 | } |
387 | 0 | } |
388 | 0 | } |
389 | 0 | } |
390 | | |
391 | | // Count the number of isolated (unprocessed) vertices. |
392 | 0 | num_isolated_vertices_ = 0; |
393 | 0 | for (bool visited : visited_vertices) { |
394 | 0 | if (!visited) { |
395 | 0 | ++num_isolated_vertices_; |
396 | 0 | } |
397 | 0 | } |
398 | 0 | return true; |
399 | 0 | } |
400 | | |
401 | 0 | bool CornerTable::IsDegenerated(FaceIndex face) const { |
402 | 0 | if (face == kInvalidFaceIndex) { |
403 | 0 | return true; |
404 | 0 | } |
405 | 0 | const CornerIndex first_face_corner = FirstCorner(face); |
406 | 0 | const VertexIndex v0 = Vertex(first_face_corner); |
407 | 0 | const VertexIndex v1 = Vertex(Next(first_face_corner)); |
408 | 0 | const VertexIndex v2 = Vertex(Previous(first_face_corner)); |
409 | 0 | if (v0 == v1 || v0 == v2 || v1 == v2) { |
410 | 0 | return true; |
411 | 0 | } |
412 | 0 | return false; |
413 | 0 | } |
414 | | |
415 | 0 | int CornerTable::Valence(VertexIndex v) const { |
416 | 0 | if (v == kInvalidVertexIndex) { |
417 | 0 | return -1; |
418 | 0 | } |
419 | 0 | return ConfidentValence(v); |
420 | 0 | } |
421 | | |
422 | 0 | int CornerTable::ConfidentValence(VertexIndex v) const { |
423 | 0 | DRACO_DCHECK_GE(v.value(), 0); |
424 | 0 | DRACO_DCHECK_LT(v.value(), num_vertices()); |
425 | 0 | VertexRingIterator<CornerTable> vi(this, v); |
426 | 0 | int valence = 0; |
427 | 0 | for (; !vi.End(); vi.Next()) { |
428 | 0 | ++valence; |
429 | 0 | } |
430 | 0 | return valence; |
431 | 0 | } |
432 | | |
433 | 0 | void CornerTable::UpdateFaceToVertexMap(const VertexIndex vertex) { |
434 | 0 | DRACO_DCHECK(GetValenceCache().IsCacheEmpty()); |
435 | 0 | VertexCornersIterator<CornerTable> it(this, vertex); |
436 | 0 | for (; !it.End(); ++it) { |
437 | 0 | const CornerIndex corner = *it; |
438 | 0 | corner_to_vertex_map_[corner] = vertex; |
439 | 0 | } |
440 | 0 | } |
441 | | |
442 | | } // namespace draco |