// Copyright 2016 The Draco Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #include "compression/mesh/mesh_edgebreaker_decoder_impl.h" #include #include "compression/attributes/mesh_attribute_indices_encoding_observer.h" #include "compression/attributes/mesh_traversal_sequencer.h" #include "compression/attributes/sequential_attribute_decoders_controller.h" #include "compression/mesh/mesh_edgebreaker_decoder.h" #include "compression/mesh/mesh_edgebreaker_traversal_predictive_decoder.h" #include "mesh/corner_table_traversal_processor.h" #include "mesh/edgebreaker_traverser.h" namespace draco { // Types of "free" edges that are used during topology decoding. // A free edge is an edge that is connected to one face only. // All edge types are stored in the opposite_corner_id_ array, where each // edge "e" is uniquely identified by the the opposite corner "C" in its parent // triangle: // * // /C\ // / \ // / e \ // *-------* // For more description about how the edges are used, see comment inside // ZipConnectivity() method. template MeshEdgeBreakerDecoderImpl::MeshEdgeBreakerDecoderImpl() : decoder_(nullptr), num_processed_hole_events_(0), last_symbol_id_(-1), last_vert_id_(-1), last_face_id_(-1), num_new_vertices_(0), num_encoded_vertices_(0) {} template bool MeshEdgeBreakerDecoderImpl::Init( MeshEdgeBreakerDecoder *decoder) { decoder_ = decoder; return true; } template const MeshAttributeCornerTable * MeshEdgeBreakerDecoderImpl::GetAttributeCornerTable( int att_id) const { for (uint32_t i = 0; i < attribute_data_.size(); ++i) { const AttributesDecoder *const dec = decoder_->attributes_decoder(attribute_data_[i].decoder_id); for (int j = 0; j < dec->num_attributes(); ++j) { if (dec->GetAttributeId(j) == att_id) { if (attribute_data_[i].is_connectivity_used) return &attribute_data_[i].connectivity_data; return nullptr; } } } return nullptr; } template const MeshAttributeIndicesEncodingData * MeshEdgeBreakerDecoderImpl::GetAttributeEncodingData( int att_id) const { for (uint32_t i = 0; i < attribute_data_.size(); ++i) { const AttributesDecoder *const dec = decoder_->attributes_decoder(attribute_data_[i].decoder_id); for (int j = 0; j < dec->num_attributes(); ++j) { if (dec->GetAttributeId(j) == att_id) return &attribute_data_[i].encoding_data; } } return &pos_encoding_data_; } template bool MeshEdgeBreakerDecoderImpl::CreateAttributesDecoder( int32_t att_decoder_id) { int8_t att_data_id; if (!decoder_->buffer()->Decode(&att_data_id)) return false; uint8_t decoder_type; if (!decoder_->buffer()->Decode(&decoder_type)) return false; if (att_data_id >= 0) { if (att_data_id >= attribute_data_.size()) { return false; // Unexpected attribute data. } attribute_data_[att_data_id].decoder_id = att_decoder_id; } const Mesh *mesh = decoder_->mesh(); std::unique_ptr sequencer; if (decoder_type == MESH_VERTEX_ATTRIBUTE) { // Per-vertex attribute decoder. typedef CornerTableTraversalProcessor AttProcessor; typedef MeshAttributeIndicesEncodingObserver AttObserver; // Traverser that is used to generate the encoding order of each attribute. typedef EdgeBreakerTraverser AttTraverser; MeshAttributeIndicesEncodingData *encoding_data = nullptr; if (att_data_id < 0) { encoding_data = &pos_encoding_data_; } else { encoding_data = &attribute_data_[att_data_id].encoding_data; // Mark the attribute connectivity data invalid to ensure it's not used // later on. attribute_data_[att_data_id].is_connectivity_used = false; } std::unique_ptr> traversal_sequencer( new MeshTraversalSequencer(mesh, encoding_data)); AttTraverser att_traverser; AttObserver att_observer(corner_table_.get(), mesh, traversal_sequencer.get(), encoding_data); AttProcessor att_processor; att_processor.ResetProcessor(corner_table_.get()); att_traverser.Init(att_processor, att_observer); traversal_sequencer->SetTraverser(att_traverser); sequencer = std::move(traversal_sequencer); } else { if (att_data_id < 0) return false; // Attribute data must be specified. // Per-corner attribute decoder. typedef CornerTableTraversalProcessor AttProcessor; typedef MeshAttributeIndicesEncodingObserver AttObserver; // Traverser that is used to generate the encoding order of each attribute. typedef EdgeBreakerTraverser AttTraverser; std::unique_ptr> traversal_sequencer( new MeshTraversalSequencer( mesh, &attribute_data_[att_data_id].encoding_data)); AttTraverser att_traverser; AttObserver att_observer(&attribute_data_[att_data_id].connectivity_data, mesh, traversal_sequencer.get(), &attribute_data_[att_data_id].encoding_data); AttProcessor att_processor; att_processor.ResetProcessor( &attribute_data_[att_data_id].connectivity_data); att_traverser.Init(att_processor, att_observer); traversal_sequencer->SetTraverser(att_traverser); sequencer = std::move(traversal_sequencer); } if (!sequencer) return false; std::unique_ptr att_controller( new SequentialAttributeDecodersController(std::move(sequencer))); decoder_->SetAttributesDecoder(att_decoder_id, std::move(att_controller)); return true; } template bool MeshEdgeBreakerDecoderImpl::DecodeConnectivity() { num_new_vertices_ = 0; new_to_parent_vertex_map_.clear(); uint32_t num_new_verts; if (!decoder_->buffer()->Decode(&num_new_verts)) return false; num_new_vertices_ = num_new_verts; uint32_t num_encoded_vertices; if (!decoder_->buffer()->Decode(&num_encoded_vertices)) return false; num_encoded_vertices_ = num_encoded_vertices; uint32_t num_faces; if (!decoder_->buffer()->Decode(&num_faces)) return false; // Decode topology (connectivity). vertex_traversal_length_.clear(); corner_table_ = std::unique_ptr(new CornerTable()); if (corner_table_ == nullptr) return false; corner_table_->Reset(num_faces); processed_corner_ids_.clear(); processed_corner_ids_.reserve(num_faces); processed_connectivity_corners_.clear(); processed_connectivity_corners_.reserve(num_faces); topology_split_data_.clear(); hole_event_data_.clear(); init_face_configurations_.clear(); init_corners_.clear(); num_processed_hole_events_ = 0; last_symbol_id_ = -1; last_face_id_ = -1; last_vert_id_ = -1; int8_t num_attribute_data; if (!decoder_->buffer()->Decode(&num_attribute_data) || num_attribute_data < 0) return false; attribute_data_.clear(); // Add one attribute data for each attribute decoder. attribute_data_.resize(num_attribute_data); uint32_t num_encoded_symbols; if (!decoder_->buffer()->Decode(&num_encoded_symbols)) return false; if (num_faces < num_encoded_symbols) { // Number of faces needs to be the same or greater than the number of // symbols (it can be greater because the initial face may not be encoded as // a symbol). return false; } uint32_t num_encoded_split_symbols; if (!decoder_->buffer()->Decode(&num_encoded_split_symbols)) return false; // Start with all vertices marked as holes (boundaries). // Only vertices decoded with TOPOLOGY_C symbol (and the initial face) will // be marked as non hole vertices. We need to allocate the array larger // because split symbols can create extra vertices during the decoding // process (these extra vertices are then eliminated during deduplication). is_vert_hole_.assign(num_encoded_vertices_ + num_encoded_split_symbols, true); uint32_t encoded_connectivity_size; decoder_->buffer()->Decode(&encoded_connectivity_size); DecoderBuffer event_buffer; event_buffer.Init( decoder_->buffer()->data_head() + encoded_connectivity_size, decoder_->buffer()->remaining_size() - encoded_connectivity_size); // Decode hole and topology split events. int32_t topology_split_decoded_bytes = DecodeHoleAndTopologySplitEvents(&event_buffer); if (topology_split_decoded_bytes == -1) return false; traversal_decoder_.Init(this); traversal_decoder_.SetNumEncodedVertices(num_encoded_vertices_); traversal_decoder_.SetNumAttributeData(num_attribute_data); DecoderBuffer traversal_end_buffer; if (!traversal_decoder_.Start(&traversal_end_buffer)) return false; const int num_connectivity_verts = DecodeConnectivity(num_encoded_symbols); if (num_connectivity_verts == -1) return false; // Set the main buffer to the end of the traversal. decoder_->buffer()->Init(traversal_end_buffer.data_head(), traversal_end_buffer.remaining_size()); // Skip topology split data that was already decoded earlier. decoder_->buffer()->Advance(topology_split_decoded_bytes); // Decode connectivity of non-position attributes. if (attribute_data_.size() > 0) { for (CornerIndex ci(0); ci < corner_table_->num_corners(); ci += 3) { if (!DecodeAttributeConnectivitiesOnFace(ci)) return false; } } traversal_decoder_.Done(); // Update vertex to corner mapping on boundary vertices as it was not set // correctly in the previous steps. for (int i = 0; i < corner_table_->num_vertices(); ++i) { if (is_vert_hole_[i]) { corner_table_->UpdateVertexToCornerMap(VertexIndex(i)); } } // Decode attribute connectivity. // Prepare data structure for decoding non-position attribute connectivites. for (uint32_t i = 0; i < attribute_data_.size(); ++i) { attribute_data_[i].connectivity_data.InitEmpty(corner_table_.get()); // Add all seams. for (int32_t c : attribute_data_[i].attribute_seam_corners) { attribute_data_[i].connectivity_data.AddSeamEdge(CornerIndex(c)); } // Recompute vertices from the newly added seam edges. attribute_data_[i].connectivity_data.RecomputeVertices(nullptr, nullptr); } pos_encoding_data_.vertex_to_encoded_attribute_value_index_map.resize( corner_table_->num_vertices()); for (uint32_t i = 0; i < attribute_data_.size(); ++i) { // For non-position attributes, preallocate the vertex to value mapping // using the maximum number of vertices from the base corner table and the // attribute corner table (since the attribute decoder may use either of // it). int32_t att_connectivity_verts = attribute_data_[i].connectivity_data.num_vertices(); if (att_connectivity_verts < corner_table_->num_vertices()) att_connectivity_verts = corner_table_->num_vertices(); attribute_data_[i] .encoding_data.vertex_to_encoded_attribute_value_index_map.resize( att_connectivity_verts); } if (!AssignPointsToCorners()) return false; return true; } template bool MeshEdgeBreakerDecoderImpl::OnAttributesDecoded() { return true; } template int MeshEdgeBreakerDecoderImpl::DecodeConnectivity( int num_symbols) { // Algorithm does the reverse decoding of the symbols encoded with the // edgebreaker method. The reverse decoding always keeps track of the active // edge identified by its opposite corner (active corner). New faces are // always added to this active edge. There may be multiple active corners at // one time that either correspond to separate mesh components or to // sub-components of one mesh that are going to be merged together using the // TOPOLOGY_S symbol. We can store these active edges on a stack, because the // decoder always processes only the latest active edge. TOPOLOGY_S then // removes the top edge from the stack and TOPOLOGY_E adds a new edge to the // stack. std::vector active_corner_stack; // Additional active edges may be added as a result of topology split events. // They can be added in arbitrary order, but we always know the split symbol // id they belong to, so we can address them using this symbol id. std::unordered_map topology_split_active_corners; int num_vertices = 0; int max_num_vertices = is_vert_hole_.size(); int num_faces = 0; for (int symbol_id = 0; symbol_id < num_symbols; ++symbol_id) { const FaceIndex face(num_faces++); // Used to flag cases where we need to look for topology split events. bool check_topology_split = false; const uint32_t symbol = traversal_decoder_.DecodeSymbol(); if (symbol == TOPOLOGY_C) { // Create a new face between two edges on the open boundary. // The first edge is opposite to the corner "a" from the image below. // The other edge is opposite to the corner "b" that can be reached // through a CCW traversal around the vertex "v". // One new active boundary edge is created, opposite to the new corner // "x". // // *-------* // / \ / \ // / \ / \ // / \ / \ // *-------v-------* // \b /x\ a/ // \ / \ / // \ / C \ / // *.......* // Find the corner "b" from the corner "a" which is the corner on the // top of the active stack. const CornerIndex corner_a = active_corner_stack.back(); CornerIndex corner_b = corner_table_->Previous(corner_a); while (corner_table_->Opposite(corner_b) >= 0) { corner_b = corner_table_->Previous(corner_table_->Opposite(corner_b)); } // New tip corner. const CornerIndex corner(3 * face.value()); // Update opposite corner mappings. SetOppositeCorners(corner_a, corner + 1); SetOppositeCorners(corner_b, corner + 2); const VertexIndex vertex_x = corner_table_->Vertex(corner_table_->Next(corner_a)); // Update vertex mapping. corner_table_->MapCornerToVertex(corner, vertex_x); corner_table_->MapCornerToVertex( corner + 1, corner_table_->Vertex(corner_table_->Next(corner_b))); corner_table_->MapCornerToVertex( corner + 2, corner_table_->Vertex(corner_table_->Previous(corner_a))); if (num_vertices > max_num_vertices) return -1; // Unexpected number of decoded vertices. // Mark the vertex |x| as interior. is_vert_hole_[vertex_x.value()] = false; // Update the corner on the active stack. active_corner_stack.back() = corner; } else if (symbol == TOPOLOGY_R || symbol == TOPOLOGY_L) { // Create a new face extending from the open boundary edge opposite to the // corner "a" from the image below. Two new boundary edges are created // opposite to corners "r" and "l". New active corner is set to either "r" // or "l" depending on the decoded symbol. One new vertex is created // at the opposite corner to corner "a". // *-------* // /a\ / \ // / \ / \ // / \ / \ // *-------v-------* // .l r. // . . // . . // * const CornerIndex corner_a = active_corner_stack.back(); // First corner on the new face is either corner "l" or "r". const CornerIndex corner(3 * face.value()); CornerIndex opp_corner; if (symbol == TOPOLOGY_R) { // "r" is the new first corner. opp_corner = corner + 2; } else { // "l" is the new first corner. opp_corner = corner + 1; } SetOppositeCorners(opp_corner, corner_a); // Update vertex mapping. corner_table_->MapCornerToVertex(opp_corner, VertexIndex(num_vertices++)); corner_table_->MapCornerToVertex( corner_table_->Next(opp_corner), corner_table_->Vertex(corner_table_->Previous(corner_a))); corner_table_->MapCornerToVertex( corner_table_->Previous(opp_corner), corner_table_->Vertex(corner_table_->Next(corner_a))); active_corner_stack.back() = corner; check_topology_split = true; } else if (symbol == TOPOLOGY_S) { // Create a new face that merges two last active edges from the active // stack. No new vertex is created, but two vertices at corners "p" and // "n" need to be merged into a single vertex. // // *-------v-------* // \a p/x\n b/ // \ / \ / // \ / S \ / // *.......* // const CornerIndex corner_b = active_corner_stack.back(); active_corner_stack.pop_back(); // Corner "a" can correspond either to a normal active edge, or to an edge // created from the topology split event. const auto it = topology_split_active_corners.find(symbol_id); if (it != topology_split_active_corners.end()) { // Topology split event. Move the retrieved edge to the stack. active_corner_stack.push_back(it->second); } const CornerIndex corner_a = active_corner_stack.back(); // First corner on the new face is corner "x" from the image above. const CornerIndex corner(3 * face.value()); // Update the opposite corner mapping. SetOppositeCorners(corner_a, corner + 2); SetOppositeCorners(corner_b, corner + 1); // Update vertices. For the vertex at corner "x", use the vertex id from // the corner "p". const VertexIndex vertex_p = corner_table_->Vertex(corner_table_->Previous(corner_a)); corner_table_->MapCornerToVertex(corner, vertex_p); corner_table_->MapCornerToVertex( corner + 1, corner_table_->Vertex(corner_table_->Next(corner_a))); corner_table_->MapCornerToVertex( corner + 2, corner_table_->Vertex(corner_table_->Previous(corner_b))); // Also update the vertex id at corner "n" and all corners that are // connected to it in the CCW direction. CornerIndex corner_n = corner_table_->Next(corner_b); const VertexIndex vertex_n = corner_table_->Vertex(corner_n); traversal_decoder_.MergeVertices(vertex_p, vertex_n); while (corner_n >= 0) { corner_table_->MapCornerToVertex(corner_n, vertex_p); corner_n = corner_table_->SwingLeft(corner_n); } // Make sure the old vertex n is now mapped to an invalid corner (make it // isolated). corner_table_->MakeVertexIsolated(vertex_n); active_corner_stack.back() = corner; } else if (symbol == TOPOLOGY_E) { const CornerIndex corner(3 * face.value()); // Create three new vertices at the corners of the new face. corner_table_->MapCornerToVertex(corner, VertexIndex(num_vertices++)); corner_table_->MapCornerToVertex(corner + 1, VertexIndex(num_vertices++)); corner_table_->MapCornerToVertex(corner + 2, VertexIndex(num_vertices++)); // Add the tip corner to the active stack. active_corner_stack.push_back(corner); check_topology_split = true; } else { // Error. Unknown symbol decoded. return -1; } // Inform the traversal decoder that a new corner has been reached. traversal_decoder_.NewActiveCornerReached(active_corner_stack.back()); if (check_topology_split) { // Check for topology splits happens only for TOPOLOGY_L, TOPOLOGY_R and // TOPOLOGY_E symbols because those are the symbols that correspond to // faces that can be directly connected a TOPOLOGY_S face through the // topology split event. // If a topology split is detected, we need to add a new active edge // onto the active_corner_stack because it will be used later when the // corresponding TOPOLOGY_S event is decoded. // Symbol id used by the encoder (reverse). const int encoder_symbol_id = num_symbols - symbol_id - 1; EdgeFaceName split_edge; int encoder_split_symbol_id; while (IsTopologySplit(encoder_symbol_id, &split_edge, &encoder_split_symbol_id)) { if (encoder_split_symbol_id < 0) return -1; // Wrong split symbol id. // Symbol was part of a topology split. Now we need to determine which // edge should be added to the active edges stack. const CornerIndex act_top_corner = active_corner_stack.back(); // The current symbol has one active edge (stored in act_top_corner) and // two remaining inactive edges that are attached to it. // * // / \ // left_edge / \ right_edge // / \ // *.......* // active_edge CornerIndex new_active_corner; if (split_edge == RIGHT_FACE_EDGE) { new_active_corner = corner_table_->Next(act_top_corner); } else { new_active_corner = corner_table_->Previous(act_top_corner); } // Add the new active edge. // Convert the encoder split symbol id to decoder symbol id. const int decoder_split_symbol_id = num_symbols - encoder_split_symbol_id - 1; topology_split_active_corners[decoder_split_symbol_id] = new_active_corner; } } } if (num_vertices > max_num_vertices) return -1; // Unexpected number of decoded vertices. // Decode start faces and connect them to the faces from the active stack. while (active_corner_stack.size() > 0) { const CornerIndex corner = active_corner_stack.back(); active_corner_stack.pop_back(); const bool interior_face = traversal_decoder_.DecodeStartFaceConfiguration(); if (interior_face) { // The start face is interior, we need to find three corners that are // opposite to it. The first opposite corner "a" is the corner from the // top of the active corner stack and the remaining two corners "b" and // "c" can be obtained by circulating around vertices "n" and "p" in CW // and CCW directions respectively. // // *-------* // / \ / \ // / \ / \ // / \ / \ // *-------p-------* // / \a . . c/ \ // / \ . . / \ // / \ . I . / \ // *-------n.......*------* // \ / \ / \ / // \ / \ / \ / // \ / \b/ \ / // *-------*-------* // // TODO(ostava): The ciruclation below should be replaced by functions // that can be reused elsewhere. CornerIndex corner_b = corner_table_->Previous(corner); while (corner_table_->Opposite(corner_b) >= 0) { corner_b = corner_table_->Previous(corner_table_->Opposite(corner_b)); } CornerIndex corner_c = corner_table_->Next(corner); while (corner_table_->Opposite(corner_c) >= 0) { corner_c = corner_table_->Next(corner_table_->Opposite(corner_c)); } const FaceIndex face(num_faces++); // The first corner of the initial face is the corner opposite to "a". const CornerIndex new_corner(3 * face.value()); SetOppositeCorners(new_corner, corner); SetOppositeCorners(new_corner + 1, corner_b); SetOppositeCorners(new_corner + 2, corner_c); // Map new corners to existing vertices. corner_table_->MapCornerToVertex( new_corner, corner_table_->Vertex(corner_table_->Next(corner_b))); corner_table_->MapCornerToVertex( new_corner + 1, corner_table_->Vertex(corner_table_->Next(corner_c))); corner_table_->MapCornerToVertex( new_corner + 2, corner_table_->Vertex(corner_table_->Next(corner))); // Mark all three vertices as interior. for (int ci = 0; ci < 3; ++ci) { is_vert_hole_[corner_table_->Vertex(new_corner + ci).value()] = false; } init_face_configurations_.push_back(true); init_corners_.push_back(new_corner); } else { // The initial face wasn't interior and the traversal had to start from // an open boundary. In this case no new face is added, but we need to // keep record about the first opposite corner to this boundary. init_face_configurations_.push_back(false); init_corners_.push_back(corner); } } if (num_faces != corner_table_->num_faces()) return -1; // Unexcpected number of decoded faces. vertex_id_map_.resize(num_vertices); return num_vertices; } template int32_t MeshEdgeBreakerDecoderImpl::DecodeHoleAndTopologySplitEvents( DecoderBuffer *decoder_buffer) { // Prepare a new decoder from the provided buffer offset. uint32_t num_topology_splits; if (!decoder_buffer->Decode(&num_topology_splits)) return -1; for (uint32_t i = 0; i < num_topology_splits; ++i) { TopologySplitEventData event_data; if (!decoder_buffer->Decode(&event_data.split_symbol_id)) return -1; if (!decoder_buffer->Decode(&event_data.source_symbol_id)) return -1; uint8_t edge_data; if (!decoder_buffer->Decode(&edge_data)) return -1; event_data.source_edge = edge_data & 1; event_data.split_edge = (edge_data >> 1) & 1; topology_split_data_.push_back(event_data); } uint32_t num_hole_events; if (!decoder_buffer->Decode(&num_hole_events)) return -1; for (uint32_t i = 0; i < num_hole_events; ++i) { HoleEventData event_data; if (!decoder_buffer->Decode(&event_data)) return -1; hole_event_data_.push_back(event_data); } return decoder_buffer->decoded_size(); } template bool MeshEdgeBreakerDecoderImpl< TraversalDecoder>::DecodeAttributeConnectivitiesOnFace(CornerIndex corner) { // Three corners of the face. const CornerIndex corners[3] = {corner, corner_table_->Next(corner), corner_table_->Previous(corner)}; for (int c = 0; c < 3; ++c) { const CornerIndex opp_corner = corner_table_->Opposite(corners[c]); if (opp_corner < 0) { // Don't decode attribute seams on boundary edges (every boundary edge // is automatically an attribute seam). for (uint32_t i = 0; i < attribute_data_.size(); ++i) { attribute_data_[i].attribute_seam_corners.push_back(corners[c].value()); } continue; } for (uint32_t i = 0; i < attribute_data_.size(); ++i) { const bool is_seam = traversal_decoder_.DecodeAttributeSeam(i); if (is_seam) attribute_data_[i].attribute_seam_corners.push_back(corners[c].value()); } } return true; } template bool MeshEdgeBreakerDecoderImpl::AssignPointsToCorners() { // Map between the existing and deduplicated point ids. // Note that at this point we have one point id for each corner of the // mesh so there is corner_table_->num_corners() point ids. decoder_->mesh()->SetNumFaces(corner_table_->num_faces()); if (attribute_data_.size() == 0) { // We have position only. In this case we can simplify the deduplication // because the only thing we need to do is to remove isolated vertices that // were introduced during the decoding. int32_t num_points = 0; std::vector vertex_to_point_map(corner_table_->num_vertices(), -1); // Add faces. for (FaceIndex f(0); f < decoder_->mesh()->num_faces(); ++f) { Mesh::Face face; for (int c = 0; c < 3; ++c) { // Remap old points to the new ones. const int32_t vert_id = corner_table_->Vertex(CornerIndex(3 * f.value() + c)).value(); int32_t &point_id = vertex_to_point_map[vert_id]; if (point_id == -1) point_id = num_points++; face[c] = point_id; } decoder_->mesh()->SetFace(f, face); } decoder_->point_cloud()->set_num_points(num_points); return true; } // Else we need to deduplicate multiple attributes. // Map between point id and an associated corner id. Only one corner for // each point is stored. The corners are used to sample the attribute values // in the last stage of the deduplication. std::vector point_to_corner_map; // Map between every corner and their new point ids. std::vector corner_to_point_map(corner_table_->num_corners()); for (int v = 0; v < corner_table_->num_vertices(); ++v) { CornerIndex c = corner_table_->LeftMostCorner(VertexIndex(v)); if (c < 0) continue; // Isolated vertex. CornerIndex deduplication_first_corner = c; if (is_vert_hole_[v]) { // If the vertex is on a boundary, start deduplication from the left most // corner that is guaranteed to lie on the boundary. deduplication_first_corner = c; } else { // If we are not on the boundary we need to find the first seam (of any // attribute). for (uint32_t i = 0; i < attribute_data_.size(); ++i) { if (!attribute_data_[i].connectivity_data.IsCornerOnSeam(c)) continue; // No seam for this attribute, ignore it. // Else there needs to be at least one seam edge. // At this point, we use identity mapping between corners and point ids. const VertexIndex vert_id = attribute_data_[i].connectivity_data.Vertex(c); CornerIndex act_c = corner_table_->SwingRight(c); bool seam_found = false; while (act_c != c) { if (attribute_data_[i].connectivity_data.Vertex(act_c) != vert_id) { // Attribute seam found. Stop. deduplication_first_corner = act_c; seam_found = true; break; } act_c = corner_table_->SwingRight(act_c); } if (seam_found) break; // No reason to process other attributes if we found a seam. } } // Do a deduplication pass over the corners on the processed vertex. // At this point each corner corresponds to one point id and our goal is to // merge similar points into a single point id. // We do one one pass in a clocwise direction over the corners and we add // a new point id whenever one of the attributes change. c = deduplication_first_corner; // Create a new point. corner_to_point_map[c.value()] = point_to_corner_map.size(); point_to_corner_map.push_back(c.value()); // Traverse in CW direction. CornerIndex prev_c = c; c = corner_table_->SwingRight(c); while (c >= 0 && c != deduplication_first_corner) { bool attribute_seam = false; for (uint32_t i = 0; i < attribute_data_.size(); ++i) { if (attribute_data_[i].connectivity_data.Vertex(c) != attribute_data_[i].connectivity_data.Vertex(prev_c)) { // Attribute index changed from the previous corner. We need to add a // new point here. attribute_seam = true; break; } } if (attribute_seam) { corner_to_point_map[c.value()] = point_to_corner_map.size(); point_to_corner_map.push_back(c.value()); } else { corner_to_point_map[c.value()] = corner_to_point_map[prev_c.value()]; } prev_c = c; c = corner_table_->SwingRight(c); } } // Add faces. for (FaceIndex f(0); f < decoder_->mesh()->num_faces(); ++f) { Mesh::Face face; for (int c = 0; c < 3; ++c) { // Remap old points to the new ones. face[c] = corner_to_point_map[3 * f.value() + c]; } decoder_->mesh()->SetFace(f, face); } decoder_->point_cloud()->set_num_points(point_to_corner_map.size()); return true; } template class MeshEdgeBreakerDecoderImpl; template class MeshEdgeBreakerDecoderImpl< MeshEdgeBreakerTraversalPredictiveDecoder>; } // namespace draco