1 //
2 //   Copyright 2014 DreamWorks Animation LLC.
3 //
4 //   Licensed under the Apache License, Version 2.0 (the "Apache License")
5 //   with the following modification; you may not use this file except in
6 //   compliance with the Apache License and the following modification to it:
7 //   Section 6. Trademarks. is deleted and replaced with:
8 //
9 //   6. Trademarks. This License does not grant permission to use the trade
10 //      names, trademarks, service marks, or product names of the Licensor
11 //      and its affiliates, except as required to comply with Section 4(c) of
12 //      the License and to reproduce the content of the NOTICE file.
13 //
14 //   You may obtain a copy of the Apache License at
15 //
16 //       http://www.apache.org/licenses/LICENSE-2.0
17 //
18 //   Unless required by applicable law or agreed to in writing, software
19 //   distributed under the Apache License with the above modification is
20 //   distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 //   KIND, either express or implied. See the Apache License for the specific
22 //   language governing permissions and limitations under the Apache License.
23 //
24 #include "../far/topologyRefiner.h"
25 #include "../far/error.h"
26 #include "../vtr/fvarLevel.h"
27 #include "../vtr/sparseSelector.h"
28 #include "../vtr/quadRefinement.h"
29 #include "../vtr/triRefinement.h"
30 
31 #include <cassert>
32 #include <cstdio>
33 
34 
35 namespace OpenSubdiv {
36 namespace OPENSUBDIV_VERSION {
37 
38 namespace Far {
39 
40 //
41 //  Relatively trivial construction/destruction -- the base level (level[0]) needs
42 //  to be explicitly initialized after construction and refinement then applied
43 //
TopologyRefiner(Sdc::SchemeType schemeType,Sdc::Options schemeOptions)44 TopologyRefiner::TopologyRefiner(Sdc::SchemeType schemeType, Sdc::Options schemeOptions) :
45     _subdivType(schemeType),
46     _subdivOptions(schemeOptions),
47     _isUniform(true),
48     _hasHoles(false),
49     _hasIrregFaces(false),
50     _regFaceSize(Sdc::SchemeTypeTraits::GetRegularFaceSize(schemeType)),
51     _maxLevel(0),
52     _uniformOptions(0),
53     _adaptiveOptions(0),
54     _totalVertices(0),
55     _totalEdges(0),
56     _totalFaces(0),
57     _totalFaceVertices(0),
58     _maxValence(0),
59     _baseLevelOwned(true) {
60 
61     //  Need to revisit allocation scheme here -- want to use smart-ptrs for these
62     //  but will probably have to settle for explicit new/delete...
63     _levels.reserve(10);
64     _levels.push_back(new Vtr::internal::Level);
65     _farLevels.reserve(10);
66     assembleFarLevels();
67 }
68 
69 //
70 //  The copy constructor is protected and used by the factory to create a new instance
71 //  from only the base level of the given instance -- it does not create a full copy.
72 //  So members reflecting any refinement are default-initialized while those dependent
73 //  on the base level are copied or explicitly initialized after its assignment.
74 //
TopologyRefiner(TopologyRefiner const & source)75 TopologyRefiner::TopologyRefiner(TopologyRefiner const & source) :
76     _subdivType(source._subdivType),
77     _subdivOptions(source._subdivOptions),
78     _isUniform(true),
79     _hasHoles(source._hasHoles),
80     _hasIrregFaces(source._hasIrregFaces),
81     _regFaceSize(source._regFaceSize),
82     _maxLevel(0),
83     _uniformOptions(0),
84     _adaptiveOptions(0),
85     _baseLevelOwned(false) {
86 
87     _levels.reserve(10);
88     _levels.push_back(source._levels[0]);
89     initializeInventory();
90 
91     _farLevels.reserve(10);
92     assembleFarLevels();
93 }
94 
95 
~TopologyRefiner()96 TopologyRefiner::~TopologyRefiner() {
97 
98     for (int i=0; i<(int)_levels.size(); ++i) {
99         if ((i > 0) || _baseLevelOwned) delete _levels[i];
100     }
101 
102     for (int i=0; i<(int)_refinements.size(); ++i) {
103         delete _refinements[i];
104     }
105 }
106 
107 void
Unrefine()108 TopologyRefiner::Unrefine() {
109 
110     if (_levels.size()) {
111         for (int i=1; i<(int)_levels.size(); ++i) {
112             delete _levels[i];
113         }
114         _levels.resize(1);
115         initializeInventory();
116     }
117     for (int i=0; i<(int)_refinements.size(); ++i) {
118         delete _refinements[i];
119     }
120     _refinements.clear();
121     _maxLevel = 0;
122 
123     assembleFarLevels();
124 }
125 
126 
127 //
128 //  Initializing and updating the component inventory:
129 //
130 void
initializeInventory()131 TopologyRefiner::initializeInventory() {
132 
133     if (_levels.size()) {
134         assert(_levels.size() == 1);
135 
136         Vtr::internal::Level const & baseLevel = *_levels[0];
137 
138         _totalVertices     = baseLevel.getNumVertices();
139         _totalEdges        = baseLevel.getNumEdges();
140         _totalFaces        = baseLevel.getNumFaces();
141         _totalFaceVertices = baseLevel.getNumFaceVerticesTotal();
142 
143         _maxValence = baseLevel.getMaxValence();
144     } else {
145         _totalVertices     = 0;
146         _totalEdges        = 0;
147         _totalFaces        = 0;
148         _totalFaceVertices = 0;
149 
150         _maxValence = 0;
151     }
152 }
153 
154 void
updateInventory(Vtr::internal::Level const & newLevel)155 TopologyRefiner::updateInventory(Vtr::internal::Level const & newLevel) {
156 
157     _totalVertices     += newLevel.getNumVertices();
158     _totalEdges        += newLevel.getNumEdges();
159     _totalFaces        += newLevel.getNumFaces();
160     _totalFaceVertices += newLevel.getNumFaceVerticesTotal();
161 
162     _maxValence = std::max(_maxValence, newLevel.getMaxValence());
163 }
164 
165 void
appendLevel(Vtr::internal::Level & newLevel)166 TopologyRefiner::appendLevel(Vtr::internal::Level & newLevel) {
167 
168     _levels.push_back(&newLevel);
169 
170     updateInventory(newLevel);
171 }
172 
173 void
appendRefinement(Vtr::internal::Refinement & newRefinement)174 TopologyRefiner::appendRefinement(Vtr::internal::Refinement & newRefinement) {
175 
176     _refinements.push_back(&newRefinement);
177 }
178 
179 void
assembleFarLevels()180 TopologyRefiner::assembleFarLevels() {
181 
182     _farLevels.resize(_levels.size());
183 
184     _farLevels[0]._refToParent = 0;
185     _farLevels[0]._level       = _levels[0];
186     _farLevels[0]._refToChild  = 0;
187 
188     int nRefinements = (int)_refinements.size();
189     if (nRefinements) {
190         _farLevels[0]._refToChild = _refinements[0];
191 
192         for (int i = 1; i < nRefinements; ++i) {
193             _farLevels[i]._refToParent = _refinements[i - 1];
194             _farLevels[i]._level       = _levels[i];
195             _farLevels[i]._refToChild  = _refinements[i];;
196         }
197 
198         _farLevels[nRefinements]._refToParent = _refinements[nRefinements - 1];
199         _farLevels[nRefinements]._level       = _levels[nRefinements];
200         _farLevels[nRefinements]._refToChild  = 0;
201     }
202 }
203 
204 
205 //
206 //  Accessors to the topology information:
207 //
208 int
GetNumFVarValuesTotal(int channel) const209 TopologyRefiner::GetNumFVarValuesTotal(int channel) const {
210     int sum = 0;
211     for (int i = 0; i < (int)_levels.size(); ++i) {
212         sum += _levels[i]->getNumFVarValues(channel);
213     }
214     return sum;
215 }
216 
217 
218 //
219 //  Main refinement method -- allocating and initializing levels and refinements:
220 //
221 void
RefineUniform(UniformOptions options)222 TopologyRefiner::RefineUniform(UniformOptions options) {
223 
224     if (_levels[0]->getNumVertices() == 0) {
225         Error(FAR_RUNTIME_ERROR,
226             "Failure in TopologyRefiner::RefineUniform() -- base level is uninitialized.");
227         return;
228     }
229     if (_refinements.size()) {
230         Error(FAR_RUNTIME_ERROR,
231             "Failure in TopologyRefiner::RefineUniform() -- previous refinements already applied.");
232         return;
233     }
234 
235     //
236     //  Allocate the stack of levels and the refinements between them:
237     //
238     _uniformOptions = options;
239 
240     _isUniform = true;
241     _maxLevel = options.refinementLevel;
242 
243     Sdc::Split splitType = Sdc::SchemeTypeTraits::GetTopologicalSplitType(_subdivType);
244 
245     //
246     //  Initialize refinement options for Vtr -- adjusting full-topology for the last level:
247     //
248     Vtr::internal::Refinement::Options refineOptions;
249     refineOptions._sparse         = false;
250     refineOptions._faceVertsFirst = options.orderVerticesFromFacesFirst;
251 
252     for (int i = 1; i <= (int)options.refinementLevel; ++i) {
253         refineOptions._minimalTopology =
254             options.fullTopologyInLastLevel ? false : (i == (int)options.refinementLevel);
255 
256         Vtr::internal::Level& parentLevel = getLevel(i-1);
257         Vtr::internal::Level& childLevel  = *(new Vtr::internal::Level);
258 
259         Vtr::internal::Refinement* refinement = 0;
260         if (splitType == Sdc::SPLIT_TO_QUADS) {
261             refinement = new Vtr::internal::QuadRefinement(parentLevel, childLevel, _subdivOptions);
262         } else {
263             refinement = new Vtr::internal::TriRefinement(parentLevel, childLevel, _subdivOptions);
264         }
265         refinement->refine(refineOptions);
266 
267         appendLevel(childLevel);
268         appendRefinement(*refinement);
269     }
270     assembleFarLevels();
271 }
272 
273 //
274 //  Internal utility class and function supporting feature adaptive selection of faces...
275 //
276 namespace internal {
277     //
278     //  FeatureMask is a simple set of bits identifying features to be selected during a level of
279     //  adaptive refinement.  Adaptive refinement options passed the Refiner are interpreted as a
280     //  specific set of features defined here.  Given options to reduce faces generated at deeper
281     //  levels, a method to "reduce" the set of features is also provided here.
282     //
283     //  This class was specifically not nested in TopologyRefiner to allow simple non-class methods
284     //  to make use of it in the core selection methods.  Those selection methods were similarly
285     //  made non-class methods to ensure they conform to the feature set defined by the FeatureMask
286     //  and not some internal class state.
287     //
288     class FeatureMask {
289     public:
290         typedef TopologyRefiner::AdaptiveOptions Options;
291         typedef unsigned int                     int_type;
292 
Clear()293         void Clear()         { *((int_type*)this) = 0; }
IsEmpty() const294         bool IsEmpty() const { return *((int_type*)this) == 0; }
295 
FeatureMask()296         FeatureMask() { Clear(); }
FeatureMask(Options const & options,int regFaceSize)297         FeatureMask(Options const & options, int regFaceSize) {
298             Clear();
299             InitializeFeatures(options, regFaceSize);
300         }
301 
302         //  These are the two primary methods intended for use -- intialization via a set of Options
303         //  and reduction of the subsequent feature set (which presumes prior initialization with the
304         //  same set as give)
305         //
306         void InitializeFeatures(Options const & options, int regFaceSize);
307         void ReduceFeatures(    Options const & options);
308 
309     public:
310         int_type selectXOrdinaryInterior : 1;
311         int_type selectXOrdinaryBoundary : 1;
312 
313         int_type selectSemiSharpSingle    : 1;
314         int_type selectSemiSharpNonSingle : 1;
315 
316         int_type selectInfSharpRegularCrease   : 1;
317         int_type selectInfSharpRegularCorner   : 1;
318         int_type selectInfSharpIrregularDart   : 1;
319         int_type selectInfSharpIrregularCrease : 1;
320         int_type selectInfSharpIrregularCorner : 1;
321 
322         int_type selectUnisolatedInteriorEdge : 1;
323 
324         int_type selectNonManifold  : 1;
325         int_type selectFVarFeatures : 1;
326     };
327 
328     void
InitializeFeatures(Options const & options,int regFaceSize)329     FeatureMask::InitializeFeatures(Options const & options, int regFaceSize) {
330 
331         //
332         //  Support for the "single-crease patch" case is limited to the subdivision scheme
333         //  (currently only Catmull-Clark).  It has historically been applied to both semi-
334         //  sharp and inf-sharp creases -- the semi-sharp application is still relevant,
335         //  but the inf-sharp has been superceded.
336         //
337         //  The inf-sharp single-crease case now corresponds to an inf-sharp regular crease
338         //  in the interior -- and since such regular creases on the boundary are never
339         //  considered for selection (just as interior smoot regular faces are not), this
340         //  feature is only relevant for the interior case.  So aside from it being used
341         //  when regular inf-sharp features are all selected, it can also be used for the
342         //  single-crease case.
343         //
344         bool useSingleCreasePatch = options.useSingleCreasePatch && (regFaceSize == 4);
345 
346         //  Extra-ordinary features (independent of the inf-sharp options):
347         selectXOrdinaryInterior = true;
348         selectXOrdinaryBoundary = true;
349 
350         //  Semi-sharp features -- the regular single crease case and all others:
351         selectSemiSharpSingle    = !useSingleCreasePatch;
352         selectSemiSharpNonSingle = true;
353 
354         //  Inf-sharp features -- boundary extra-ordinary vertices are irreg creases:
355         selectInfSharpRegularCrease   = !(options.useInfSharpPatch || useSingleCreasePatch);
356         selectInfSharpRegularCorner   = !options.useInfSharpPatch;
357         selectInfSharpIrregularDart   = true;
358         selectInfSharpIrregularCrease = true;
359         selectInfSharpIrregularCorner = true;
360 
361         selectUnisolatedInteriorEdge = useSingleCreasePatch && !options.useInfSharpPatch;
362 
363         selectNonManifold  = true;
364         selectFVarFeatures = options.considerFVarChannels;
365     }
366 
367     void
ReduceFeatures(Options const & options)368     FeatureMask::ReduceFeatures(Options const & options) {
369 
370         //  Disable typical xordinary vertices:
371         selectXOrdinaryInterior = false;
372         selectXOrdinaryBoundary = false;
373 
374         //  If minimizing inf-sharp patches, disable all but sharp/corner irregularities
375         if (options.useInfSharpPatch) {
376             selectInfSharpRegularCrease    = false;
377             selectInfSharpRegularCorner    = false;
378             selectInfSharpIrregularDart    = false;
379             selectInfSharpIrregularCrease  = false;
380         }
381     }
382 } // end namespace internal
383 
384 void
RefineAdaptive(AdaptiveOptions options,ConstIndexArray baseFacesToRefine)385 TopologyRefiner::RefineAdaptive(AdaptiveOptions options,
386                                 ConstIndexArray baseFacesToRefine) {
387 
388     if (_levels[0]->getNumVertices() == 0) {
389         Error(FAR_RUNTIME_ERROR,
390             "Failure in TopologyRefiner::RefineAdaptive() -- base level is uninitialized.");
391         return;
392     }
393     if (_refinements.size()) {
394         Error(FAR_RUNTIME_ERROR,
395             "Failure in TopologyRefiner::RefineAdaptive() -- previous refinements already applied.");
396         return;
397     }
398 
399     //
400     //  Initialize member and local variables from the adaptive options:
401     //
402     _isUniform = false;
403     _adaptiveOptions = options;
404 
405     //
406     //  Initialize the feature-selection options based on given options -- with two sets
407     //  of levels isolating different sets of features, initialize the two feature sets
408     //  up front and use the appropriate one for each level:
409     //
410     int nonLinearScheme = Sdc::SchemeTypeTraits::GetLocalNeighborhoodSize(_subdivType);
411 
412     int shallowLevel = std::min<int>(options.secondaryLevel, options.isolationLevel);
413     int deeperLevel  = options.isolationLevel;
414 
415     int potentialMaxLevel = nonLinearScheme ? deeperLevel : _hasIrregFaces;
416 
417     internal::FeatureMask moreFeaturesMask(options, _regFaceSize);
418     internal::FeatureMask lessFeaturesMask = moreFeaturesMask;
419 
420     if (shallowLevel < potentialMaxLevel) {
421         lessFeaturesMask.ReduceFeatures(options);
422     }
423 
424     //
425     //  If face-varying channels are considered, make sure non-linear channels are present
426     //  and turn off consideration if none present:
427     //
428     if (moreFeaturesMask.selectFVarFeatures && nonLinearScheme) {
429         bool nonLinearChannelsPresent = false;
430         for (int channel = 0; channel < _levels[0]->getNumFVarChannels(); ++channel) {
431             nonLinearChannelsPresent |= !_levels[0]->getFVarLevel(channel).isLinear();
432         }
433         if (!nonLinearChannelsPresent) {
434             moreFeaturesMask.selectFVarFeatures = false;
435             lessFeaturesMask.selectFVarFeatures = false;
436         }
437     }
438 
439     //
440     //  Initialize refinement options for Vtr -- full topology is always generated in
441     //  the last level as expected usage is for patch retrieval:
442     //
443     Vtr::internal::Refinement::Options refineOptions;
444 
445     refineOptions._sparse          = true;
446     refineOptions._minimalTopology = false;
447     refineOptions._faceVertsFirst  = options.orderVerticesFromFacesFirst;
448 
449     Sdc::Split splitType = Sdc::SchemeTypeTraits::GetTopologicalSplitType(_subdivType);
450 
451     for (int i = 1; i <= potentialMaxLevel; ++i) {
452 
453         Vtr::internal::Level& parentLevel     = getLevel(i-1);
454         Vtr::internal::Level& childLevel      = *(new Vtr::internal::Level);
455 
456         Vtr::internal::Refinement* refinement = 0;
457         if (splitType == Sdc::SPLIT_TO_QUADS) {
458             refinement = new Vtr::internal::QuadRefinement(parentLevel, childLevel, _subdivOptions);
459         } else {
460             refinement = new Vtr::internal::TriRefinement(parentLevel, childLevel, _subdivOptions);
461         }
462 
463         //
464         //  Initialize a Selector to mark a sparse set of components for refinement -- choose
465         //  the feature selection mask appropriate to the level:
466         //
467         Vtr::internal::SparseSelector selector(*refinement);
468 
469         internal::FeatureMask const & levelFeatures = (i <= shallowLevel) ? moreFeaturesMask
470                                                                           : lessFeaturesMask;
471 
472         if (i > 1) {
473             selectFeatureAdaptiveComponents(selector, levelFeatures, ConstIndexArray());
474         } else if (nonLinearScheme) {
475             selectFeatureAdaptiveComponents(selector, levelFeatures, baseFacesToRefine);
476         } else {
477             selectLinearIrregularFaces(selector, baseFacesToRefine);
478         }
479 
480         if (selector.isSelectionEmpty()) {
481             delete refinement;
482             delete &childLevel;
483             break;
484         } else {
485             refinement->refine(refineOptions);
486 
487             appendLevel(childLevel);
488             appendRefinement(*refinement);
489         }
490     }
491     _maxLevel = (unsigned int) _refinements.size();
492 
493     assembleFarLevels();
494 }
495 
496 //
497 //  Local utility functions for selecting features in faces for adaptive refinement:
498 //
499 namespace {
500     //
501     //  First are a couple of low-level utility methods to perform the same analysis
502     //  at a corner or the entire face for specific detection of inf-sharp or boundary
503     //  features.  These are shared between the analysis of the main face and those in
504     //  face-varying channels (which only differ from the main face in the presence of
505     //  face-varying boundaries).
506     //
507     //  The first can be applied equally to an individual corner or to the entire face
508     //  (using its composite tag).  The second applies to the entire face, making use
509     //  of the first, and is the main entry point for dealng with inf-sharp features.
510     //
511     //  Note we can use the composite tag here even though it arises from all corners
512     //  of the face and so does not represent a specific corner.  When at least one
513     //  smooth interior vertex exists, it limits the combinations that can exist on the
514     //  remaining corners (though quads and tris cannot be treated equally here).
515     //
516     //  If any inf-sharp features are to be selected, identify them first as irregular
517     //  or not, then qualify them more specifically.  (Remember that a regular vertex
518     //  may have its neighboring faces partitioned into irregular regions in the
519     //  presence of inf-sharp edges.  Similarly an irregular vertex may have its
520     //  neighborhood partitioned into regular regions.)
521     //
522     inline bool
doesInfSharpVTagHaveFeatures(Vtr::internal::Level::VTag compVTag,internal::FeatureMask const & featureMask)523     doesInfSharpVTagHaveFeatures(Vtr::internal::Level::VTag compVTag,
524                                  internal::FeatureMask const & featureMask) {
525 
526         //  Note that even though the given VTag may represent an individual corner, we
527         //  use more general bitwise tests here (particularly the Rule) so that we can
528         //  pass in a composite tag for the entire face and have the same tests applied:
529         //
530         if (compVTag._infIrregular) {
531             if (compVTag._rule & Sdc::Crease::RULE_CORNER) {
532                 return featureMask.selectInfSharpIrregularCorner;
533             } else if (compVTag._rule & Sdc::Crease::RULE_CREASE) {
534                 return compVTag._boundary ? featureMask.selectXOrdinaryBoundary :
535                                             featureMask.selectInfSharpIrregularCrease;
536             } else if (compVTag._rule & Sdc::Crease::RULE_DART) {
537                 return featureMask.selectInfSharpIrregularDart;
538             }
539         } else if (compVTag._boundary) {
540             //  Remember that regular boundary features should never be selected, except
541             //  for a boundary crease sharpened (and so a Corner) by an interior edge:
542 
543             if (compVTag._rule & Sdc::Crease::RULE_CORNER) {
544                 return compVTag._corner ? false : featureMask.selectInfSharpRegularCorner;
545             } else {
546                 return false;
547             }
548         } else {
549             if (compVTag._rule & Sdc::Crease::RULE_CORNER) {
550                 return featureMask.selectInfSharpRegularCorner;
551             } else {
552                 return featureMask.selectInfSharpRegularCrease;
553             }
554         }
555         return false;
556     }
557 
558     inline bool
doesInfSharpFaceHaveFeatures(Vtr::internal::Level::VTag compVTag,Vtr::internal::Level::VTag vTags[],int numVerts,internal::FeatureMask const & featureMask)559     doesInfSharpFaceHaveFeatures(Vtr::internal::Level::VTag compVTag,
560                                  Vtr::internal::Level::VTag vTags[], int numVerts,
561                                  internal::FeatureMask const & featureMask) {
562         //
563         //  For quads, if at least one smooth corner of a regular face, features
564         //  are isolated enough to make use of the composite tag alone (unless
565         //  boundary isolation is enabled, in which case trivially return).
566         //
567         //  For tris, the presence of boundaries creates more ambiguity, so we
568         //  need to exclude that case and inspect corner features individually.
569         //
570         bool isolateQuadBoundaries = false;
571 
572         bool atLeastOneSmoothCorner = (compVTag._rule & Sdc::Crease::RULE_SMOOTH);
573         if (numVerts == 4) {
574             if (atLeastOneSmoothCorner) {
575                 return doesInfSharpVTagHaveFeatures(compVTag, featureMask);
576             } else if (isolateQuadBoundaries) {
577                 return true;
578             } else if (featureMask.selectUnisolatedInteriorEdge) {
579                 //  Needed for single-crease approximation to inf-sharp interior edge:
580                 for (int i = 0; i < 4; ++i) {
581                     if (vTags[i]._infSharpEdges && !vTags[i]._boundary) {
582                         return true;
583                     }
584                 }
585             }
586         } else {
587             if (atLeastOneSmoothCorner && !compVTag._boundary) {
588                 return doesInfSharpVTagHaveFeatures(compVTag, featureMask);
589             }
590         }
591 
592         for (int i = 0; i < numVerts; ++i) {
593             if (!(vTags[i]._rule & Sdc::Crease::RULE_SMOOTH)) {
594                 if (doesInfSharpVTagHaveFeatures(vTags[i], featureMask)) {
595                     return true;
596                 }
597             }
598         }
599         return false;
600     }
601 
602     //
603     //  This is the core method/function for analyzing a face and deciding whether or not
604     //  to included it during feature-adaptive refinement.
605     //
606     //  Topological analysis of the face exploits tags that are applied to corner vertices
607     //  and carried through the refinement hierarchy.  The tags were designed with this
608     //  in mind and also to be combined via bitwise-OR to make collective decisions about
609     //  the neighborhood of the entire face.
610     //
611     //  After a few trivial acceptances/rejections, feature detection is divided up into
612     //  semi-sharp and inf-sharp cases -- note that both may be present, but semi-sharp
613     //  features have an implicit precedence until they decay and so are handled first.
614     //  They are also fairly trivial to deal with (most often requiring selection) while
615     //  the presence of boundaries and additional options complicates the inf-sharp case.
616     //  Since the inf-sharp logic needs to be applied in face-varying cases, it exists in
617     //  a separate method.
618     //
619     //  This was originally written specific to the quad-centric Catmark scheme and was
620     //  since generalized to support Loop given the enhanced tagging of components based
621     //  on the scheme.  Any enhancements here should be aware of the intended generality.
622     //  Ultimately it may not be worth trying to keep this general and we will be better
623     //  off specializing it for each scheme.  The fact that this method is intimately tied
624     //  to patch generation also begs for it to become part of a class that encompasses
625     //  both the feature adaptive tagging and the identification of the intended patches
626     //  that result from it.
627     //
628     bool
doesFaceHaveFeatures(Vtr::internal::Level const & level,Index face,internal::FeatureMask const & featureMask,int regFaceSize)629     doesFaceHaveFeatures(Vtr::internal::Level const& level, Index face,
630                          internal::FeatureMask const & featureMask, int regFaceSize) {
631 
632         using Vtr::internal::Level;
633 
634         ConstIndexArray fVerts = level.getFaceVertices(face);
635 
636         //  Irregular faces (base level) are unconditionally included:
637         if (fVerts.size() != regFaceSize) {
638             return true;
639         }
640 
641         //  Gather and combine the VTags:
642         Level::VTag vTags[4];
643         level.getFaceVTags(face, vTags);
644 
645         Level::VTag compFaceVTag = Level::VTag::BitwiseOr(vTags, fVerts.size());
646 
647         //  Faces incident irregular faces (base level) are unconditionally included:
648         if (compFaceVTag._incidIrregFace) {
649             return true;
650         }
651         //  Incomplete faces (incomplete neighborhood) are unconditionally excluded:
652         if (compFaceVTag._incomplete) {
653             return false;
654         }
655 
656         //  Select non-manifold features if specified, otherwise treat as inf-sharp:
657         if (compFaceVTag._nonManifold && featureMask.selectNonManifold) {
658             return true;
659         }
660 
661         //  Select (smooth) xord vertices if specified, boundaries handled with inf-sharp:
662         if (compFaceVTag._xordinary && featureMask.selectXOrdinaryInterior) {
663             if (compFaceVTag._rule == Sdc::Crease::RULE_SMOOTH) {
664                 return true;
665             } else if (level.getDepth() < 2) {
666                 for (int i = 0; i < fVerts.size(); ++i) {
667                     if (vTags[i]._xordinary && (vTags[i]._rule == Sdc::Crease::RULE_SMOOTH)) {
668                         return true;
669                     }
670                 }
671             }
672         }
673 
674         //  If all smooth corners, no remaining features to select (x-ordinary dealt with):
675         if (compFaceVTag._rule == Sdc::Crease::RULE_SMOOTH) {
676             return false;
677         }
678 
679         //  Semi-sharp features -- select all immediately or test the single-crease case:
680         if (compFaceVTag._semiSharp || compFaceVTag._semiSharpEdges) {
681             if (featureMask.selectSemiSharpSingle && featureMask.selectSemiSharpNonSingle) {
682                 return true;
683             } else if (level.isSingleCreasePatch(face)) {
684                 return featureMask.selectSemiSharpSingle;
685             } else {
686                 return featureMask.selectSemiSharpNonSingle;
687             }
688         }
689 
690         //  Inf-sharp features (including boundaries) -- delegate to shared method:
691         if (compFaceVTag._infSharp || compFaceVTag._infSharpEdges) {
692             return doesInfSharpFaceHaveFeatures(compFaceVTag, vTags, fVerts.size(), featureMask);
693         }
694         return false;
695     }
696 
697     //
698     //  Analyzing the face-varying topology for selection is considerably simpler that
699     //  for the face and its vertices -- in part due to the fact that these faces lie on
700     //  face-varying boundaries, and also due to assumptions about prior inspection:
701     //
702     //      - it is assumed the face topologgy does not match, so the face must lie on
703     //        a FVar boundary, i.e. inf-sharp
704     //
705     //      - it is assumed the face vertices were already inspected, so cases such as
706     //        semi-sharp or smooth interior x-ordinary features have already triggered
707     //        selection
708     //
709     //  That leaves the inspection of inf-sharp features, for the tags from the face
710     //  varying channel -- code that is shared with the main face.
711     //
712     bool
doesFaceHaveDistinctFaceVaryingFeatures(Vtr::internal::Level const & level,Index face,internal::FeatureMask const & featureMask,int fvarChannel)713     doesFaceHaveDistinctFaceVaryingFeatures(Vtr::internal::Level const& level, Index face,
714                                 internal::FeatureMask const & featureMask, int fvarChannel) {
715 
716         using Vtr::internal::Level;
717 
718         ConstIndexArray fVerts = level.getFaceVertices(face);
719 
720         assert(!level.doesFaceFVarTopologyMatch(face, fvarChannel));
721 
722         //  We can't use the composite VTag for the face here as it only includes the FVar
723         //  values specific to this face.  We need to account for all FVar values around
724         //  each corner of the face -- including those in potentially completely disjoint
725         //  sets -- to ensure that adjacent faces remain compatibly refined (i.e. differ
726         //  by only one level), so we use the composite tags for the corner vertices:
727         //
728         Level::VTag vTags[4];
729 
730         for (int i = 0; i < fVerts.size(); ++i) {
731             vTags[i] = level.getVertexCompositeFVarVTag(fVerts[i], fvarChannel);
732         }
733         Level::VTag compVTag = Level::VTag::BitwiseOr(vTags, fVerts.size());
734 
735         //  Incomplete faces (incomplete neighborhood) are unconditionally excluded:
736         if (compVTag._incomplete) {
737             return false;
738         }
739 
740         //  Select non-manifold features if specified, otherwise treat as inf-sharp:
741         if (compVTag._nonManifold && featureMask.selectNonManifold) {
742             return true;
743         }
744 
745         //  Any remaining locally extra-ordinary face-varying boundaries warrant selection:
746         if (compVTag._xordinary && featureMask.selectXOrdinaryInterior) {
747             return true;
748         }
749 
750         //  Given faces with differing FVar topology are on boundaries, defer to inf-sharp:
751         return doesInfSharpFaceHaveFeatures(compVTag, vTags, fVerts.size(), featureMask);
752     }
753 
754 } // end namespace
755 
756 //
757 //   Method for selecting components for sparse refinement based on the feature-adaptive needs
758 //   of patch generation.
759 //
760 //   It assumes we have a freshly initialized SparseSelector (i.e. nothing already selected)
761 //   and will select all relevant topological features for inclusion in the subsequent sparse
762 //   refinement.
763 //
764 void
selectFeatureAdaptiveComponents(Vtr::internal::SparseSelector & selector,internal::FeatureMask const & featureMask,ConstIndexArray facesToRefine)765 TopologyRefiner::selectFeatureAdaptiveComponents(Vtr::internal::SparseSelector& selector,
766                                                  internal::FeatureMask const & featureMask,
767                                                  ConstIndexArray facesToRefine) {
768 
769     //
770     //  Inspect each face and the properties tagged at all of its corners:
771     //
772     Vtr::internal::Level const& level = selector.getRefinement().parent();
773 
774     int numFacesToRefine = facesToRefine.size() ? facesToRefine.size() : level.getNumFaces();
775 
776     int numFVarChannels = featureMask.selectFVarFeatures ? level.getNumFVarChannels() : 0;
777 
778     for (int fIndex = 0; fIndex < numFacesToRefine; ++fIndex) {
779 
780         Vtr::Index face = facesToRefine.size() ? facesToRefine[fIndex] : (Index) fIndex;
781 
782         if (HasHoles() && level.isFaceHole(face)) continue;
783 
784         //
785         //  Test if the face has any of the specified features present.  If not, and FVar
786         //  channels are to be considered, look for features in the FVar channels:
787         //
788         bool selectFace = doesFaceHaveFeatures(level, face, featureMask, _regFaceSize);
789 
790         if (!selectFace && featureMask.selectFVarFeatures) {
791             for (int channel = 0; !selectFace && (channel < numFVarChannels); ++channel) {
792 
793                 //  Only test the face for this channel if the topology does not match:
794                 if (!level.doesFaceFVarTopologyMatch(face, channel)) {
795                     selectFace = doesFaceHaveDistinctFaceVaryingFeatures(
796                                         level, face, featureMask, channel);
797                 }
798             }
799         }
800         if (selectFace) {
801             selector.selectFace(face);
802         }
803     }
804 }
805 
806 void
selectLinearIrregularFaces(Vtr::internal::SparseSelector & selector,ConstIndexArray facesToRefine)807 TopologyRefiner::selectLinearIrregularFaces(Vtr::internal::SparseSelector& selector,
808                                             ConstIndexArray facesToRefine) {
809 
810     //
811     //  Inspect each face and select only irregular faces:
812     //
813     Vtr::internal::Level const& level = selector.getRefinement().parent();
814 
815     int numFacesToRefine = facesToRefine.size() ? facesToRefine.size() : level.getNumFaces();
816 
817     for (int fIndex = 0; fIndex < numFacesToRefine; ++fIndex) {
818 
819         Vtr::Index face = facesToRefine.size() ? facesToRefine[fIndex] : (Index) fIndex;
820 
821         if (HasHoles() && level.isFaceHole(face)) continue;
822 
823         if (level.getFaceVertices(face).size() != _regFaceSize) {
824             selector.selectFace(face);
825         }
826     }
827 }
828 
829 } // end namespace Far
830 
831 } // end namespace OPENSUBDIV_VERSION
832 } // end namespace OpenSubdiv
833