diff options
Diffstat (limited to 'intern/opensubdiv/internal/topology/topology_refiner_capi.cc')
-rw-r--r-- | intern/opensubdiv/internal/topology/topology_refiner_capi.cc | 387 |
1 files changed, 1 insertions, 386 deletions
diff --git a/intern/opensubdiv/internal/topology/topology_refiner_capi.cc b/intern/opensubdiv/internal/topology/topology_refiner_capi.cc index 8c3f8f1f896..b30d509be20 100644 --- a/intern/opensubdiv/internal/topology/topology_refiner_capi.cc +++ b/intern/opensubdiv/internal/topology/topology_refiner_capi.cc @@ -19,12 +19,8 @@ #include "opensubdiv_topology_refiner_capi.h" #include "MEM_guardedalloc.h" -#include "internal/base/edge_map.h" -#include "internal/base/type.h" #include "internal/base/type_convert.h" -#include "internal/topology/mesh_topology.h" #include "internal/topology/topology_refiner_impl.h" -#include "opensubdiv_converter_capi.h" using blender::opensubdiv::vector; @@ -259,389 +255,8 @@ void openSubdiv_deleteTopologyRefiner(OpenSubdiv_TopologyRefiner *topology_refin OBJECT_GUARDED_DELETE(topology_refiner, OpenSubdiv_TopologyRefiner); } -//////////////////////////////////////////////////////////////////////////////// -// Comparison with converter. - -namespace blender { -namespace opensubdiv { -namespace { - -/////////////////////////////////////////////////////////// -// Quick preliminary checks. - -bool checkSchemeTypeMatches(const OpenSubdiv_TopologyRefiner *topology_refiner, - const OpenSubdiv_Converter *converter) -{ - const OpenSubdiv::Sdc::SchemeType converter_scheme_type = - blender::opensubdiv::getSchemeTypeFromCAPI(converter->getSchemeType(converter)); - return (converter_scheme_type == getOSDTopologyRefiner(topology_refiner)->GetSchemeType()); -} - -bool checkOptionsMatches(const OpenSubdiv_TopologyRefiner *topology_refiner, - const OpenSubdiv_Converter *converter) -{ - typedef OpenSubdiv::Sdc::Options Options; - const Options options = getOSDTopologyRefiner(topology_refiner)->GetSchemeOptions(); - const Options::FVarLinearInterpolation fvar_interpolation = options.GetFVarLinearInterpolation(); - const Options::FVarLinearInterpolation converter_fvar_interpolation = - blender::opensubdiv::getFVarLinearInterpolationFromCAPI( - converter->getFVarLinearInterpolation(converter)); - if (fvar_interpolation != converter_fvar_interpolation) { - return false; - } - return true; -} - -bool checkGeometryCountersMatches(const OpenSubdiv_TopologyRefiner *topology_refiner, - const OpenSubdiv_Converter *converter) -{ - using OpenSubdiv::Far::TopologyLevel; - const TopologyLevel &base_level = getOSDTopologyBaseLevel(topology_refiner); - return ((converter->getNumVertices(converter) == base_level.GetNumVertices()) && - (converter->getNumEdges(converter) == base_level.GetNumEdges()) && - (converter->getNumFaces(converter) == base_level.GetNumFaces())); -} - -bool checkPreliminaryMatches(const OpenSubdiv_TopologyRefiner *topology_refiner, - const OpenSubdiv_Converter *converter) -{ - return checkSchemeTypeMatches(topology_refiner, converter) && - checkOptionsMatches(topology_refiner, converter) && - checkGeometryCountersMatches(topology_refiner, converter); -} - -/////////////////////////////////////////////////////////// -// Geometry comparison. - -// A thin wrapper around index like array which does cyclic access. This means, -// it basically does indices[requested_index % num_indices]. -// -// NOTE: This array does not own the memory. -// -// TODO(sergey): Consider moving this to a more reusable place. -class CyclicArray { - public: - typedef int value_type; - typedef int size_type; - static constexpr size_type npos = -1; - - explicit CyclicArray(const std::vector<int> &data) : data_(data.data()), size_(data.size()) - { - } - - explicit CyclicArray(const OpenSubdiv::Far::ConstIndexArray &data) - : data_(&data[0]), size_(data.size()) - { - } - - inline value_type operator[](int index) const - { - assert(index >= 0); - // TODO(sergey): Check whether doing check for element index exceeding total - // number of indices prior to modulo helps performance. - return data_[index % size()]; - } - - inline size_type size() const - { - return size_; - } - - // Find index of first occurrence of a given value. - inline size_type find(const value_type value) const - { - const int num_indices = size(); - for (size_type i = 0; i < num_indices; ++i) { - if (value == (*this)[i]) { - return i; - } - } - return npos; - } - - protected: - const value_type *data_; - const size_type size_; -}; - -bool compareCyclicForward(const CyclicArray &array_a, - const int start_a, - const CyclicArray &array_b, - const int start_b) -{ - const int num_elements = array_a.size(); - for (int i = 0; i < num_elements; ++i) { - if (array_a[start_a + i] != array_b[start_b + i]) { - return false; - } - } - return true; -} - -bool compareCyclicBackward(const CyclicArray &array_a, - const int start_a, - const CyclicArray &array_b, - const int start_b) -{ - const int num_elements = array_a.size(); - // TODO(sergey): Some optimization might be possible with memcmp trickery. - for (int i = 0; i < num_elements; ++i) { - if (array_a[start_a + (num_elements - i - 1)] != array_b[start_b + (num_elements - i - 1)]) { - return false; - } - } - return true; -} - -// Utility function dedicated for checking whether whether vertices indices -// used by two faces match. -// The tricky part here is that we can't trust 1:1 array match here, since it's -// possible that OpenSubdiv oriented edges of a face to make it compatible with -// an internal representation of non-manifold meshes. -// -// TODO(sergey): Check whether this is needed, ot whether OpenSubdiv is only -// creating edges in a proper orientation without modifying indices of face -// vertices. -bool checkVerticesOfFacesMatch(const CyclicArray &indices_a, const CyclicArray &indices_b) -{ - if (indices_a.size() != indices_b.size()) { - return false; - } - // "Align" the arrays so we know first matched element. - const int start_b = indices_b.find(indices_a[0]); - if (start_b == indices_b.npos) { - return false; - } - // Check match in both directions, for the case OpenSubdiv did orient face in - // a way which made normals more consistent internally. - if (compareCyclicForward(indices_a, 0, indices_b, start_b)) { - return true; - } - if (compareCyclicBackward(indices_a, 0, indices_b, start_b)) { - return true; - } - return false; -} - -bool checkGeometryFacesMatch(const OpenSubdiv_TopologyRefiner *topology_refiner, - const OpenSubdiv_Converter *converter) -{ - using OpenSubdiv::Far::ConstIndexArray; - using OpenSubdiv::Far::TopologyLevel; - const TopologyLevel &base_level = getOSDTopologyBaseLevel(topology_refiner); - const int num_faces = base_level.GetNumFaces(); - // TODO(sergey): Consider using data structure which keeps handful of - // elements on stack before doing heep allocation. - vector<int> conv_face_vertices; - for (int face_index = 0; face_index < num_faces; ++face_index) { - const ConstIndexArray &face_vertices = base_level.GetFaceVertices(face_index); - const int num_face_vertices = face_vertices.size(); - if (num_face_vertices != converter->getNumFaceVertices(converter, face_index)) { - return false; - } - conv_face_vertices.resize(num_face_vertices); - converter->getFaceVertices(converter, face_index, &conv_face_vertices[0]); - if (!checkVerticesOfFacesMatch(CyclicArray(conv_face_vertices), CyclicArray(face_vertices))) { - return false; - } - } - return true; -} - -bool checkGeometryMatches(const OpenSubdiv_TopologyRefiner *topology_refiner, - const OpenSubdiv_Converter *converter) -{ - // NOTE: Since OpenSubdiv's topology refiner doesn't contain loose edges, we - // are only checking for faces to be matched. Changes in edges we don't care - // here too much (they'll be checked for creases changes later). - return checkGeometryFacesMatch(topology_refiner, converter); -} - -/////////////////////////////////////////////////////////// -// Compare attributes which affects on topology - -inline bool checkSingleEdgeSharpnessMatch(const OpenSubdiv::Far::TopologyLevel &base_level, - int base_level_edge_index, - const OpenSubdiv_Converter *converter, - int converter_edge_index) -{ - // NOTE: Boundary and non-manifold edges are internally forced to an infinite - // sharpness. So we can not reliably compare those. - // - // TODO(sergey): Watch for NON_MANIFOLD_SHARP option. - if (base_level.IsEdgeBoundary(base_level_edge_index) || - base_level.IsEdgeNonManifold(base_level_edge_index)) { - return true; - } - const float sharpness = base_level.GetEdgeSharpness(base_level_edge_index); - const float converter_sharpness = converter->getEdgeSharpness(converter, converter_edge_index); - if (sharpness != converter_sharpness) { - return false; - } - return true; -} - -inline bool checkSingleEdgeTagMatch(const OpenSubdiv::Far::TopologyLevel &base_level, - int base_level_edge_index, - const OpenSubdiv_Converter *converter, - int converter_edge_index) -{ - return checkSingleEdgeSharpnessMatch( - base_level, base_level_edge_index, converter, converter_edge_index); -} - -// Compares edge tags between topology refiner and converter in a case when -// converter specifies a full topology. -// This is simplest loop, since we know that order of edges matches. -bool checkEdgeTagsMatchFullTopology(const OpenSubdiv_TopologyRefiner *topology_refiner, - const OpenSubdiv_Converter *converter) -{ - using OpenSubdiv::Far::ConstIndexArray; - using OpenSubdiv::Far::TopologyLevel; - const TopologyLevel &base_level = getOSDTopologyBaseLevel(topology_refiner); - const int num_edges = base_level.GetNumEdges(); - for (int edge_index = 0; edge_index < num_edges; ++edge_index) { - if (!checkSingleEdgeTagMatch(base_level, edge_index, converter, edge_index)) { - return false; - } - } - return true; -} - -// Compares tags of edges in the case when orientation of edges is left up to -// OpenSubdiv. In this case we do need to take care of mapping edges from the -// converter to current topology refiner, since the order is not guaranteed. -bool checkEdgeTagsMatchAutoOrient(const OpenSubdiv_TopologyRefiner *topology_refiner, - const OpenSubdiv_Converter *converter) -{ - using OpenSubdiv::Far::ConstIndexArray; - using OpenSubdiv::Far::TopologyLevel; - const TopologyLevel &base_level = getOSDTopologyBaseLevel(topology_refiner); - const int num_edges = base_level.GetNumEdges(); - // Create mapping for quick lookup of edge index from its vertices indices. - // - // TODO(sergey): Consider caching it in some sort of wrapper around topology - // refiner. - EdgeTagMap<int> edge_map; - for (int edge_index = 0; edge_index < num_edges; ++edge_index) { - ConstIndexArray edge_vertices = base_level.GetEdgeVertices(edge_index); - edge_map.insert(edge_vertices[0], edge_vertices[1], edge_index); - } - // Compare all edges. - for (int converter_edge_index = 0; converter_edge_index < num_edges; ++converter_edge_index) { - // Get edge vertices indices, and lookup corresponding edge index in the - // base topology level. - int edge_vertices[2]; - converter->getEdgeVertices(converter, converter_edge_index, edge_vertices); - const int base_level_edge_index = edge_map.at(edge_vertices[0], edge_vertices[1]); - // Perform actual test. - if (!checkSingleEdgeTagMatch( - base_level, base_level_edge_index, converter, converter_edge_index)) { - return false; - } - } - return true; -} - -bool checkEdgeTagsMatch(const OpenSubdiv_TopologyRefiner *topology_refiner, - const OpenSubdiv_Converter *converter) -{ - if (converter->specifiesFullTopology(converter)) { - return checkEdgeTagsMatchFullTopology(topology_refiner, converter); - } - else { - return checkEdgeTagsMatchAutoOrient(topology_refiner, converter); - } -} - -float getEffectiveVertexSharpness(const OpenSubdiv_Converter *converter, const int vertex_index) -{ - if (converter->isInfiniteSharpVertex != nullptr && - converter->isInfiniteSharpVertex(converter, vertex_index)) { - return OpenSubdiv::Sdc::Crease::SHARPNESS_INFINITE; - } - - if (converter->getVertexSharpness != nullptr) { - return converter->getVertexSharpness(converter, vertex_index); - } - - return 0.0f; -} - -bool checkVertexSharpnessMatch(const OpenSubdiv_TopologyRefiner *topology_refiner, - const OpenSubdiv_Converter *converter) -{ - const MeshTopology &base_mesh_topology = topology_refiner->impl->base_mesh_topology; - - const int num_vertices = base_mesh_topology.getNumVertices(); - for (int vertex_index = 0; vertex_index < num_vertices; ++vertex_index) { - const float current_sharpness = base_mesh_topology.vertices[vertex_index].sharpness; - const float requested_sharpness = getEffectiveVertexSharpness(converter, vertex_index); - - if (current_sharpness != requested_sharpness) { - return false; - } - } - return true; -} - -bool checkSingleUVLayerMatch(const OpenSubdiv::Far::TopologyLevel &base_level, - const OpenSubdiv_Converter *converter, - const int layer_index) -{ - converter->precalcUVLayer(converter, layer_index); - const int num_faces = base_level.GetNumFaces(); - // TODO(sergey): Need to check whether converter changed the winding of - // face to match OpenSubdiv's expectations. - for (int face_index = 0; face_index < num_faces; ++face_index) { - OpenSubdiv::Far::ConstIndexArray base_level_face_uvs = base_level.GetFaceFVarValues( - face_index, layer_index); - for (int corner = 0; corner < base_level_face_uvs.size(); ++corner) { - const int uv_index = converter->getFaceCornerUVIndex(converter, face_index, corner); - if (base_level_face_uvs[corner] != uv_index) { - converter->finishUVLayer(converter); - return false; - } - } - } - converter->finishUVLayer(converter); - return true; -} - -bool checkUVLayersMatch(const OpenSubdiv_TopologyRefiner *topology_refiner, - const OpenSubdiv_Converter *converter) -{ - using OpenSubdiv::Far::TopologyLevel; - const int num_layers = converter->getNumUVLayers(converter); - const TopologyLevel &base_level = getOSDTopologyBaseLevel(topology_refiner); - // Number of UV layers should match. - if (base_level.GetNumFVarChannels() != num_layers) { - return false; - } - for (int layer_index = 0; layer_index < num_layers; ++layer_index) { - if (!checkSingleUVLayerMatch(base_level, converter, layer_index)) { - return false; - } - } - return true; -} - -bool checkTopologyAttributesMatch(const OpenSubdiv_TopologyRefiner *topology_refiner, - const OpenSubdiv_Converter *converter) -{ - return checkEdgeTagsMatch(topology_refiner, converter) && - checkVertexSharpnessMatch(topology_refiner, converter) && - checkUVLayersMatch(topology_refiner, converter); -} - -} // namespace -} // namespace opensubdiv -} // namespace blender - bool openSubdiv_topologyRefinerCompareWithConverter( const OpenSubdiv_TopologyRefiner *topology_refiner, const OpenSubdiv_Converter *converter) { - return (blender::opensubdiv::checkPreliminaryMatches(topology_refiner, converter) && - blender::opensubdiv::checkGeometryMatches(topology_refiner, converter) && - blender::opensubdiv::checkTopologyAttributesMatch(topology_refiner, converter)); + return topology_refiner->impl->isEqualToConverter(converter); } |