9b2fb19808c1e4a7b436778c0504423bdeeff8ce
[blender.git] / intern / opensubdiv / opensubdiv_converter.cc
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2015 Blender Foundation.
19  * All rights reserved.
20  *
21  * Contributor(s): Sergey Sharybin.
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 #include <cstdio>
27 #include <vector>
28
29 #ifdef _MSC_VER
30 #  include "iso646.h"
31 #endif
32
33 #include <opensubdiv/far/topologyRefinerFactory.h>
34
35 #include "MEM_guardedalloc.h"
36
37 #include "opensubdiv_converter_capi.h"
38 #include "opensubdiv_intern.h"
39 #include "opensubdiv_topology_refiner.h"
40
41
42 #include <stack>
43
44 #ifdef OPENSUBDIV_ORIENT_TOPOLOGY
45 namespace {
46
47 inline void reverse_face_verts(int *face_verts, int num_verts)
48 {
49         int last_vert = face_verts[num_verts - 1];
50         for (int i = num_verts - 1; i > 0;  --i) {
51                 face_verts[i] = face_verts[i - 1];
52         }
53         face_verts[0] = last_vert;
54 }
55
56 struct TopologyRefinerData {
57         const OpenSubdiv_Converter& conv;
58         std::vector<float> *uvs;
59 };
60
61 }  /* namespace */
62 #endif /* OPENSUBDIV_ORIENT_TOPOLOGY */
63
64 namespace OpenSubdiv {
65 namespace OPENSUBDIV_VERSION {
66 namespace Far {
67
68 namespace {
69
70 template <typename T>
71 inline int findInArray(T array, int value)
72 {
73         return (int)(std::find(array.begin(), array.end(), value) - array.begin());
74 }
75
76 #ifdef OPENSUBDIV_ORIENT_TOPOLOGY
77 inline int get_loop_winding(int vert0_of_face, int vert1_of_face)
78 {
79         int delta_face = vert1_of_face - vert0_of_face;
80         if (abs(delta_face) != 1) {
81                 if (delta_face > 0) {
82                         delta_face = -1;
83                 }
84                 else {
85                         delta_face = 1;
86                 }
87         }
88         return delta_face;
89 }
90
91 inline void reverse_face_loops(IndexArray face_verts, IndexArray face_edges)
92 {
93         for (int i = 0; i < face_verts.size() / 2; ++i) {
94                 int j = face_verts.size() - i - 1;
95                 if (i != j) {
96                         std::swap(face_verts[i], face_verts[j]);
97                         std::swap(face_edges[i], face_edges[j]);
98                 }
99         }
100         reverse_face_verts(&face_verts[0], face_verts.size());
101 }
102
103 inline void check_oriented_vert_connectivity(const int num_vert_edges,
104                                              const int num_vert_faces,
105                                              const int *vert_edges,
106                                              const int *vert_faces,
107                                              const int *dst_vert_edges,
108                                              const int *dst_vert_faces)
109 {
110 #  ifndef NDEBUG
111         for (int i = 0; i < num_vert_faces; ++i) {
112                 bool found = false;
113                 for (int j = 0; j < num_vert_faces; ++j) {
114                         if (vert_faces[i] == dst_vert_faces[j]) {
115                                 found = true;
116                                 break;
117                         }
118                 }
119                 if (!found) {
120                         assert(!"vert-faces connectivity ruined");
121                 }
122         }
123         for (int i = 0; i < num_vert_edges; ++i) {
124                 bool found = false;
125                 for (int j = 0; j < num_vert_edges; ++j) {
126                         if (vert_edges[i] == dst_vert_edges[j]) {
127                                 found = true;
128                                 break;
129                         }
130                 }
131                 if (!found) {
132                         assert(!"vert-edges connectivity ruined");
133                 }
134         }
135 #  else
136         (void)num_vert_edges;
137         (void)num_vert_faces;
138         (void)vert_edges;
139         (void)vert_faces;
140         (void)dst_vert_edges;
141         (void)dst_vert_faces;
142 #  endif
143 }
144 #endif
145
146 }  /* namespace */
147
148 template <>
149 inline bool TopologyRefinerFactory<TopologyRefinerData>::resizeComponentTopology(
150         TopologyRefiner& refiner,
151         const TopologyRefinerData& cb_data)
152 {
153         const OpenSubdiv_Converter& conv = cb_data.conv;
154         /* Faces and face-verts */
155         const int num_faces = conv.get_num_faces(&conv);
156         setNumBaseFaces(refiner, num_faces);
157         for (int face = 0; face < num_faces; ++face) {
158                 const int num_verts = conv.get_num_face_verts(&conv, face);
159                 setNumBaseFaceVertices(refiner, face, num_verts);
160         }
161         /* Edges and edge-faces. */
162         const int num_edges = conv.get_num_edges(&conv);
163         setNumBaseEdges(refiner, num_edges);
164         for (int edge = 0; edge < num_edges; ++edge) {
165                 const int num_edge_faces = conv.get_num_edge_faces(&conv, edge);
166                 setNumBaseEdgeFaces(refiner, edge, num_edge_faces);
167         }
168         /* Vertices and vert-faces and vert-edges/ */
169         const int num_verts = conv.get_num_verts(&conv);
170         setNumBaseVertices(refiner, num_verts);
171         for (int vert = 0; vert < num_verts; ++vert) {
172                 const int num_vert_edges = conv.get_num_vert_edges(&conv, vert),
173                           num_vert_faces = conv.get_num_vert_faces(&conv, vert);
174                 setNumBaseVertexEdges(refiner, vert, num_vert_edges);
175                 setNumBaseVertexFaces(refiner, vert, num_vert_faces);
176         }
177         return true;
178 }
179
180 template <>
181 inline bool TopologyRefinerFactory<TopologyRefinerData>::assignComponentTopology(
182           TopologyRefiner& refiner,
183           const TopologyRefinerData &cb_data)
184 {
185         const OpenSubdiv_Converter& conv = cb_data.conv;
186         using Far::IndexArray;
187         /* Face relations. */
188         const int num_faces = conv.get_num_faces(&conv);
189         for (int face = 0; face < num_faces; ++face) {
190                 IndexArray dst_face_verts = getBaseFaceVertices(refiner, face);
191                 conv.get_face_verts(&conv, face, &dst_face_verts[0]);
192                 IndexArray dst_face_edges = getBaseFaceEdges(refiner, face);
193                 conv.get_face_edges(&conv, face, &dst_face_edges[0]);
194         }
195         /* Edge relations. */
196         const int num_edges = conv.get_num_edges(&conv);
197         for (int edge = 0; edge < num_edges; ++edge) {
198                 /* Edge-vertices */
199                 IndexArray dst_edge_verts = getBaseEdgeVertices(refiner, edge);
200                 conv.get_edge_verts(&conv, edge, &dst_edge_verts[0]);
201                 /* Edge-faces */
202                 IndexArray dst_edge_faces = getBaseEdgeFaces(refiner, edge);
203                 conv.get_edge_faces(&conv, edge, &dst_edge_faces[0]);
204         }
205 #ifdef OPENSUBDIV_ORIENT_TOPOLOGY
206         /* Make face normals consistent. */
207         bool *face_used = new bool[num_faces];
208         memset(face_used, 0, sizeof(bool) * num_faces);
209         std::stack<int> traverse_stack;
210         int face_start = 0, num_traversed_faces = 0;
211         /* Traverse all islands. */
212         while (num_traversed_faces != num_faces) {
213                 /* Find first face of any untraversed islands. */
214                 while (face_used[face_start]) {
215                         ++face_start;
216                 }
217                 /* Add first face to the stack. */
218                 traverse_stack.push(face_start);
219                 face_used[face_start] = true;
220                 /* Go over whole connected component. */
221                 while (!traverse_stack.empty()) {
222                         int face = traverse_stack.top();
223                         traverse_stack.pop();
224                         IndexArray face_edges = getBaseFaceEdges(refiner, face);
225                         ConstIndexArray face_verts = getBaseFaceVertices(refiner, face);
226                         for (int edge_index = 0; edge_index < face_edges.size(); ++edge_index) {
227                                 const int edge = face_edges[edge_index];
228                                 ConstIndexArray edge_faces = getBaseEdgeFaces(refiner, edge);
229                                 if (edge_faces.size() != 2) {
230                                         /* Can't make consistent normals for non-manifolds. */
231                                         continue;
232                                 }
233                                 ConstIndexArray edge_verts = getBaseEdgeVertices(refiner, edge);
234                                 /* Get winding of the reference face. */
235                                 int vert0_of_face = findInArray(face_verts, edge_verts[0]),
236                                     vert1_of_face = findInArray(face_verts, edge_verts[1]);
237                                 int delta_face = get_loop_winding(vert0_of_face, vert1_of_face);
238                                 for (int edge_face = 0; edge_face < edge_faces.size(); ++edge_face) {
239                                         int other_face = edge_faces[edge_face];
240                                         /* Never re-traverse faces, only move forward. */
241                                         if (face_used[other_face]) {
242                                                 continue;
243                                         }
244                                         IndexArray other_face_verts = getBaseFaceVertices(refiner,
245                                                                                           other_face);
246                                         int vert0_of_other_face = findInArray(other_face_verts,
247                                                                               edge_verts[0]),
248                                             vert1_of_other_face = findInArray(other_face_verts,
249                                                                               edge_verts[1]);
250                                         int delta_other_face = get_loop_winding(vert0_of_other_face,
251                                                                                 vert1_of_other_face);
252                                         if (delta_face * delta_other_face > 0) {
253                                                 IndexArray other_face_verts = getBaseFaceVertices(refiner,
254                                                                                                   other_face),
255                                                            other_face_edges = getBaseFaceEdges(refiner,
256                                                                                                other_face);
257                                                 reverse_face_loops(other_face_verts,
258                                                                    other_face_edges);
259                                         }
260                                         traverse_stack.push(other_face);
261                                         face_used[other_face] = true;
262                                 }
263                         }
264                         ++num_traversed_faces;
265                 }
266         }
267 #endif  /* OPENSUBDIV_ORIENT_TOPOLOGY */
268         /* Vertex relations */
269         const int num_verts = conv.get_num_verts(&conv);
270         for (int vert = 0; vert < num_verts; ++vert) {
271
272                 /* Vert-Faces */
273                 IndexArray dst_vert_faces = getBaseVertexFaces(refiner, vert);
274                 int num_vert_faces = conv.get_num_vert_faces(&conv, vert);
275                 int *vert_faces = new int[num_vert_faces];
276                 conv.get_vert_faces(&conv, vert, vert_faces);
277                 /* Vert-Edges */
278                 IndexArray dst_vert_edges = getBaseVertexEdges(refiner, vert);
279                 int num_vert_edges = conv.get_num_vert_edges(&conv, vert);
280                 int *vert_edges = new int[num_vert_edges];
281                 conv.get_vert_edges(&conv, vert, vert_edges);
282 #ifdef OPENSUBDIV_ORIENT_TOPOLOGY
283                 /* ** Order vertex edges and faces in a CCW order. ** */
284                 memset(face_used, 0, sizeof(bool) * num_faces);
285                 /* Number of edges and faces added to the ordered array. */
286                 int edge_count_ordered = 0, face_count_ordered = 0;
287                 /* Add loose edges straight into the edges array. */
288                 bool has_fan_connections = false;
289                 for (int i = 0; i < num_vert_edges; ++i) {
290                         IndexArray edge_faces = getBaseEdgeFaces(refiner, vert_edges[i]);
291                         if (edge_faces.size() == 0) {
292                                 dst_vert_edges[edge_count_ordered++] = vert_edges[i];
293                         }
294                         else if (edge_faces.size() > 2) {
295                                 has_fan_connections = true;
296                         }
297                 }
298                 if (has_fan_connections) {
299                         /* OpenSubdiv currently doesn't give us clues how to handle
300                          * fan face connections. and since handling such connections
301                          * complicates the loop below we simply don't do special
302                          * orientation for them.
303                          */
304                         memcpy(&dst_vert_edges[0], vert_edges, sizeof(int) * num_vert_edges);
305                         memcpy(&dst_vert_faces[0], vert_faces, sizeof(int) * num_vert_faces);
306                         delete [] vert_edges;
307                         delete [] vert_faces;
308                         continue;
309                 }
310                 /* Perform at max numbder of vert-edges iteration and try to avoid
311                  * deadlock here for malformed mesh.
312                  */
313                 for (int global_iter = 0; global_iter < num_vert_edges; ++global_iter) {
314                         /* Numbr of edges and faces which are still to be ordered. */
315                         int num_vert_edges_remained = num_vert_edges - edge_count_ordered,
316                             num_vert_faces_remained = num_vert_faces - face_count_ordered;
317                         if (num_vert_edges_remained == 0 && num_vert_faces_remained == 0) {
318                                 /* All done, nothing to do anymore. */
319                                 break;
320                         }
321                         /* Face, edge and face-vertex inndex to start traversal from. */
322                         int face_start = -1, edge_start = -1, face_vert_start = -1;
323                         if (num_vert_edges_remained == num_vert_faces_remained) {
324                                 /* Vertex is eitehr complete manifold or is connected to seevral
325                                  * manifold islands (hourglass-like configuration), can pick up
326                                  * random edge unused and start from it.
327                                  */
328                                 /* TODO(sergey): Start from previous edge from which traversal
329                                  * began at previous iteration.
330                                  */
331                                 for (int i = 0; i < num_vert_edges; ++i) {
332                                         face_start = vert_faces[i];
333                                         if (!face_used[face_start]) {
334                                                 ConstIndexArray
335                                                     face_verts = getBaseFaceVertices(refiner, face_start),
336                                                     face_edges = getBaseFaceEdges(refiner, face_start);
337                                                 face_vert_start = findInArray(face_verts, vert);
338                                                 edge_start = face_edges[face_vert_start];
339                                                 break;
340                                         }
341                                 }
342                         }
343                         else {
344                                 /* Special handle of non-manifold vertex. */
345                                 for (int i = 0; i < num_vert_edges; ++i) {
346                                         edge_start = vert_edges[i];
347                                         IndexArray edge_faces = getBaseEdgeFaces(refiner, edge_start);
348                                         if (edge_faces.size() == 1) {
349                                                 face_start = edge_faces[0];
350                                                 if (!face_used[face_start]) {
351                                                         ConstIndexArray
352                                                             face_verts = getBaseFaceVertices(refiner, face_start),
353                                                             face_edges = getBaseFaceEdges(refiner, face_start);
354                                                         face_vert_start = findInArray(face_verts, vert);
355                                                         if (edge_start == face_edges[face_vert_start]) {
356                                                                 break;
357                                                         }
358                                                 }
359                                         }
360                                         /* Reset indices for sanity check below. */
361                                         face_start = edge_start = face_vert_start =  -1;
362                                 }
363                         }
364                         /* Sanity check. */
365                         assert(face_start != -1 &&
366                                edge_start != -1 &&
367                                face_vert_start != -1);
368                         /* Traverse faces starting from the current one. */
369                         int edge_first = edge_start;
370                         dst_vert_faces[face_count_ordered++] = face_start;
371                         dst_vert_edges[edge_count_ordered++] = edge_start;
372                         face_used[face_start] = true;
373                         while (edge_count_ordered < num_vert_edges) {
374                                 IndexArray face_verts = getBaseFaceVertices(refiner, face_start);
375                                 IndexArray face_edges = getBaseFaceEdges(refiner, face_start);
376                                 int face_edge_start = face_vert_start;
377                                 int face_edge_next = (face_edge_start > 0) ? (face_edge_start - 1) : (face_verts.size() - 1);
378                                 Index edge_next = face_edges[face_edge_next];
379                                 if (edge_next == edge_first) {
380                                         /* Multiple manifolds found, stop for now and handle rest
381                                          * in the next iteration.
382                                          */
383                                         break;
384                                 }
385                                 dst_vert_edges[edge_count_ordered++] = edge_next;
386                                 if (face_count_ordered < num_vert_faces) {
387                                         IndexArray edge_faces = getBaseEdgeFaces(refiner, edge_next);
388                                         assert(edge_faces.size() != 0);
389                                         if (edge_faces.size() == 1) {
390                                                 assert(edge_faces[0] == face_start);
391                                                 break;
392                                         }
393                                         else if (edge_faces.size() != 2) {
394                                                 break;
395                                         }
396                                         assert(edge_faces.size() == 2);
397                                         face_start = edge_faces[(edge_faces[0] == face_start) ? 1 : 0];
398                                         face_vert_start = findInArray(getBaseFaceEdges(refiner, face_start), edge_next);
399                                         dst_vert_faces[face_count_ordered++] = face_start;
400                                         face_used[face_start] = true;
401                                 }
402                                 edge_start = edge_next;
403                         }
404                 }
405                 /* Verify ordering doesn't ruin connectivity information. */
406                 assert(face_count_ordered == num_vert_faces);
407                 assert(edge_count_ordered == num_vert_edges);
408                 check_oriented_vert_connectivity(num_vert_edges,
409                                                  num_vert_faces,
410                                                  vert_edges,
411                                                  vert_faces,
412                                                  &dst_vert_edges[0],
413                                                  &dst_vert_faces[0]);
414                 /* For the release builds we're failing mesh construction so instead
415                  * of nasty bugs the unsupported mesh will simply disappear from the
416                  * viewport.
417                  */
418                 if (face_count_ordered != num_vert_faces ||
419                     edge_count_ordered != num_vert_edges)
420                 {
421                         delete [] vert_edges;
422                         delete [] vert_faces;
423                         return false;
424                 }
425 #else  /* OPENSUBDIV_ORIENT_TOPOLOGY */
426                 memcpy(&dst_vert_edges[0], vert_edges, sizeof(int) * num_vert_edges);
427                 memcpy(&dst_vert_faces[0], vert_faces, sizeof(int) * num_vert_faces);
428 #endif  /* OPENSUBDIV_ORIENT_TOPOLOGY */
429                 delete [] vert_edges;
430                 delete [] vert_faces;
431         }
432 #ifdef OPENSUBDIV_ORIENT_TOPOLOGY
433         delete [] face_used;
434 #endif
435         populateBaseLocalIndices(refiner);
436         return true;
437 };
438
439 template <>
440 inline bool TopologyRefinerFactory<TopologyRefinerData>::assignComponentTags(
441         TopologyRefiner& refiner,
442         const TopologyRefinerData& cb_data)
443 {
444         const OpenSubdiv_Converter& conv = cb_data.conv;
445         typedef OpenSubdiv::Sdc::Crease Crease;
446
447         int num_edges = conv.get_num_edges(&conv);
448         for (int edge = 0; edge < num_edges; ++edge) {
449                 float sharpness;
450                 ConstIndexArray edge_faces = getBaseEdgeFaces(refiner, edge);
451                 if (edge_faces.size() == 2) {
452                         sharpness = conv.get_edge_sharpness(&conv, edge);
453                 }
454                 else {
455                         /* Non-manifold edges must be sharp. */
456                         sharpness = Crease::SHARPNESS_INFINITE;
457                 }
458                 setBaseEdgeSharpness(refiner, edge, sharpness);
459         }
460
461         /* OpenSubdiv expects non-manifold vertices to be sharp but at the
462          * time it handles correct cases when vertex is a corner of plane.
463          * Currently mark verts which are adjacent to a loose edge as sharp,
464          * but this decision needs some more investigation.
465          */
466         int num_vert = conv.get_num_verts(&conv);
467         for (int vert = 0; vert < num_vert; ++vert) {
468                 ConstIndexArray vert_edges = getBaseVertexEdges(refiner, vert);
469                 for (int edge_index = 0; edge_index < vert_edges.size(); ++edge_index) {
470                         int edge = vert_edges[edge_index];
471                         ConstIndexArray edge_faces = getBaseEdgeFaces(refiner, edge);
472                         if (edge_faces.size() == 0) {
473                                 setBaseVertexSharpness(refiner, vert, Crease::SHARPNESS_INFINITE);
474                                 break;
475                         }
476                 }
477                 if (vert_edges.size() == 2) {
478                         int edge0 = vert_edges[0],
479                             edge1 = vert_edges[1];
480                         float sharpness0 = conv.get_edge_sharpness(&conv, edge0),
481                               sharpness1 = conv.get_edge_sharpness(&conv, edge1);
482                         float sharpness = std::min(sharpness0,  sharpness1);
483                         setBaseVertexSharpness(refiner, vert, sharpness);
484                 }
485         }
486
487         return true;
488 }
489
490 template <>
491 inline void TopologyRefinerFactory<TopologyRefinerData>::reportInvalidTopology(
492         TopologyError /*errCode*/,
493         const char *msg,
494         const TopologyRefinerData& /*mesh*/)
495 {
496         printf("OpenSubdiv Error: %s\n", msg);
497 }
498
499 template <>
500 inline bool TopologyRefinerFactory<TopologyRefinerData>::assignFaceVaryingTopology(
501         TopologyRefiner& refiner,
502         const TopologyRefinerData& cb_data)
503 {
504         const OpenSubdiv_Converter& conv = cb_data.conv;
505         const int num_layers = conv.get_num_uv_layers(&conv);
506         if (num_layers <= 0) {
507                 /* No UV maps, we can skip any face-varying data. */
508                 return true;
509         }
510         const int num_faces = getNumBaseFaces(refiner);
511         size_t uvs_offset = 0;
512         for (int layer = 0; layer < num_layers; ++layer) {
513                 conv.precalc_uv_layer(&conv, layer);
514                 const int num_uvs = conv.get_num_uvs(&conv);
515                 /* Fill in UV coordinates. */
516                 cb_data.uvs->resize(cb_data.uvs->size() + num_uvs * 2);
517                 conv.get_uvs(&conv, &cb_data.uvs->at(uvs_offset));
518                 uvs_offset += num_uvs * 2;
519                 /* Fill in per-corner index of the UV. */
520                 const int channel = createBaseFVarChannel(refiner, num_uvs);
521                 for (int face = 0; face < num_faces; ++face) {
522                         Far::IndexArray dst_face_uvs = getBaseFaceFVarValues(refiner,
523                                                                              face,
524                                                                              channel);
525                         for (int corner = 0; corner < dst_face_uvs.size(); ++corner) {
526                                 const int uv_index = conv.get_face_corner_uv_index(&conv,
527                                                                                    face,
528                                                                                    corner);
529                                 dst_face_uvs[corner] = uv_index;
530                         }
531                 }
532                 conv.finish_uv_layer(&conv);
533         }
534         return true;
535 }
536
537 }  /* namespace Far */
538 }  /* namespace OPENSUBDIV_VERSION */
539 }  /* namespace OpenSubdiv */
540
541 namespace {
542
543 OpenSubdiv::Sdc::SchemeType get_capi_scheme_type(OpenSubdiv_SchemeType type)
544 {
545         switch(type) {
546                 case OSD_SCHEME_BILINEAR:
547                         return OpenSubdiv::Sdc::SCHEME_BILINEAR;
548                 case OSD_SCHEME_CATMARK:
549                         return OpenSubdiv::Sdc::SCHEME_CATMARK;
550                 case OSD_SCHEME_LOOP:
551                         return OpenSubdiv::Sdc::SCHEME_LOOP;
552         }
553         assert(!"Unknown sceme type passed via C-API");
554         return OpenSubdiv::Sdc::SCHEME_CATMARK;
555 }
556
557 }  /* namespace */
558
559 struct OpenSubdiv_TopologyRefinerDescr *openSubdiv_createTopologyRefinerDescr(
560         OpenSubdiv_Converter *converter)
561 {
562         typedef OpenSubdiv::Sdc::Options Options;
563
564         using OpenSubdiv::Far::TopologyRefinerFactory;
565         OpenSubdiv::Sdc::SchemeType scheme_type =
566                 get_capi_scheme_type(converter->get_type(converter));
567         Options options;
568         options.SetVtxBoundaryInterpolation(Options::VTX_BOUNDARY_EDGE_ONLY);
569         options.SetCreasingMethod(Options::CREASE_UNIFORM);
570         /* TODO(sergey): Get proper UV subdivide flag. */
571         // options.SetFVarLinearInterpolation(Options::FVAR_LINEAR_ALL);
572         options.SetFVarLinearInterpolation(Options::FVAR_LINEAR_CORNERS_ONLY);
573
574         TopologyRefinerFactory<TopologyRefinerData>::Options
575                 topology_options(scheme_type, options);
576 #ifdef OPENSUBDIV_VALIDATE_TOPOLOGY
577         topology_options.validateFullTopology = true;
578 #endif
579         OpenSubdiv_TopologyRefinerDescr *result = OBJECT_GUARDED_NEW(OpenSubdiv_TopologyRefinerDescr);
580         TopologyRefinerData cb_data = {*converter, &result->uvs};
581         /* We don't use guarded allocation here so we can re-use the refiner
582          * for GL mesh creation directly.
583          */
584         result->osd_refiner =
585                 TopologyRefinerFactory<TopologyRefinerData>::Create(
586                         cb_data,
587                         topology_options);
588
589         return result;
590 }
591
592 void openSubdiv_deleteTopologyRefinerDescr(
593         OpenSubdiv_TopologyRefinerDescr *topology_refiner)
594 {
595         delete topology_refiner->osd_refiner;
596         OBJECT_GUARDED_DELETE(topology_refiner, OpenSubdiv_TopologyRefinerDescr);
597 }
598
599 int openSubdiv_topologyRefinerGetSubdivLevel(
600         const OpenSubdiv_TopologyRefinerDescr *topology_refiner)
601 {
602         using OpenSubdiv::Far::TopologyRefiner;
603         const TopologyRefiner *refiner = topology_refiner->osd_refiner;
604         return refiner->GetMaxLevel();
605 }
606
607 int openSubdiv_topologyRefinerGetNumVerts(
608         const OpenSubdiv_TopologyRefinerDescr *topology_refiner)
609 {
610         using OpenSubdiv::Far::TopologyLevel;
611         using OpenSubdiv::Far::TopologyRefiner;
612         const TopologyRefiner *refiner = topology_refiner->osd_refiner;
613         const TopologyLevel &base_level = refiner->GetLevel(0);
614         return base_level.GetNumVertices();
615 }
616
617 int openSubdiv_topologyRefinerGetNumEdges(
618         const OpenSubdiv_TopologyRefinerDescr *topology_refiner)
619 {
620         using OpenSubdiv::Far::TopologyLevel;
621         using OpenSubdiv::Far::TopologyRefiner;
622         const TopologyRefiner *refiner = topology_refiner->osd_refiner;
623         const TopologyLevel &base_level = refiner->GetLevel(0);
624         return base_level.GetNumEdges();
625 }
626
627 int openSubdiv_topologyRefinerGetNumFaces(
628         const OpenSubdiv_TopologyRefinerDescr *topology_refiner)
629 {
630         using OpenSubdiv::Far::TopologyLevel;
631         using OpenSubdiv::Far::TopologyRefiner;
632         const TopologyRefiner *refiner = topology_refiner->osd_refiner;
633         const TopologyLevel &base_level = refiner->GetLevel(0);
634         return base_level.GetNumFaces();
635 }
636
637 int openSubdiv_topologyRefinerGetNumFaceVerts(
638         const OpenSubdiv_TopologyRefinerDescr *topology_refiner,
639         int face)
640 {
641         using OpenSubdiv::Far::TopologyLevel;
642         using OpenSubdiv::Far::TopologyRefiner;
643         const TopologyRefiner *refiner = topology_refiner->osd_refiner;
644         const TopologyLevel &base_level = refiner->GetLevel(0);
645         return base_level.GetFaceVertices(face).size();
646 }
647
648 int openSubdiv_topologyRefnerCompareConverter(
649         const OpenSubdiv_TopologyRefinerDescr *topology_refiner,
650         OpenSubdiv_Converter *converter)
651 {
652         using OpenSubdiv::Far::ConstIndexArray;
653         using OpenSubdiv::Far::TopologyRefiner;
654         using OpenSubdiv::Far::TopologyLevel;
655         const TopologyRefiner *refiner = topology_refiner->osd_refiner;
656         const TopologyLevel &base_level = refiner->GetLevel(0);
657         const int num_verts = base_level.GetNumVertices();
658         const int num_edges = base_level.GetNumEdges();
659         const int num_faces = base_level.GetNumFaces();
660         /* Quick preliminary check. */
661         OpenSubdiv::Sdc::SchemeType scheme_type =
662                 get_capi_scheme_type(converter->get_type(converter));
663         if (scheme_type != refiner->GetSchemeType()) {
664                 return false;
665         }
666         if (converter->get_num_verts(converter) != num_verts ||
667             converter->get_num_edges(converter) != num_edges ||
668             converter->get_num_faces(converter) != num_faces)
669         {
670                 return false;
671         }
672         /* Compare all edges. */
673         for (int edge = 0; edge < num_edges; ++edge) {
674                 ConstIndexArray edge_verts = base_level.GetEdgeVertices(edge);
675                 int conv_edge_verts[2];
676                 converter->get_edge_verts(converter, edge, conv_edge_verts);
677                 if (conv_edge_verts[0] != edge_verts[0] ||
678                     conv_edge_verts[1] != edge_verts[1])
679                 {
680                         return false;
681                 }
682         }
683         /* Compare all faces. */
684         std::vector<int> conv_face_verts;
685         for (int face = 0; face < num_faces; ++face) {
686                 ConstIndexArray face_verts = base_level.GetFaceVertices(face);
687                 if (face_verts.size() != converter->get_num_face_verts(converter,
688                                                                        face))
689                 {
690                         return false;
691                 }
692                 conv_face_verts.resize(face_verts.size());
693                 converter->get_face_verts(converter, face, &conv_face_verts[0]);
694                 bool direct_match = true;
695                 for (int i = 0; i < face_verts.size(); ++i) {
696                         if (conv_face_verts[i] != face_verts[i]) {
697                                 direct_match = false;
698                                 break;
699                         }
700                 }
701                 if (!direct_match) {
702                         /* If face didn't match in direct direction we also test if it
703                          * matches in reversed direction. This is because conversion might
704                          * reverse loops to make normals consistent.
705                          */
706 #ifdef OPENSUBDIV_ORIENT_TOPOLOGY
707                         reverse_face_verts(&conv_face_verts[0], conv_face_verts.size());
708                         for (int i = 0; i < face_verts.size(); ++i) {
709                                 if (conv_face_verts[i] != face_verts[i]) {
710                                         return false;
711                                 }
712                         }
713 #else
714                         return false;
715 #endif
716                 }
717         }
718         /* Compare sharpness. */
719         for (int edge = 0; edge < num_edges; ++edge) {
720                 ConstIndexArray edge_faces = base_level.GetEdgeFaces(edge);
721                 float sharpness = base_level.GetEdgeSharpness(edge);
722                 float conv_sharpness;
723                 if (edge_faces.size() == 2) {
724                         conv_sharpness = converter->get_edge_sharpness(converter, edge);
725                 }
726                 else {
727                         conv_sharpness = OpenSubdiv::Sdc::Crease::SHARPNESS_INFINITE;
728                 }
729                 if (sharpness != conv_sharpness) {
730                         return false;
731                 }
732         }
733         return true;
734 }