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/mesh_attribute_corner_table.h"
16 #include "draco/mesh/corner_table_iterators.h"
17 #include "draco/mesh/mesh_misc_functions.h"
18
19 namespace draco {
20
MeshAttributeCornerTable()21 MeshAttributeCornerTable::MeshAttributeCornerTable()
22 : no_interior_seams_(true), corner_table_(nullptr), valence_cache_(*this) {}
23
InitEmpty(const CornerTable * table)24 bool MeshAttributeCornerTable::InitEmpty(const CornerTable *table) {
25 if (table == nullptr)
26 return false;
27 valence_cache_.ClearValenceCache();
28 valence_cache_.ClearValenceCacheInaccurate();
29 is_edge_on_seam_.assign(table->num_corners(), false);
30 is_vertex_on_seam_.assign(table->num_vertices(), false);
31 corner_to_vertex_map_.assign(table->num_corners(), kInvalidVertexIndex);
32 vertex_to_attribute_entry_id_map_.reserve(table->num_vertices());
33 vertex_to_left_most_corner_map_.reserve(table->num_vertices());
34 corner_table_ = table;
35 no_interior_seams_ = true;
36 return true;
37 }
38
InitFromAttribute(const Mesh * mesh,const CornerTable * table,const PointAttribute * att)39 bool MeshAttributeCornerTable::InitFromAttribute(const Mesh *mesh,
40 const CornerTable *table,
41 const PointAttribute *att) {
42 if (!InitEmpty(table))
43 return false;
44 valence_cache_.ClearValenceCache();
45 valence_cache_.ClearValenceCacheInaccurate();
46
47 // Find all necessary data for encoding attributes. For now we check which of
48 // the mesh vertices is part of an attribute seam, because seams require
49 // special handling.
50 for (CornerIndex c(0); c < corner_table_->num_corners(); ++c) {
51 const FaceIndex f = corner_table_->Face(c);
52 if (corner_table_->IsDegenerated(f))
53 continue; // Ignore corners on degenerated faces.
54 const CornerIndex opp_corner = corner_table_->Opposite(c);
55 if (opp_corner == kInvalidCornerIndex) {
56 // Boundary. Mark it as seam edge.
57 is_edge_on_seam_[c.value()] = true;
58 // Mark seam vertices.
59 VertexIndex v;
60 v = corner_table_->Vertex(corner_table_->Next(c));
61 is_vertex_on_seam_[v.value()] = true;
62 v = corner_table_->Vertex(corner_table_->Previous(c));
63 is_vertex_on_seam_[v.value()] = true;
64 continue;
65 }
66 if (opp_corner < c)
67 continue; // Opposite corner was already processed.
68
69 CornerIndex act_c(c), act_sibling_c(opp_corner);
70 for (int i = 0; i < 2; ++i) {
71 // Get the sibling corners. I.e., the two corners attached to the same
72 // vertex but divided by the seam edge.
73 act_c = corner_table_->Next(act_c);
74 act_sibling_c = corner_table_->Previous(act_sibling_c);
75 const PointIndex point_id = mesh->CornerToPointId(act_c.value());
76 const PointIndex sibling_point_id =
77 mesh->CornerToPointId(act_sibling_c.value());
78 if (att->mapped_index(point_id) != att->mapped_index(sibling_point_id)) {
79 no_interior_seams_ = false;
80 is_edge_on_seam_[c.value()] = true;
81 is_edge_on_seam_[opp_corner.value()] = true;
82 // Mark seam vertices.
83 is_vertex_on_seam_[corner_table_
84 ->Vertex(corner_table_->Next(CornerIndex(c)))
85 .value()] = true;
86 is_vertex_on_seam_[corner_table_
87 ->Vertex(corner_table_->Previous(CornerIndex(c)))
88 .value()] = true;
89 is_vertex_on_seam_
90 [corner_table_->Vertex(corner_table_->Next(opp_corner)).value()] =
91 true;
92 is_vertex_on_seam_[corner_table_
93 ->Vertex(corner_table_->Previous(opp_corner))
94 .value()] = true;
95 break;
96 }
97 }
98 }
99 RecomputeVertices(mesh, att);
100 return true;
101 }
102
AddSeamEdge(CornerIndex c)103 void MeshAttributeCornerTable::AddSeamEdge(CornerIndex c) {
104 DRACO_DCHECK(GetValenceCache().IsCacheEmpty());
105 is_edge_on_seam_[c.value()] = true;
106 // Mark seam vertices.
107 is_vertex_on_seam_[corner_table_->Vertex(corner_table_->Next(c)).value()] =
108 true;
109 is_vertex_on_seam_[corner_table_->Vertex(corner_table_->Previous(c))
110 .value()] = true;
111
112 const CornerIndex opp_corner = corner_table_->Opposite(c);
113 if (opp_corner != kInvalidCornerIndex) {
114 no_interior_seams_ = false;
115 is_edge_on_seam_[opp_corner.value()] = true;
116 is_vertex_on_seam_[corner_table_->Vertex(corner_table_->Next(opp_corner))
117 .value()] = true;
118 is_vertex_on_seam_
119 [corner_table_->Vertex(corner_table_->Previous(opp_corner)).value()] =
120 true;
121 }
122 }
123
RecomputeVertices(const Mesh * mesh,const PointAttribute * att)124 void MeshAttributeCornerTable::RecomputeVertices(const Mesh *mesh,
125 const PointAttribute *att) {
126 DRACO_DCHECK(GetValenceCache().IsCacheEmpty());
127 if (mesh != nullptr && att != nullptr) {
128 RecomputeVerticesInternal<true>(mesh, att);
129 } else {
130 RecomputeVerticesInternal<false>(nullptr, nullptr);
131 }
132 }
133
134 template <bool init_vertex_to_attribute_entry_map>
RecomputeVerticesInternal(const Mesh * mesh,const PointAttribute * att)135 void MeshAttributeCornerTable::RecomputeVerticesInternal(
136 const Mesh *mesh, const PointAttribute *att) {
137 DRACO_DCHECK(GetValenceCache().IsCacheEmpty());
138 int num_new_vertices = 0;
139 for (VertexIndex v(0); v < corner_table_->num_vertices(); ++v) {
140 const CornerIndex c = corner_table_->LeftMostCorner(v);
141 if (c == kInvalidCornerIndex)
142 continue; // Isolated vertex?
143 AttributeValueIndex first_vert_id(num_new_vertices++);
144 if (init_vertex_to_attribute_entry_map) {
145 const PointIndex point_id = mesh->CornerToPointId(c.value());
146 vertex_to_attribute_entry_id_map_.push_back(att->mapped_index(point_id));
147 } else {
148 // Identity mapping
149 vertex_to_attribute_entry_id_map_.push_back(first_vert_id);
150 }
151 CornerIndex first_c = c;
152 CornerIndex act_c;
153 // Check if the vertex is on a seam edge, if it is we need to find the first
154 // attribute entry on the seam edge when traversing in the CCW direction.
155 if (is_vertex_on_seam_[v.value()]) {
156 // Try to swing left on the modified corner table. We need to get the
157 // first corner that defines an attribute seam.
158 act_c = SwingLeft(first_c);
159 while (act_c != kInvalidCornerIndex) {
160 first_c = act_c;
161 act_c = SwingLeft(act_c);
162 }
163 }
164 corner_to_vertex_map_[first_c.value()] = VertexIndex(first_vert_id.value());
165 vertex_to_left_most_corner_map_.push_back(first_c);
166 act_c = corner_table_->SwingRight(first_c);
167 while (act_c != kInvalidCornerIndex && act_c != first_c) {
168 if (IsCornerOppositeToSeamEdge(corner_table_->Next(act_c))) {
169 first_vert_id = AttributeValueIndex(num_new_vertices++);
170 if (init_vertex_to_attribute_entry_map) {
171 const PointIndex point_id = mesh->CornerToPointId(act_c.value());
172 vertex_to_attribute_entry_id_map_.push_back(
173 att->mapped_index(point_id));
174 } else {
175 // Identity mapping.
176 vertex_to_attribute_entry_id_map_.push_back(first_vert_id);
177 }
178 vertex_to_left_most_corner_map_.push_back(act_c);
179 }
180 corner_to_vertex_map_[act_c.value()] = VertexIndex(first_vert_id.value());
181 act_c = corner_table_->SwingRight(act_c);
182 }
183 }
184 }
185
Valence(VertexIndex v) const186 int MeshAttributeCornerTable::Valence(VertexIndex v) const {
187 if (v == kInvalidVertexIndex)
188 return -1;
189 return ConfidentValence(v);
190 }
191
ConfidentValence(VertexIndex v) const192 int MeshAttributeCornerTable::ConfidentValence(VertexIndex v) const {
193 DRACO_DCHECK_LT(v.value(), num_vertices());
194 draco::VertexRingIterator<MeshAttributeCornerTable> vi(this, v);
195 int valence = 0;
196 for (; !vi.End(); vi.Next()) {
197 ++valence;
198 }
199 return valence;
200 }
201
202 } // namespace draco
203