draco/compression/mesh/mesh_edgebreaker_encoder_impl.cc
2017-01-12 16:50:49 -08:00

739 lines
30 KiB
C++

// 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_encoder_impl.h"
#include <algorithm>
#include "compression/attributes/mesh_attribute_indices_encoding_observer.h"
#include "compression/attributes/mesh_traversal_sequencer.h"
#include "compression/attributes/sequential_attribute_encoders_controller.h"
#include "compression/mesh/mesh_edgebreaker_encoder.h"
#include "compression/mesh/mesh_edgebreaker_traversal_predictive_encoder.h"
#include "mesh/corner_table_iterators.h"
#include "mesh/corner_table_traversal_processor.h"
#include "mesh/edgebreaker_traverser.h"
#include "mesh/mesh_misc_functions.h"
namespace draco {
typedef CornerIndex CornerIndex;
typedef FaceIndex FaceIndex;
typedef VertexIndex VertexIndex;
template <class TraversalEncoder>
MeshEdgeBreakerEncoderImpl<TraversalEncoder>::MeshEdgeBreakerEncoderImpl()
: encoder_(nullptr),
mesh_(nullptr),
last_encoded_symbol_id_(-1),
num_split_symbols_(0) {}
template <class TraversalEncoder>
bool MeshEdgeBreakerEncoderImpl<TraversalEncoder>::Init(
MeshEdgeBreakerEncoder *encoder) {
encoder_ = encoder;
mesh_ = encoder->mesh();
attribute_encoder_to_data_id_map_.clear();
return true;
}
template <class TraversalEncoder>
const MeshAttributeCornerTable *
MeshEdgeBreakerEncoderImpl<TraversalEncoder>::GetAttributeCornerTable(
int att_id) const {
for (uint32_t i = 0; i < attribute_data_.size(); ++i) {
if (attribute_data_[i].attribute_index == att_id) {
if (attribute_data_[i].is_connectivity_used)
return &attribute_data_[i].connectivity_data;
return nullptr;
}
}
return nullptr;
}
template <class TraversalEncoder>
const MeshAttributeIndicesEncodingData *
MeshEdgeBreakerEncoderImpl<TraversalEncoder>::GetAttributeEncodingData(
int att_id) const {
for (uint32_t i = 0; i < attribute_data_.size(); ++i) {
if (attribute_data_[i].attribute_index == att_id)
return &attribute_data_[i].encoding_data;
}
return &pos_encoding_data_;
}
template <class TraversalEncoder>
bool MeshEdgeBreakerEncoderImpl<TraversalEncoder>::GenerateAttributesEncoder(
int32_t att_id) {
// For now, create one encoder for each attribute. Ideally we can share
// the same encoder for attributes with the same connectivity (this is
// especially true for per-vertex attributes).
const int32_t element_type =
GetEncoder()->mesh()->GetAttributeElementType(att_id);
const PointAttribute *const att =
GetEncoder()->point_cloud()->attribute(att_id);
int32_t att_data_id = -1;
for (uint32_t i = 0; i < attribute_data_.size(); ++i) {
if (attribute_data_[i].attribute_index == att_id) {
att_data_id = i;
break;
}
}
std::unique_ptr<PointsSequencer> sequencer;
if (att->attribute_type() == GeometryAttribute::POSITION ||
element_type == MESH_VERTEX_ATTRIBUTE ||
(element_type == MESH_CORNER_ATTRIBUTE &&
attribute_data_[att_data_id].connectivity_data.no_interior_seams())) {
// Per-vertex attribute reached, use the basic corner table to traverse the
// mesh.
typedef CornerTableTraversalProcessor<CornerTable> AttProcessor;
typedef MeshAttributeIndicesEncodingObserver<CornerTable> AttObserver;
// Traverser that is used to generate the encoding order of each attribute.
typedef EdgeBreakerTraverser<AttProcessor, AttObserver> AttTraverser;
MeshAttributeIndicesEncodingData *encoding_data;
if (att->attribute_type() == GeometryAttribute::POSITION) {
encoding_data = &pos_encoding_data_;
} else {
encoding_data = &attribute_data_[att_data_id].encoding_data;
attribute_data_[att_data_id].is_connectivity_used = false;
}
std::unique_ptr<MeshTraversalSequencer<AttTraverser>> traversal_sequencer(
new MeshTraversalSequencer<AttTraverser>(mesh_, encoding_data));
AttProcessor att_processor;
AttObserver att_observer(corner_table_.get(), mesh_,
traversal_sequencer.get(), encoding_data);
AttTraverser att_traverser;
att_processor.ResetProcessor(corner_table_.get());
att_traverser.Init(att_processor, att_observer);
traversal_sequencer->SetCornerOrder(processed_connectivity_corners_);
traversal_sequencer->SetTraverser(att_traverser);
sequencer = std::move(traversal_sequencer);
} else {
// Else use a general per-corner encoder.
typedef CornerTableTraversalProcessor<MeshAttributeCornerTable>
AttProcessor;
typedef MeshAttributeIndicesEncodingObserver<MeshAttributeCornerTable>
AttObserver;
// Traverser that is used to generate the encoding order of each attribute.
typedef EdgeBreakerTraverser<AttProcessor, AttObserver> AttTraverser;
std::unique_ptr<MeshTraversalSequencer<AttTraverser>> traversal_sequencer(
new MeshTraversalSequencer<AttTraverser>(
mesh_, &attribute_data_[att_data_id].encoding_data));
AttProcessor att_processor;
AttObserver att_observer(&attribute_data_[att_data_id].connectivity_data,
mesh_, traversal_sequencer.get(),
&attribute_data_[att_data_id].encoding_data);
AttTraverser att_traverser;
att_processor.ResetProcessor(
&attribute_data_[att_data_id].connectivity_data);
att_traverser.Init(att_processor, att_observer);
traversal_sequencer->SetCornerOrder(processed_connectivity_corners_);
traversal_sequencer->SetTraverser(att_traverser);
sequencer = std::move(traversal_sequencer);
}
if (!sequencer)
return false;
std::unique_ptr<SequentialAttributeEncodersController> att_controller(
new SequentialAttributeEncodersController(std::move(sequencer), att_id));
// Update the mapping between the encoder id and the attribute data id.
// This will be used by the decoder to select the approperiate attribute
// decoder and the correct connectivity.
attribute_encoder_to_data_id_map_.push_back(att_data_id);
GetEncoder()->AddAttributesEncoder(std::move(att_controller));
return true;
}
template <class TraversalEncoder>
bool MeshEdgeBreakerEncoderImpl<TraversalEncoder>::
EncodeAttributesEncoderIdentifier(int32_t att_encoder_id) {
const int8_t att_data_id = attribute_encoder_to_data_id_map_[att_encoder_id];
encoder_->buffer()->Encode(att_data_id);
// Also encode the type of the encoder that we used.
int32_t element_type = MESH_VERTEX_ATTRIBUTE;
if (att_data_id >= 0) {
const int32_t att_id = attribute_data_[att_data_id].attribute_index;
element_type = GetEncoder()->mesh()->GetAttributeElementType(att_id);
}
if (element_type == MESH_VERTEX_ATTRIBUTE ||
(element_type == MESH_CORNER_ATTRIBUTE &&
attribute_data_[att_data_id].connectivity_data.no_interior_seams())) {
// Per-vertex encoder.
encoder_->buffer()->Encode(static_cast<uint8_t>(MESH_VERTEX_ATTRIBUTE));
} else {
// Per-corner encoder.
encoder_->buffer()->Encode(static_cast<uint8_t>(MESH_CORNER_ATTRIBUTE));
}
return true;
}
template <class TraversalEncoder>
bool MeshEdgeBreakerEncoderImpl<TraversalEncoder>::EncodeConnectivity() {
// To encode the mesh, we need face connectivity data stored in a corner
// table. To compute the connectivity we must use indices associated with
// POSITION attribute, because they define which edges can be connected
// together.
corner_table_ = CreateCornerTable(mesh_);
if (corner_table_ == nullptr) {
// Failed to construct the corner table.
return false;
}
traversal_encoder_.Init(this);
// Encode the number of new vertices that were added because of non-manifold
// edges/vertices in the input mesh. We will encode the actual mapping between
// new and original vertices later because their order (and ids) may change
// during the encoding.
const uint32_t num_new_vertices = corner_table_->NumNewVertices();
encoder_->buffer()->Encode(num_new_vertices);
// Also encode the total number of vertices that is going to be encoded.
// This can be different from the mesh_->num_points() + num_new_vertices,
// because some of the vertices of the input mesh can be ignored (e.g.
// vertices on degenerated faces or isolated vertices not attached to any
// face).
const uint32_t num_vertices_to_be_encoded =
corner_table_->num_vertices() - corner_table_->NumIsolatedVertices();
encoder_->buffer()->Encode(num_vertices_to_be_encoded);
const uint32_t num_faces =
corner_table_->num_faces() - corner_table_->NumDegeneratedFaces();
encoder_->buffer()->Encode(num_faces);
// Reset encoder data that may have been initialized in previous runs.
visited_faces_.assign(mesh_->num_faces(), false);
pos_encoding_data_.vertex_to_encoded_attribute_value_index_map.assign(
corner_table_->num_vertices(), -1);
pos_encoding_data_.encoded_attribute_value_index_to_corner_map.clear();
pos_encoding_data_.encoded_attribute_value_index_to_corner_map.reserve(
corner_table_->num_faces() * 3);
visited_vertex_ids_.assign(corner_table_->num_vertices(), false);
vertex_traversal_length_.clear();
last_encoded_symbol_id_ = -1;
num_split_symbols_ = 0;
topology_split_event_data_.clear();
face_to_split_symbol_map_.clear();
visited_holes_.clear();
vertex_hole_id_.assign(corner_table_->num_vertices(), -1);
hole_event_data_.clear();
processed_connectivity_corners_.clear();
processed_connectivity_corners_.reserve(corner_table_->num_faces());
pos_encoding_data_.num_values = 0;
if (!FindHoles())
return false;
if (!InitAttributeData())
return false;
const int8_t num_attribute_data = attribute_data_.size();
encoder_->buffer()->Encode(num_attribute_data);
const int num_corners = corner_table_->num_corners();
traversal_encoder_.Start();
std::vector<CornerIndex> init_face_connectivity_corners;
// Traverse the surface starting from each unvisited corner.
for (int c_id = 0; c_id < num_corners; ++c_id) {
CornerIndex corner_index(c_id);
const FaceIndex face_id = corner_table_->Face(corner_index);
if (visited_faces_[face_id.value()])
continue; // Face has been already processed.
if (corner_table_->IsDegenerated(face_id))
continue; // Ignore degenerated faces.
CornerIndex start_corner;
const bool interior_config =
FindInitFaceConfiguration(face_id, &start_corner);
traversal_encoder_.EncodeStartFaceConfiguration(interior_config);
if (interior_config) {
// Select the correct vertex on the face as the root.
corner_index = start_corner;
const VertexIndex vert_id = corner_table_->Vertex(corner_index);
// Mark all vertices of a given face as visited.
const VertexIndex next_vert_id =
corner_table_->Vertex(corner_table_->Next(corner_index));
const VertexIndex prev_vert_id =
corner_table_->Vertex(corner_table_->Previous(corner_index));
visited_vertex_ids_[vert_id.value()] = true;
visited_vertex_ids_[next_vert_id.value()] = true;
visited_vertex_ids_[prev_vert_id.value()] = true;
// New traversal started. Initiate it's length with the first vertex.
vertex_traversal_length_.push_back(1);
// Mark the face as visited.
visited_faces_[face_id.value()] = true;
// Start compressing from the opposite face of the "next" corner. This way
// the first encoded corner corresponds to the tip corner of the regular
// edgebreaker traversal (essentially the initial face can be then viewed
// as a TOPOLOGY_C face).
init_face_connectivity_corners.push_back(
corner_table_->Next(corner_index));
const CornerIndex opp_id =
corner_table_->Opposite(corner_table_->Next(corner_index));
const FaceIndex opp_face_id = corner_table_->Face(opp_id);
if (opp_face_id != kInvalidFaceIndex &&
!visited_faces_[opp_face_id.value()]) {
if (!EncodeConnectivityFromCorner(opp_id))
return false;
}
} else {
// Bounary configuration. We start on a boundary rather than on a face.
// First encode the hole that's opposite to the start_corner.
EncodeHole(corner_table_->Next(start_corner), true);
// Start processing the face opposite to the boundary edge (the face
// containing the start_corner).
if (!EncodeConnectivityFromCorner(start_corner))
return false;
}
}
// Reverse the order of connectivity corners to match the order in which
// they are going to be decoded.
std::reverse(processed_connectivity_corners_.begin(),
processed_connectivity_corners_.end());
// Append the init face connectivity corners (which are processed in order by
// the decoder after the regular corners.
processed_connectivity_corners_.insert(processed_connectivity_corners_.end(),
init_face_connectivity_corners.begin(),
init_face_connectivity_corners.end());
// Emcode connectivity for all non-position attributes.
if (attribute_data_.size() > 0) {
// Use the same order of corner that will be used by the decoder.
for (CornerIndex ci : processed_connectivity_corners_) {
EncodeAttributeConnectivitiesOnFace(ci);
}
}
traversal_encoder_.Done();
// Encode the number of symbols.
encoder_->buffer()->Encode(
static_cast<uint32_t>(traversal_encoder_.NumEncodedSymbols()));
// Encode the number of split symbols.
encoder_->buffer()->Encode(static_cast<uint32_t>(num_split_symbols_));
// Append the traversal buffer.
encoder_->buffer()->Encode(
static_cast<uint32_t>(traversal_encoder_.buffer().size()));
encoder_->buffer()->Encode(traversal_encoder_.buffer().data(),
traversal_encoder_.buffer().size());
// Encode topology split events.
uint32_t num_events = topology_split_event_data_.size();
encoder_->buffer()->Encode(num_events);
for (uint32_t i = 0; i < num_events; ++i) {
// TODO(ostava): We can do a better encoding of the event data but it's not
// really needed for now.
const TopologySplitEventData &event_data = topology_split_event_data_[i];
encoder_->buffer()->Encode(event_data.split_symbol_id);
encoder_->buffer()->Encode(event_data.source_symbol_id);
const uint8_t edge_data =
(event_data.source_edge | (event_data.split_edge << 1));
encoder_->buffer()->Encode(edge_data);
}
// Encode hole events data.
num_events = hole_event_data_.size();
encoder_->buffer()->Encode(num_events);
for (uint32_t i = 0; i < num_events; ++i) {
// TODO(ostava): We can do a better encoding of the event data but it's not
// really needed for now.
// This should be also made platform independent.
encoder_->buffer()->Encode((hole_event_data_[i]));
}
return true;
}
template <class TraversalEncoder>
bool MeshEdgeBreakerEncoderImpl<TraversalEncoder>::FindInitFaceConfiguration(
FaceIndex face_id, CornerIndex *out_corner) const {
CornerIndex corner_index = CornerIndex(3 * face_id.value());
for (int i = 0; i < 3; ++i) {
if (corner_table_->Opposite(corner_index) == kInvalidCornerIndex) {
// If there is a boundary edge, the configuration is exterior and return
// the CornerIndex opposite to the first boundary edge.
*out_corner = corner_index;
return false;
}
if (vertex_hole_id_[corner_table_->Vertex(corner_index).value()] != -1) {
// Boundary vertex found. Find the first boundary edge attached to the
// point and return the corner opposite to it.
CornerIndex right_corner = corner_index;
while (right_corner >= 0) {
corner_index = right_corner;
right_corner = corner_table_->SwingRight(right_corner);
}
// |corner_index| now lies on a boundary edge and its previous corner is
// guaranteed to be the opposite corner of the boundary edge.
*out_corner = corner_table_->Previous(corner_index);
return false;
}
corner_index = corner_table_->Next(corner_index);
}
// Else we have an interior configuration. Return the first corner id.
*out_corner = corner_index;
return true;
}
template <class TraversalEncoder>
bool MeshEdgeBreakerEncoderImpl<TraversalEncoder>::EncodeConnectivityFromCorner(
CornerIndex corner_id) {
corner_traversal_stack_.clear();
corner_traversal_stack_.push_back(corner_id);
const int num_faces = mesh_->num_faces();
while (!corner_traversal_stack_.empty()) {
// Currently processed corner.
corner_id = corner_traversal_stack_.back();
// Make sure the face hasn't been visited yet.
if (corner_id < 0 ||
visited_faces_[corner_table_->Face(corner_id).value()]) {
// This face has been already traversed.
corner_traversal_stack_.pop_back();
continue;
}
int num_visited_faces = 0;
while (num_visited_faces < num_faces) {
// Mark the current face as visited.
++num_visited_faces;
++last_encoded_symbol_id_;
const FaceIndex face_id = corner_table_->Face(corner_id);
visited_faces_[face_id.value()] = true;
processed_connectivity_corners_.push_back(corner_id);
traversal_encoder_.NewCornerReached(corner_id);
const VertexIndex vert_id = corner_table_->Vertex(corner_id);
const bool on_boundary = (vertex_hole_id_[vert_id.value()] != -1);
if (!IsVertexVisited(vert_id)) {
// A new unvisited vertex has been reached. We need to store its
// position difference using next,prev, and opposite vertices.
visited_vertex_ids_[vert_id.value()] = true;
if (!on_boundary) {
// If the vertex is on boundary it must correspond to an unvisited
// hole and it will be encoded with TOPOLOGY_S symbol later).
traversal_encoder_.EncodeSymbol(TOPOLOGY_C);
// Move to the right triangle.
corner_id = GetRightCorner(corner_id);
continue;
}
}
// The current vertex has been already visited or it was on a boundary.
// We need to determine whether we can visit any of it's neighboring
// faces.
const CornerIndex right_corner_id = GetRightCorner(corner_id);
const CornerIndex left_corner_id = GetLeftCorner(corner_id);
const FaceIndex right_face_id = corner_table_->Face(right_corner_id);
const FaceIndex left_face_id = corner_table_->Face(left_corner_id);
if (IsRightFaceVisited(corner_id)) {
// Right face has been already visited.
// Check whether there is a topology split event.
if (right_face_id != kInvalidFaceIndex)
CheckAndStoreTopologySplitEvent(last_encoded_symbol_id_,
face_id.value(), RIGHT_FACE_EDGE,
right_face_id.value());
if (IsLeftFaceVisited(corner_id)) {
// Both neighboring faces are visited. End reached.
// Check whether there is a topology split event on the left face.
if (left_face_id != kInvalidFaceIndex)
CheckAndStoreTopologySplitEvent(last_encoded_symbol_id_,
face_id.value(), LEFT_FACE_EDGE,
left_face_id.value());
traversal_encoder_.EncodeSymbol(TOPOLOGY_E);
corner_traversal_stack_.pop_back();
break; // Break from the while (num_visited_faces < num_faces) loop.
} else {
traversal_encoder_.EncodeSymbol(TOPOLOGY_R);
// Go to the left face.
corner_id = left_corner_id;
}
} else {
// Right face was not visited.
if (IsLeftFaceVisited(corner_id)) {
// Check whether there is a topology split event on the left face.
if (left_face_id != kInvalidFaceIndex)
CheckAndStoreTopologySplitEvent(last_encoded_symbol_id_,
face_id.value(), LEFT_FACE_EDGE,
left_face_id.value());
traversal_encoder_.EncodeSymbol(TOPOLOGY_L);
// Left face visited, go to the right one.
corner_id = right_corner_id;
} else {
traversal_encoder_.EncodeSymbol(TOPOLOGY_S);
++num_split_symbols_;
// Both neighboring faces are unvisited, we need to visit both of
// them.
if (on_boundary) {
// The tip vertex is on a hole boundary. If the hole hasn't been
// visited yet we need to encode it.
const int hole_id = vertex_hole_id_[vert_id.value()];
if (!visited_holes_[hole_id]) {
EncodeHole(corner_id, false);
hole_event_data_.push_back(
HoleEventData(last_encoded_symbol_id_));
}
}
face_to_split_symbol_map_[face_id.value()] = last_encoded_symbol_id_;
// Split the traversal.
// First make the top of the current corner stack point to the left
// face (this one will be processed second).
corner_traversal_stack_.back() = left_corner_id;
// Add a new corner to the top of the stack (right face needs to
// be traversed first).
corner_traversal_stack_.push_back(right_corner_id);
// Break from the while (num_visited_faces < num_faces) loop.
break;
}
}
}
}
return true; // All corners have been processed.
}
template <class TraversalEncoder>
int MeshEdgeBreakerEncoderImpl<TraversalEncoder>::EncodeHole(
CornerIndex start_corner_id, bool encode_first_vertex) {
// We know that the start corner lies on a hole but we first need to find the
// boundary edge going from that vertex. It is the first edge in CW
// direction.
CornerIndex corner_id = start_corner_id;
corner_id = corner_table_->Previous(corner_id);
while (corner_table_->Opposite(corner_id) != kInvalidCornerIndex) {
corner_id = corner_table_->Opposite(corner_id);
corner_id = corner_table_->Next(corner_id);
}
const VertexIndex start_vertex_id = corner_table_->Vertex(start_corner_id);
int num_encoded_hole_verts = 0;
if (encode_first_vertex) {
visited_vertex_ids_[start_vertex_id.value()] = true;
++num_encoded_hole_verts;
}
// corner_id is now opposite to the boundary edge.
// Mark the hole as visited.
visited_holes_[vertex_hole_id_[start_vertex_id.value()]] = true;
// Get the start vertex of the edge and use it as a reference.
VertexIndex start_vert_id =
corner_table_->Vertex(corner_table_->Next(corner_id));
// Get the end vertex of the edge.
VertexIndex act_vertex_id =
corner_table_->Vertex(corner_table_->Previous(corner_id));
while (act_vertex_id != start_vertex_id) {
// Encode the end vertex of the boundary edge.
start_vert_id = act_vertex_id;
// Mark the vertex as visited.
visited_vertex_ids_[act_vertex_id.value()] = true;
++num_encoded_hole_verts;
corner_id = corner_table_->Next(corner_id);
// Look for the next attached open boundary edge.
while (corner_table_->Opposite(corner_id) != kInvalidCornerIndex) {
corner_id = corner_table_->Opposite(corner_id);
corner_id = corner_table_->Next(corner_id);
}
act_vertex_id = corner_table_->Vertex(corner_table_->Previous(corner_id));
}
return num_encoded_hole_verts;
}
template <class TraversalEncoder>
CornerIndex MeshEdgeBreakerEncoderImpl<TraversalEncoder>::GetRightCorner(
CornerIndex corner_id) const {
const CornerIndex next_corner_id = corner_table_->Next(corner_id);
return corner_table_->Opposite(next_corner_id);
}
template <class TraversalEncoder>
CornerIndex MeshEdgeBreakerEncoderImpl<TraversalEncoder>::GetLeftCorner(
CornerIndex corner_id) const {
const CornerIndex prev_corner_id = corner_table_->Previous(corner_id);
return corner_table_->Opposite(prev_corner_id);
}
template <class TraversalEncoder>
bool MeshEdgeBreakerEncoderImpl<TraversalEncoder>::IsRightFaceVisited(
CornerIndex corner_id) const {
const CornerIndex next_corner_id = corner_table_->Next(corner_id);
const CornerIndex opp_corner_id = corner_table_->Opposite(next_corner_id);
if (opp_corner_id != kInvalidCornerIndex)
return visited_faces_[corner_table_->Face(opp_corner_id).value()];
// Else we are on a boundary.
return true;
}
template <class TraversalEncoder>
bool MeshEdgeBreakerEncoderImpl<TraversalEncoder>::IsLeftFaceVisited(
CornerIndex corner_id) const {
const CornerIndex prev_corner_id = corner_table_->Previous(corner_id);
const CornerIndex opp_corner_id = corner_table_->Opposite(prev_corner_id);
if (opp_corner_id != kInvalidCornerIndex)
return visited_faces_[corner_table_->Face(opp_corner_id).value()];
// Else we are on a boundary.
return true;
}
template <class TraversalEncoder>
bool MeshEdgeBreakerEncoderImpl<TraversalEncoder>::FindHoles() {
// TODO(ostava): Add more error checking for invalid geometry data.
const int num_corners = corner_table_->num_corners();
// Go over all corners and detect non-visited open boundaries
for (CornerIndex i(0); i < num_corners; ++i) {
if (corner_table_->IsDegenerated(corner_table_->Face(i)))
continue; // Don't process corners assigned to degenerated faces.
if (corner_table_->Opposite(i) == kInvalidCornerIndex) {
// No opposite corner means no opposite face, so the opposite edge
// of the corner is an open boundary.
// Check whether we have already traversed the boundary.
VertexIndex boundary_vert_id =
corner_table_->Vertex(corner_table_->Next(i));
if (vertex_hole_id_[boundary_vert_id.value()] != -1) {
// The start vertex of the boundary edge is already assigned to an
// open boundary. No need to traverse it again.
continue;
}
// Else we found a new open boundary and we are going to traverse along it
// and mark all visited vertices.
const int boundary_id = visited_holes_.size();
visited_holes_.push_back(false);
CornerIndex corner_id = i;
while (vertex_hole_id_[boundary_vert_id.value()] == -1) {
// Mark the first vertex on the open boundary.
vertex_hole_id_[boundary_vert_id.value()] = boundary_id;
corner_id = corner_table_->Next(corner_id);
// Look for the next attached open boundary edge.
while (corner_table_->Opposite(corner_id) != kInvalidCornerIndex) {
corner_id = corner_table_->Opposite(corner_id);
corner_id = corner_table_->Next(corner_id);
}
// Id of the next vertex in the vertex on the hole.
boundary_vert_id =
corner_table_->Vertex(corner_table_->Next(corner_id));
}
}
}
return true;
}
template <class TraversalEncoder>
int MeshEdgeBreakerEncoderImpl<TraversalEncoder>::GetSplitSymbolIdOnFace(
int face_id) const {
auto it = face_to_split_symbol_map_.find(face_id);
if (it == face_to_split_symbol_map_.end())
return -1;
return it->second;
}
template <class TraversalEncoder>
void MeshEdgeBreakerEncoderImpl<
TraversalEncoder>::CheckAndStoreTopologySplitEvent(int src_symbol_id,
int /* src_face_id */,
EdgeFaceName src_edge,
int neighbor_face_id) {
const int symbol_id = GetSplitSymbolIdOnFace(neighbor_face_id);
if (symbol_id == -1)
return; // Not a split symbol, no topology split event could happen.
TopologySplitEventData event_data;
event_data.split_symbol_id = symbol_id;
// It's always the left edge for true split symbols (as the right edge is
// traversed first).
event_data.split_edge = LEFT_FACE_EDGE;
event_data.source_symbol_id = src_symbol_id;
event_data.source_edge = src_edge;
topology_split_event_data_.push_back(event_data);
}
template <class TraversalEncoder>
bool MeshEdgeBreakerEncoderImpl<TraversalEncoder>::InitAttributeData() {
const int num_attributes = mesh_->num_attributes();
// Ignore the position attribute. It's decoded separately.
attribute_data_.resize(num_attributes - 1);
if (num_attributes == 1)
return true;
int data_index = 0;
for (int i = 0; i < num_attributes; ++i) {
const int32_t att_index = i;
if (mesh_->attribute(att_index)->attribute_type() ==
GeometryAttribute::POSITION)
continue;
const PointAttribute *const att = mesh_->attribute(att_index);
attribute_data_[data_index].attribute_index = att_index;
attribute_data_[data_index]
.encoding_data.encoded_attribute_value_index_to_corner_map.clear();
attribute_data_[data_index]
.encoding_data.encoded_attribute_value_index_to_corner_map.reserve(
corner_table_->num_corners());
attribute_data_[data_index]
.encoding_data.vertex_to_encoded_attribute_value_index_map.assign(
corner_table_->num_corners(), -1);
attribute_data_[data_index].encoding_data.num_values = 0;
attribute_data_[data_index].connectivity_data.InitFromAttribute(
mesh_, corner_table_.get(), att);
++data_index;
}
return true;
}
// TODO(ostava): Note that if the input mesh used the same attribute index on
// multiple different vertices, such attribute will be duplicated using the
// encoding below. Eventually, we may consider either using a different encoding
// scheme for such cases, or at least deduplicating the attributes in the
// decoder.
template <class TraversalEncoder>
bool MeshEdgeBreakerEncoderImpl<
TraversalEncoder>::EncodeAttributeConnectivitiesOnFace(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)
continue; // Don't encode attribute seams on boundary edges.
for (uint32_t i = 0; i < attribute_data_.size(); ++i) {
if (attribute_data_[i].connectivity_data.IsCornerOppositeToSeamEdge(
corners[c])) {
traversal_encoder_.EncodeAttributeSeam(i, true);
} else {
traversal_encoder_.EncodeAttributeSeam(i, false);
}
}
}
return true;
}
template class MeshEdgeBreakerEncoderImpl<MeshEdgeBreakerTraversalEncoder>;
template class MeshEdgeBreakerEncoderImpl<
MeshEdgeBreakerTraversalPredictiveEncoder>;
} // namespace draco