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