mirror of
https://git.mirrors.martin98.com/https://github.com/google/draco
synced 2025-06-04 11:25:44 +08:00

- Improved compression for triangular meshes (~10%) - Added WebAssembly decoder - Code cleanup + robustness fixes
238 lines
9.0 KiB
C++
238 lines
9.0 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_MESH_PREDICTION_DEGREE_TRAVERSER_H_
|
|
#define DRACO_MESH_PREDICTION_DEGREE_TRAVERSER_H_
|
|
|
|
#include <vector>
|
|
|
|
#include "mesh/corner_table.h"
|
|
|
|
namespace draco {
|
|
|
|
// PredictionDegreeTraverser provides framework for traversal over a corner
|
|
// table data structure following paper "Multi-way Geometry Encoding" by
|
|
// Cohen-or at al.'02. The traversal is implicitly guided by prediction degree
|
|
// of the destination vertices. A prediction degree is computed as the number of
|
|
// possible faces that can be used as source points for traversal to the given
|
|
// destination vertex (see image below, where faces F1 and F2 are already
|
|
// traversed and face F0 is not traversed yet. The prediction degree of vertex
|
|
// V is then equal to two).
|
|
//
|
|
// X-----V-----o
|
|
// / \ / \ / \
|
|
// / F0\ / \ / F2\
|
|
// X-----o-----o-----B
|
|
// \ F1/
|
|
// \ /
|
|
// A
|
|
//
|
|
// The class implements the same interface as the EdgebreakerTraverser
|
|
// (edgebreaker_traverser.h) and it can be controlled via the same template
|
|
// trait classes |TraversalProcessorT| and |TraversalObserverT|, that are used
|
|
// for controlling and monitoring of the traversal respectively. For details,
|
|
// please see edgebreaker_traverser.h.
|
|
template <class TraversalProcessorT, class TraversalObserverT>
|
|
class PredictionDegreeTraverser {
|
|
public:
|
|
typedef TraversalProcessorT TraversalProcessor;
|
|
typedef TraversalObserverT TraversalObserver;
|
|
typedef typename TraversalProcessorT::CornerTable CornerTable;
|
|
|
|
PredictionDegreeTraverser() {}
|
|
void Init(TraversalProcessorT &&processor) {
|
|
corner_table_ = &processor.GetCornerTable();
|
|
processor_ = std::move(processor);
|
|
}
|
|
void Init(TraversalProcessorT &&processor,
|
|
TraversalObserverT traversal_observer) {
|
|
Init(std::move(processor));
|
|
traversal_observer_ = traversal_observer;
|
|
}
|
|
|
|
// Called before any traversing starts.
|
|
void OnTraversalStart() {
|
|
prediction_degree_.resize(corner_table_->num_vertices(), 0);
|
|
}
|
|
|
|
// Called when all the traversing is done.
|
|
void OnTraversalEnd() {}
|
|
|
|
void TraverseFromCorner(CornerIndex corner_id) {
|
|
if (prediction_degree_.size() == 0)
|
|
return;
|
|
|
|
// Traversal starts from the |corner_id|. It's going to follow either the
|
|
// right or the left neighboring faces to |corner_id| based on their
|
|
// prediction degree.
|
|
traversal_stacks_[0].push_back(corner_id);
|
|
best_priority_ = 0;
|
|
// For the first face, check the remaining corners as they may not be
|
|
// processed yet.
|
|
const VertexIndex next_vert =
|
|
corner_table_->Vertex(corner_table_->Next(corner_id));
|
|
const VertexIndex prev_vert =
|
|
corner_table_->Vertex(corner_table_->Previous(corner_id));
|
|
if (!processor_.IsVertexVisited(next_vert)) {
|
|
processor_.MarkVertexVisited(next_vert);
|
|
traversal_observer_.OnNewVertexVisited(next_vert,
|
|
corner_table_->Next(corner_id));
|
|
}
|
|
if (!processor_.IsVertexVisited(prev_vert)) {
|
|
processor_.MarkVertexVisited(prev_vert);
|
|
traversal_observer_.OnNewVertexVisited(
|
|
prev_vert, corner_table_->Previous(corner_id));
|
|
}
|
|
const VertexIndex tip_vertex = corner_table_->Vertex(corner_id);
|
|
if (!processor_.IsVertexVisited(tip_vertex)) {
|
|
processor_.MarkVertexVisited(tip_vertex);
|
|
traversal_observer_.OnNewVertexVisited(tip_vertex, corner_id);
|
|
}
|
|
// Start the actual traversal.
|
|
while ((corner_id = PopNextCornerToTraverse()) >= 0) {
|
|
FaceIndex face_id(corner_id.value() / 3);
|
|
// Make sure the face hasn't been visited yet.
|
|
if (processor_.IsFaceVisited(face_id)) {
|
|
// This face has been already traversed.
|
|
continue;
|
|
}
|
|
|
|
while (true) {
|
|
face_id = FaceIndex(corner_id.value() / 3);
|
|
processor_.MarkFaceVisited(face_id);
|
|
traversal_observer_.OnNewFaceVisited(face_id);
|
|
|
|
// If the newly reached vertex hasn't been visited, mark it and notify
|
|
// the observer.
|
|
const VertexIndex vert_id = corner_table_->Vertex(corner_id);
|
|
if (!processor_.IsVertexVisited(vert_id)) {
|
|
processor_.MarkVertexVisited(vert_id);
|
|
traversal_observer_.OnNewVertexVisited(vert_id, corner_id);
|
|
}
|
|
|
|
// Check whether we can traverse to the right and left neighboring
|
|
// faces.
|
|
const CornerIndex right_corner_id =
|
|
corner_table_->GetRightCorner(corner_id);
|
|
const CornerIndex left_corner_id =
|
|
corner_table_->GetLeftCorner(corner_id);
|
|
const FaceIndex right_face_id(
|
|
(right_corner_id < 0 ? -1 : right_corner_id.value() / 3));
|
|
const FaceIndex left_face_id(
|
|
(left_corner_id < 0 ? -1 : left_corner_id.value() / 3));
|
|
const bool is_right_face_visited =
|
|
processor_.IsFaceVisited(right_face_id);
|
|
const bool is_left_face_visited =
|
|
processor_.IsFaceVisited(left_face_id);
|
|
|
|
if (!is_left_face_visited) {
|
|
// We can go to the left face.
|
|
const int priority = ComputePriority(left_corner_id);
|
|
if (is_right_face_visited && priority <= best_priority_) {
|
|
// Right face has been already visited and the priority is equal or
|
|
// better than the best priority. We are sure that the left face
|
|
// would be traversed next so there is no need to put it onto the
|
|
// stack.
|
|
corner_id = left_corner_id;
|
|
continue;
|
|
} else {
|
|
AddCornerToTraversalStack(left_corner_id, priority);
|
|
}
|
|
}
|
|
if (!is_right_face_visited) {
|
|
// Go to the right face.
|
|
const int priority = ComputePriority(right_corner_id);
|
|
if (priority <= best_priority_) {
|
|
// We are sure that the right face would be traversed next so there
|
|
// is no need to put it onto the stack.
|
|
corner_id = right_corner_id;
|
|
continue;
|
|
} else {
|
|
AddCornerToTraversalStack(right_corner_id, priority);
|
|
}
|
|
}
|
|
|
|
// Couldn't proceed directly to the next corner
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
const CornerTable *corner_table() const { return corner_table_; }
|
|
const TraversalProcessorT &traversal_processor() const { return processor_; }
|
|
const TraversalObserverT &traversal_observer() const {
|
|
return traversal_observer_;
|
|
}
|
|
|
|
private:
|
|
// Retrieves the next available corner (edge) to traverse. Edges are processed
|
|
// based on their priorities.
|
|
// Returns kInvalidCornerIndex when there is no edge available.
|
|
CornerIndex PopNextCornerToTraverse() {
|
|
for (int i = best_priority_; i < kMaxPriority; ++i) {
|
|
if (!traversal_stacks_[i].empty()) {
|
|
const CornerIndex ret = traversal_stacks_[i].back();
|
|
traversal_stacks_[i].pop_back();
|
|
best_priority_ = i;
|
|
return ret;
|
|
}
|
|
}
|
|
return kInvalidCornerIndex;
|
|
}
|
|
|
|
inline void AddCornerToTraversalStack(CornerIndex ci, int priority) {
|
|
traversal_stacks_[priority].push_back(ci);
|
|
// Make sure that the best available priority is up to date.
|
|
if (priority < best_priority_)
|
|
best_priority_ = priority;
|
|
}
|
|
|
|
// Returns the priority of traversing edge leading to |corner_id|.
|
|
inline int ComputePriority(CornerIndex corner_id) {
|
|
const VertexIndex v_tip = corner_table_->Vertex(corner_id);
|
|
// Priority 0 when traversing to already visited vertices.
|
|
int priority = 0;
|
|
if (!processor_.IsVertexVisited(v_tip)) {
|
|
const int degree = ++prediction_degree_[v_tip];
|
|
// Priority 1 when prediction degree > 1, otherwise 2.
|
|
priority = (degree > 1 ? 1 : 2);
|
|
}
|
|
// Clamp the priority to the maximum number of buckets.
|
|
if (priority >= kMaxPriority)
|
|
priority = kMaxPriority - 1;
|
|
return priority;
|
|
}
|
|
|
|
const CornerTable *corner_table_;
|
|
TraversalProcessorT processor_;
|
|
TraversalObserverT traversal_observer_;
|
|
|
|
// For efficiency reasons, the priority traversal is implemented using buckets
|
|
// where each buckets represent a stack of available corners for a given
|
|
// priority. Corners with the highest priority are always processed first.
|
|
static constexpr int kMaxPriority = 3;
|
|
std::vector<CornerIndex> traversal_stacks_[kMaxPriority];
|
|
|
|
// Keep the track of the best available priority to improve the performance
|
|
// of PopNextCornerToTraverse() method.
|
|
int best_priority_;
|
|
|
|
// Prediction degree available for each vertex.
|
|
IndexTypeVector<VertexIndex, int> prediction_degree_;
|
|
};
|
|
|
|
} // namespace draco
|
|
|
|
#endif // DRACO_MESH_PREDICTION_DEGREE_TRAVERSER_H_
|