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_cleanup.h"
16 
17 #include <unordered_set>
18 
19 #include "draco/core/hash_utils.h"
20 
21 namespace draco {
22 
Cleanup(Mesh * mesh,const MeshCleanupOptions & options)23 Status MeshCleanup::Cleanup(Mesh *mesh, const MeshCleanupOptions &options) {
24   if (!options.remove_degenerated_faces && !options.remove_unused_attributes &&
25       !options.remove_duplicate_faces && !options.make_geometry_manifold) {
26     return OkStatus();  // Nothing to cleanup.
27   }
28   const PointAttribute *const pos_att =
29       mesh->GetNamedAttribute(GeometryAttribute::POSITION);
30   if (pos_att == nullptr) {
31     return Status(Status::DRACO_ERROR, "Missing position attribute.");
32   }
33 
34   if (options.remove_degenerated_faces) {
35     RemoveDegeneratedFaces(mesh);
36   }
37 
38   if (options.remove_duplicate_faces) {
39     RemoveDuplicateFaces(mesh);
40   }
41 
42   if (options.remove_unused_attributes) {
43     RemoveUnusedAttributes(mesh);
44   }
45 
46   return OkStatus();
47 }
48 
RemoveDegeneratedFaces(Mesh * mesh)49 void MeshCleanup::RemoveDegeneratedFaces(Mesh *mesh) {
50   const PointAttribute *const pos_att =
51       mesh->GetNamedAttribute(GeometryAttribute::POSITION);
52   FaceIndex::ValueType num_degenerated_faces = 0;
53   // Array for storing position indices on a face.
54   std::array<AttributeValueIndex, 3> pos_indices;
55   for (FaceIndex f(0); f < mesh->num_faces(); ++f) {
56     const Mesh::Face &face = mesh->face(f);
57     for (int p = 0; p < 3; ++p) {
58       pos_indices[p] = pos_att->mapped_index(face[p]);
59     }
60     if (pos_indices[0] == pos_indices[1] || pos_indices[0] == pos_indices[2] ||
61         pos_indices[1] == pos_indices[2]) {
62       ++num_degenerated_faces;
63     } else if (num_degenerated_faces > 0) {
64       // Copy the face to its new location.
65       mesh->SetFace(f - num_degenerated_faces, face);
66     }
67   }
68   if (num_degenerated_faces > 0) {
69     mesh->SetNumFaces(mesh->num_faces() - num_degenerated_faces);
70   }
71 }
72 
RemoveDuplicateFaces(Mesh * mesh)73 void MeshCleanup::RemoveDuplicateFaces(Mesh *mesh) {
74   const PointAttribute *const pos_att =
75       mesh->GetNamedAttribute(GeometryAttribute::POSITION);
76 
77   typedef std::array<AttributeValueIndex::ValueType, 3> PosTriplet;
78   PosTriplet pos_indices;
79   std::unordered_set<PosTriplet, HashArray<PosTriplet>> is_face_used;
80 
81   uint32_t num_duplicate_faces = 0;
82   for (FaceIndex fi(0); fi < mesh->num_faces(); ++fi) {
83     const auto f = mesh->face(fi);
84     for (int c = 0; c < 3; ++c) {
85       pos_indices[c] = pos_att->mapped_index(f[c]).value();
86     }
87     // Shift the position indices until the smallest index is the first one.
88     while (pos_indices[0] > pos_indices[1] || pos_indices[0] > pos_indices[2]) {
89       // Shift to the left.
90       std::swap(pos_indices[0], pos_indices[1]);
91       std::swap(pos_indices[1], pos_indices[2]);
92     }
93     // Check if have encountered the same position triplet on a different face.
94     if (is_face_used.find(pos_indices) != is_face_used.end()) {
95       // Duplicate face. Ignore it.
96       num_duplicate_faces++;
97     } else {
98       // Insert new face to the set.
99       is_face_used.insert(pos_indices);
100       if (num_duplicate_faces > 0) {
101         // Copy the face to its new location.
102         mesh->SetFace(fi - num_duplicate_faces, f);
103       }
104     }
105   }
106   if (num_duplicate_faces > 0) {
107     mesh->SetNumFaces(mesh->num_faces() - num_duplicate_faces);
108   }
109 }
110 
RemoveUnusedAttributes(Mesh * mesh)111 void MeshCleanup::RemoveUnusedAttributes(Mesh *mesh) {
112   // Array that is going to store whether a corresponding point is used.
113   std::vector<bool> is_point_used;
114   PointIndex::ValueType num_new_points = 0;
115   is_point_used.resize(mesh->num_points(), false);
116   for (FaceIndex f(0); f < mesh->num_faces(); ++f) {
117     const Mesh::Face &face = mesh->face(f);
118     for (int p = 0; p < 3; ++p) {
119       if (!is_point_used[face[p].value()]) {
120         is_point_used[face[p].value()] = true;
121         ++num_new_points;
122       }
123     }
124   }
125 
126   bool points_changed = false;
127   const PointIndex::ValueType num_original_points = mesh->num_points();
128   // Map from old points to the new ones.
129   IndexTypeVector<PointIndex, PointIndex> point_map(num_original_points);
130   if (num_new_points < static_cast<int>(mesh->num_points())) {
131     // Some of the points were removed. We need to remap the old points to the
132     // new ones.
133     num_new_points = 0;
134     for (PointIndex i(0); i < num_original_points; ++i) {
135       if (is_point_used[i.value()]) {
136         point_map[i] = num_new_points++;
137       } else {
138         point_map[i] = kInvalidPointIndex;
139       }
140     }
141     // Go over faces and update their points.
142     for (FaceIndex f(0); f < mesh->num_faces(); ++f) {
143       Mesh::Face face = mesh->face(f);
144       for (int p = 0; p < 3; ++p) {
145         face[p] = point_map[face[p]];
146       }
147       mesh->SetFace(f, face);
148     }
149     // Set the new number of points.
150     mesh->set_num_points(num_new_points);
151     points_changed = true;
152   } else {
153     // No points were removed. Initialize identity map between the old and new
154     // points.
155     for (PointIndex i(0); i < num_original_points; ++i) {
156       point_map[i] = i;
157     }
158   }
159 
160   // Update index mapping for attributes.
161   IndexTypeVector<AttributeValueIndex, uint8_t> is_att_index_used;
162   IndexTypeVector<AttributeValueIndex, AttributeValueIndex> att_index_map;
163   for (int a = 0; a < mesh->num_attributes(); ++a) {
164     PointAttribute *const att = mesh->attribute(a);
165     // First detect which attribute entries are used (included in a point).
166     is_att_index_used.assign(att->size(), 0);
167     att_index_map.clear();
168     AttributeValueIndex::ValueType num_used_entries = 0;
169     for (PointIndex i(0); i < num_original_points; ++i) {
170       if (point_map[i] != kInvalidPointIndex) {
171         const AttributeValueIndex entry_id = att->mapped_index(i);
172         if (!is_att_index_used[entry_id]) {
173           is_att_index_used[entry_id] = 1;
174           ++num_used_entries;
175         }
176       }
177     }
178     bool att_indices_changed = false;
179     // If there are some unused attribute entries, remap the attribute values
180     // in the attribute buffer.
181     if (num_used_entries < static_cast<int>(att->size())) {
182       att_index_map.resize(att->size());
183       num_used_entries = 0;
184       for (AttributeValueIndex i(0); i < static_cast<uint32_t>(att->size());
185            ++i) {
186         if (is_att_index_used[i]) {
187           att_index_map[i] = num_used_entries;
188           if (i > num_used_entries) {
189             const uint8_t *const src_add = att->GetAddress(i);
190             att->buffer()->Write(
191                 att->GetBytePos(AttributeValueIndex(num_used_entries)), src_add,
192                 att->byte_stride());
193           }
194           ++num_used_entries;
195         }
196       }
197       // Update the number of unique entries in the vertex buffer.
198       att->Resize(num_used_entries);
199       att_indices_changed = true;
200     }
201     // If either the points or attribute indices have changed, we need to
202     // update the attribute index mapping.
203     if (points_changed || att_indices_changed) {
204       if (att->is_mapping_identity()) {
205         // The mapping was identity. It'll remain identity only if the
206         // number of point and attribute indices is still the same.
207         if (num_used_entries != static_cast<int>(mesh->num_points())) {
208           // We need to create an explicit mapping.
209           // First we need to initialize the explicit map to the original
210           // number of points to recreate the original identity map.
211           att->SetExplicitMapping(num_original_points);
212           // Set the entries of the explicit map to identity.
213           for (PointIndex::ValueType i = 0; i < num_original_points; ++i) {
214             att->SetPointMapEntry(PointIndex(i), AttributeValueIndex(i));
215           }
216         }
217       }
218       if (!att->is_mapping_identity()) {
219         // Explicit mapping between points and local attribute indices.
220         for (PointIndex i(0); i < num_original_points; ++i) {
221           // The new point id that maps to the currently processed attribute
222           // entry.
223           const PointIndex new_point_id = point_map[i];
224           if (new_point_id == kInvalidPointIndex) {
225             continue;
226           }
227           // Index of the currently processed attribute entry in the original
228           // mesh.
229           const AttributeValueIndex original_entry_index = att->mapped_index(i);
230           // New index of the same entry after unused entries were removed.
231           const AttributeValueIndex new_entry_index =
232               att_indices_changed ? att_index_map[original_entry_index]
233                                   : original_entry_index;
234 
235           // Update the mapping. Note that the new point index is always smaller
236           // than the processed index |i|, making this operation safe.
237           att->SetPointMapEntry(new_point_id, new_entry_index);
238         }
239         // If the number of points changed, we need to set a new explicit map
240         // size.
241         att->SetExplicitMapping(mesh->num_points());
242       }
243     }
244   }
245 }
246 
MakeGeometryManifold(Mesh * mesh)247 Status MeshCleanup::MakeGeometryManifold(Mesh *mesh) {
248   return Status(Status::DRACO_ERROR, "Unsupported function.");
249 }
250 
251 }  // namespace draco
252