/src/draco/src/draco/mesh/mesh.cc
Line | Count | Source (jump to first uncovered line) |
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.h" |
16 | | |
17 | | #include <array> |
18 | | #include <memory> |
19 | | #include <unordered_map> |
20 | | #include <utility> |
21 | | #include <vector> |
22 | | |
23 | | namespace draco { |
24 | | |
25 | | // Shortcut for typed conditionals. |
26 | | template <bool B, class T, class F> |
27 | | using conditional_t = typename std::conditional<B, T, F>::type; |
28 | | |
29 | 548 | Mesh::Mesh() {} |
30 | | |
31 | | #ifdef DRACO_TRANSCODER_SUPPORTED |
32 | | void Mesh::Copy(const Mesh &src) { |
33 | | PointCloud::Copy(src); |
34 | | name_ = src.name_; |
35 | | faces_ = src.faces_; |
36 | | attribute_data_ = src.attribute_data_; |
37 | | material_library_.Copy(src.material_library_); |
38 | | |
39 | | // Copy mesh feature ID sets. |
40 | | mesh_features_.clear(); |
41 | | for (MeshFeaturesIndex i(0); i < src.NumMeshFeatures(); i++) { |
42 | | std::unique_ptr<MeshFeatures> mesh_features(new MeshFeatures()); |
43 | | mesh_features->Copy(src.GetMeshFeatures(i)); |
44 | | AddMeshFeatures(std::move(mesh_features)); |
45 | | } |
46 | | mesh_features_material_mask_ = src.mesh_features_material_mask_; |
47 | | |
48 | | // Copy non-material textures. |
49 | | non_material_texture_library_.Copy(src.non_material_texture_library_); |
50 | | |
51 | | // Update pointers to non-material textures in mesh feature ID sets. |
52 | | if (non_material_texture_library_.NumTextures() != 0) { |
53 | | const auto texture_to_index_map = |
54 | | src.non_material_texture_library_.ComputeTextureToIndexMap(); |
55 | | for (MeshFeaturesIndex j(0); j < NumMeshFeatures(); ++j) { |
56 | | Mesh::UpdateMeshFeaturesTexturePointer(texture_to_index_map, |
57 | | &non_material_texture_library_, |
58 | | &GetMeshFeatures(j)); |
59 | | } |
60 | | } |
61 | | |
62 | | // Copy structural metadata. |
63 | | structural_metadata_.Copy(src.structural_metadata_); |
64 | | property_attributes_ = src.property_attributes_; |
65 | | property_attributes_material_mask_ = src.property_attributes_material_mask_; |
66 | | } |
67 | | |
68 | | namespace { |
69 | | // A helper struct that augments a point index with an attribute value index. |
70 | | // A unique combination of |point_index| and |attribute_value_index| |
71 | | // corresponds to a unique point on the mesh. Used to identify unique points |
72 | | // after a new attribute is added to the mesh. |
73 | | struct AugmentedPointData { |
74 | | PointIndex point_index; |
75 | | AttributeValueIndex attribute_value_index; |
76 | | bool operator<(const AugmentedPointData &pd) const { |
77 | | if (point_index < pd.point_index) { |
78 | | return true; |
79 | | } |
80 | | if (point_index > pd.point_index) { |
81 | | return false; |
82 | | } |
83 | | return attribute_value_index < pd.attribute_value_index; |
84 | | } |
85 | | }; |
86 | | } // namespace |
87 | | |
88 | | int32_t Mesh::AddAttributeWithConnectivity( |
89 | | std::unique_ptr<PointAttribute> att, |
90 | | const IndexTypeVector<CornerIndex, AttributeValueIndex> &corner_to_value) { |
91 | | // Map between augmented point and new point indices (one augmented point |
92 | | // corresponds to one PointIndex). |
93 | | std::map<AugmentedPointData, PointIndex> old_to_new_point_map; |
94 | | |
95 | | // Map between corners and the new point indices. |
96 | | IndexTypeVector<CornerIndex, PointIndex> corner_to_point(num_faces() * 3, |
97 | | kInvalidPointIndex); |
98 | | |
99 | | // Flag whether a given existing point index has been used. Used to ensure |
100 | | // that mapping between existing and new point indices that are smaller |
101 | | // than num_points() is identity. In other words, we want to keep indices of |
102 | | // the existing points intact and add new points to end. |
103 | | IndexTypeVector<PointIndex, bool> is_point_used(num_points(), false); |
104 | | |
105 | | int new_num_points = num_points(); |
106 | | for (CornerIndex ci(0); ci < num_faces() * 3; ++ci) { |
107 | | AugmentedPointData apd; |
108 | | apd.point_index = CornerToPointId(ci); |
109 | | apd.attribute_value_index = corner_to_value[ci]; |
110 | | const auto it = old_to_new_point_map.find(apd); |
111 | | if (it != old_to_new_point_map.end()) { |
112 | | // Augmented point is already mapped to a point index. Reuse it. |
113 | | corner_to_point[ci] = it->second; |
114 | | } else { |
115 | | // New combination of point index + attribute value index. Map it to a |
116 | | // unique point index. |
117 | | PointIndex new_point_index; |
118 | | if (!is_point_used[apd.point_index]) { |
119 | | // Reuse the existing (old) point index. |
120 | | new_point_index = apd.point_index; |
121 | | is_point_used[apd.point_index] = true; |
122 | | } else { |
123 | | // Add a new point index to the end. |
124 | | new_point_index = PointIndex(new_num_points++); |
125 | | } |
126 | | old_to_new_point_map[apd] = new_point_index; |
127 | | corner_to_point[ci] = new_point_index; |
128 | | } |
129 | | } |
130 | | |
131 | | // Update point to attribute value mapping for the new attribute. |
132 | | att->SetExplicitMapping(new_num_points); |
133 | | for (CornerIndex ci(0); ci < num_faces() * 3; ++ci) { |
134 | | att->SetPointMapEntry(corner_to_point[ci], corner_to_value[ci]); |
135 | | } |
136 | | |
137 | | // Update point to attribute value mapping on the remaining attributes if |
138 | | // needed. |
139 | | if (new_num_points > num_points()) { |
140 | | set_num_points(new_num_points); |
141 | | |
142 | | // Setup attributes for the new number of points. |
143 | | for (int ai = 0; ai < num_attributes(); ++ai) { |
144 | | const bool mapping_was_identity = attribute(ai)->is_mapping_identity(); |
145 | | attribute(ai)->SetExplicitMapping(new_num_points); |
146 | | if (mapping_was_identity) { |
147 | | // Convert all old points from identity to explicit mapping. |
148 | | for (AttributeValueIndex avi(0); avi < attribute(ai)->size(); ++avi) { |
149 | | attribute(ai)->SetPointMapEntry(PointIndex(avi.value()), avi); |
150 | | } |
151 | | } |
152 | | } |
153 | | |
154 | | for (CornerIndex ci(0); ci < num_faces() * 3; ++ci) { |
155 | | const PointIndex old_point_index = CornerToPointId(ci); |
156 | | const PointIndex new_point_index = corner_to_point[ci]; |
157 | | if (old_point_index == new_point_index) { |
158 | | continue; |
159 | | } |
160 | | // Update point to value mapping for all existing attributes. |
161 | | for (int ai = 0; ai < num_attributes(); ++ai) { |
162 | | attribute(ai)->SetPointMapEntry( |
163 | | new_point_index, attribute(ai)->mapped_index(old_point_index)); |
164 | | } |
165 | | // Update mapping between the corner and the new point index. |
166 | | faces_[FaceIndex(ci.value() / 3)][ci.value() % 3] = new_point_index; |
167 | | } |
168 | | } |
169 | | |
170 | | // If any of the old points have not been used, initialize dummy mapping for |
171 | | // the new attribute. |
172 | | for (PointIndex pi(0); pi < is_point_used.size(); ++pi) { |
173 | | if (!is_point_used[pi]) { |
174 | | att->SetPointMapEntry(pi, AttributeValueIndex(0)); |
175 | | } |
176 | | } |
177 | | |
178 | | return PointCloud::AddAttribute(std::move(att)); |
179 | | } |
180 | | |
181 | | int32_t Mesh::AddPerVertexAttribute(std::unique_ptr<PointAttribute> att) { |
182 | | const PointAttribute *const pos_att = |
183 | | GetNamedAttribute(GeometryAttribute::POSITION); |
184 | | if (pos_att == nullptr) { |
185 | | return -1; |
186 | | } |
187 | | if (att->size() != pos_att->size()) { |
188 | | return -1; // Number of values must be same as in the position attribute. |
189 | | } |
190 | | |
191 | | if (pos_att->is_mapping_identity()) { |
192 | | att->SetIdentityMapping(); |
193 | | } else { |
194 | | // Copy point to attribute value mapping from the position attribute to |
195 | | // |att|. |
196 | | att->SetExplicitMapping(num_points()); |
197 | | for (PointIndex pi(0); pi < num_points(); ++pi) { |
198 | | att->SetPointMapEntry(pi, pos_att->mapped_index(pi)); |
199 | | } |
200 | | } |
201 | | |
202 | | return PointCloud::AddAttribute(std::move(att)); |
203 | | } |
204 | | |
205 | | void Mesh::RemoveIsolatedPoints() { |
206 | | // For each point, check if it is mapped to a face. |
207 | | IndexTypeVector<PointIndex, bool> is_point_used(num_points(), false); |
208 | | int num_used_points = 0; |
209 | | for (FaceIndex fi(0); fi < num_faces(); ++fi) { |
210 | | const auto &f = face(fi); |
211 | | for (int c = 0; c < 3; ++c) { |
212 | | if (!is_point_used[f[c]]) { |
213 | | num_used_points++; |
214 | | is_point_used[f[c]] = true; |
215 | | } |
216 | | } |
217 | | } |
218 | | if (num_used_points == num_points()) { |
219 | | return; // All points are used. |
220 | | } |
221 | | |
222 | | // Create mapping between the old and new point indices. |
223 | | IndexTypeVector<PointIndex, PointIndex> old_to_new_point_map( |
224 | | num_points(), kInvalidPointIndex); |
225 | | PointIndex new_point_index(0); |
226 | | for (PointIndex pi(0); pi < num_points(); ++pi) { |
227 | | if (is_point_used[pi]) { |
228 | | old_to_new_point_map[pi] = new_point_index++; |
229 | | } |
230 | | } |
231 | | |
232 | | // Update point to attribute value index map for all attributes. |
233 | | for (int ai = 0; ai < num_attributes(); ++ai) { |
234 | | PointAttribute *att = attribute(ai); |
235 | | if (att->is_mapping_identity()) { |
236 | | // When the attribute uses identity mapping we need to reorder to the |
237 | | // attribute values to match the new point indices. |
238 | | for (PointIndex pi(0); pi < num_points(); ++pi) { |
239 | | const PointIndex new_pi = old_to_new_point_map[pi]; |
240 | | if (new_pi == pi || new_pi == kInvalidPointIndex) { |
241 | | continue; |
242 | | } |
243 | | att->SetAttributeValue( |
244 | | AttributeValueIndex(new_pi.value()), |
245 | | att->GetAddress(AttributeValueIndex(pi.value()))); |
246 | | } |
247 | | att->Resize(num_used_points); |
248 | | } else { |
249 | | // For explicitly mapped attributes, we first update the point to |
250 | | // attribute value mapping and then we remove all unused values from the |
251 | | // attribute. |
252 | | for (PointIndex pi(0); pi < num_points(); ++pi) { |
253 | | const PointIndex new_pi = old_to_new_point_map[pi]; |
254 | | if (new_pi == pi || new_pi == kInvalidPointIndex) { |
255 | | continue; |
256 | | } |
257 | | att->SetPointMapEntry(new_pi, att->mapped_index(pi)); |
258 | | } |
259 | | att->SetExplicitMapping(num_used_points); |
260 | | |
261 | | att->RemoveUnusedValues(); |
262 | | } |
263 | | } |
264 | | |
265 | | // Update the mapping between faces and point indices. |
266 | | for (FaceIndex fi(0); fi < num_faces(); ++fi) { |
267 | | auto &f = faces_[fi]; |
268 | | for (int c = 0; c < 3; ++c) { |
269 | | f[c] = old_to_new_point_map[f[c]]; |
270 | | } |
271 | | } |
272 | | |
273 | | set_num_points(num_used_points); |
274 | | } |
275 | | |
276 | | void Mesh::RemoveUnusedMaterials() { RemoveUnusedMaterials(true); } |
277 | | |
278 | | void Mesh::RemoveUnusedMaterials(bool remove_unused_material_indices) { |
279 | | const int mat_att_index = GetNamedAttributeId(GeometryAttribute::MATERIAL); |
280 | | if (mat_att_index == -1) { |
281 | | // Remove all materials except for the first one. |
282 | | while (GetMaterialLibrary().NumMaterials() > 1) { |
283 | | GetMaterialLibrary().RemoveMaterial(1); |
284 | | } |
285 | | GetMaterialLibrary().RemoveUnusedTextures(); |
286 | | return; |
287 | | } |
288 | | auto mat_att = attribute(mat_att_index); |
289 | | |
290 | | // Deduplicate attribute values in the material attribute to ensure that one |
291 | | // attribute value index corresponds to one unique material index. |
292 | | // Note that this does not remove unused material indices. |
293 | | mat_att->DeduplicateValues(*mat_att); |
294 | | |
295 | | // Gather all material indices that are referenced by faces of the mesh. |
296 | | const int num_materials = GetMaterialLibrary().NumMaterials(); |
297 | | std::vector<bool> is_material_used(num_materials, false); |
298 | | int num_used_materials = 0; |
299 | | |
300 | | // Helper function that updates |is_material_used| for the processed mesh. |
301 | | auto update_used_materials = [&is_material_used, &num_used_materials, mat_att, |
302 | | num_materials](PointIndex pi) { |
303 | | uint32_t mat_index = 0; |
304 | | mat_att->GetMappedValue(pi, &mat_index); |
305 | | if (mat_index < num_materials) { |
306 | | if (!is_material_used[mat_index]) { |
307 | | is_material_used[mat_index] = true; |
308 | | num_used_materials++; |
309 | | } |
310 | | } |
311 | | }; |
312 | | |
313 | | if (num_faces() > 0) { |
314 | | for (FaceIndex fi(0); fi < num_faces(); ++fi) { |
315 | | update_used_materials(faces_[fi][0]); |
316 | | } |
317 | | } else { |
318 | | // Handle the mesh as a point cloud and check materials used by points. |
319 | | for (PointIndex pi(0); pi < num_points(); ++pi) { |
320 | | update_used_materials(pi); |
321 | | } |
322 | | } |
323 | | |
324 | | // Check if any of the (unused) materials is used by mesh features. If so, |
325 | | // user should remove unused mesh features first. |
326 | | for (MeshFeaturesIndex mfi(0); mfi < NumMeshFeatures(); ++mfi) { |
327 | | for (int mask_index = 0; mask_index < NumMeshFeaturesMaterialMasks(mfi); |
328 | | ++mask_index) { |
329 | | const int mat_index = GetMeshFeaturesMaterialMask(mfi, mask_index); |
330 | | if (mat_index < num_materials && !is_material_used[mat_index]) { |
331 | | is_material_used[mat_index] = true; |
332 | | num_used_materials++; |
333 | | } |
334 | | } |
335 | | } |
336 | | |
337 | | // Check if any of the (unused) materials is used by property attributes |
338 | | // indices. If so, user should remove unused property attributes indices |
339 | | // first. |
340 | | for (int i = 0; i < NumPropertyAttributesIndices(); ++i) { |
341 | | for (int mask_index = 0; |
342 | | mask_index < NumPropertyAttributesIndexMaterialMasks(i); |
343 | | ++mask_index) { |
344 | | const int mat_index = |
345 | | GetPropertyAttributesIndexMaterialMask(i, mask_index); |
346 | | if (mat_index < num_materials && !is_material_used[mat_index]) { |
347 | | is_material_used[mat_index] = true; |
348 | | num_used_materials++; |
349 | | } |
350 | | } |
351 | | } |
352 | | |
353 | | if (num_used_materials == num_materials) { |
354 | | return; // All materials are used, don't do anything. |
355 | | } |
356 | | |
357 | | // Remove unused materials from the material library or replace them with |
358 | | // default materials if we do not remove unused material indices. |
359 | | for (int mi = num_materials - 1; mi >= 0; --mi) { |
360 | | if (!is_material_used[mi] && mi < GetMaterialLibrary().NumMaterials()) { |
361 | | if (remove_unused_material_indices) { |
362 | | GetMaterialLibrary().RemoveMaterial(mi); |
363 | | } else { |
364 | | GetMaterialLibrary().MutableMaterial(mi)->Clear(); |
365 | | } |
366 | | } |
367 | | } |
368 | | GetMaterialLibrary().RemoveUnusedTextures(); |
369 | | |
370 | | if (!remove_unused_material_indices) { |
371 | | // All the code below handles updating of material indices. Since we do not |
372 | | // want to update them, we can return early. |
373 | | return; |
374 | | } |
375 | | |
376 | | // Compute map between old and new material indices. |
377 | | std::vector<int> old_to_new_material_index_map(num_materials, -1); |
378 | | for (int mi = 0, new_material_index = 0; mi < num_materials; ++mi) { |
379 | | if (is_material_used[mi]) { |
380 | | old_to_new_material_index_map[mi] = new_material_index; |
381 | | ++new_material_index; |
382 | | } |
383 | | } |
384 | | IndexTypeVector<AttributeValueIndex, int> |
385 | | old_to_new_material_attribute_value_index_map(mat_att->size(), -1); |
386 | | for (AttributeValueIndex avi(0); avi < mat_att->size(); ++avi) { |
387 | | uint32_t mat_index = 0; |
388 | | mat_att->GetValue(avi, &mat_index); |
389 | | if (mat_index < num_materials && is_material_used[mat_index]) { |
390 | | old_to_new_material_attribute_value_index_map[avi] = |
391 | | old_to_new_material_index_map[mat_index]; |
392 | | } |
393 | | } |
394 | | |
395 | | // Update attribute values with the new number of materials. |
396 | | mat_att->Reset(num_used_materials); |
397 | | |
398 | | // Set identity mapping between AttributeValueIndex and material indices. |
399 | | for (AttributeValueIndex avi(0); avi < mat_att->size(); ++avi) { |
400 | | const uint32_t mat_index = avi.value(); |
401 | | mat_att->SetAttributeValue(avi, &mat_index); |
402 | | } |
403 | | |
404 | | // Update mapping between points and attribute values. |
405 | | for (PointIndex pi(0); pi < num_points(); ++pi) { |
406 | | const AttributeValueIndex old_avi = mat_att->mapped_index(pi); |
407 | | mat_att->SetPointMapEntry( |
408 | | pi, AttributeValueIndex( |
409 | | old_to_new_material_attribute_value_index_map[old_avi])); |
410 | | } |
411 | | |
412 | | // Update material indices on mesh features. |
413 | | for (MeshFeaturesIndex mfi(0); mfi < NumMeshFeatures(); ++mfi) { |
414 | | for (int mask_index = 0; mask_index < NumMeshFeaturesMaterialMasks(mfi); |
415 | | ++mask_index) { |
416 | | const int old_mat_index = GetMeshFeaturesMaterialMask(mfi, mask_index); |
417 | | if (old_mat_index < num_materials && is_material_used[old_mat_index]) { |
418 | | mesh_features_material_mask_[mfi][mask_index] = |
419 | | old_to_new_material_index_map[old_mat_index]; |
420 | | } |
421 | | } |
422 | | } |
423 | | |
424 | | // Update material indices on property attributes incices. |
425 | | for (int i = 0; i < NumPropertyAttributesIndices(); ++i) { |
426 | | for (int mask_index = 0; |
427 | | mask_index < NumPropertyAttributesIndexMaterialMasks(i); |
428 | | ++mask_index) { |
429 | | const int old_mat_index = |
430 | | GetPropertyAttributesIndexMaterialMask(i, mask_index); |
431 | | if (old_mat_index < num_materials && is_material_used[old_mat_index]) { |
432 | | property_attributes_material_mask_[i][mask_index] = |
433 | | old_to_new_material_index_map[old_mat_index]; |
434 | | } |
435 | | } |
436 | | } |
437 | | } |
438 | | |
439 | | bool Mesh::IsAttributeUsedByMeshFeatures(int att_id) const { |
440 | | for (MeshFeaturesIndex mfi(0); mfi < NumMeshFeatures(); ++mfi) { |
441 | | const auto &mf = GetMeshFeatures(mfi); |
442 | | if (mf.GetAttributeIndex() == att_id) { |
443 | | return true; |
444 | | } |
445 | | } |
446 | | return false; |
447 | | } |
448 | | |
449 | | void Mesh::UpdateMeshFeaturesTexturePointer( |
450 | | const std::unordered_map<const Texture *, int> &texture_to_index_map, |
451 | | TextureLibrary *texture_library, MeshFeatures *mesh_features) { |
452 | | TextureMap &texture_map = mesh_features->GetTextureMap(); |
453 | | if (texture_map.texture() == nullptr) { |
454 | | return; |
455 | | } |
456 | | const auto it = texture_to_index_map.find(texture_map.texture()); |
457 | | DRACO_DCHECK(it != texture_to_index_map.end()); |
458 | | const int texture_index = it->second; |
459 | | DRACO_DCHECK(texture_index < texture_library->NumTextures()); |
460 | | texture_map.SetTexture(texture_library->GetTexture(texture_index)); |
461 | | } |
462 | | |
463 | | void Mesh::CopyMeshFeaturesForMaterial(const Mesh &source_mesh, |
464 | | Mesh *target_mesh, int material_index) { |
465 | | for (MeshFeaturesIndex mfi(0); mfi < source_mesh.NumMeshFeatures(); ++mfi) { |
466 | | // Mesh features is used if it doesn't have any material mask or if one |
467 | | // of the material masks matches |material_index|. |
468 | | bool is_used = source_mesh.NumMeshFeaturesMaterialMasks(mfi) == 0; |
469 | | for (int mask_index = 0; |
470 | | !is_used && mask_index < source_mesh.NumMeshFeaturesMaterialMasks(mfi); |
471 | | ++mask_index) { |
472 | | if (source_mesh.GetMeshFeaturesMaterialMask(mfi, mask_index) == |
473 | | material_index) { |
474 | | is_used = true; |
475 | | } |
476 | | } |
477 | | if (is_used) { |
478 | | // Copy over the mesh features to the target mesh. Note that texture |
479 | | // pointers are not updated at this step. |
480 | | std::unique_ptr<MeshFeatures> new_mf(new MeshFeatures()); |
481 | | new_mf->Copy(source_mesh.GetMeshFeatures(mfi)); |
482 | | target_mesh->AddMeshFeatures(std::move(new_mf)); |
483 | | } |
484 | | } |
485 | | } |
486 | | |
487 | | void Mesh::CopyPropertyAttributesIndicesForMaterial(const Mesh &source_mesh, |
488 | | Mesh *target_mesh, |
489 | | int material_index) { |
490 | | for (int i = 0; i < source_mesh.NumPropertyAttributesIndices(); ++i) { |
491 | | // Property attributes index is used if it doesn't have any material mask or |
492 | | // if one of the material masks matches |material_index|. |
493 | | bool is_used = source_mesh.NumPropertyAttributesIndexMaterialMasks(i) == 0; |
494 | | for (int mask_index = 0; |
495 | | !is_used && |
496 | | mask_index < source_mesh.NumPropertyAttributesIndexMaterialMasks(i); |
497 | | ++mask_index) { |
498 | | if (source_mesh.GetPropertyAttributesIndexMaterialMask(i, mask_index) == |
499 | | material_index) { |
500 | | is_used = true; |
501 | | } |
502 | | } |
503 | | if (is_used) { |
504 | | // Copy over the property attributes index to the target mesh. |
505 | | target_mesh->AddPropertyAttributesIndex( |
506 | | source_mesh.GetPropertyAttributesIndex(i)); |
507 | | } |
508 | | } |
509 | | } |
510 | | |
511 | | void Mesh::UpdateMeshFeaturesAfterDeletedAttribute(int att_id) { |
512 | | for (MeshFeaturesIndex mfi(0); mfi < NumMeshFeatures(); ++mfi) { |
513 | | auto &mf = GetMeshFeatures(mfi); |
514 | | if (mf.GetAttributeIndex() == att_id) { |
515 | | // Mesh features is no longer associated with a vertex attribute. |
516 | | mf.SetAttributeIndex(-1); |
517 | | } else if (mf.GetAttributeIndex() > att_id) { |
518 | | // Attribute index decremented by one. |
519 | | mf.SetAttributeIndex(mf.GetAttributeIndex() - 1); |
520 | | } |
521 | | } |
522 | | } |
523 | | |
524 | | int32_t Mesh::AddPerFaceAttribute(std::unique_ptr<PointAttribute> att) { |
525 | | IndexTypeVector<CornerIndex, AttributeValueIndex> corner_map(num_faces() * 3); |
526 | | for (CornerIndex ci(0); ci < num_faces() * 3; ++ci) { |
527 | | corner_map[ci] = AttributeValueIndex(ci.value() / 3); |
528 | | } |
529 | | return AddAttributeWithConnectivity(std::move(att), corner_map); |
530 | | } |
531 | | #endif // DRACO_TRANSCODER_SUPPORTED |
532 | | |
533 | | #ifdef DRACO_ATTRIBUTE_INDICES_DEDUPLICATION_SUPPORTED |
534 | | void Mesh::ApplyPointIdDeduplication( |
535 | | const IndexTypeVector<PointIndex, PointIndex> &id_map, |
536 | 0 | const std::vector<PointIndex> &unique_point_ids) { |
537 | 0 | PointCloud::ApplyPointIdDeduplication(id_map, unique_point_ids); |
538 | 0 | for (FaceIndex f(0); f < num_faces(); ++f) { |
539 | 0 | for (int32_t c = 0; c < 3; ++c) { |
540 | 0 | faces_[f][c] = id_map[faces_[f][c]]; |
541 | 0 | } |
542 | 0 | } |
543 | 0 | } |
544 | | #endif |
545 | | |
546 | | } // namespace draco |