mirror of
https://git.mirrors.martin98.com/https://github.com/google/draco
synced 2025-06-04 11:25:44 +08:00
170 lines
6.8 KiB
C++
170 lines
6.8 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_PREDICTIVE_ENCODER_H_
|
|
#define DRACO_COMPRESSION_MESH_MESH_EDGEBREAKER_TRAVERSAL_PREDICTIVE_ENCODER_H_
|
|
|
|
#include "compression/mesh/mesh_edgebreaker_traversal_encoder.h"
|
|
|
|
namespace draco {
|
|
|
|
// Encoder that tries to predict the edgebreaker traversal symbols based on the
|
|
// vertex valences of the unencoded portion of the mesh. The current prediction
|
|
// scheme assumes that each vertex has valence 6 which can be used to predict
|
|
// the symbol preceeding the one that is currently encoded. Predictions are
|
|
// encoded using an arithmetic coding which can lead to less than 1 bit per
|
|
// triangle encoding for highly regular meshes.
|
|
class MeshEdgeBreakerTraversalPredictiveEncoder
|
|
: public MeshEdgeBreakerTraversalEncoder {
|
|
public:
|
|
MeshEdgeBreakerTraversalPredictiveEncoder()
|
|
: corner_table_(nullptr),
|
|
prev_symbol_(-1),
|
|
num_split_symbols_(0),
|
|
last_corner_(kInvalidCornerIndex),
|
|
num_symbols_(0) {}
|
|
|
|
void Init(MeshEdgeBreakerEncoderImplInterface *encoder) {
|
|
MeshEdgeBreakerTraversalEncoder::Init(encoder);
|
|
corner_table_ = encoder->GetCornerTable();
|
|
// Initialize valences of all vertices.
|
|
vertex_valences_.resize(corner_table_->num_vertices());
|
|
for (int i = 0; i < vertex_valences_.size(); ++i) {
|
|
vertex_valences_[i] = corner_table_->Valence(VertexIndex(i));
|
|
}
|
|
}
|
|
|
|
inline void NewCornerReached(CornerIndex corner) { last_corner_ = corner; }
|
|
|
|
inline int32_t ComputePredictedSymbol(VertexIndex pivot) {
|
|
const int valence = vertex_valences_[pivot.value()];
|
|
if (valence < 0) {
|
|
// This situation can happen only for split vertices. Returning
|
|
// TOPOLOGY_INVALID always cases misprediction.
|
|
return TOPOLOGY_INVALID;
|
|
}
|
|
if (valence < 6) {
|
|
return TOPOLOGY_R;
|
|
}
|
|
return TOPOLOGY_C;
|
|
}
|
|
|
|
inline void EncodeSymbol(EdgeBreakerTopologyBitPattern symbol) {
|
|
++num_symbols_;
|
|
// Update valences on the mesh. And compute the predicted preceding symbol.
|
|
// Note that the valences are computed for the so far unencoded part of the
|
|
// mesh. Adding a new symbol either reduces valences on the vertices or
|
|
// leaves the valence unchanged.
|
|
int32_t predicted_symbol = -1;
|
|
const CornerIndex next = corner_table_->Next(last_corner_);
|
|
const CornerIndex prev = corner_table_->Previous(last_corner_);
|
|
switch (symbol) {
|
|
case TOPOLOGY_C:
|
|
// Compute prediction.
|
|
predicted_symbol = ComputePredictedSymbol(corner_table_->Vertex(next));
|
|
FALLTHROUGH_INTENDED;
|
|
case TOPOLOGY_S:
|
|
// Update velences.
|
|
vertex_valences_[corner_table_->Vertex(next).value()] -= 1;
|
|
vertex_valences_[corner_table_->Vertex(prev).value()] -= 1;
|
|
if (symbol == TOPOLOGY_S) {
|
|
// Whenever we reach a split symbol, mark its tip vertex as invalid by
|
|
// setting the valence to a negative value. Any prediction that will
|
|
// use this vertex will then cause a misprediction. This is currently
|
|
// necessary because the decodding works in the reverse direction and
|
|
// the decoder doesn't know about these vertices until the split
|
|
// symbol is decoded at which point two vertices are merged into one.
|
|
// This can be most likely solved on the encoder side by spliting the
|
|
// tip vertex into two, but since split symbols are relatively rare,
|
|
// it's probably not worth doing it.
|
|
vertex_valences_[corner_table_->Vertex(last_corner_).value()] = -1;
|
|
++num_split_symbols_;
|
|
}
|
|
break;
|
|
case TOPOLOGY_R:
|
|
// Compute prediction.
|
|
predicted_symbol = ComputePredictedSymbol(corner_table_->Vertex(next));
|
|
// Update valences.
|
|
vertex_valences_[corner_table_->Vertex(last_corner_).value()] -= 1;
|
|
vertex_valences_[corner_table_->Vertex(next).value()] -= 1;
|
|
vertex_valences_[corner_table_->Vertex(prev).value()] -= 2;
|
|
break;
|
|
case TOPOLOGY_L:
|
|
vertex_valences_[corner_table_->Vertex(last_corner_).value()] -= 1;
|
|
vertex_valences_[corner_table_->Vertex(next).value()] -= 2;
|
|
vertex_valences_[corner_table_->Vertex(prev).value()] -= 1;
|
|
break;
|
|
case TOPOLOGY_E:
|
|
vertex_valences_[corner_table_->Vertex(last_corner_).value()] -= 2;
|
|
vertex_valences_[corner_table_->Vertex(next).value()] -= 2;
|
|
vertex_valences_[corner_table_->Vertex(prev).value()] -= 2;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
// Flag used when it's necessary to explicitly store the previous symbol.
|
|
bool store_prev_symbol = true;
|
|
if (predicted_symbol != -1) {
|
|
if (predicted_symbol == prev_symbol_) {
|
|
predictions_.push_back(true);
|
|
store_prev_symbol = false;
|
|
} else if (prev_symbol_ != -1) {
|
|
predictions_.push_back(false);
|
|
}
|
|
}
|
|
if (store_prev_symbol && prev_symbol_ != -1) {
|
|
MeshEdgeBreakerTraversalEncoder::EncodeSymbol(
|
|
static_cast<EdgeBreakerTopologyBitPattern>(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.
|
|
GetOutputBuffer()->Encode(num_split_symbols_);
|
|
// Store the predictions.
|
|
BinaryEncoder prediction_encoder;
|
|
prediction_encoder.StartEncoding();
|
|
for (int i = predictions_.size() - 1; i >= 0; --i) {
|
|
prediction_encoder.EncodeBit(predictions_[i]);
|
|
}
|
|
prediction_encoder.EndEncoding(GetOutputBuffer());
|
|
}
|
|
|
|
int NumEncodedSymbols() const { return num_symbols_; }
|
|
|
|
private:
|
|
const CornerTable *corner_table_;
|
|
std::vector<int> vertex_valences_;
|
|
std::vector<bool> predictions_;
|
|
// 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_;
|
|
};
|
|
|
|
} // namespace draco
|
|
|
|
#endif // DRACO_COMPRESSION_MESH_MESH_EDGEBREAKER_TRAVERSAL_PREDICTIVE_ENCODER_H_
|