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/compression/mesh/mesh_edgebreaker_encoder.h"
16 
17 #include "draco/compression/mesh/mesh_edgebreaker_encoder_impl.h"
18 #include "draco/compression/mesh/mesh_edgebreaker_traversal_predictive_encoder.h"
19 #include "draco/compression/mesh/mesh_edgebreaker_traversal_valence_encoder.h"
20 
21 namespace draco {
22 
MeshEdgebreakerEncoder()23 MeshEdgebreakerEncoder::MeshEdgebreakerEncoder() {}
24 
InitializeEncoder()25 bool MeshEdgebreakerEncoder::InitializeEncoder() {
26   const bool is_standard_edgebreaker_available =
27       options()->IsFeatureSupported(features::kEdgebreaker);
28   const bool is_predictive_edgebreaker_available =
29       options()->IsFeatureSupported(features::kPredictiveEdgebreaker);
30 
31   impl_ = nullptr;
32   // For tiny meshes it's usually better to use the basic edgebreaker as the
33   // overhead of the predictive one may turn out to be too big.
34   const bool is_tiny_mesh = mesh()->num_faces() < 1000;
35 
36   int selected_edgebreaker_method =
37       options()->GetGlobalInt("edgebreaker_method", -1);
38   if (selected_edgebreaker_method == -1) {
39     if (is_standard_edgebreaker_available &&
40         (options()->GetSpeed() >= 5 || !is_predictive_edgebreaker_available ||
41          is_tiny_mesh)) {
42       selected_edgebreaker_method = MESH_EDGEBREAKER_STANDARD_ENCODING;
43     } else {
44       selected_edgebreaker_method = MESH_EDGEBREAKER_VALENCE_ENCODING;
45     }
46   }
47 
48   if (selected_edgebreaker_method == MESH_EDGEBREAKER_STANDARD_ENCODING) {
49     if (is_standard_edgebreaker_available) {
50       buffer()->Encode(
51           static_cast<uint8_t>(MESH_EDGEBREAKER_STANDARD_ENCODING));
52       impl_ = std::unique_ptr<MeshEdgebreakerEncoderImplInterface>(
53           new MeshEdgebreakerEncoderImpl<MeshEdgebreakerTraversalEncoder>());
54     }
55   } else if (selected_edgebreaker_method == MESH_EDGEBREAKER_VALENCE_ENCODING) {
56     buffer()->Encode(static_cast<uint8_t>(MESH_EDGEBREAKER_VALENCE_ENCODING));
57     impl_ = std::unique_ptr<MeshEdgebreakerEncoderImplInterface>(
58         new MeshEdgebreakerEncoderImpl<
59             MeshEdgebreakerTraversalValenceEncoder>());
60   }
61   if (!impl_) {
62     return false;
63   }
64   if (!impl_->Init(this)) {
65     return false;
66   }
67   return true;
68 }
69 
GenerateAttributesEncoder(int32_t att_id)70 bool MeshEdgebreakerEncoder::GenerateAttributesEncoder(int32_t att_id) {
71   if (!impl_->GenerateAttributesEncoder(att_id)) {
72     return false;
73   }
74   return true;
75 }
76 
EncodeAttributesEncoderIdentifier(int32_t att_encoder_id)77 bool MeshEdgebreakerEncoder::EncodeAttributesEncoderIdentifier(
78     int32_t att_encoder_id) {
79   if (!impl_->EncodeAttributesEncoderIdentifier(att_encoder_id)) {
80     return false;
81   }
82   return true;
83 }
84 
EncodeConnectivity()85 Status MeshEdgebreakerEncoder::EncodeConnectivity() {
86   return impl_->EncodeConnectivity();
87 }
88 
ComputeNumberOfEncodedPoints()89 void MeshEdgebreakerEncoder::ComputeNumberOfEncodedPoints() {
90   if (!impl_) {
91     return;
92   }
93   const CornerTable *const corner_table = impl_->GetCornerTable();
94   if (!corner_table) {
95     return;
96   }
97   size_t num_points =
98       corner_table->num_vertices() - corner_table->NumIsolatedVertices();
99 
100   if (mesh()->num_attributes() > 1) {
101     // Gather all corner tables for all non-position attributes.
102     std::vector<const MeshAttributeCornerTable *> attribute_corner_tables;
103     for (int i = 0; i < mesh()->num_attributes(); ++i) {
104       if (mesh()->attribute(i)->attribute_type() ==
105           GeometryAttribute::POSITION) {
106         continue;
107       }
108       const MeshAttributeCornerTable *const att_corner_table =
109           GetAttributeCornerTable(i);
110       // Attribute corner table may not be used in some configurations. For
111       // these cases we can assume the attribute connectivity to be the same as
112       // the connectivity of the position data.
113       if (att_corner_table) {
114         attribute_corner_tables.push_back(att_corner_table);
115       }
116     }
117 
118     // Add a new point based on the configuration of interior attribute seams
119     // (replicating what the decoder would do).
120     for (VertexIndex vi(0); vi < corner_table->num_vertices(); ++vi) {
121       if (corner_table->IsVertexIsolated(vi)) {
122         continue;
123       }
124       // Go around all corners of the vertex and keep track of the observed
125       // attribute seams.
126       const CornerIndex first_corner_index = corner_table->LeftMostCorner(vi);
127       const PointIndex first_point_index =
128           mesh()->CornerToPointId(first_corner_index);
129 
130       PointIndex last_point_index = first_point_index;
131       CornerIndex last_corner_index = first_corner_index;
132       CornerIndex corner_index = corner_table->SwingRight(first_corner_index);
133       size_t num_attribute_seams = 0;
134       while (corner_index != kInvalidCornerIndex) {
135         const PointIndex point_index = mesh()->CornerToPointId(corner_index);
136         bool seam_found = false;
137         if (point_index != last_point_index) {
138           // Point index changed - new attribute seam detected.
139           seam_found = true;
140           last_point_index = point_index;
141         } else {
142           // Even though point indices matches, there still may be a seam caused
143           // by non-manifold connectivity of non-position attribute data.
144           for (int i = 0; i < attribute_corner_tables.size(); ++i) {
145             if (attribute_corner_tables[i]->Vertex(corner_index) !=
146                 attribute_corner_tables[i]->Vertex(last_corner_index)) {
147               seam_found = true;
148               break;  // No need to process other attributes.
149             }
150           }
151         }
152         if (seam_found) {
153           ++num_attribute_seams;
154         }
155 
156         if (corner_index == first_corner_index) {
157           break;
158         }
159 
160         // Proceed to the next corner
161         last_corner_index = corner_index;
162         corner_index = corner_table->SwingRight(corner_index);
163       }
164 
165       if (!corner_table->IsOnBoundary(vi) && num_attribute_seams > 0) {
166         // If the last visited point index is the same as the first point index
167         // we traveled all the way around the vertex. In this case the number of
168         // new points should be num_attribute_seams - 1
169         num_points += num_attribute_seams - 1;
170       } else {
171         // Else the vertex was either on a boundary (i.e. we couldn't travel all
172         // around the vertex), or we ended up at a different point. In both of
173         // these cases, the number of new points is equal to the number of
174         // attribute seams.
175         num_points += num_attribute_seams;
176       }
177     }
178   }
179   set_num_encoded_points(num_points);
180 }
181 
ComputeNumberOfEncodedFaces()182 void MeshEdgebreakerEncoder::ComputeNumberOfEncodedFaces() {
183   if (!impl_) {
184     return;
185   }
186   const CornerTable *const corner_table = impl_->GetCornerTable();
187   if (!corner_table) {
188     return;
189   }
190   set_num_encoded_faces(corner_table->num_faces() -
191                         corner_table->NumDegeneratedFaces());
192 }
193 
194 }  // namespace draco
195