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()23MeshEdgebreakerEncoder::MeshEdgebreakerEncoder() {} 24 InitializeEncoder()25bool 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)70bool 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)77bool 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()85Status MeshEdgebreakerEncoder::EncodeConnectivity() { 86 return impl_->EncodeConnectivity(); 87 } 88 ComputeNumberOfEncodedPoints()89void 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()182void 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