// Copyright 2018 Blender Foundation. All rights reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software Foundation, // Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // Author: Sergey Sharybin #include "opensubdiv_topology_refiner_capi.h" #include #include "MEM_guardedalloc.h" #include "internal/opensubdiv_converter_factory.h" #include "internal/opensubdiv_converter_internal.h" #include "internal/opensubdiv_internal.h" #include "internal/opensubdiv_topology_refiner_internal.h" namespace { const OpenSubdiv::Far::TopologyRefiner* getOSDTopologyRefiner( const OpenSubdiv_TopologyRefiner* topology_refiner) { return topology_refiner->internal->osd_topology_refiner; } const OpenSubdiv::Far::TopologyLevel* getOSDTopologyBaseLevel( const OpenSubdiv_TopologyRefiner* topology_refiner) { return &getOSDTopologyRefiner(topology_refiner)->GetLevel(0); } int getSubdivisionLevel(const OpenSubdiv_TopologyRefiner* topology_refiner) { return topology_refiner->internal->settings.level; } bool getIsAdaptive(const OpenSubdiv_TopologyRefiner* topology_refiner) { return topology_refiner->internal->settings.is_adaptive; } //////////////////////////////////////////////////////////////////////////////// // Query basic topology information from base level. int getNumVertices(const OpenSubdiv_TopologyRefiner* topology_refiner) { return getOSDTopologyBaseLevel(topology_refiner)->GetNumVertices(); } int getNumEdges(const OpenSubdiv_TopologyRefiner* topology_refiner) { return getOSDTopologyBaseLevel(topology_refiner)->GetNumEdges(); } int getNumFaces(const OpenSubdiv_TopologyRefiner* topology_refiner) { return getOSDTopologyBaseLevel(topology_refiner)->GetNumFaces(); } //////////////////////////////////////////////////////////////////////////////// // PTex face geometry queries. static void convertArrayToRaw( const OpenSubdiv::Far::ConstIndexArray& array, int* raw_array) { for (int i = 0; i < array.size(); ++i) { raw_array[i] = array[i]; } } int getNumFaceVertices(const OpenSubdiv_TopologyRefiner* topology_refiner, const int face_index) { const OpenSubdiv::Far::TopologyLevel* base_level = getOSDTopologyBaseLevel(topology_refiner); return base_level->GetFaceVertices(face_index).size(); } void getFaceVertices(const OpenSubdiv_TopologyRefiner* topology_refiner, const int face_index, int* face_vertices_indices) { const OpenSubdiv::Far::TopologyLevel* base_level = getOSDTopologyBaseLevel(topology_refiner); OpenSubdiv::Far::ConstIndexArray array = base_level->GetFaceVertices(face_index); convertArrayToRaw(array, face_vertices_indices); } int getNumFaceEdges(const OpenSubdiv_TopologyRefiner* topology_refiner, const int face_index) { const OpenSubdiv::Far::TopologyLevel* base_level = getOSDTopologyBaseLevel(topology_refiner); return base_level->GetFaceEdges(face_index).size(); } void getFaceEdges(const OpenSubdiv_TopologyRefiner* topology_refiner, const int face_index, int* face_edges_indices) { const OpenSubdiv::Far::TopologyLevel* base_level = getOSDTopologyBaseLevel(topology_refiner); OpenSubdiv::Far::ConstIndexArray array = base_level->GetFaceEdges(face_index); convertArrayToRaw(array, face_edges_indices); } void getEdgeVertices(const OpenSubdiv_TopologyRefiner* topology_refiner, const int edge_index, int edge_vertices_indices[2]) { const OpenSubdiv::Far::TopologyLevel* base_level = getOSDTopologyBaseLevel(topology_refiner); OpenSubdiv::Far::ConstIndexArray array = base_level->GetEdgeVertices(edge_index); assert(array.size() == 2); edge_vertices_indices[0] = array[0]; edge_vertices_indices[1] = array[1]; } int getNumFacePtexFaces(const OpenSubdiv_TopologyRefiner* topology_refiner, const int face_index) { const int num_face_vertices = topology_refiner->getNumFaceVertices(topology_refiner, face_index); if (num_face_vertices == 4) { return 1; } else { return num_face_vertices; } } int getNumPtexFaces(const OpenSubdiv_TopologyRefiner* topology_refiner) { const int num_faces = topology_refiner->getNumFaces(topology_refiner); int num_ptex_faces = 0; for (int face_index = 0; face_index < num_faces; ++face_index) { num_ptex_faces += topology_refiner->getNumFacePtexFaces(topology_refiner, face_index); } return num_ptex_faces; } void fillFacePtexIndexOffset(const OpenSubdiv_TopologyRefiner* topology_refiner, int* face_ptex_index_offset) { const int num_faces = topology_refiner->getNumFaces(topology_refiner); int num_ptex_faces = 0; for (int face_index = 0; face_index < num_faces; ++face_index) { face_ptex_index_offset[face_index] = num_ptex_faces; num_ptex_faces += topology_refiner->getNumFacePtexFaces(topology_refiner, face_index); } } ////////////////////////////////////////////////////////////////////////////// // Face-varying data. int getNumFVarChannels( const struct OpenSubdiv_TopologyRefiner* topology_refiner) { const OpenSubdiv::Far::TopologyLevel* base_level = getOSDTopologyBaseLevel(topology_refiner); return base_level->GetNumFVarChannels(); } OpenSubdiv_FVarLinearInterpolation getFVarLinearInterpolation( const struct OpenSubdiv_TopologyRefiner* topology_refiner) { return opensubdiv_capi::getCAPIFVarLinearInterpolationFromOSD( getOSDTopologyRefiner(topology_refiner)->GetFVarLinearInterpolation()); } int getNumFVarValues( const struct OpenSubdiv_TopologyRefiner* topology_refiner, const int channel) { const OpenSubdiv::Far::TopologyLevel* base_level = getOSDTopologyBaseLevel(topology_refiner); return base_level->GetNumFVarValues(channel); } const int* getFaceFVarValueIndices( const struct OpenSubdiv_TopologyRefiner* topology_refiner, const int face_index, const int channel) { const OpenSubdiv::Far::TopologyLevel* base_level = getOSDTopologyBaseLevel(topology_refiner); return &base_level->GetFaceFVarValues(face_index, channel)[0]; } ////////////////////////////////////////////////////////////////////////////// // Internal helpers. void assignFunctionPointers(OpenSubdiv_TopologyRefiner* topology_refiner) { topology_refiner->getSubdivisionLevel = getSubdivisionLevel; topology_refiner->getIsAdaptive = getIsAdaptive; // Basic topology information. topology_refiner->getNumVertices = getNumVertices; topology_refiner->getNumEdges = getNumEdges; topology_refiner->getNumFaces = getNumFaces; topology_refiner->getNumFaceVertices = getNumFaceVertices; topology_refiner->getFaceVertices = getFaceVertices; topology_refiner->getNumFaceEdges = getNumFaceEdges; topology_refiner->getFaceEdges = getFaceEdges; topology_refiner->getEdgeVertices = getEdgeVertices; // PTex face geometry. topology_refiner->getNumFacePtexFaces = getNumFacePtexFaces; topology_refiner->getNumPtexFaces = getNumPtexFaces; topology_refiner->fillFacePtexIndexOffset = fillFacePtexIndexOffset; // Face-varying data. topology_refiner->getNumFVarChannels = getNumFVarChannels; topology_refiner->getFVarLinearInterpolation = getFVarLinearInterpolation; topology_refiner->getNumFVarValues = getNumFVarValues; topology_refiner->getFaceFVarValueIndices = getFaceFVarValueIndices; } OpenSubdiv_TopologyRefiner* allocateTopologyRefiner() { OpenSubdiv_TopologyRefiner* topology_refiner = OBJECT_GUARDED_NEW(OpenSubdiv_TopologyRefiner); topology_refiner->internal = OBJECT_GUARDED_NEW(OpenSubdiv_TopologyRefinerInternal); assignFunctionPointers(topology_refiner); return topology_refiner; } } // namespace OpenSubdiv_TopologyRefiner* openSubdiv_createTopologyRefinerFromConverter( OpenSubdiv_Converter* converter, const OpenSubdiv_TopologyRefinerSettings* settings) { OpenSubdiv::Far::TopologyRefiner* osd_topology_refiner = opensubdiv_capi::createOSDTopologyRefinerFromConverter(converter); if (osd_topology_refiner == NULL) { // Happens on empty or bad topology. return NULL; } OpenSubdiv_TopologyRefiner* topology_refiner = allocateTopologyRefiner(); topology_refiner->internal->osd_topology_refiner = osd_topology_refiner; // Store setting which we want to keep track of and which can not be stored // in OpenSubdiv's descriptor yet. topology_refiner->internal->settings = *settings; return topology_refiner; } void openSubdiv_deleteTopologyRefiner( OpenSubdiv_TopologyRefiner* topology_refiner) { OBJECT_GUARDED_DELETE(topology_refiner->internal, OpenSubdiv_TopologyRefinerInternal); OBJECT_GUARDED_DELETE(topology_refiner, OpenSubdiv_TopologyRefiner); } //////////////////////////////////////////////////////////////////////////////// // Comparison with converter. namespace { /////////////////////////////////////////////////////////// // Quick preliminary checks. bool checkSchemeTypeMatches( const OpenSubdiv::Far::TopologyRefiner* topology_refiner, const OpenSubdiv_Converter* converter) { const OpenSubdiv::Sdc::SchemeType converter_scheme_type = opensubdiv_capi::getSchemeTypeFromCAPI( converter->getSchemeType(converter)); return (converter_scheme_type == topology_refiner->GetSchemeType()); } bool checkOptionsMatches( const OpenSubdiv::Far::TopologyRefiner* topology_refiner, const OpenSubdiv_Converter* converter) { typedef OpenSubdiv::Sdc::Options Options; const Options options = topology_refiner->GetSchemeOptions(); const Options::FVarLinearInterpolation fvar_interpolation = options.GetFVarLinearInterpolation(); const Options::FVarLinearInterpolation converter_fvar_interpolation = opensubdiv_capi::getFVarLinearInterpolationFromCAPI( converter->getFVarLinearInterpolation(converter)); if (fvar_interpolation != converter_fvar_interpolation) { return false; } return true; } bool checkGeometryCoountersMatches( const OpenSubdiv::Far::TopologyRefiner* topology_refiner, const OpenSubdiv_Converter* converter) { using OpenSubdiv::Far::TopologyLevel; const TopologyLevel& base_level = topology_refiner->GetLevel(0); return ( (converter->getNumVertices(converter) == base_level.GetNumVertices()) && (converter->getNumEdges(converter) == base_level.GetNumEdges()) && (converter->getNumFaces(converter) == base_level.GetNumFaces())); } bool checkPreliminaryMatches( const OpenSubdiv::Far::TopologyRefiner* topology_refiner, const OpenSubdiv_Converter* converter) { return checkSchemeTypeMatches(topology_refiner, converter) && checkOptionsMatches(topology_refiner, converter) && checkGeometryCoountersMatches(topology_refiner, converter); } /////////////////////////////////////////////////////////// // Geometry comparison. bool checkGeometryEdgesMatch( const OpenSubdiv::Far::TopologyRefiner* topology_refiner, const OpenSubdiv_Converter* converter) { using OpenSubdiv::Far::ConstIndexArray; using OpenSubdiv::Far::TopologyLevel; const TopologyLevel& base_level = topology_refiner->GetLevel(0); const int num_edges = base_level.GetNumEdges(); for (int edge_index = 0; edge_index < num_edges; ++edge_index) { const ConstIndexArray& edge_vertices = base_level.GetEdgeVertices(edge_index); int conv_edge_vertices[2]; converter->getEdgeVertices(converter, edge_index, conv_edge_vertices); if (conv_edge_vertices[0] != edge_vertices[0] || conv_edge_vertices[1] != edge_vertices[1]) { return false; } } return true; } bool checkGeometryFacesMatch( const OpenSubdiv::Far::TopologyRefiner* topology_refiner, const OpenSubdiv_Converter* converter) { using OpenSubdiv::Far::ConstIndexArray; using OpenSubdiv::Far::TopologyLevel; const TopologyLevel& base_level = topology_refiner->GetLevel(0); const int num_faces = base_level.GetNumFaces(); // TODO(sergey): Consider using data structure which keeps handful of // elements on stack before doing heep allocation. std::vector 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]); // Check face-vertex indices in the direct order (assuming topology // orientation is disabled or did not flip order of the face-vertices). // // TODO(sergey): Can we simply memcmp() with OpenSubdiv's array? bool direct_match = true; for (int face_vertex_index = 0; face_vertex_index < num_face_vertices; ++face_vertex_index) { if (conv_face_vertices[face_vertex_index] != face_vertices[face_vertex_index]) { direct_match = false; break; } } if (!direct_match) { // If face didn't match in direct direction we also test if it matches in // reversed direction. This is because conversion might reverse loops to // make normals consistent. #ifdef OPENSUBDIV_ORIENT_TOPOLOGY for (int face_vertex_index = 0; face_vertex_index < num_face_vertices; ++face_vertex_index) { if (conv_face_vertices[face_vertex_index] != face_vertices[num_face_vertices - face_vertex_index - 1]) { return false; } } #endif return false; } } return true; } bool checkGeometryMatches( const OpenSubdiv::Far::TopologyRefiner* topology_refiner, const OpenSubdiv_Converter* converter) { return checkGeometryEdgesMatch(topology_refiner, converter) && checkGeometryFacesMatch(topology_refiner, converter); } /////////////////////////////////////////////////////////// // Compare attributes which affects on topology bool checkEdgeSharpnessMatch( const OpenSubdiv::Far::TopologyRefiner* topology_refiner, const OpenSubdiv_Converter* converter) { using OpenSubdiv::Far::ConstIndexArray; using OpenSubdiv::Far::TopologyLevel; const TopologyLevel& base_level = topology_refiner->GetLevel(0); const int num_edges = base_level.GetNumEdges(); for (int edge_index = 0; edge_index < num_edges; ++edge_index) { const float sharpness = base_level.GetEdgeSharpness(edge_index); const float conv_sharpness = converter->getEdgeSharpness(converter, edge_index); if (sharpness != conv_sharpness) { return false; } } return true; } bool checkTopologyAttributesMatch( const OpenSubdiv::Far::TopologyRefiner* topology_refiner, const OpenSubdiv_Converter* converter) { return checkEdgeSharpnessMatch(topology_refiner, converter); } } // namespace bool openSubdiv_topologyRefinerCompareWithConverter( const OpenSubdiv_TopologyRefiner* topology_refiner, const OpenSubdiv_Converter* converter) { const OpenSubdiv::Far::TopologyRefiner* refiner = getOSDTopologyRefiner(topology_refiner); return (checkPreliminaryMatches(refiner, converter) && checkGeometryMatches(refiner, converter) && checkTopologyAttributesMatch(refiner, converter)); }