DYT/Tool/OpenSceneGraph-3.6.5/include/draco/mesh/mesh_stripifier.h
2024-12-25 07:49:36 +08:00

259 lines
9.6 KiB
C++

// Copyright 2017 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_MESH_STRIPIFIER_H_
#define DRACO_MESH_MESH_STRIPIFIER_H_
#include "draco/mesh/mesh_misc_functions.h"
namespace draco {
// Class that generates triangle strips from a provided draco::Mesh data
// structure. The strips represent a more memory efficient storage of triangle
// connectivity that can be used directly on the GPU (see
// https://en.wikipedia.org/wiki/Triangle_strip ). In general, a mesh needs to
// be represented by several triangle strips and it has been proven that finding
// the optimal set of triangle strips is an NP-complete problem. The algorithm
// implemented by this class finds this set of triangle strips based on a greedy
// heuristic that always selects the longest available strip that covers the
// next unprocessed face. The longest strip is found by analyzing all strips
// that can cover the given face (three strips corresponding to three
// directions).
class MeshStripifier {
public:
MeshStripifier()
: mesh_(nullptr),
num_strips_(0),
num_encoded_faces_(0),
last_encoded_point_(kInvalidPointIndex) {}
// Generate triangle strips for a given mesh and output them to the output
// iterator |out_it|. In most cases |out_it| stores the values in a buffer
// that can be used directly on the GPU. Note that the algorithm can generate
// multiple strips to represent the whole mesh. In such cases multiple strips
// are separated using a so called primitive restart index that is specified
// by the |primitive_restart_index| (usually defined as the maximum allowed
// value for the given type).
// https://www.khronos.org/opengl/wiki/Vertex_Rendering#Primitive_Restart
template <typename OutputIteratorT, typename IndexTypeT>
bool GenerateTriangleStripsWithPrimitiveRestart(
const Mesh &mesh, IndexTypeT primitive_restart_index,
OutputIteratorT out_it);
// The same as above but disjoint triangle strips are separated by degenerate
// triangles instead of the primitive restart index. Degenerate triangles are
// zero area triangles that are automatically discarded by the GPU. Using
// degenerate triangles usually results in a slightly longer output indices
// array compared to the similar triangle strips that use primitive restart
// index. The advantage of this method is that it is supported by all hardware
// and all relevant APIs (including WebGL 1.0).
template <typename OutputIteratorT>
bool GenerateTriangleStripsWithDegenerateTriangles(const Mesh &mesh,
OutputIteratorT out_it);
// Returns the number of strips generated by the last call of the
// GenerateTriangleStrips() method.
int num_strips() const { return num_strips_; }
private:
bool Prepare(const Mesh &mesh) {
mesh_ = &mesh;
num_strips_ = 0;
num_encoded_faces_ = 0;
corner_table_ = CreateCornerTableFromPositionAttribute(mesh_);
if (corner_table_ == nullptr) {
return false;
}
// Mark all faces as unvisited.
is_face_visited_.assign(mesh.num_faces(), false);
return true;
}
// Returns local id of the longest strip that can be created from the given
// face |fi|.
int FindLongestStripFromFace(FaceIndex fi) {
// There are three possible strip directions that can contain the provided
// input face. We try all of them and select the direction that result in
// the longest strip.
const CornerIndex first_ci = corner_table_->FirstCorner(fi);
int longest_strip_id = -1;
int longest_strip_length = 0;
for (int i = 0; i < 3; ++i) {
GenerateStripsFromCorner(i, first_ci + i);
if (strip_faces_[i].size() > longest_strip_length) {
longest_strip_length = static_cast<int>(strip_faces_[i].size());
longest_strip_id = i;
}
}
return longest_strip_id;
}
// Generates strip from the data stored in |strip_faces_| and
// |strip_start_start_corners_| and stores it to |out_it|.
template <typename OutputIteratorT>
void StoreStrip(int local_strip_id, OutputIteratorT out_it) {
++num_strips_;
const int num_strip_faces = strip_faces_[local_strip_id].size();
CornerIndex ci = strip_start_corners_[local_strip_id];
for (int i = 0; i < num_strip_faces; ++i) {
const FaceIndex fi = corner_table_->Face(ci);
is_face_visited_[fi] = true;
++num_encoded_faces_;
if (i == 0) {
// Add the start face (three indices).
*out_it++ = CornerToPointIndex(ci).value();
*out_it++ = CornerToPointIndex(corner_table_->Next(ci)).value();
last_encoded_point_ = CornerToPointIndex(corner_table_->Previous(ci));
*out_it++ = last_encoded_point_.value();
} else {
// Store the point on the newly reached corner.
last_encoded_point_ = CornerToPointIndex(ci);
*out_it++ = last_encoded_point_.value();
// Go to the correct source corner to proceed to the next face.
if (i & 1) {
ci = corner_table_->Previous(ci);
} else {
ci = corner_table_->Next(ci);
}
}
ci = corner_table_->Opposite(ci);
}
}
PointIndex CornerToPointIndex(CornerIndex ci) const {
return mesh_->CornerToPointId(ci);
}
// Returns the opposite corner in case the opposite triangle does not lie
// across an attribute seam. Otherwise return kInvalidCornerIndex.
CornerIndex GetOppositeCorner(CornerIndex ci) const {
const CornerIndex oci = corner_table_->Opposite(ci);
if (oci < 0) {
return kInvalidCornerIndex;
}
// Ensure the point ids are same on both sides of the shared edge between
// the triangles.
if (CornerToPointIndex(corner_table_->Next(ci)) !=
CornerToPointIndex(corner_table_->Previous(oci))) {
return kInvalidCornerIndex;
}
if (CornerToPointIndex(corner_table_->Previous(ci)) !=
CornerToPointIndex(corner_table_->Next(oci))) {
return kInvalidCornerIndex;
}
return oci;
}
void GenerateStripsFromCorner(int local_strip_id, CornerIndex ci);
const Mesh *mesh_;
std::unique_ptr<CornerTable> corner_table_;
// Store strip faces for each of three possible directions from a given face.
std::vector<FaceIndex> strip_faces_[3];
// Start corner for each direction of the strip containing the processed face.
CornerIndex strip_start_corners_[3];
IndexTypeVector<FaceIndex, bool> is_face_visited_;
// The number of strips generated by this method.
int num_strips_;
// The number of encoded triangles.
int num_encoded_faces_;
// Last encoded point.
PointIndex last_encoded_point_;
};
template <typename OutputIteratorT, typename IndexTypeT>
bool MeshStripifier::GenerateTriangleStripsWithPrimitiveRestart(
const Mesh &mesh, IndexTypeT primitive_restart_index,
OutputIteratorT out_it) {
if (!Prepare(mesh)) {
return false;
}
// Go over all faces and generate strips from the first unvisited one.
for (FaceIndex fi(0); fi < mesh.num_faces(); ++fi) {
if (is_face_visited_[fi]) {
continue;
}
const int longest_strip_id = FindLongestStripFromFace(fi);
// Separate triangle strips with the primitive restart index.
if (num_strips_ > 0) {
*out_it++ = primitive_restart_index;
}
StoreStrip(longest_strip_id, out_it);
}
return true;
}
template <typename OutputIteratorT>
bool MeshStripifier::GenerateTriangleStripsWithDegenerateTriangles(
const Mesh &mesh, OutputIteratorT out_it) {
if (!Prepare(mesh)) {
return false;
}
// Go over all faces and generate strips from the first unvisited one.
for (FaceIndex fi(0); fi < mesh.num_faces(); ++fi) {
if (is_face_visited_[fi]) {
continue;
}
const int longest_strip_id = FindLongestStripFromFace(fi);
// Separate triangle strips by degenerate triangles. There will be either
// three or four degenerate triangles inserted based on the number of
// triangles that are already encoded in the output strip (three degenerate
// triangles for even number of existing triangles, four degenerate
// triangles for odd number of triangles).
if (num_strips_ > 0) {
// Duplicate last encoded index (first degenerate face).
*out_it++ = last_encoded_point_.value();
// Connect it to the start point of the new triangle strip (second
// degenerate face).
const CornerIndex new_start_corner =
strip_start_corners_[longest_strip_id];
const PointIndex new_start_point = CornerToPointIndex(new_start_corner);
*out_it++ = new_start_point.value();
num_encoded_faces_ += 2;
// If we have previously encoded number of faces we need to duplicate the
// point one more time to preserve the correct orientation of the next
// strip.
if (num_encoded_faces_ & 1) {
*out_it++ = new_start_point.value();
num_encoded_faces_ += 1;
}
// The last degenerate face will be added implicitly in the StoreStrip()
// function below as the first point index is going to be encoded there
// again.
}
StoreStrip(longest_strip_id, out_it);
}
return true;
}
} // namespace draco
#endif // DRACO_MESH_MESH_STRIPIFIER_H_