Welcome to mirror list, hosted at ThFree Co, Russian Federation.

opensubdiv_converter.cc « opensubdiv « intern - git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: ea41a56768fd3c62329b09b588553ba8f3d857b9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
/*
 * ***** BEGIN GPL LICENSE BLOCK *****
 *
 * 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.
 *
 * The Original Code is Copyright (C) 2015 Blender Foundation.
 * All rights reserved.
 *
 * Contributor(s): Sergey Sharybin.
 *
 * ***** END GPL LICENSE BLOCK *****
 */

#include <cstdio>
#include <vector>

#ifdef _MSC_VER
#  include "iso646.h"
#endif

#include <opensubdiv/far/topologyRefinerFactory.h>

#include "MEM_guardedalloc.h"

#include "opensubdiv_converter_capi.h"
#include "opensubdiv_intern.h"
#include "opensubdiv_topology_refiner.h"


#include <stack>

#ifdef OPENSUBDIV_ORIENT_TOPOLOGY
namespace {

inline void reverse_face_verts(int *face_verts, int num_verts)
{
	int last_vert = face_verts[num_verts - 1];
	for (int i = num_verts - 1; i > 0;  --i) {
		face_verts[i] = face_verts[i - 1];
	}
	face_verts[0] = last_vert;
}

struct TopologyRefinerData {
	const OpenSubdiv_Converter& conv;
	std::vector<float> *uvs;
};

}  /* namespace */
#endif /* OPENSUBDIV_ORIENT_TOPOLOGY */

namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {

namespace {

template <typename T>
inline int findInArray(T array, int value)
{
	return (int)(std::find(array.begin(), array.end(), value) - array.begin());
}

#ifdef OPENSUBDIV_ORIENT_TOPOLOGY
inline int get_loop_winding(int vert0_of_face, int vert1_of_face)
{
	int delta_face = vert1_of_face - vert0_of_face;
	if (abs(delta_face) != 1) {
		if (delta_face > 0) {
			delta_face = -1;
		}
		else {
			delta_face = 1;
		}
	}
	return delta_face;
}

inline void reverse_face_loops(IndexArray face_verts, IndexArray face_edges)
{
	for (int i = 0; i < face_verts.size() / 2; ++i) {
		int j = face_verts.size() - i - 1;
		if (i != j) {
			std::swap(face_verts[i], face_verts[j]);
			std::swap(face_edges[i], face_edges[j]);
		}
	}
	reverse_face_verts(&face_verts[0], face_verts.size());
}

inline void check_oriented_vert_connectivity(const int num_vert_edges,
                                             const int num_vert_faces,
                                             const int *vert_edges,
                                             const int *vert_faces,
                                             const int *dst_vert_edges,
                                             const int *dst_vert_faces)
{
#  ifndef NDEBUG
	for (int i = 0; i < num_vert_faces; ++i) {
		bool found = false;
		for (int j = 0; j < num_vert_faces; ++j) {
			if (vert_faces[i] == dst_vert_faces[j]) {
				found = true;
				break;
			}
		}
		if (!found) {
			assert(!"vert-faces connectivity ruined");
		}
	}
	for (int i = 0; i < num_vert_edges; ++i) {
		bool found = false;
		for (int j = 0; j < num_vert_edges; ++j) {
			if (vert_edges[i] == dst_vert_edges[j]) {
				found = true;
				break;
			}
		}
		if (!found) {
			assert(!"vert-edges connectivity ruined");
		}
	}
#  else
	(void)num_vert_edges;
	(void)num_vert_faces;
	(void)vert_edges;
	(void)vert_faces;
	(void)dst_vert_edges;
	(void)dst_vert_faces;
#  endif
}
#endif

}  /* namespace */

template <>
inline bool TopologyRefinerFactory<TopologyRefinerData>::resizeComponentTopology(
        TopologyRefiner& refiner,
        const TopologyRefinerData& cb_data)
{
	const OpenSubdiv_Converter& conv = cb_data.conv;
	/* Faces and face-verts */
	const int num_faces = conv.get_num_faces(&conv);
	setNumBaseFaces(refiner, num_faces);
	for (int face = 0; face < num_faces; ++face) {
		const int num_verts = conv.get_num_face_verts(&conv, face);
		setNumBaseFaceVertices(refiner, face, num_verts);
	}
	/* Edges and edge-faces. */
	const int num_edges = conv.get_num_edges(&conv);
	setNumBaseEdges(refiner, num_edges);
	for (int edge = 0; edge < num_edges; ++edge) {
		const int num_edge_faces = conv.get_num_edge_faces(&conv, edge);
		setNumBaseEdgeFaces(refiner, edge, num_edge_faces);
	}
	/* Vertices and vert-faces and vert-edges/ */
	const int num_verts = conv.get_num_verts(&conv);
	setNumBaseVertices(refiner, num_verts);
	for (int vert = 0; vert < num_verts; ++vert) {
		const int num_vert_edges = conv.get_num_vert_edges(&conv, vert),
		          num_vert_faces = conv.get_num_vert_faces(&conv, vert);
		setNumBaseVertexEdges(refiner, vert, num_vert_edges);
		setNumBaseVertexFaces(refiner, vert, num_vert_faces);
	}
	return true;
}

template <>
inline bool TopologyRefinerFactory<TopologyRefinerData>::assignComponentTopology(
	  TopologyRefiner& refiner,
	  const TopologyRefinerData &cb_data)
{
	const OpenSubdiv_Converter& conv = cb_data.conv;
	using Far::IndexArray;
	/* Face relations. */
	const int num_faces = conv.get_num_faces(&conv);
	for (int face = 0; face < num_faces; ++face) {
		IndexArray dst_face_verts = getBaseFaceVertices(refiner, face);
		conv.get_face_verts(&conv, face, &dst_face_verts[0]);
		IndexArray dst_face_edges = getBaseFaceEdges(refiner, face);
		conv.get_face_edges(&conv, face, &dst_face_edges[0]);
	}
	/* Edge relations. */
	const int num_edges = conv.get_num_edges(&conv);
	for (int edge = 0; edge < num_edges; ++edge) {
		/* Edge-vertices */
		IndexArray dst_edge_verts = getBaseEdgeVertices(refiner, edge);
		conv.get_edge_verts(&conv, edge, &dst_edge_verts[0]);
		/* Edge-faces */
		IndexArray dst_edge_faces = getBaseEdgeFaces(refiner, edge);
		conv.get_edge_faces(&conv, edge, &dst_edge_faces[0]);
	}
#ifdef OPENSUBDIV_ORIENT_TOPOLOGY
	/* Make face normals consistent. */
	bool *face_used = new bool[num_faces];
	memset(face_used, 0, sizeof(bool) * num_faces);
	std::stack<int> traverse_stack;
	int face_start = 0, num_traversed_faces = 0;
	/* Traverse all islands. */
	while (num_traversed_faces != num_faces) {
		/* Find first face of any untraversed islands. */
		while (face_used[face_start]) {
			++face_start;
		}
		/* Add first face to the stack. */
		traverse_stack.push(face_start);
		face_used[face_start] = true;
		/* Go over whole connected component. */
		while (!traverse_stack.empty()) {
			int face = traverse_stack.top();
			traverse_stack.pop();
			IndexArray face_edges = getBaseFaceEdges(refiner, face);
			ConstIndexArray face_verts = getBaseFaceVertices(refiner, face);
			for (int edge_index = 0; edge_index < face_edges.size(); ++edge_index) {
				const int edge = face_edges[edge_index];
				ConstIndexArray edge_faces = getBaseEdgeFaces(refiner, edge);
				if (edge_faces.size() != 2) {
					/* Can't make consistent normals for non-manifolds. */
					continue;
				}
				ConstIndexArray edge_verts = getBaseEdgeVertices(refiner, edge);
				/* Get winding of the reference face. */
				int vert0_of_face = findInArray(face_verts, edge_verts[0]),
				    vert1_of_face = findInArray(face_verts, edge_verts[1]);
				int delta_face = get_loop_winding(vert0_of_face, vert1_of_face);
				for (int edge_face = 0; edge_face < edge_faces.size(); ++edge_face) {
					int other_face = edge_faces[edge_face];
					/* Never re-traverse faces, only move forward. */
					if (face_used[other_face]) {
						continue;
					}
					IndexArray other_face_verts = getBaseFaceVertices(refiner,
					                                                  other_face);
					int vert0_of_other_face = findInArray(other_face_verts,
					                                      edge_verts[0]),
					    vert1_of_other_face = findInArray(other_face_verts,
					                                      edge_verts[1]);
					int delta_other_face = get_loop_winding(vert0_of_other_face,
					                                        vert1_of_other_face);
					if (delta_face * delta_other_face > 0) {
						IndexArray other_face_verts = getBaseFaceVertices(refiner,
						                                                  other_face),
						           other_face_edges = getBaseFaceEdges(refiner,
						                                               other_face);
						reverse_face_loops(other_face_verts,
						                   other_face_edges);
					}
					traverse_stack.push(other_face);
					face_used[other_face] = true;
				}
			}
			++num_traversed_faces;
		}
	}
#endif  /* OPENSUBDIV_ORIENT_TOPOLOGY */
	/* Vertex relations */
	const int num_verts = conv.get_num_verts(&conv);
	for (int vert = 0; vert < num_verts; ++vert) {

		/* Vert-Faces */
		IndexArray dst_vert_faces = getBaseVertexFaces(refiner, vert);
		int num_vert_faces = conv.get_num_vert_faces(&conv, vert);
		int *vert_faces = new int[num_vert_faces];
		conv.get_vert_faces(&conv, vert, vert_faces);
		/* Vert-Edges */
		IndexArray dst_vert_edges = getBaseVertexEdges(refiner, vert);
		int num_vert_edges = conv.get_num_vert_edges(&conv, vert);
		int *vert_edges = new int[num_vert_edges];
		conv.get_vert_edges(&conv, vert, vert_edges);
#ifdef OPENSUBDIV_ORIENT_TOPOLOGY
		/* ** Order vertex edges and faces in a CCW order. ** */
		memset(face_used, 0, sizeof(bool) * num_faces);
		/* Number of edges and faces added to the ordered array. */
		int edge_count_ordered = 0, face_count_ordered = 0;
		/* Add loose edges straight into the edges array. */
		bool has_fan_connections = false;
		for (int i = 0; i < num_vert_edges; ++i) {
			IndexArray edge_faces = getBaseEdgeFaces(refiner, vert_edges[i]);
			if (edge_faces.size() == 0) {
				dst_vert_edges[edge_count_ordered++] = vert_edges[i];
			}
			else if (edge_faces.size() > 2) {
				has_fan_connections = true;
			}
		}
		if (has_fan_connections) {
			/* OpenSubdiv currently doesn't give us clues how to handle
			 * fan face connections. and since handling such connections
			 * complicates the loop below we simply don't do special
			 * orientation for them.
			 */
			memcpy(&dst_vert_edges[0], vert_edges, sizeof(int) * num_vert_edges);
			memcpy(&dst_vert_faces[0], vert_faces, sizeof(int) * num_vert_faces);
			delete [] vert_edges;
			delete [] vert_faces;
			continue;
		}
		/* Perform at max numbder of vert-edges iteration and try to avoid
		 * deadlock here for malformed mesh.
		 */
		for (int global_iter = 0; global_iter < num_vert_edges; ++global_iter) {
			/* Numbr of edges and faces which are still to be ordered. */
			int num_vert_edges_remained = num_vert_edges - edge_count_ordered,
			    num_vert_faces_remained = num_vert_faces - face_count_ordered;
			if (num_vert_edges_remained == 0 && num_vert_faces_remained == 0) {
				/* All done, nothing to do anymore. */
				break;
			}
			/* Face, edge and face-vertex inndex to start traversal from. */
			int face_start = -1, edge_start = -1, face_vert_start = -1;
			if (num_vert_edges_remained == num_vert_faces_remained) {
				/* Vertex is eitehr complete manifold or is connected to seevral
				 * manifold islands (hourglass-like configuration), can pick up
				 * random edge unused and start from it.
				 */
				/* TODO(sergey): Start from previous edge from which traversal
				 * began at previous iteration.
				 */
				for (int i = 0; i < num_vert_edges; ++i) {
					face_start = vert_faces[i];
					if (!face_used[face_start]) {
						ConstIndexArray
						    face_verts = getBaseFaceVertices(refiner, face_start),
						    face_edges = getBaseFaceEdges(refiner, face_start);
						face_vert_start = findInArray(face_verts, vert);
						edge_start = face_edges[face_vert_start];
						break;
					}
				}
			}
			else {
				/* Special handle of non-manifold vertex. */
				for (int i = 0; i < num_vert_edges; ++i) {
					edge_start = vert_edges[i];
					IndexArray edge_faces = getBaseEdgeFaces(refiner, edge_start);
					if (edge_faces.size() == 1) {
						face_start = edge_faces[0];
						if (!face_used[face_start]) {
							ConstIndexArray
							    face_verts = getBaseFaceVertices(refiner, face_start),
							    face_edges = getBaseFaceEdges(refiner, face_start);
							face_vert_start = findInArray(face_verts, vert);
							if (edge_start == face_edges[face_vert_start]) {
								break;
							}
						}
					}
					/* Reset indices for sanity check below. */
					face_start = edge_start = face_vert_start =  -1;
				}
			}
			/* Sanity check. */
			assert(face_start != -1 &&
			       edge_start != -1 &&
			       face_vert_start != -1);
			/* Traverse faces starting from the current one. */
			int edge_first = edge_start;
			dst_vert_faces[face_count_ordered++] = face_start;
			dst_vert_edges[edge_count_ordered++] = edge_start;
			face_used[face_start] = true;
			while (edge_count_ordered < num_vert_edges) {
				IndexArray face_verts = getBaseFaceVertices(refiner, face_start);
				IndexArray face_edges = getBaseFaceEdges(refiner, face_start);
				int face_edge_start = face_vert_start;
				int face_edge_next = (face_edge_start > 0) ? (face_edge_start - 1) : (face_verts.size() - 1);
				Index edge_next = face_edges[face_edge_next];
				if (edge_next == edge_first) {
					/* Multiple manifolds found, stop for now and handle rest
					 * in the next iteration.
					 */
					break;
				}
				dst_vert_edges[edge_count_ordered++] = edge_next;
				if (face_count_ordered < num_vert_faces) {
					IndexArray edge_faces = getBaseEdgeFaces(refiner, edge_next);
					assert(edge_faces.size() != 0);
					if (edge_faces.size() == 1) {
						assert(edge_faces[0] == face_start);
						break;
					}
					else if (edge_faces.size() != 2) {
						break;
					}
					assert(edge_faces.size() == 2);
					face_start = edge_faces[(edge_faces[0] == face_start) ? 1 : 0];
					face_vert_start = findInArray(getBaseFaceEdges(refiner, face_start), edge_next);
					dst_vert_faces[face_count_ordered++] = face_start;
					face_used[face_start] = true;
				}
				edge_start = edge_next;
			}
		}
		/* Verify ordering doesn't ruin connectivity information. */
		assert(face_count_ordered == num_vert_faces);
		assert(edge_count_ordered == num_vert_edges);
		check_oriented_vert_connectivity(num_vert_edges,
		                                 num_vert_faces,
		                                 vert_edges,
		                                 vert_faces,
		                                 &dst_vert_edges[0],
		                                 &dst_vert_faces[0]);
		/* For the release builds we're failing mesh construction so instead
		 * of nasty bugs the unsupported mesh will simply disappear from the
		 * viewport.
		 */
		if (face_count_ordered != num_vert_faces ||
		    edge_count_ordered != num_vert_edges)
		{
			delete [] vert_edges;
			delete [] vert_faces;
			return false;
		}
#else  /* OPENSUBDIV_ORIENT_TOPOLOGY */
		memcpy(&dst_vert_edges[0], vert_edges, sizeof(int) * num_vert_edges);
		memcpy(&dst_vert_faces[0], vert_faces, sizeof(int) * num_vert_faces);
#endif  /* OPENSUBDIV_ORIENT_TOPOLOGY */
		delete [] vert_edges;
		delete [] vert_faces;
	}
#ifdef OPENSUBDIV_ORIENT_TOPOLOGY
	delete [] face_used;
#endif
	populateBaseLocalIndices(refiner);
	return true;
};

template <>
inline bool TopologyRefinerFactory<TopologyRefinerData>::assignComponentTags(
        TopologyRefiner& refiner,
        const TopologyRefinerData& cb_data)
{
	const OpenSubdiv_Converter& conv = cb_data.conv;
	typedef OpenSubdiv::Sdc::Crease Crease;

	int num_edges = conv.get_num_edges(&conv);
	for (int edge = 0; edge < num_edges; ++edge) {
		float sharpness;
		ConstIndexArray edge_faces = getBaseEdgeFaces(refiner, edge);
		if (edge_faces.size() == 2) {
			sharpness = conv.get_edge_sharpness(&conv, edge);
		}
		else {
			/* Non-manifold edges must be sharp. */
			sharpness = Crease::SHARPNESS_INFINITE;
		}
		setBaseEdgeSharpness(refiner, edge, sharpness);
	}

	/* OpenSubdiv expects non-manifold vertices to be sharp but at the
	 * time it handles correct cases when vertex is a corner of plane.
	 * Currently mark verts which are adjacent to a loose edge as sharp,
	 * but this decision needs some more investigation.
	 */
	int num_vert = conv.get_num_verts(&conv);
	for (int vert = 0; vert < num_vert; ++vert) {
		ConstIndexArray vert_edges = getBaseVertexEdges(refiner, vert);
		for (int edge_index = 0; edge_index < vert_edges.size(); ++edge_index) {
			int edge = vert_edges[edge_index];
			ConstIndexArray edge_faces = getBaseEdgeFaces(refiner, edge);
			if (edge_faces.size() == 0) {
				setBaseVertexSharpness(refiner, vert, Crease::SHARPNESS_INFINITE);
				break;
			}
		}
		if (vert_edges.size() == 2) {
			int edge0 = vert_edges[0],
			    edge1 = vert_edges[1];
			float sharpness0 = conv.get_edge_sharpness(&conv, edge0),
			      sharpness1 = conv.get_edge_sharpness(&conv, edge1);
			float sharpness = std::min(sharpness0,  sharpness1);
			setBaseVertexSharpness(refiner, vert, sharpness);
		}
	}

	return true;
}

template <>
inline void TopologyRefinerFactory<TopologyRefinerData>::reportInvalidTopology(
        TopologyError /*errCode*/,
        const char *msg,
        const TopologyRefinerData& /*mesh*/)
{
	printf("OpenSubdiv Error: %s\n", msg);
}

template <>
inline bool TopologyRefinerFactory<TopologyRefinerData>::assignFaceVaryingTopology(
        TopologyRefiner& refiner,
        const TopologyRefinerData& cb_data)
{
	const OpenSubdiv_Converter& conv = cb_data.conv;
	const int num_layers = conv.get_num_uv_layers(&conv);
	if (num_layers <= 0) {
		/* No UV maps, we can skip any face-varying data. */
		return true;
	}
	const int num_faces = getNumBaseFaces(refiner);
	size_t uvs_offset = 0;
	for (int layer = 0; layer < num_layers; ++layer) {
		conv.precalc_uv_layer(&conv, layer);
		const int num_uvs = conv.get_num_uvs(&conv);
		/* Fill in UV coordinates. */
		cb_data.uvs->resize(cb_data.uvs->size() + num_uvs * 2);
		conv.get_uvs(&conv, &cb_data.uvs->at(uvs_offset));
		uvs_offset += num_uvs * 2;
		/* Fill in per-corner index of the UV. */
		const int channel = createBaseFVarChannel(refiner, num_uvs);
		for (int face = 0; face < num_faces; ++face) {
			Far::IndexArray dst_face_uvs = getBaseFaceFVarValues(refiner,
			                                                     face,
			                                                     channel);
			for (int corner = 0; corner < dst_face_uvs.size(); ++corner) {
				const int uv_index = conv.get_face_corner_uv_index(&conv,
				                                                   face,
				                                                   corner);
				dst_face_uvs[corner] = uv_index;
			}
		}
		conv.finish_uv_layer(&conv);
	}
	return true;
}

}  /* namespace Far */
}  /* namespace OPENSUBDIV_VERSION */
}  /* namespace OpenSubdiv */

namespace {

OpenSubdiv::Sdc::SchemeType get_capi_scheme_type(OpenSubdiv_SchemeType type)
{
	switch(type) {
		case OSD_SCHEME_BILINEAR:
			return OpenSubdiv::Sdc::SCHEME_BILINEAR;
		case OSD_SCHEME_CATMARK:
			return OpenSubdiv::Sdc::SCHEME_CATMARK;
		case OSD_SCHEME_LOOP:
			return OpenSubdiv::Sdc::SCHEME_LOOP;
	}
	assert(!"Unknown sceme type passed via C-API");
	return OpenSubdiv::Sdc::SCHEME_CATMARK;
}

}  /* namespace */

struct OpenSubdiv_TopologyRefinerDescr *openSubdiv_createTopologyRefinerDescr(
        OpenSubdiv_Converter *converter)
{
	typedef OpenSubdiv::Sdc::Options Options;

	using OpenSubdiv::Far::TopologyRefinerFactory;
	OpenSubdiv::Sdc::SchemeType scheme_type =
	        get_capi_scheme_type(converter->get_type(converter));
	Options options;
	options.SetVtxBoundaryInterpolation(Options::VTX_BOUNDARY_EDGE_ONLY);
	options.SetCreasingMethod(Options::CREASE_UNIFORM);
	if (converter->get_subdiv_uvs(converter)) {
		options.SetFVarLinearInterpolation(Options::FVAR_LINEAR_CORNERS_ONLY);
	}
	else {
		options.SetFVarLinearInterpolation(Options::FVAR_LINEAR_ALL);
	}

	TopologyRefinerFactory<TopologyRefinerData>::Options
	        topology_options(scheme_type, options);
#ifdef OPENSUBDIV_VALIDATE_TOPOLOGY
	topology_options.validateFullTopology = true;
#endif
	OpenSubdiv_TopologyRefinerDescr *result = OBJECT_GUARDED_NEW(OpenSubdiv_TopologyRefinerDescr);
	TopologyRefinerData cb_data = {*converter, &result->uvs};
	/* We don't use guarded allocation here so we can re-use the refiner
	 * for GL mesh creation directly.
	 */
	result->osd_refiner =
	        TopologyRefinerFactory<TopologyRefinerData>::Create(
	                cb_data,
	                topology_options);

	return result;
}

void openSubdiv_deleteTopologyRefinerDescr(
        OpenSubdiv_TopologyRefinerDescr *topology_refiner)
{
	delete topology_refiner->osd_refiner;
	OBJECT_GUARDED_DELETE(topology_refiner, OpenSubdiv_TopologyRefinerDescr);
}

int openSubdiv_topologyRefinerGetSubdivLevel(
        const OpenSubdiv_TopologyRefinerDescr *topology_refiner)
{
	using OpenSubdiv::Far::TopologyRefiner;
	const TopologyRefiner *refiner = topology_refiner->osd_refiner;
	return refiner->GetMaxLevel();
}

int openSubdiv_topologyRefinerGetNumVerts(
        const OpenSubdiv_TopologyRefinerDescr *topology_refiner)
{
	using OpenSubdiv::Far::TopologyLevel;
	using OpenSubdiv::Far::TopologyRefiner;
	const TopologyRefiner *refiner = topology_refiner->osd_refiner;
	const TopologyLevel &base_level = refiner->GetLevel(0);
	return base_level.GetNumVertices();
}

int openSubdiv_topologyRefinerGetNumEdges(
        const OpenSubdiv_TopologyRefinerDescr *topology_refiner)
{
	using OpenSubdiv::Far::TopologyLevel;
	using OpenSubdiv::Far::TopologyRefiner;
	const TopologyRefiner *refiner = topology_refiner->osd_refiner;
	const TopologyLevel &base_level = refiner->GetLevel(0);
	return base_level.GetNumEdges();
}

int openSubdiv_topologyRefinerGetNumFaces(
        const OpenSubdiv_TopologyRefinerDescr *topology_refiner)
{
	using OpenSubdiv::Far::TopologyLevel;
	using OpenSubdiv::Far::TopologyRefiner;
	const TopologyRefiner *refiner = topology_refiner->osd_refiner;
	const TopologyLevel &base_level = refiner->GetLevel(0);
	return base_level.GetNumFaces();
}

int openSubdiv_topologyRefinerGetNumFaceVerts(
        const OpenSubdiv_TopologyRefinerDescr *topology_refiner,
        int face)
{
	using OpenSubdiv::Far::TopologyLevel;
	using OpenSubdiv::Far::TopologyRefiner;
	const TopologyRefiner *refiner = topology_refiner->osd_refiner;
	const TopologyLevel &base_level = refiner->GetLevel(0);
	return base_level.GetFaceVertices(face).size();
}

int openSubdiv_topologyRefnerCompareConverter(
        const OpenSubdiv_TopologyRefinerDescr *topology_refiner,
        OpenSubdiv_Converter *converter)
{
	typedef OpenSubdiv::Sdc::Options Options;
	using OpenSubdiv::Far::ConstIndexArray;
	using OpenSubdiv::Far::TopologyRefiner;
	using OpenSubdiv::Far::TopologyLevel;
	const TopologyRefiner *refiner = topology_refiner->osd_refiner;
	const TopologyLevel &base_level = refiner->GetLevel(0);
	const int num_verts = base_level.GetNumVertices();
	const int num_edges = base_level.GetNumEdges();
	const int num_faces = base_level.GetNumFaces();
	/* Quick preliminary check. */
	OpenSubdiv::Sdc::SchemeType scheme_type =
	        get_capi_scheme_type(converter->get_type(converter));
	if (scheme_type != refiner->GetSchemeType()) {
		return false;
	}
	const Options options = refiner->GetSchemeOptions();
	Options::FVarLinearInterpolation interp = options.GetFVarLinearInterpolation();
	const bool subdiv_uvs = (interp != Options::FVAR_LINEAR_ALL);
	if (converter->get_subdiv_uvs(converter) != subdiv_uvs) {
		return false;
	}
	if (converter->get_num_verts(converter) != num_verts ||
	    converter->get_num_edges(converter) != num_edges ||
	    converter->get_num_faces(converter) != num_faces)
	{
		return false;
	}
	/* Compare all edges. */
	for (int edge = 0; edge < num_edges; ++edge) {
		ConstIndexArray edge_verts = base_level.GetEdgeVertices(edge);
		int conv_edge_verts[2];
		converter->get_edge_verts(converter, edge, conv_edge_verts);
		if (conv_edge_verts[0] != edge_verts[0] ||
		    conv_edge_verts[1] != edge_verts[1])
		{
			return false;
		}
	}
	/* Compare all faces. */
	std::vector<int> conv_face_verts;
	for (int face = 0; face < num_faces; ++face) {
		ConstIndexArray face_verts = base_level.GetFaceVertices(face);
		if (face_verts.size() != converter->get_num_face_verts(converter,
		                                                       face))
		{
			return false;
		}
		conv_face_verts.resize(face_verts.size());
		converter->get_face_verts(converter, face, &conv_face_verts[0]);
		bool direct_match = true;
		for (int i = 0; i < face_verts.size(); ++i) {
			if (conv_face_verts[i] != face_verts[i]) {
				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
			reverse_face_verts(&conv_face_verts[0], conv_face_verts.size());
			for (int i = 0; i < face_verts.size(); ++i) {
				if (conv_face_verts[i] != face_verts[i]) {
					return false;
				}
			}
#else
			return false;
#endif
		}
	}
	/* Compare sharpness. */
	for (int edge = 0; edge < num_edges; ++edge) {
		ConstIndexArray edge_faces = base_level.GetEdgeFaces(edge);
		float sharpness = base_level.GetEdgeSharpness(edge);
		float conv_sharpness;
		if (edge_faces.size() == 2) {
			conv_sharpness = converter->get_edge_sharpness(converter, edge);
		}
		else {
			conv_sharpness = OpenSubdiv::Sdc::Crease::SHARPNESS_INFINITE;
		}
		if (sharpness != conv_sharpness) {
			return false;
		}
	}
	return true;
}