Changeset View
Changeset View
Standalone View
Standalone View
extern/draco/dracoenc/src/draco/mesh/corner_table.cc
- This file was added.
| // Copyright 2016 The Draco Authors. | |||||
| // | |||||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||||
| // you may not use this file except in compliance with the License. | |||||
| // You may obtain a copy of the License at | |||||
| // | |||||
| // http://www.apache.org/licenses/LICENSE-2.0 | |||||
| // | |||||
| // Unless required by applicable law or agreed to in writing, software | |||||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
| // See the License for the specific language governing permissions and | |||||
| // limitations under the License. | |||||
| // | |||||
| #include "draco/mesh/corner_table.h" | |||||
| #include <limits> | |||||
| #include "draco/mesh/corner_table_iterators.h" | |||||
| namespace draco { | |||||
| CornerTable::CornerTable() | |||||
| : num_original_vertices_(0), | |||||
| num_degenerated_faces_(0), | |||||
| num_isolated_vertices_(0), | |||||
| valence_cache_(*this) {} | |||||
| std::unique_ptr<CornerTable> CornerTable::Create( | |||||
| const IndexTypeVector<FaceIndex, FaceType> &faces) { | |||||
| std::unique_ptr<CornerTable> ct(new CornerTable()); | |||||
| if (!ct->Init(faces)) | |||||
| return nullptr; | |||||
| return ct; | |||||
| } | |||||
| bool CornerTable::Init(const IndexTypeVector<FaceIndex, FaceType> &faces) { | |||||
| valence_cache_.ClearValenceCache(); | |||||
| valence_cache_.ClearValenceCacheInaccurate(); | |||||
| corner_to_vertex_map_.resize(faces.size() * 3); | |||||
| for (FaceIndex fi(0); fi < static_cast<uint32_t>(faces.size()); ++fi) { | |||||
| for (int i = 0; i < 3; ++i) { | |||||
| corner_to_vertex_map_[FirstCorner(fi) + i] = faces[fi][i]; | |||||
| } | |||||
| } | |||||
| int num_vertices = -1; | |||||
| if (!ComputeOppositeCorners(&num_vertices)) | |||||
| return false; | |||||
| if (!ComputeVertexCorners(num_vertices)) | |||||
| return false; | |||||
| return true; | |||||
| } | |||||
| bool CornerTable::Reset(int num_faces) { | |||||
| return Reset(num_faces, num_faces * 3); | |||||
| } | |||||
| bool CornerTable::Reset(int num_faces, int num_vertices) { | |||||
| if (num_faces < 0 || num_vertices < 0) | |||||
| return false; | |||||
| if (static_cast<unsigned int>(num_faces) > | |||||
| std::numeric_limits<CornerIndex::ValueType>::max() / 3) | |||||
| return false; | |||||
| corner_to_vertex_map_.assign(num_faces * 3, kInvalidVertexIndex); | |||||
| opposite_corners_.assign(num_faces * 3, kInvalidCornerIndex); | |||||
| vertex_corners_.reserve(num_vertices); | |||||
| valence_cache_.ClearValenceCache(); | |||||
| valence_cache_.ClearValenceCacheInaccurate(); | |||||
| return true; | |||||
| } | |||||
| bool CornerTable::ComputeOppositeCorners(int *num_vertices) { | |||||
| DRACO_DCHECK(GetValenceCache().IsCacheEmpty()); | |||||
| if (num_vertices == nullptr) | |||||
| return false; | |||||
| opposite_corners_.resize(num_corners(), kInvalidCornerIndex); | |||||
| // Out implementation for finding opposite corners is based on keeping track | |||||
| // of outgoing half-edges for each vertex of the mesh. Half-edges (defined by | |||||
| // their opposite corners) are processed one by one and whenever a new | |||||
| // half-edge (corner) is processed, we check whether the sink vertex of | |||||
| // this half-edge contains its sibling half-edge. If yes, we connect them and | |||||
| // remove the sibling half-edge from the sink vertex, otherwise we add the new | |||||
| // half-edge to its source vertex. | |||||
| // First compute the number of outgoing half-edges (corners) attached to each | |||||
| // vertex. | |||||
| std::vector<int> num_corners_on_vertices; | |||||
| num_corners_on_vertices.reserve(num_corners()); | |||||
| for (CornerIndex c(0); c < num_corners(); ++c) { | |||||
| const VertexIndex v1 = Vertex(c); | |||||
| if (v1.value() >= static_cast<int>(num_corners_on_vertices.size())) | |||||
| num_corners_on_vertices.resize(v1.value() + 1, 0); | |||||
| // For each corner there is always exactly one outgoing half-edge attached | |||||
| // to its vertex. | |||||
| num_corners_on_vertices[v1.value()]++; | |||||
| } | |||||
| // Create a storage for half-edges on each vertex. We store all half-edges in | |||||
| // one array, where each entry is identified by the half-edge's sink vertex id | |||||
| // and the associated half-edge corner id (corner opposite to the half-edge). | |||||
| // Each vertex will be assigned storage for up to | |||||
| // |num_corners_on_vertices[vert_id]| half-edges. Unused half-edges are marked | |||||
| // with |sink_vert| == kInvalidVertexIndex. | |||||
| struct VertexEdgePair { | |||||
| VertexEdgePair() | |||||
| : sink_vert(kInvalidVertexIndex), edge_corner(kInvalidCornerIndex) {} | |||||
| VertexIndex sink_vert; | |||||
| CornerIndex edge_corner; | |||||
| }; | |||||
| std::vector<VertexEdgePair> vertex_edges(num_corners(), VertexEdgePair()); | |||||
| // For each vertex compute the offset (location where the first half-edge | |||||
| // entry of a given vertex is going to be stored). This way each vertex is | |||||
| // guaranteed to have a non-overlapping storage with respect to the other | |||||
| // vertices. | |||||
| std::vector<int> vertex_offset(num_corners_on_vertices.size()); | |||||
| int offset = 0; | |||||
| for (size_t i = 0; i < num_corners_on_vertices.size(); ++i) { | |||||
| vertex_offset[i] = offset; | |||||
| offset += num_corners_on_vertices[i]; | |||||
| } | |||||
| // Now go over the all half-edges (using their opposite corners) and either | |||||
| // insert them to the |vertex_edge| array or connect them with existing | |||||
| // half-edges. | |||||
| for (CornerIndex c(0); c < num_corners(); ++c) { | |||||
| const VertexIndex tip_v = Vertex(c); | |||||
| const VertexIndex source_v = Vertex(Next(c)); | |||||
| const VertexIndex sink_v = Vertex(Previous(c)); | |||||
| const FaceIndex face_index = Face(c); | |||||
| if (c == FirstCorner(face_index)) { | |||||
| // Check whether the face is degenerated, if so ignore it. | |||||
| const VertexIndex v0 = Vertex(c); | |||||
| if (v0 == source_v || v0 == sink_v || source_v == sink_v) { | |||||
| ++num_degenerated_faces_; | |||||
| c += 2; // Ignore the next two corners of the same face. | |||||
| continue; | |||||
| } | |||||
| } | |||||
| CornerIndex opposite_c(kInvalidCornerIndex); | |||||
| // The maximum number of half-edges attached to the sink vertex. | |||||
| const int num_corners_on_vert = num_corners_on_vertices[sink_v.value()]; | |||||
| // Where to look for the first half-edge on the sink vertex. | |||||
| offset = vertex_offset[sink_v.value()]; | |||||
| for (int i = 0; i < num_corners_on_vert; ++i, ++offset) { | |||||
| const VertexIndex other_v = vertex_edges[offset].sink_vert; | |||||
| if (other_v == kInvalidVertexIndex) | |||||
| break; // No matching half-edge found on the sink vertex. | |||||
| if (other_v == source_v) { | |||||
| if (tip_v == Vertex(vertex_edges[offset].edge_corner)) | |||||
| continue; // Don't connect mirrored faces. | |||||
| // A matching half-edge was found on the sink vertex. Mark the | |||||
| // half-edge's opposite corner. | |||||
| opposite_c = vertex_edges[offset].edge_corner; | |||||
| // Remove the half-edge from the sink vertex. We remap all subsequent | |||||
| // half-edges one slot down. | |||||
| // TODO(ostava): This can be optimized a little bit, by remapping only | |||||
| // the half-edge on the last valid slot into the deleted half-edge's | |||||
| // slot. | |||||
| for (int j = i + 1; j < num_corners_on_vert; ++j, ++offset) { | |||||
| vertex_edges[offset] = vertex_edges[offset + 1]; | |||||
| if (vertex_edges[offset].sink_vert == kInvalidVertexIndex) | |||||
| break; // Unused half-edge reached. | |||||
| } | |||||
| // Mark the last entry as unused. | |||||
| vertex_edges[offset].sink_vert = kInvalidVertexIndex; | |||||
| break; | |||||
| } | |||||
| } | |||||
| if (opposite_c == kInvalidCornerIndex) { | |||||
| // No opposite corner found. Insert the new edge | |||||
| const int num_corners_on_source_vert = | |||||
| num_corners_on_vertices[source_v.value()]; | |||||
| offset = vertex_offset[source_v.value()]; | |||||
| for (int i = 0; i < num_corners_on_source_vert; ++i, ++offset) { | |||||
| // Find the first unused half-edge slot on the source vertex. | |||||
| if (vertex_edges[offset].sink_vert == kInvalidVertexIndex) { | |||||
| vertex_edges[offset].sink_vert = sink_v; | |||||
| vertex_edges[offset].edge_corner = c; | |||||
| break; | |||||
| } | |||||
| } | |||||
| } else { | |||||
| // Opposite corner found. | |||||
| opposite_corners_[c] = opposite_c; | |||||
| opposite_corners_[opposite_c] = c; | |||||
| } | |||||
| } | |||||
| *num_vertices = static_cast<int>(num_corners_on_vertices.size()); | |||||
| return true; | |||||
| } | |||||
| bool CornerTable::ComputeVertexCorners(int num_vertices) { | |||||
| DRACO_DCHECK(GetValenceCache().IsCacheEmpty()); | |||||
| num_original_vertices_ = num_vertices; | |||||
| vertex_corners_.resize(num_vertices, kInvalidCornerIndex); | |||||
| // Arrays for marking visited vertices and corners that allow us to detect | |||||
| // non-manifold vertices. | |||||
| std::vector<bool> visited_vertices(num_vertices, false); | |||||
| std::vector<bool> visited_corners(num_corners(), false); | |||||
| for (FaceIndex f(0); f < num_faces(); ++f) { | |||||
| const CornerIndex first_face_corner = FirstCorner(f); | |||||
| // Check whether the face is degenerated. If so ignore it. | |||||
| if (IsDegenerated(f)) | |||||
| continue; | |||||
| for (int k = 0; k < 3; ++k) { | |||||
| const CornerIndex c = first_face_corner + k; | |||||
| if (visited_corners[c.value()]) | |||||
| continue; | |||||
| VertexIndex v = corner_to_vertex_map_[c]; | |||||
| // Note that one vertex maps to many corners, but we just keep track | |||||
| // of the vertex which has a boundary on the left if the vertex lies on | |||||
| // the boundary. This means that all the related corners can be accessed | |||||
| // by iterating over the SwingRight() operator. | |||||
| // In case of a vertex inside the mesh, the choice is arbitrary. | |||||
| bool is_non_manifold_vertex = false; | |||||
| if (visited_vertices[v.value()]) { | |||||
| // A visited vertex of an unvisited corner found. Must be a non-manifold | |||||
| // vertex. | |||||
| // Create a new vertex for it. | |||||
| vertex_corners_.push_back(kInvalidCornerIndex); | |||||
| non_manifold_vertex_parents_.push_back(v); | |||||
| visited_vertices.push_back(false); | |||||
| v = VertexIndex(num_vertices++); | |||||
| is_non_manifold_vertex = true; | |||||
| } | |||||
| // Mark the vertex as visited. | |||||
| visited_vertices[v.value()] = true; | |||||
| // First swing all the way to the left and mark all corners on the way. | |||||
| CornerIndex act_c(c); | |||||
| while (act_c != kInvalidCornerIndex) { | |||||
| visited_corners[act_c.value()] = true; | |||||
| // Vertex will eventually point to the left most corner. | |||||
| vertex_corners_[v] = act_c; | |||||
| if (is_non_manifold_vertex) { | |||||
| // Update vertex index in the corresponding face. | |||||
| corner_to_vertex_map_[act_c] = v; | |||||
| } | |||||
| act_c = SwingLeft(act_c); | |||||
| if (act_c == c) | |||||
| break; // Full circle reached. | |||||
| } | |||||
| if (act_c == kInvalidCornerIndex) { | |||||
| // If we have reached an open boundary we need to swing right from the | |||||
| // initial corner to mark all corners in the opposite direction. | |||||
| act_c = SwingRight(c); | |||||
| while (act_c != kInvalidCornerIndex) { | |||||
| visited_corners[act_c.value()] = true; | |||||
| if (is_non_manifold_vertex) { | |||||
| // Update vertex index in the corresponding face. | |||||
| corner_to_vertex_map_[act_c] = v; | |||||
| } | |||||
| act_c = SwingRight(act_c); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| // Count the number of isolated (unprocessed) vertices. | |||||
| num_isolated_vertices_ = 0; | |||||
| for (bool visited : visited_vertices) { | |||||
| if (!visited) | |||||
| ++num_isolated_vertices_; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| bool CornerTable::IsDegenerated(FaceIndex face) const { | |||||
| if (face == kInvalidFaceIndex) | |||||
| return true; | |||||
| const CornerIndex first_face_corner = FirstCorner(face); | |||||
| const VertexIndex v0 = Vertex(first_face_corner); | |||||
| const VertexIndex v1 = Vertex(Next(first_face_corner)); | |||||
| const VertexIndex v2 = Vertex(Previous(first_face_corner)); | |||||
| if (v0 == v1 || v0 == v2 || v1 == v2) | |||||
| return true; | |||||
| return false; | |||||
| } | |||||
| int CornerTable::Valence(VertexIndex v) const { | |||||
| if (v == kInvalidVertexIndex) | |||||
| return -1; | |||||
| return ConfidentValence(v); | |||||
| } | |||||
| int CornerTable::ConfidentValence(VertexIndex v) const { | |||||
| DRACO_DCHECK_GE(v.value(), 0); | |||||
| DRACO_DCHECK_LT(v.value(), num_vertices()); | |||||
| VertexRingIterator<CornerTable> vi(this, v); | |||||
| int valence = 0; | |||||
| for (; !vi.End(); vi.Next()) { | |||||
| ++valence; | |||||
| } | |||||
| return valence; | |||||
| } | |||||
| void CornerTable::UpdateFaceToVertexMap(const VertexIndex vertex) { | |||||
| DRACO_DCHECK(GetValenceCache().IsCacheEmpty()); | |||||
| VertexCornersIterator<CornerTable> it(this, vertex); | |||||
| for (; !it.End(); ++it) { | |||||
| const CornerIndex corner = *it; | |||||
| corner_to_vertex_map_[corner] = vertex; | |||||
| } | |||||
| } | |||||
| } // namespace draco | |||||