OpenSubdiv/opensubdiv/far/topologyRefiner.cpp
barfowl 272efb6911 Minor changes to Far::TopologyRefiner:
- now declared non-copyable
    - replaced alloca() usage with StackBuffer (to allow overflow to heap)
    - overloaded Limit() to compute limit tangents (two tangents per vertex)
    - disabled face-varying influence on feature-adaptive selection
2015-04-30 19:37:53 -07:00

547 lines
22 KiB
C++

//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#include "../far/topologyRefiner.h"
#include "../far/error.h"
#include "../vtr/sparseSelector.h"
#include "../vtr/quadRefinement.h"
#include "../vtr/triRefinement.h"
#include <cassert>
#include <cstdio>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
//
// Relatively trivial construction/destruction -- the base level (level[0]) needs
// to be explicitly initialized after construction and refinement then applied
//
TopologyRefiner::TopologyRefiner(Sdc::SchemeType schemeType, Sdc::Options schemeOptions) :
_subdivType(schemeType),
_subdivOptions(schemeOptions),
_isUniform(true),
_hasHoles(false),
_maxLevel(0),
_uniformOptions(0),
_adaptiveOptions(0),
_totalVertices(0),
_totalEdges(0),
_totalFaces(0),
_totalFaceVertices(0),
_maxValence(0) {
// Need to revisit allocation scheme here -- want to use smart-ptrs for these
// but will probably have to settle for explicit new/delete...
_levels.reserve(10);
_levels.push_back(new Vtr::Level);
}
TopologyRefiner::~TopologyRefiner() {
for (int i=0; i<(int)_levels.size(); ++i) {
delete _levels[i];
}
for (int i=0; i<(int)_refinements.size(); ++i) {
delete _refinements[i];
}
}
void
TopologyRefiner::Unrefine() {
if (_levels.size()) {
for (int i=1; i<(int)_levels.size(); ++i) {
delete _levels[i];
}
_levels.resize(1);
initializeInventory();
}
for (int i=0; i<(int)_refinements.size(); ++i) {
delete _refinements[i];
}
_refinements.clear();
}
//
// Intializing and updating the component inventory:
//
void
TopologyRefiner::initializeInventory() {
if (_levels.size()) {
assert(_levels.size() == 1);
Vtr::Level const & baseLevel = *_levels[0];
_totalVertices = baseLevel.getNumVertices();
_totalEdges = baseLevel.getNumEdges();
_totalFaces = baseLevel.getNumFaces();
_totalFaceVertices = baseLevel.getNumFaceVerticesTotal();
_maxValence = baseLevel.getMaxValence();
} else {
_totalVertices = 0;
_totalEdges = 0;
_totalFaces = 0;
_totalFaceVertices = 0;
_maxValence = 0;
}
}
void
TopologyRefiner::updateInventory(Vtr::Level const & newLevel) {
_totalVertices += newLevel.getNumVertices();
_totalEdges += newLevel.getNumEdges();
_totalFaces += newLevel.getNumFaces();
_totalFaceVertices += newLevel.getNumFaceVerticesTotal();
_maxValence = std::max(_maxValence, newLevel.getMaxValence());
}
void
TopologyRefiner::appendLevel(Vtr::Level & newLevel) {
_levels.push_back(&newLevel);
updateInventory(newLevel);
}
void
TopologyRefiner::appendRefinement(Vtr::Refinement & newRefinement) {
//
// There may be properties to transfer between refinements that cannot be passed on
// when refining between the parent and child since they exist "above" the parent:
//
bool applyBaseFace = (_isUniform && _uniformOptions.applyBaseFacePerFace) ||
(!_isUniform && _adaptiveOptions.applyBaseFacePerFace);
if (applyBaseFace) {
newRefinement.propagateBaseFace(_refinements.size() ? _refinements.back() : 0);
}
_refinements.push_back(&newRefinement);
}
//
// Accessors to the topology information:
//
int
TopologyRefiner::GetNumFVarValuesTotal(int channel) const {
int sum = 0;
for (int i = 0; i < (int)_levels.size(); ++i) {
sum += _levels[i]->getNumFVarValues(channel);
}
return sum;
}
int
TopologyRefiner::GetNumHoles(int level) const {
int sum = 0;
Vtr::Level const & lvl = getLevel(level);
for (Index face = 0; face < lvl.getNumFaces(); ++face) {
if (lvl.isFaceHole(face)) {
++sum;
}
}
return sum;
}
//
// Main refinement method -- allocating and initializing levels and refinements:
//
void
TopologyRefiner::RefineUniform(UniformOptions options) {
if (_levels[0]->getNumVertices() == 0) {
Error(FAR_RUNTIME_ERROR,
"Cannot apply uniform refinement -- base level appears to be uninitialized.");
return;
}
if (_refinements.size()) {
Error(FAR_RUNTIME_ERROR,
"Cannot apply uniform refinement -- previous refinements already applied.");
return;
}
//
// Allocate the stack of levels and the refinements between them:
//
_uniformOptions = options;
_isUniform = true;
_maxLevel = options.refinementLevel;
Sdc::Split splitType = (_subdivType == Sdc::SCHEME_LOOP) ? Sdc::SPLIT_TO_TRIS : Sdc::SPLIT_TO_QUADS;
//
// Initialize refinement options for Vtr -- adjusting full-topology for the last level:
//
Vtr::Refinement::Options refineOptions;
refineOptions._sparse = false;
refineOptions._faceVertsFirst = options.orderVerticesFromFacesFirst;
for (int i = 1; i <= (int)options.refinementLevel; ++i) {
refineOptions._minimalTopology =
options.fullTopologyInLastLevel ? false : (i == options.refinementLevel);
Vtr::Level& parentLevel = getLevel(i-1);
Vtr::Level& childLevel = *(new Vtr::Level);
Vtr::Refinement* refinement = 0;
if (splitType == Sdc::SPLIT_TO_QUADS) {
refinement = new Vtr::QuadRefinement(parentLevel, childLevel, _subdivOptions);
} else {
refinement = new Vtr::TriRefinement(parentLevel, childLevel, _subdivOptions);
}
refinement->refine(refineOptions);
appendLevel(childLevel);
appendRefinement(*refinement);
}
}
void
TopologyRefiner::RefineAdaptive(AdaptiveOptions options) {
if (_levels[0]->getNumVertices() == 0) {
Error(FAR_RUNTIME_ERROR,
"Cannot apply adaptive refinement -- base level appears to be uninitialized.");
return;
}
if (_refinements.size()) {
Error(FAR_RUNTIME_ERROR,
"Cannot apply adaptive refinement -- previous refinements already applied.");
return;
}
//
// Allocate the stack of levels and the refinements between them:
//
_adaptiveOptions = options;
_isUniform = false;
_maxLevel = options.isolationLevel;
//
// Initialize refinement options for Vtr -- full topology is always generated in
// the last level as expected usage is for patch retrieval:
//
Vtr::Refinement::Options refineOptions;
refineOptions._sparse = true;
refineOptions._minimalTopology = false;
refineOptions._faceVertsFirst = options.orderVerticesFromFacesFirst;
Sdc::Split splitType = (_subdivType == Sdc::SCHEME_LOOP) ? Sdc::SPLIT_TO_TRIS : Sdc::SPLIT_TO_QUADS;
for (int i = 1; i <= (int)options.isolationLevel; ++i) {
Vtr::Level& parentLevel = getLevel(i-1);
Vtr::Level& childLevel = *(new Vtr::Level);
Vtr::Refinement* refinement = 0;
if (splitType == Sdc::SPLIT_TO_QUADS) {
refinement = new Vtr::QuadRefinement(parentLevel, childLevel, _subdivOptions);
} else {
refinement = new Vtr::TriRefinement(parentLevel, childLevel, _subdivOptions);
}
//
// Initialize a Selector to mark a sparse set of components for refinement. If
// nothing was selected, discard the new refinement and child level, trim the
// maximum level and stop refinining any further. Otherwise, refine and append
// the new refinement and child.
//
Vtr::SparseSelector selector(*refinement);
selectFeatureAdaptiveComponents(selector);
if (selector.isSelectionEmpty()) {
_maxLevel = i - 1;
delete refinement;
delete &childLevel;
break;
}
refinement->refine(refineOptions);
appendLevel(childLevel);
appendRefinement(*refinement);
}
}
//
// Method for selecting components for sparse refinement based on the feature-adaptive needs
// of patch generation.
//
// It assumes we have a freshly initialized Vtr::SparseSelector (i.e. nothing already selected)
// and will select all relevant topological features for inclusion in the subsequent sparse
// refinement.
//
// This was originally written specific to the quad-centric Catmark scheme and was since
// generalized to support Loop given the enhanced tagging of components based on the scheme.
// Any further enhancements here, e.g. new approaches for dealing with infinitely sharp
// creases, should be aware of the intended generality. Ultimately it may not be worth
// trying to keep this general and we will be better off specializing it for each scheme.
// The fact that this method is intimately tied to patch generation also begs for it to
// become part of a class that encompasses both the feature adaptive tagging and the
// identification of the intended patch that result from it.
//
void
TopologyRefiner::selectFeatureAdaptiveComponents(Vtr::SparseSelector& selector) {
Vtr::Level const& level = selector.getRefinement().parent();
int regularFaceSize = selector.getRefinement()._regFaceSize;
bool considerSingleCreasePatch = _adaptiveOptions.useSingleCreasePatch && (regularFaceSize == 4);
//
// Face-varying consideration when isolating features:
// - there must obviously be face-varying channels for any consideration
// - we can ignore all purely linear face-varying channels -- a common case that
// will allow us to avoid the repeated per-face inspection of FVar data
// - may allow a subset of face-varying channels to be considered in future:
//
// Note that some of this consideration can be given at the highest level and then
// reflected potentially in the Selector, e.g. when all FVar channels are linear,
// any request to inspect them can be overridden for all levels and not repeatedly
// reassessed here for each level.
//
int numFVarChannels = level.getNumFVarChannels();
bool considerFVarChannels = false;
if (considerFVarChannels) {
considerFVarChannels = false;
for (int channel = 0; channel < numFVarChannels; ++channel) {
if (not level._fvarChannels[channel]->isLinear()) {
considerFVarChannels = true;
break;
}
}
}
//
// Inspect each face and the properties tagged at all of its corners:
//
for (Vtr::Index face = 0; face < level.getNumFaces(); ++face) {
if (level.isFaceHole(face)) {
continue;
}
Vtr::ConstIndexArray faceVerts = level.getFaceVertices(face);
//
// Testing irregular faces is only necessary at level 0, and potentially warrants
// separating out as the caller can detect these:
//
if (faceVerts.size() != regularFaceSize) {
//
// We need to also ensure that all adjacent faces to this are selected, so we
// select every face incident every vertex of the face. This is the only place
// where other faces are selected as a side effect and somewhat undermines the
// whole intent of the per-face traversal.
//
Vtr::ConstIndexArray fVerts = level.getFaceVertices(face);
for (int i = 0; i < fVerts.size(); ++i) {
ConstIndexArray fVertFaces = level.getVertexFaces(fVerts[i]);
for (int j = 0; j < fVertFaces.size(); ++j) {
selector.selectFace(fVertFaces[j]);
}
}
continue;
}
//
// Combine the tags for all vertices of the face and quickly accept/reject based on
// the presence/absence of properties where we can (further inspection is likely to
// be necessary in some cases, particularly when we start trying to be clever about
// minimizing refinement for inf-sharp creases, etc.):
//
Vtr::Level::VTag compFaceVTag = level.getFaceCompositeVTag(faceVerts);
if (compFaceVTag._incomplete) {
continue;
}
bool selectFace = false;
if (compFaceVTag._xordinary) {
selectFace = true;
} else if (compFaceVTag._nonManifold) {
// Warrants further inspection in future -- isolate for now
// - will want to defer inf-sharp treatment to below
selectFace = true;
} else if (compFaceVTag._rule == Sdc::Crease::RULE_SMOOTH) {
// Avoid isolation when ALL vertices are Smooth. All vertices must be regular by
// now and all vertices Smooth implies they are all interior vertices. (If any
// adjacent faces are not regular, this face will have been previously selected).
selectFace = false;
} else if (compFaceVTag._rule & Sdc::Crease::RULE_DART) {
// Any occurrence of a Dart vertex requires isolation
selectFace = true;
} else if (not (compFaceVTag._rule & Sdc::Crease::RULE_SMOOTH)) {
// None of the vertices is Smooth, so we have all vertices either Crease or Corner.
// Though some may be regular patches, this currently warrants isolation as we only
// support regular patches with one corner or one boundary, i.e. with one or more
// smooth interior vertices.
selectFace = true;
} else if (compFaceVTag._semiSharp || compFaceVTag._semiSharpEdges) {
// Any semi-sharp feature at or around the vertex warrants isolation -- unless we
// optimize for the single-crease patch, i.e. only edge sharpness of a constant value
// along the entire regular patch boundary (quickly exclude the Corner case first):
if (considerSingleCreasePatch && not (compFaceVTag._rule & Sdc::Crease::RULE_CORNER)) {
selectFace = not level.isSingleCreasePatch(face);
} else {
selectFace = true;
}
} else if (not compFaceVTag._boundary) {
// At this point we are left with a mix of smooth and inf-sharp features. If not
// on a boundary, the interior inf-sharp features need isolation -- unless we are
// again optimizing for the single-crease patch, infinitely sharp in this case.
//
// Note this case of detecting a single-crease patch, while similar to the above,
// is kept separate for the inf-sharp case: a separate and much more efficient
// test can be made for the inf-sharp case, and there are other opportunities here
// to optimize for regular patches at infinitely sharp corners.
if (considerSingleCreasePatch && not (compFaceVTag._rule & Sdc::Crease::RULE_CORNER)) {
selectFace = not level.isSingleCreasePatch(face);
} else {
selectFace = true;
}
} else if (not (compFaceVTag._rule & Sdc::Crease::RULE_CORNER)) {
// We are now left with boundary faces -- if no Corner vertex, we have a mix of both
// regular Smooth and Crease vertices on a boundary face, which can only be a regular
// boundary patch, so don't isolate.
selectFace = false;
} else {
// The last case with at least one Corner vertex and one Smooth (interior) vertex --
// distinguish the regular corner case from others:
if (not compFaceVTag._corner) {
// We may consider interior sharp corners as regular in future, but for now we
// only accept a topological corner for the regular corner case:
selectFace = true;
} else if (level.getDepth() > 0) {
// A true corner at a subdivided level -- adjacent verts must be Crease and the
// opposite Smooth so we must have a regular corner:
selectFace = false;
} else {
// Make sure the adjacent boundary vertices were not sharpened, or equivalently,
// that only one corner is sharp:
unsigned int infSharpCount = level._vertTags[faceVerts[0]]._infSharp;
for (int i = 1; i < faceVerts.size(); ++i) {
infSharpCount += level._vertTags[faceVerts[i]]._infSharp;
}
selectFace = (infSharpCount != 1);
}
}
//
// If still not selected, inspect the face-varying channels (when present) for similar
// irregular features requiring isolation:
//
if (not selectFace and considerFVarChannels) {
for (int channel = 0; not selectFace && (channel < numFVarChannels); ++channel) {
Vtr::FVarLevel const & fvarLevel = *level._fvarChannels[channel];
//
// Retrieve the counterpart to the face-vertices composite tag for the face-values
// for this channel. We can make some quick accept/reject tests but eventually we
// will need to combine the face-vertex and face-varying topology to determine the
// regularity of faces along face-varying boundaries.
//
Vtr::ConstIndexArray faceValues = fvarLevel.getFaceValues(face);
Vtr::FVarLevel::ValueTag compFVarFaceTag =
fvarLevel.getFaceCompositeValueTag(faceValues, faceVerts);
// No mismatch in topology -> no need to further isolate...
if (not compFVarFaceTag._mismatch) continue;
if (compFVarFaceTag._xordinary) {
// An xordinary boundary value always requires isolation:
selectFace = true;
} else {
// Combine the FVar topology tags (ValueTags) at corners with the vertex topology
// tags (VTags), then make similar inferences from the combined tags as was done
// for the face.
Vtr::Level::VTag fvarVTags[4];
Vtr::Level::VTag compFVarVTag =
fvarLevel.getFaceCompositeValueAndVTag(faceValues, faceVerts, fvarVTags);
if (not (compFVarVTag._rule & Sdc::Crease::RULE_SMOOTH)) {
// No Smooth corners so too many boundaries/corners -- need to isolate:
selectFace = true;
} else if (not (compFVarVTag._rule & Sdc::Crease::RULE_CORNER)) {
// A mix of Smooth and Crease corners -- must be regular so don't isolate:
selectFace = false;
} else {
// Since FVar boundaries can be "sharpened" based on the linear interpolation
// rules, we again have to inspect more closely (as we did with the original
// face) to ensure we have a regular corner and not a sharpened crease:
unsigned int boundaryCount = fvarVTags[0]._boundary,
infSharpCount = fvarVTags[0]._infSharp;
for (int i = 1; i < faceVerts.size(); ++i) {
boundaryCount += fvarVTags[i]._boundary;
infSharpCount += fvarVTags[i]._infSharp;
}
selectFace = (boundaryCount != 3) || (infSharpCount != 1);
// There is a possibility of a false positive at level 0 -- Smooth interior
// vertex with adjacent Corner and two opposite boundary Crease vertices
// (the topological corner tag catches this above). Verify that the corner
// vertex is opposite the smooth vertex (and consider doing this above)...
//
if (not selectFace && (level.getDepth() == 0)) {
if (fvarVTags[0]._infSharp && fvarVTags[2]._boundary) selectFace = true;
if (fvarVTags[1]._infSharp && fvarVTags[3]._boundary) selectFace = true;
if (fvarVTags[2]._infSharp && fvarVTags[0]._boundary) selectFace = true;
if (fvarVTags[3]._infSharp && fvarVTags[1]._boundary) selectFace = true;
}
}
}
}
}
// Finally, select the face for further refinement:
if (selectFace) {
selector.selectFace(face);
}
}
}
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv