mirror of
https://git.mirrors.martin98.com/https://github.com/google/draco
synced 2025-07-28 05:22:03 +08:00
241 lines
9.3 KiB
C++
241 lines
9.3 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.
|
|
//
|
|
#ifndef DRACO_COMPRESSION_MESH_MESH_EDGEBREAKER_TRAVERSAL_VALENCE_ENCODER_H_
|
|
#define DRACO_COMPRESSION_MESH_MESH_EDGEBREAKER_TRAVERSAL_VALENCE_ENCODER_H_
|
|
|
|
#include "compression/mesh/mesh_edgebreaker_traversal_encoder.h"
|
|
#include "core/symbol_encoding.h"
|
|
#include "core/varint_encoding.h"
|
|
|
|
namespace draco {
|
|
|
|
// Predictive encoder for the Edgebreaker symbols based on valences of the
|
|
// previously encoded vertices, following the method described in: Szymczak'02,
|
|
// "Optimized Edgebreaker Encoding for Large and Regular Triangle Meshes". Each
|
|
// valence is used to specify a different entropy context for encoding of the
|
|
// symbols.
|
|
// Encoder can operate in various predefined modes that can be used to select
|
|
// the way in which the entropy contexts are computed (e.g. using different
|
|
// clamping for valences, or even using different inputs to compute the
|
|
// contexts), see EdgeBreakerValenceCodingMode in mesh_edgebreaker_shared.h for
|
|
// a list of supported modes.
|
|
class MeshEdgeBreakerTraversalValenceEncoder
|
|
: public MeshEdgeBreakerTraversalEncoder {
|
|
public:
|
|
MeshEdgeBreakerTraversalValenceEncoder()
|
|
: corner_table_(nullptr),
|
|
prev_symbol_(-1),
|
|
num_split_symbols_(0),
|
|
last_corner_(kInvalidCornerIndex),
|
|
num_symbols_(0),
|
|
min_valence_(2),
|
|
max_valence_(7),
|
|
mode_(EDGEBREAKER_VALENCE_MODE_2_7) {}
|
|
|
|
bool Init(MeshEdgeBreakerEncoderImplInterface *encoder) {
|
|
if (!MeshEdgeBreakerTraversalEncoder::Init(encoder))
|
|
return false;
|
|
if (mode_ == EDGEBREAKER_VALENCE_MODE_2_7) {
|
|
min_valence_ = 2;
|
|
max_valence_ = 7;
|
|
} else {
|
|
return false; // Unsupported mode.
|
|
}
|
|
corner_table_ = encoder->GetCornerTable();
|
|
|
|
// Initialize valences of all vertices.
|
|
vertex_valences_.resize(corner_table_->num_vertices());
|
|
for (VertexIndex i(0); i < vertex_valences_.size(); ++i) {
|
|
vertex_valences_[i] = corner_table_->Valence(VertexIndex(i));
|
|
}
|
|
|
|
// Replicate the corner to vertex map from the corner table. We need to do
|
|
// this because the map may get updated during encoding because we add new
|
|
// vertices when we encounter split symbols.
|
|
corner_to_vertex_map_.resize(corner_table_->num_corners());
|
|
for (CornerIndex i(0); i < corner_table_->num_corners(); ++i) {
|
|
corner_to_vertex_map_[i] = corner_table_->Vertex(i);
|
|
}
|
|
const int32_t num_unique_valences = max_valence_ - min_valence_ + 1;
|
|
|
|
context_symbols_.resize(num_unique_valences);
|
|
return true;
|
|
}
|
|
|
|
inline void NewCornerReached(CornerIndex corner) { last_corner_ = corner; }
|
|
|
|
inline void EncodeSymbol(EdgeBreakerTopologyBitPattern symbol) {
|
|
++num_symbols_;
|
|
// Update valences on the mesh and compute the context that is going to be
|
|
// used to encode the processed symbol.
|
|
// Note that the valences are computed for the so far unencoded part of the
|
|
// mesh (i.e. the decoding is reverse). Adding a new symbol either reduces
|
|
// valences on the vertices or leaves the valence unchanged.
|
|
|
|
const CornerIndex next = corner_table_->Next(last_corner_);
|
|
const CornerIndex prev = corner_table_->Previous(last_corner_);
|
|
|
|
// Get valence on the tip corner of the active edge (outgoing edge that is
|
|
// going to be used in reverse decoding of the connectivity to predict the
|
|
// next symbol).
|
|
const int active_valence = vertex_valences_[corner_to_vertex_map_[next]];
|
|
switch (symbol) {
|
|
case TOPOLOGY_C:
|
|
// Compute prediction.
|
|
FALLTHROUGH_INTENDED;
|
|
case TOPOLOGY_S:
|
|
// Update velences.
|
|
vertex_valences_[corner_to_vertex_map_[next]] -= 1;
|
|
vertex_valences_[corner_to_vertex_map_[prev]] -= 1;
|
|
if (symbol == TOPOLOGY_S) {
|
|
// Whenever we reach a split symbol, we need to split the vertex into
|
|
// two and attach all corners on the left and right sides of the split
|
|
// vertex to the respective vertices (see image below). This is
|
|
// necessary since the decoder works in the reverse order and it
|
|
// merges the two vertices only after the split symbol is processed.
|
|
//
|
|
// * -----
|
|
// / \--------
|
|
// / \--------
|
|
// / \-------
|
|
// *-------v-------*
|
|
// \ /c\ /
|
|
// \ / \ /
|
|
// \ /n S p\ /
|
|
// *.......*
|
|
//
|
|
|
|
// Count the number of faces on the left side of the split vertex and
|
|
// update the valence on the "left vertex".
|
|
int num_left_faces = 0;
|
|
CornerIndex act_c = corner_table_->Opposite(prev);
|
|
while (act_c >= 0) {
|
|
if (encoder_impl()->IsFaceEncoded(corner_table_->Face(act_c)))
|
|
break; // Stop when we reach the first visited face.
|
|
++num_left_faces;
|
|
act_c = corner_table_->Opposite(corner_table_->Next(act_c));
|
|
}
|
|
vertex_valences_[corner_to_vertex_map_[last_corner_]] =
|
|
num_left_faces + 1;
|
|
|
|
// Create a new vertex for the right side and count the number of
|
|
// faces that should be attached to this vertex.
|
|
const int new_vert_id = vertex_valences_.size();
|
|
int num_right_faces = 0;
|
|
|
|
act_c = corner_table_->Opposite(next);
|
|
while (act_c >= 0) {
|
|
if (encoder_impl()->IsFaceEncoded(corner_table_->Face(act_c)))
|
|
break; // Stop when we reach the first visited face.
|
|
++num_right_faces;
|
|
// Map corners on the right side to the newly created vertex.
|
|
corner_to_vertex_map_[corner_table_->Next(act_c)] = new_vert_id;
|
|
act_c = corner_table_->Opposite(corner_table_->Previous(act_c));
|
|
}
|
|
vertex_valences_.push_back(num_right_faces + 1);
|
|
|
|
++num_split_symbols_;
|
|
}
|
|
break;
|
|
case TOPOLOGY_R:
|
|
// Update valences.
|
|
vertex_valences_[corner_to_vertex_map_[last_corner_]] -= 1;
|
|
vertex_valences_[corner_to_vertex_map_[next]] -= 1;
|
|
vertex_valences_[corner_to_vertex_map_[prev]] -= 2;
|
|
break;
|
|
case TOPOLOGY_L:
|
|
|
|
vertex_valences_[corner_to_vertex_map_[last_corner_]] -= 1;
|
|
vertex_valences_[corner_to_vertex_map_[next]] -= 2;
|
|
vertex_valences_[corner_to_vertex_map_[prev]] -= 1;
|
|
break;
|
|
case TOPOLOGY_E:
|
|
vertex_valences_[corner_to_vertex_map_[last_corner_]] -= 2;
|
|
vertex_valences_[corner_to_vertex_map_[next]] -= 2;
|
|
vertex_valences_[corner_to_vertex_map_[prev]] -= 2;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (prev_symbol_ != -1) {
|
|
int clamped_valence;
|
|
if (active_valence < min_valence_) {
|
|
clamped_valence = min_valence_;
|
|
} else if (active_valence > max_valence_) {
|
|
clamped_valence = max_valence_;
|
|
} else {
|
|
clamped_valence = active_valence;
|
|
}
|
|
|
|
const int context = clamped_valence - min_valence_;
|
|
context_symbols_[context].push_back(
|
|
edge_breaker_topology_to_symbol_id[prev_symbol_]);
|
|
}
|
|
|
|
prev_symbol_ = symbol;
|
|
}
|
|
|
|
void Done() {
|
|
// We still need to store the last encoded symbol.
|
|
if (prev_symbol_ != -1) {
|
|
MeshEdgeBreakerTraversalEncoder::EncodeSymbol(
|
|
static_cast<EdgeBreakerTopologyBitPattern>(prev_symbol_));
|
|
}
|
|
// Store the init face configurations and the explicitly encoded symbols.
|
|
MeshEdgeBreakerTraversalEncoder::Done();
|
|
// Encode the number of split symbols (needed to set the correct number of
|
|
// vertices on the decoder side).
|
|
GetOutputBuffer()->Encode(num_split_symbols_);
|
|
// Encode the valance encoder mode.
|
|
GetOutputBuffer()->Encode(static_cast<int8_t>(mode_));
|
|
// Store the contexts.
|
|
for (int i = 0; i < context_symbols_.size(); ++i) {
|
|
EncodeVarint<uint32_t>(context_symbols_[i].size(), GetOutputBuffer());
|
|
if (context_symbols_[i].size() > 0) {
|
|
EncodeSymbols(context_symbols_[i].data(), context_symbols_[i].size(), 1,
|
|
GetOutputBuffer());
|
|
}
|
|
}
|
|
}
|
|
|
|
int NumEncodedSymbols() const { return num_symbols_; }
|
|
|
|
private:
|
|
const CornerTable *corner_table_;
|
|
// Explicit map between corners and vertices. We cannot use the one stored
|
|
// in the |corner_table_| because we may need to add additional vertices to
|
|
// handle split symbols.
|
|
IndexTypeVector<CornerIndex, VertexIndex> corner_to_vertex_map_;
|
|
IndexTypeVector<VertexIndex, int> vertex_valences_;
|
|
// Previously encoded symbol.
|
|
int32_t prev_symbol_;
|
|
// The total number of encoded split symbols.
|
|
int32_t num_split_symbols_;
|
|
CornerIndex last_corner_;
|
|
// Explicitly count the number of encoded symbols.
|
|
int num_symbols_;
|
|
|
|
int min_valence_;
|
|
int max_valence_;
|
|
EdgeBreakerValenceCodingMode mode_;
|
|
|
|
std::vector<std::vector<uint32_t>> context_symbols_;
|
|
};
|
|
|
|
} // namespace draco
|
|
|
|
#endif // DRACO_COMPRESSION_MESH_MESH_EDGEBREAKER_TRAVERSAL_VALENCE_ENCODER_H_
|