ClangFormat: apply to source, most of intern
[blender.git] / intern / opensubdiv / internal / opensubdiv_topology_refiner.cc
1 // Copyright 2018 Blender Foundation. All rights reserved.
2 //
3 // This program is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU General Public License
5 // as published by the Free Software Foundation; either version 2
6 // of the License, or (at your option) any later version.
7 //
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 // GNU General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with this program; if not, write to the Free Software Foundation,
15 // Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 //
17 // Author: Sergey Sharybin
18
19 #include "opensubdiv_topology_refiner_capi.h"
20
21 #include <vector>
22
23 #include "MEM_guardedalloc.h"
24 #include "internal/opensubdiv_converter_factory.h"
25 #include "internal/opensubdiv_converter_internal.h"
26 #include "internal/opensubdiv_edge_map.h"
27 #include "internal/opensubdiv_internal.h"
28 #include "internal/opensubdiv_topology_refiner_internal.h"
29 #include "internal/opensubdiv_util.h"
30
31 using opensubdiv_capi::vector;
32
33 namespace {
34
35 const OpenSubdiv::Far::TopologyRefiner *getOSDTopologyRefiner(
36     const OpenSubdiv_TopologyRefiner *topology_refiner)
37 {
38   return topology_refiner->internal->osd_topology_refiner;
39 }
40
41 const OpenSubdiv::Far::TopologyLevel *getOSDTopologyBaseLevel(
42     const OpenSubdiv_TopologyRefiner *topology_refiner)
43 {
44   return &getOSDTopologyRefiner(topology_refiner)->GetLevel(0);
45 }
46
47 int getSubdivisionLevel(const OpenSubdiv_TopologyRefiner *topology_refiner)
48 {
49   return topology_refiner->internal->settings.level;
50 }
51
52 bool getIsAdaptive(const OpenSubdiv_TopologyRefiner *topology_refiner)
53 {
54   return topology_refiner->internal->settings.is_adaptive;
55 }
56
57 ////////////////////////////////////////////////////////////////////////////////
58 // Query basic topology information from base level.
59
60 int getNumVertices(const OpenSubdiv_TopologyRefiner *topology_refiner)
61 {
62   return getOSDTopologyBaseLevel(topology_refiner)->GetNumVertices();
63 }
64
65 int getNumEdges(const OpenSubdiv_TopologyRefiner *topology_refiner)
66 {
67   return getOSDTopologyBaseLevel(topology_refiner)->GetNumEdges();
68 }
69
70 int getNumFaces(const OpenSubdiv_TopologyRefiner *topology_refiner)
71 {
72   return getOSDTopologyBaseLevel(topology_refiner)->GetNumFaces();
73 }
74
75 ////////////////////////////////////////////////////////////////////////////////
76 // PTex face geometry queries.
77
78 static void convertArrayToRaw(const OpenSubdiv::Far::ConstIndexArray &array, int *raw_array)
79 {
80   for (int i = 0; i < array.size(); ++i) {
81     raw_array[i] = array[i];
82   }
83 }
84
85 int getNumFaceVertices(const OpenSubdiv_TopologyRefiner *topology_refiner, const int face_index)
86 {
87   const OpenSubdiv::Far::TopologyLevel *base_level = getOSDTopologyBaseLevel(topology_refiner);
88   return base_level->GetFaceVertices(face_index).size();
89 }
90
91 void getFaceVertices(const OpenSubdiv_TopologyRefiner *topology_refiner,
92                      const int face_index,
93                      int *face_vertices_indices)
94 {
95   const OpenSubdiv::Far::TopologyLevel *base_level = getOSDTopologyBaseLevel(topology_refiner);
96   OpenSubdiv::Far::ConstIndexArray array = base_level->GetFaceVertices(face_index);
97   convertArrayToRaw(array, face_vertices_indices);
98 }
99
100 int getNumFaceEdges(const OpenSubdiv_TopologyRefiner *topology_refiner, const int face_index)
101 {
102   const OpenSubdiv::Far::TopologyLevel *base_level = getOSDTopologyBaseLevel(topology_refiner);
103   return base_level->GetFaceEdges(face_index).size();
104 }
105
106 void getFaceEdges(const OpenSubdiv_TopologyRefiner *topology_refiner,
107                   const int face_index,
108                   int *face_edges_indices)
109 {
110   const OpenSubdiv::Far::TopologyLevel *base_level = getOSDTopologyBaseLevel(topology_refiner);
111   OpenSubdiv::Far::ConstIndexArray array = base_level->GetFaceEdges(face_index);
112   convertArrayToRaw(array, face_edges_indices);
113 }
114
115 void getEdgeVertices(const OpenSubdiv_TopologyRefiner *topology_refiner,
116                      const int edge_index,
117                      int edge_vertices_indices[2])
118 {
119   const OpenSubdiv::Far::TopologyLevel *base_level = getOSDTopologyBaseLevel(topology_refiner);
120   OpenSubdiv::Far::ConstIndexArray array = base_level->GetEdgeVertices(edge_index);
121   assert(array.size() == 2);
122   edge_vertices_indices[0] = array[0];
123   edge_vertices_indices[1] = array[1];
124 }
125
126 int getNumFacePtexFaces(const OpenSubdiv_TopologyRefiner *topology_refiner, const int face_index)
127 {
128   const int num_face_vertices = topology_refiner->getNumFaceVertices(topology_refiner, face_index);
129   if (num_face_vertices == 4) {
130     return 1;
131   }
132   else {
133     return num_face_vertices;
134   }
135 }
136
137 int getNumPtexFaces(const OpenSubdiv_TopologyRefiner *topology_refiner)
138 {
139   const int num_faces = topology_refiner->getNumFaces(topology_refiner);
140   int num_ptex_faces = 0;
141   for (int face_index = 0; face_index < num_faces; ++face_index) {
142     num_ptex_faces += topology_refiner->getNumFacePtexFaces(topology_refiner, face_index);
143   }
144   return num_ptex_faces;
145 }
146
147 void fillFacePtexIndexOffset(const OpenSubdiv_TopologyRefiner *topology_refiner,
148                              int *face_ptex_index_offset)
149 {
150   const int num_faces = topology_refiner->getNumFaces(topology_refiner);
151   int num_ptex_faces = 0;
152   for (int face_index = 0; face_index < num_faces; ++face_index) {
153     face_ptex_index_offset[face_index] = num_ptex_faces;
154     num_ptex_faces += topology_refiner->getNumFacePtexFaces(topology_refiner, face_index);
155   }
156 }
157
158 ////////////////////////////////////////////////////////////////////////////////
159 // Face-varying data.
160
161 int getNumFVarChannels(const struct OpenSubdiv_TopologyRefiner *topology_refiner)
162 {
163   const OpenSubdiv::Far::TopologyLevel *base_level = getOSDTopologyBaseLevel(topology_refiner);
164   return base_level->GetNumFVarChannels();
165 }
166
167 OpenSubdiv_FVarLinearInterpolation getFVarLinearInterpolation(
168     const struct OpenSubdiv_TopologyRefiner *topology_refiner)
169 {
170   return opensubdiv_capi::getCAPIFVarLinearInterpolationFromOSD(
171       getOSDTopologyRefiner(topology_refiner)->GetFVarLinearInterpolation());
172 }
173
174 int getNumFVarValues(const struct OpenSubdiv_TopologyRefiner *topology_refiner, const int channel)
175 {
176   const OpenSubdiv::Far::TopologyLevel *base_level = getOSDTopologyBaseLevel(topology_refiner);
177   return base_level->GetNumFVarValues(channel);
178 }
179
180 const int *getFaceFVarValueIndices(const struct OpenSubdiv_TopologyRefiner *topology_refiner,
181                                    const int face_index,
182                                    const int channel)
183 {
184   const OpenSubdiv::Far::TopologyLevel *base_level = getOSDTopologyBaseLevel(topology_refiner);
185   return &base_level->GetFaceFVarValues(face_index, channel)[0];
186 }
187
188 ////////////////////////////////////////////////////////////////////////////////
189 // Internal helpers.
190
191 void assignFunctionPointers(OpenSubdiv_TopologyRefiner *topology_refiner)
192 {
193   topology_refiner->getSubdivisionLevel = getSubdivisionLevel;
194   topology_refiner->getIsAdaptive = getIsAdaptive;
195   // Basic topology information.
196   topology_refiner->getNumVertices = getNumVertices;
197   topology_refiner->getNumEdges = getNumEdges;
198   topology_refiner->getNumFaces = getNumFaces;
199   topology_refiner->getNumFaceVertices = getNumFaceVertices;
200   topology_refiner->getFaceVertices = getFaceVertices;
201   topology_refiner->getNumFaceEdges = getNumFaceEdges;
202   topology_refiner->getFaceEdges = getFaceEdges;
203   topology_refiner->getEdgeVertices = getEdgeVertices;
204   // PTex face geometry.
205   topology_refiner->getNumFacePtexFaces = getNumFacePtexFaces;
206   topology_refiner->getNumPtexFaces = getNumPtexFaces;
207   topology_refiner->fillFacePtexIndexOffset = fillFacePtexIndexOffset;
208   // Face-varying data.
209   topology_refiner->getNumFVarChannels = getNumFVarChannels;
210   topology_refiner->getFVarLinearInterpolation = getFVarLinearInterpolation;
211   topology_refiner->getNumFVarValues = getNumFVarValues;
212   topology_refiner->getFaceFVarValueIndices = getFaceFVarValueIndices;
213 }
214
215 OpenSubdiv_TopologyRefiner *allocateTopologyRefiner()
216 {
217   OpenSubdiv_TopologyRefiner *topology_refiner = OBJECT_GUARDED_NEW(OpenSubdiv_TopologyRefiner);
218   topology_refiner->internal = OBJECT_GUARDED_NEW(OpenSubdiv_TopologyRefinerInternal);
219   assignFunctionPointers(topology_refiner);
220   return topology_refiner;
221 }
222
223 }  // namespace
224
225 OpenSubdiv_TopologyRefiner *openSubdiv_createTopologyRefinerFromConverter(
226     OpenSubdiv_Converter *converter, const OpenSubdiv_TopologyRefinerSettings *settings)
227 {
228   OpenSubdiv::Far::TopologyRefiner *osd_topology_refiner =
229       opensubdiv_capi::createOSDTopologyRefinerFromConverter(converter);
230   if (osd_topology_refiner == NULL) {
231     // Happens on empty or bad topology.
232     return NULL;
233   }
234   OpenSubdiv_TopologyRefiner *topology_refiner = allocateTopologyRefiner();
235   topology_refiner->internal->osd_topology_refiner = osd_topology_refiner;
236   // Store setting which we want to keep track of and which can not be stored
237   // in OpenSubdiv's descriptor yet.
238   topology_refiner->internal->settings = *settings;
239   return topology_refiner;
240 }
241
242 void openSubdiv_deleteTopologyRefiner(OpenSubdiv_TopologyRefiner *topology_refiner)
243 {
244   OBJECT_GUARDED_DELETE(topology_refiner->internal, OpenSubdiv_TopologyRefinerInternal);
245   OBJECT_GUARDED_DELETE(topology_refiner, OpenSubdiv_TopologyRefiner);
246 }
247
248 ////////////////////////////////////////////////////////////////////////////////
249 // Comparison with converter.
250
251 namespace opensubdiv_capi {
252 namespace {
253
254 ///////////////////////////////////////////////////////////
255 // Quick preliminary checks.
256
257 bool checkSchemeTypeMatches(const OpenSubdiv::Far::TopologyRefiner *topology_refiner,
258                             const OpenSubdiv_Converter *converter)
259 {
260   const OpenSubdiv::Sdc::SchemeType converter_scheme_type = opensubdiv_capi::getSchemeTypeFromCAPI(
261       converter->getSchemeType(converter));
262   return (converter_scheme_type == topology_refiner->GetSchemeType());
263 }
264
265 bool checkOptionsMatches(const OpenSubdiv::Far::TopologyRefiner *topology_refiner,
266                          const OpenSubdiv_Converter *converter)
267 {
268   typedef OpenSubdiv::Sdc::Options Options;
269   const Options options = topology_refiner->GetSchemeOptions();
270   const Options::FVarLinearInterpolation fvar_interpolation = options.GetFVarLinearInterpolation();
271   const Options::FVarLinearInterpolation converter_fvar_interpolation =
272       opensubdiv_capi::getFVarLinearInterpolationFromCAPI(
273           converter->getFVarLinearInterpolation(converter));
274   if (fvar_interpolation != converter_fvar_interpolation) {
275     return false;
276   }
277   return true;
278 }
279
280 bool checkGeometryCountersMatches(const OpenSubdiv::Far::TopologyRefiner *topology_refiner,
281                                   const OpenSubdiv_Converter *converter)
282 {
283   using OpenSubdiv::Far::TopologyLevel;
284   const TopologyLevel &base_level = topology_refiner->GetLevel(0);
285   return ((converter->getNumVertices(converter) == base_level.GetNumVertices()) &&
286           (converter->getNumEdges(converter) == base_level.GetNumEdges()) &&
287           (converter->getNumFaces(converter) == base_level.GetNumFaces()));
288 }
289
290 bool checkPreliminaryMatches(const OpenSubdiv::Far::TopologyRefiner *topology_refiner,
291                              const OpenSubdiv_Converter *converter)
292 {
293   return checkSchemeTypeMatches(topology_refiner, converter) &&
294          checkOptionsMatches(topology_refiner, converter) &&
295          checkGeometryCountersMatches(topology_refiner, converter);
296 }
297
298 ///////////////////////////////////////////////////////////
299 // Geometry comparison.
300
301 // A thin wrapper around index like array which does cyclic access. This means,
302 // it basically does indices[requested_index % num_indices].
303 //
304 // NOTE: This array does not own the memory.
305 //
306 // TODO(sergey): Consider moving this to a more reusable place.
307 class CyclicArray {
308  public:
309   typedef int value_type;
310   typedef int size_type;
311   static constexpr size_type npos = -1;
312
313   explicit CyclicArray(const std::vector<int> &data) : data_(data.data()), size_(data.size())
314   {
315   }
316
317   explicit CyclicArray(const OpenSubdiv::Far::ConstIndexArray &data)
318       : data_(&data[0]), size_(data.size())
319   {
320   }
321
322   inline value_type operator[](int index) const
323   {
324     assert(index >= 0);
325     // TODO(sergey): Check whether doing check for element index exceeding total
326     // number of indices prior to modulo helps performance.
327     return data_[index % size()];
328   }
329
330   inline size_type size() const
331   {
332     return size_;
333   }
334
335   // Find index of first occurrence of a given value.
336   inline size_type find(const value_type value) const
337   {
338     const int num_indices = size();
339     for (size_type i = 0; i < num_indices; ++i) {
340       if (value == (*this)[i]) {
341         return i;
342       }
343     }
344     return npos;
345   }
346
347  protected:
348   const value_type *data_;
349   const size_type size_;
350 };
351
352 bool compareCyclicForward(const CyclicArray &array_a,
353                           const int start_a,
354                           const CyclicArray &array_b,
355                           const int start_b)
356 {
357   const int num_elements = array_a.size();
358   for (int i = 0; i < num_elements; ++i) {
359     if (array_a[start_a + i] != array_b[start_b + i]) {
360       return false;
361     }
362   }
363   return true;
364 }
365
366 bool compareCyclicBackward(const CyclicArray &array_a,
367                            const int start_a,
368                            const CyclicArray &array_b,
369                            const int start_b)
370 {
371   const int num_elements = array_a.size();
372   // TODO(sergey): Some optimization might be possible with memcmp trickery.
373   for (int i = 0; i < num_elements; ++i) {
374     if (array_a[start_a + (num_elements - i - 1)] != array_b[start_b + (num_elements - i - 1)]) {
375       return false;
376     }
377   }
378   return true;
379 }
380
381 // Utility function dedicated for checking whether whether verticies indices
382 // used by two faces match.
383 // The tricky part here is that we can't trust 1:1 array match here, since it's
384 // possible that OpenSubdiv oriented edges of a face to make it compatible with
385 // an internal representation of non-manifold meshes.
386 //
387 // TODO(sergey): Check whether this is needed, ot whether OpenSubdiv is only
388 // creating edges in a proper orientation without modifying indices of face
389 // verticies.
390 bool checkVerticesOfFacesMatch(const CyclicArray &indices_a, const CyclicArray &indices_b)
391 {
392   if (indices_a.size() != indices_a.size()) {
393     return false;
394   }
395   // "Align" the arrays so we know first matched element.
396   const int start_b = indices_b.find(indices_a[0]);
397   if (start_b == indices_b.npos) {
398     return false;
399   }
400   // Check match in both directions, for the case OpenSubdiv did orient face in
401   // a way which made normals more consistent internally.
402   if (compareCyclicForward(indices_a, 0, indices_b, start_b)) {
403     return true;
404   }
405   if (compareCyclicBackward(indices_a, 0, indices_b, start_b)) {
406     return true;
407   }
408   return false;
409 }
410
411 bool checkGeometryFacesMatch(const OpenSubdiv::Far::TopologyRefiner *topology_refiner,
412                              const OpenSubdiv_Converter *converter)
413 {
414   using OpenSubdiv::Far::ConstIndexArray;
415   using OpenSubdiv::Far::TopologyLevel;
416   const TopologyLevel &base_level = topology_refiner->GetLevel(0);
417   const int num_faces = base_level.GetNumFaces();
418   // TODO(sergey): Consider using data structure which keeps handful of
419   // elements on stack before doing heep allocation.
420   vector<int> conv_face_vertices;
421   for (int face_index = 0; face_index < num_faces; ++face_index) {
422     const ConstIndexArray &face_vertices = base_level.GetFaceVertices(face_index);
423     const int num_face_vertices = face_vertices.size();
424     if (num_face_vertices != converter->getNumFaceVertices(converter, face_index)) {
425       return false;
426     }
427     conv_face_vertices.resize(num_face_vertices);
428     converter->getFaceVertices(converter, face_index, &conv_face_vertices[0]);
429     if (!checkVerticesOfFacesMatch(CyclicArray(conv_face_vertices), CyclicArray(face_vertices))) {
430       return false;
431     }
432   }
433   return true;
434 }
435
436 bool checkGeometryMatches(const OpenSubdiv::Far::TopologyRefiner *topology_refiner,
437                           const OpenSubdiv_Converter *converter)
438 {
439   // NOTE: Since OpenSubdiv's topology refiner doesn't contain loose edges, we
440   // are only checking for faces to be matched. Changes in edges we don't care
441   // here too much (they'll be checked for creases changes later).
442   return checkGeometryFacesMatch(topology_refiner, converter);
443 }
444
445 ///////////////////////////////////////////////////////////
446 // Compare attributes which affects on topology
447
448 inline bool checkSingleEdgeSharpnessMatch(const OpenSubdiv::Far::TopologyLevel &base_level,
449                                           int base_level_edge_index,
450                                           const OpenSubdiv_Converter *converter,
451                                           int converter_edge_index)
452 {
453   // NOTE: Boundary and non-manifold edges are internally forced to an infinite
454   // sharpness. So we can not reliably compare those.
455   //
456   // TODO(sergey): Watch for NON_MANIFOLD_SHARP option.
457   if (base_level.IsEdgeBoundary(base_level_edge_index) ||
458       base_level.IsEdgeNonManifold(base_level_edge_index)) {
459     return true;
460   }
461   const float sharpness = base_level.GetEdgeSharpness(base_level_edge_index);
462   const float converter_sharpness = converter->getEdgeSharpness(converter, converter_edge_index);
463   if (sharpness != converter_sharpness) {
464     return false;
465   }
466   return true;
467 }
468
469 inline bool checkSingleEdgeTagMatch(const OpenSubdiv::Far::TopologyLevel &base_level,
470                                     int base_level_edge_index,
471                                     const OpenSubdiv_Converter *converter,
472                                     int converter_edge_index)
473 {
474   return checkSingleEdgeSharpnessMatch(
475       base_level, base_level_edge_index, converter, converter_edge_index);
476 }
477
478 // Compares edge tags between topology refiner and converter in a case when
479 // converter specifies a full topology.
480 // This is simplest loop, since we know that order of edges matches.
481 bool checkEdgeTagsMatchFullTopology(const OpenSubdiv::Far::TopologyRefiner *topology_refiner,
482                                     const OpenSubdiv_Converter *converter)
483 {
484   using OpenSubdiv::Far::ConstIndexArray;
485   using OpenSubdiv::Far::TopologyLevel;
486   const TopologyLevel &base_level = topology_refiner->GetLevel(0);
487   const int num_edges = base_level.GetNumEdges();
488   for (int edge_index = 0; edge_index < num_edges; ++edge_index) {
489     if (!checkSingleEdgeTagMatch(base_level, edge_index, converter, edge_index)) {
490       return false;
491     }
492   }
493   return true;
494 }
495
496 // Compares tags of edges in the case when orientation of edges is left up to
497 // OpenSubdiv. In this case we do need to take care of mapping edges from the
498 // converter to current topology refiner, since the order is not guaranteed.
499 bool checkEdgeTagsMatchAutoOrient(const OpenSubdiv::Far::TopologyRefiner *topology_refiner,
500                                   const OpenSubdiv_Converter *converter)
501 {
502   using OpenSubdiv::Far::ConstIndexArray;
503   using OpenSubdiv::Far::TopologyLevel;
504   const TopologyLevel &base_level = topology_refiner->GetLevel(0);
505   const int num_edges = base_level.GetNumEdges();
506   // Create mapping for quick lookup of edge index from its verticies indices.
507   //
508   // TODO(sergey): Consider caching it in some sort of wrapper around topology
509   // refiner.
510   EdgeTagMap<int> edge_map;
511   for (int edge_index = 0; edge_index < num_edges; ++edge_index) {
512     ConstIndexArray edge_vertices = base_level.GetEdgeVertices(edge_index);
513     edge_map.insert(edge_vertices[0], edge_vertices[1], edge_index);
514   }
515   // Compare all edges.
516   for (int converter_edge_index = 0; converter_edge_index < num_edges; ++converter_edge_index) {
517     // Get edge verticies indices, and lookup corresponding edge index in the
518     // base topology level.
519     int edge_vertices[2];
520     converter->getEdgeVertices(converter, converter_edge_index, edge_vertices);
521     const int base_level_edge_index = edge_map.at(edge_vertices[0], edge_vertices[1]);
522     // Perform actual test.
523     if (!checkSingleEdgeTagMatch(
524             base_level, base_level_edge_index, converter, converter_edge_index)) {
525       return false;
526     }
527   }
528   return true;
529 }
530
531 bool checkEdgeTagsMatch(const OpenSubdiv::Far::TopologyRefiner *topology_refiner,
532                         const OpenSubdiv_Converter *converter)
533 {
534   if (converter->specifiesFullTopology(converter)) {
535     return checkEdgeTagsMatchFullTopology(topology_refiner, converter);
536   }
537   else {
538     return checkEdgeTagsMatchAutoOrient(topology_refiner, converter);
539   }
540 }
541
542 bool checkvertexSharpnessMatch(const OpenSubdiv::Far::TopologyRefiner *topology_refiner,
543                                const OpenSubdiv_Converter *converter)
544 {
545   using OpenSubdiv::Far::ConstIndexArray;
546   using OpenSubdiv::Far::TopologyLevel;
547   using OpenSubdiv::Sdc::Crease;
548   const TopologyLevel &base_level = topology_refiner->GetLevel(0);
549   // Create mapping for quick lookup of edge index from its verticies indices.
550   //
551   // TODO(sergey): Consider caching it in some sort of wrapper around topology
552   // refiner.
553   const int num_edges = base_level.GetNumEdges();
554   EdgeTagMap<int> edge_map;
555   for (int edge_index = 0; edge_index < num_edges; ++edge_index) {
556     int edge_vertices[2];
557     converter->getEdgeVertices(converter, edge_index, edge_vertices);
558     edge_map.insert(edge_vertices[0], edge_vertices[1], edge_index);
559   }
560   const int num_vertices = base_level.GetNumVertices();
561   for (int vertex_index = 0; vertex_index < num_vertices; ++vertex_index) {
562     const float current_sharpness = base_level.GetVertexSharpness(vertex_index);
563     if (converter->isInfiniteSharpVertex(converter, vertex_index)) {
564       if (current_sharpness != Crease::SHARPNESS_INFINITE) {
565         return false;
566       }
567     }
568     else {
569       ConstIndexArray vertex_edges = base_level.GetVertexEdges(vertex_index);
570       float sharpness = converter->getVertexSharpness(converter, vertex_index);
571       if (vertex_edges.size() == 2) {
572         const int edge0 = vertex_edges[0], edge1 = vertex_edges[1];
573         // Construct keys for lookup.
574         ConstIndexArray edge0_vertices = base_level.GetEdgeVertices(edge0);
575         ConstIndexArray edge1_vertices = base_level.GetEdgeVertices(edge1);
576         EdgeKey edge0_key(edge0_vertices[0], edge0_vertices[1]);
577         EdgeKey edge1_key(edge1_vertices[0], edge1_vertices[1]);
578         // Lookup edge indices in the converter.
579         const int edge0_converter_index = edge_map[edge0_key];
580         const int edge1_converter_index = edge_map[edge1_key];
581         // Lookup sharpness.
582         const float sharpness0 = converter->getEdgeSharpness(converter, edge0_converter_index);
583         const float sharpness1 = converter->getEdgeSharpness(converter, edge1_converter_index);
584         // TODO(sergey): Find a better mixing between edge and vertex sharpness.
585         sharpness += min(sharpness0, sharpness1);
586         sharpness = min(sharpness, 10.0f);
587       }
588       if (sharpness != current_sharpness) {
589         return false;
590       }
591     }
592   }
593   return true;
594 }
595
596 bool checkSingleUVLayerMatch(const OpenSubdiv::Far::TopologyLevel &base_level,
597                              const OpenSubdiv_Converter *converter,
598                              const int layer_index)
599 {
600   converter->precalcUVLayer(converter, layer_index);
601   const int num_faces = base_level.GetNumFaces();
602   // TODO(sergey): Need to check whether converter changed the winding of
603   // face to match OpenSubdiv's expectations.
604   for (int face_index = 0; face_index < num_faces; ++face_index) {
605     OpenSubdiv::Far::ConstIndexArray base_level_face_uvs = base_level.GetFaceFVarValues(
606         face_index, layer_index);
607     for (int corner = 0; corner < base_level_face_uvs.size(); ++corner) {
608       const int uv_index = converter->getFaceCornerUVIndex(converter, face_index, corner);
609       if (base_level_face_uvs[corner] != uv_index) {
610         converter->finishUVLayer(converter);
611         return false;
612       }
613     }
614   }
615   converter->finishUVLayer(converter);
616   return true;
617 }
618
619 bool checkUVLayersMatch(const OpenSubdiv::Far::TopologyRefiner *topology_refiner,
620                         const OpenSubdiv_Converter *converter)
621 {
622   using OpenSubdiv::Far::TopologyLevel;
623   const int num_layers = converter->getNumUVLayers(converter);
624   const TopologyLevel &base_level = topology_refiner->GetLevel(0);
625   // Number of UV layers should match.
626   if (base_level.GetNumFVarChannels() != num_layers) {
627     return false;
628   }
629   for (int layer_index = 0; layer_index < num_layers; ++layer_index) {
630     if (!checkSingleUVLayerMatch(base_level, converter, layer_index)) {
631       return false;
632     }
633   }
634   return true;
635 }
636
637 bool checkTopologyAttributesMatch(const OpenSubdiv::Far::TopologyRefiner *topology_refiner,
638                                   const OpenSubdiv_Converter *converter)
639 {
640   return checkEdgeTagsMatch(topology_refiner, converter) &&
641          checkvertexSharpnessMatch(topology_refiner, converter) &&
642          checkUVLayersMatch(topology_refiner, converter);
643 }
644
645 }  // namespace
646 }  // namespace opensubdiv_capi
647
648 bool openSubdiv_topologyRefinerCompareWithConverter(
649     const OpenSubdiv_TopologyRefiner *topology_refiner, const OpenSubdiv_Converter *converter)
650 {
651   const OpenSubdiv::Far::TopologyRefiner *refiner = getOSDTopologyRefiner(topology_refiner);
652   return (opensubdiv_capi::checkPreliminaryMatches(refiner, converter) &&
653           opensubdiv_capi::checkGeometryMatches(refiner, converter) &&
654           opensubdiv_capi::checkTopologyAttributesMatch(refiner, converter));
655 }