From 69e6da8fabdd4b8b762e40f63e1003811f39f41b Mon Sep 17 00:00:00 2001 From: barfowl Date: Fri, 24 Oct 2014 16:15:22 -0700 Subject: [PATCH] Fixed face-varying cases where the sharpness of values are inter-dependent: - added ValueTag indicating sharpness dependency on another value - updated base level tagging to identify dependent semi-sharp values - updated refinement to consider dependency when reassessing semi-sharpness - updated interpolation to use dependent fractional weight when necessary --- opensubdiv/far/topologyRefiner.h | 11 ++- opensubdiv/vtr/fvarLevel.cpp | 119 +++++++++++++++--------------- opensubdiv/vtr/fvarLevel.h | 3 + opensubdiv/vtr/fvarRefinement.cpp | 85 ++++++++++++++------- 4 files changed, 131 insertions(+), 87 deletions(-) diff --git a/opensubdiv/far/topologyRefiner.h b/opensubdiv/far/topologyRefiner.h index b6a79a27..fcb08df3 100644 --- a/opensubdiv/far/topologyRefiner.h +++ b/opensubdiv/far/topologyRefiner.h @@ -1210,8 +1210,15 @@ TopologyRefiner::faceVaryingInterpolateChildVertsFromVerts( float vWeight = 0.75f; float eWeight = 0.125f; - if (parentFVar.isValueSemiSharp(parentFVar.getVertexValueIndex(vert, pSibling))) { - float wCorner = refineFVar.getFractionalWeight(vert, pSibling, cVert, cSibling); + int pVertValueIndex = parentFVar.getVertexValueIndex(vert, pSibling); + if (parentFVar.isValueSemiSharp(pVertValueIndex)) { + // + // If made sharp because of the other sibling, use the fractional weight + // from that other sibling (should only occur when there are 2): + // + float wCorner = parentFVar.isValueDepSharp(pVertValueIndex) + ? refineFVar.getFractionalWeight(vert, !pSibling, cVert, !cSibling) + : refineFVar.getFractionalWeight(vert, pSibling, cVert, cSibling); float wCrease = 1.0f - wCorner; vWeight = wCrease * 0.75f + wCorner; diff --git a/opensubdiv/vtr/fvarLevel.cpp b/opensubdiv/vtr/fvarLevel.cpp index da407065..66590436 100644 --- a/opensubdiv/vtr/fvarLevel.cpp +++ b/opensubdiv/vtr/fvarLevel.cpp @@ -49,7 +49,11 @@ namespace Vtr { // Simple (for now) constructor and destructor: // FVarLevel::FVarLevel(Level const& level) : - _level(level), _isLinear(false), _hasSmoothBoundaries(false), _valueCount(0) { + _level(level), + _isLinear(false), + _hasSmoothBoundaries(false), + _hasDependentSharpness(false), + _valueCount(0) { } FVarLevel::~FVarLevel() { @@ -108,7 +112,8 @@ FVarLevel::completeTopologyFromFaceValues() { // // Given the growing number of options and behaviors to support, this is likely going // to get another pass. It may be worth identifying the behavior for each "feature", - // i.e. determine smooth or sharp for corners, creases and darts... + // i.e. determine smooth or sharp for corners, creases and darts, but the fact that + // the rule for one value may be dependent on that of another complicates this. // using Sdc::Options; @@ -120,35 +125,17 @@ FVarLevel::completeTopologyFromFaceValues() { _hasSmoothBoundaries = (fvarOptions != Options::FVAR_LINEAR_ALL) && (fvarOptions != Options::FVAR_LINEAR_BOUNDARIES); + _hasDependentSharpness = (fvarOptions == Options::FVAR_LINEAR_CORNERS_PLUS1) || + (fvarOptions == Options::FVAR_LINEAR_CORNERS_PLUS2); + bool geomCornersAreSmooth = (geomOptions != Options::VVAR_BOUNDARY_EDGE_AND_CORNER); bool fvarCornersAreSharp = (fvarOptions != Options::FVAR_LINEAR_NONE); bool makeCornersSharp = geomCornersAreSmooth && fvarCornersAreSharp; - // - // Two "options" conditionally sharpen all values for a vertex depending on the collective topology - // around the vertex rather than of the topology of the value itself within its disjoint region. - // - // Historically Hbr will sharpen all values if there are more than 3 values associated with a vertex - // (making at least 3 discts or boundary edges) while the option to sharpen corners is enabled. This - // is still being done for now but is not desirable in some cases and may become optional (consider - // two adjacent UV sets with smooth UV boundaries, then splitting one of them in two -- the other - // UV set will have its boundary sharpened at the vertex where the other was split). - // - // The "propogate corners" associated with the smooth boundary and sharp corners option will sharpen - // all values if any one is a corner. This preserves the sharpness of a concave corner in regular - // areas where one face is made a corner. Since the above option has historically sharpened all - // values in cases where there are more than 2 values at a vertex, its unclear what the intent of - // "propagate corners" is if more than 2 are present. - // - bool cornersPlus1 = (fvarOptions == Options::FVAR_LINEAR_CORNERS_PLUS1); - bool cornersPlus2 = (fvarOptions == Options::FVAR_LINEAR_CORNERS_PLUS2); + bool sharpenBothIfOneCorner = (fvarOptions == Options::FVAR_LINEAR_CORNERS_PLUS2); - bool considerEntireVertex = cornersPlus1 || cornersPlus2; - - bool sharpenAllIfMoreThan2 = considerEntireVertex; - bool sharpenAllIfAnyCorner = cornersPlus2; - bool sharpenDarts = cornersPlus2 || !_hasSmoothBoundaries; + bool sharpenDarts = sharpenBothIfOneCorner || !_hasSmoothBoundaries; // // Its awkward and potentially inefficient to try and accomplish everything in one @@ -324,6 +311,9 @@ FVarLevel::completeTopologyFromFaceValues() { ValueTag valueTagSemiSharp = valueTagMismatch; valueTagSemiSharp._semiSharp = true; + ValueTag valueTagDepSharp = valueTagSemiSharp; + valueTagDepSharp._depSharp = true; + for (int vIndex = 0; vIndex < _level.getNumVertices(); ++vIndex) { IndexArray const vFaces = _level.getVertexFaces(vIndex); LocalIndexArray const vInFace = _level.getVertexFaceLocalIndices(vIndex); @@ -371,7 +361,7 @@ FVarLevel::completeTopologyFromFaceValues() { bool allCornersAreSharp = !_hasSmoothBoundaries || Sdc::Crease::IsInfinite(vSharpness) || - (sharpenAllIfMoreThan2 && (vValueCount > 2)) || + (_hasDependentSharpness && (vValueCount > 2)) || (sharpenDarts && (vValueCount == 1) && !vIsBoundary) || _level._vertTags[vIndex]._nonManifold; if (allCornersAreSharp) { @@ -379,10 +369,8 @@ FVarLevel::completeTopologyFromFaceValues() { } // - // Values may be a mix of sharp corners and smooth boundaries... - // - // Gather information about the "span" of faces for each value. Use the results - // in one last chance for all values to be made sharp via interpolation options... + // Values may be a mix of sharp corners and smooth boundaries -- start by + // gathering information about the "span" of faces for each value: // assert(sizeof(ValueSpan) <= sizeof(int)); ValueSpan * vValueSpans = (ValueSpan *) indexBuffer; @@ -390,43 +378,58 @@ FVarLevel::completeTopologyFromFaceValues() { gatherValueSpans(vIndex, vValueSpans); - bool testForSmoothBoundaries = true; - if (sharpenAllIfAnyCorner) { - for (int i = 0; i < vValueCount; ++i) { - if (vValueSpans[i]._size == 1) { - testForSmoothBoundaries = false; - break; - } + // + // Spans are identified as sharp (disjoint) or smooth based on their own local + // topology, but depending on the specified options, the sharpness of one span + // may be dependent on the sharpness of another. Mark both as infinitely sharp + // where possible to avoid re-assessing this dependency as sharpness is reduced + // during refinement. + // + allCornersAreSharp = false; + + bool hasDependentValuesToSharpen = false; + if (_hasDependentSharpness && (vValueCount == 2)) { + // Detect interior inf-sharp (or discts) edge: + allCornersAreSharp = vValueSpans[0]._disjoint || vValueSpans[1]._disjoint; + + // Detect a sharp corner, making both sharp: + if (sharpenBothIfOneCorner) { + allCornersAreSharp |= (vValueSpans[0]._size == 1) || (vValueSpans[1]._size == 1); } + + // If only one semi-sharp, need to mark the other as dependent on it: + hasDependentValuesToSharpen = vValueSpans[0]._semiSharp != vValueSpans[1]._semiSharp; + } + if (allCornersAreSharp) { + continue; } // - // Test each vertex value to determine if it is a smooth boundary (crease) -- if not - // disjoint and not a sharp corner, tag as a smooth boundary and identify its ends. - // Note if semi-sharp, and do not tag it as a crease now -- the refinement will tag - // it once all sharpness has decayed to zero: + // Inspect each vertex value to determine if it is a smooth boundary (crease) and tag + // it accordingly. If not semi-sharp, be sure to consider those values sharpened by + // the topology of other values. // - if (testForSmoothBoundaries) { - for (int i = 0; i < vValueCount; ++i) { - ValueSpan& vSpan = vValueSpans[i]; + for (int i = 0; i < vValueCount; ++i) { + ValueSpan& vSpan = vValueSpans[i]; - if (!vSpan._disjoint && ((vSpan._size > 1) || !fvarCornersAreSharp)) { - Index valueIndex = (i == 0) ? vIndex : (vSiblingOffset + i - 1); + if (!vSpan._disjoint && ((vSpan._size > 1) || !fvarCornersAreSharp)) { + Index valueIndex = (i == 0) ? vIndex : (vSiblingOffset + i - 1); - if ((vSpan._semiSharp > 0) || (vSharpness > 0)) { - _vertValueTags[valueIndex] = valueTagSemiSharp; - } else { - _vertValueTags[valueIndex] = valueTagCrease; - } + if ((vSpan._semiSharp > 0) || Sdc::Crease::IsSharp(vSharpness)) { + _vertValueTags[valueIndex] = valueTagSemiSharp; + } else if (hasDependentValuesToSharpen) { + _vertValueTags[valueIndex] = valueTagDepSharp; + } else { + _vertValueTags[valueIndex] = valueTagCrease; + } - LocalIndex * endFaces = &_vertValueCreaseEnds[2 * valueIndex]; + LocalIndex * endFaces = &_vertValueCreaseEnds[2 * valueIndex]; - endFaces[0] = vSpan._start; - if ((i == 0) && (vSpan._start != 0)) { - endFaces[1] = (LocalIndex) (vSpan._start + vSpan._size - 1 - vFaces.size()); - } else { - endFaces[1] = vSpan._start + vSpan._size - 1; - } + endFaces[0] = vSpan._start; + if ((i == 0) && (vSpan._start != 0)) { + endFaces[1] = (LocalIndex) (vSpan._start + vSpan._size - 1 - vFaces.size()); + } else { + endFaces[1] = vSpan._start + vSpan._size - 1; } } } diff --git a/opensubdiv/vtr/fvarLevel.h b/opensubdiv/vtr/fvarLevel.h index ec447944..c3601f09 100644 --- a/opensubdiv/vtr/fvarLevel.h +++ b/opensubdiv/vtr/fvarLevel.h @@ -118,6 +118,7 @@ public: ValueTagSize _mismatch : 1; // local FVar topology does not match ValueTagSize _crease : 1; // value is a crease, otherwise a corner ValueTagSize _semiSharp : 1; // value is a corner decaying to crease + ValueTagSize _depSharp : 1; // value a corner by dependency on another }; public: @@ -155,6 +156,7 @@ public: bool isValueSemiSharp(Index valueIndex) const { return _vertValueTags[valueIndex]._semiSharp; } bool isValueInfSharp(Index valueIndex) const { return !_vertValueTags[valueIndex]._semiSharp && !_vertValueTags[valueIndex]._crease; } + bool isValueDepSharp(Index valueIndex) const { return _vertValueTags[valueIndex]._depSharp; } // Higher-level topological queries, i.e. values in a neighborhood: @@ -201,6 +203,7 @@ public: bool _isLinear; bool _hasSmoothBoundaries; + bool _hasDependentSharpness; int _valueCount; // diff --git a/opensubdiv/vtr/fvarRefinement.cpp b/opensubdiv/vtr/fvarRefinement.cpp index 523bb66e..09a5819e 100644 --- a/opensubdiv/vtr/fvarRefinement.cpp +++ b/opensubdiv/vtr/fvarRefinement.cpp @@ -77,9 +77,10 @@ FVarRefinement::applyRefinement() { // // Transfer basic properties from the parent to child level: // - _child->_options = _parent->_options; - _child->_isLinear = _parent->_isLinear; - _child->_hasSmoothBoundaries = _parent->_hasSmoothBoundaries; + _child->_options = _parent->_options; + _child->_isLinear = _parent->_isLinear; + _child->_hasSmoothBoundaries = _parent->_hasSmoothBoundaries; + _child->_hasDependentSharpness = _parent->_hasDependentSharpness; // // It's difficult to know immediately how many child values arise from the @@ -495,6 +496,8 @@ FVarRefinement::reclassifySemisharpValues() { // their parent values -- we will be able to clear it in many simple cases but // ultimately will need to inspect each value: // + bool hasDependentSharpness = _parent->_hasDependentSharpness; + FVarLevel::ValueTag valTagCrease; valTagCrease.clear(); valTagCrease._mismatch = true; @@ -521,41 +524,69 @@ FVarRefinement::reclassifySemisharpValues() { // // We are left with some or no semi-sharp edges in the child vertex. If none // left the child-vertex will no longer be semi-sharp and we can just clear - // those values marked as semi-sharp. Otherwise, its simplest to assume all - // semi-sharp edges have decayed (so clearing them again) and using the - // remaining semi-sharp edges to reset those values that still are. - // - // So convert all semi-sharp tags to creases and reset those that remain if - // the child vertex is still tagged as semi-sharp: + // those values marked as semi-sharp and continue: // int vSiblingOffset = _child->_vertSiblingOffsets[cVert]; int vSiblingCount = _child->_vertSiblingCounts[cVert]; int vValueCount = 1 + vSiblingCount; + + if (!_refinement._child->_vertTags[cVert]._semiSharp) { + for (int j = 0; j < vValueCount; ++j) { + int vValueIndex = (j == 0) ? cVert : (vSiblingOffset + j - 1); + + if (_child->isValueSemiSharp(vValueIndex)) { + _child->_vertValueTags[vValueIndex] = valTagCrease; + } + } + continue; + } + + // + // There are some semi-sharp edges left. For those values tagged as semi-sharp, + // see if they are still semi-sharp and clear those that are not: + // + IndexArray const cVertEdges = _refinement._child->getVertexEdges(cVert); + for (int j = 0; j < vValueCount; ++j) { int vValueIndex = (j == 0) ? cVert : (vSiblingOffset + j - 1); - if (_child->isValueSemiSharp(vValueIndex)) { - _child->_vertValueTags[vValueIndex] = valTagCrease; + FVarLevel::ValueTag & cValueTag = _child->_vertValueTags[vValueIndex]; + + if (cValueTag._semiSharp && !cValueTag._depSharp) { + LocalIndex * vCreaseEnds = &_child->_vertValueCreaseEnds[2 * vValueIndex]; + + bool isStillSemiSharp = false; + if (vCreaseEnds[1] > vCreaseEnds[0]) { + for (int k = vCreaseEnds[0] + 1; !isStillSemiSharp && (k <= vCreaseEnds[1]); ++k) { + isStillSemiSharp = _refinement._child->_edgeTags[cVertEdges[k]]._semiSharp; + } + } else if (vCreaseEnds[0] > vCreaseEnds[1]) { + for (int k = vCreaseEnds[0] + 1; !isStillSemiSharp && (k < cVertEdges.size()); ++k) { + isStillSemiSharp = _refinement._child->_edgeTags[cVertEdges[k]]._semiSharp; + } + for (int k = 0; !isStillSemiSharp && (k <= vCreaseEnds[1]); ++k) { + isStillSemiSharp = _refinement._child->_edgeTags[cVertEdges[k]]._semiSharp; + } + } + if (!isStillSemiSharp) { + cValueTag = valTagCrease; + } } } - if (!_refinement._child->_vertTags[cVert]._semiSharp) continue; // - // Identify the remaining semi-sharp edges and the corresponding values that - // should be retained as semi-sharp: + // Now account for "dependent sharpness" (only matters when we have two values) -- + // if one was dependent/sharpened based on the other being sharp, clear the dependency + // tag if it is no longer sharp: // - SiblingArray const vFaceSiblings = _child->getVertexFaceSiblings(cVert); + if ((vValueCount == 2) && hasDependentSharpness) { + FVarLevel::ValueTag & v0Tag = _child->_vertValueTags[cVert]; + FVarLevel::ValueTag & v1Tag = _child->_vertValueTags[vSiblingOffset]; - IndexArray const cVertEdges = _refinement._child->getVertexEdges(cVert); - for (int j = 0; j < cVertEdges.size(); ++j) { - if (_refinement._child->_edgeTags[cVertEdges[j]]._semiSharp) { - int jPrev = j ? (j - 1) : (vFaceSiblings.size() - 1); - if (vFaceSiblings[jPrev] == vFaceSiblings[j]) { - int vSibling = vFaceSiblings[j]; - int vValueIndex = (vSibling == 0) ? cVert : (vSiblingOffset + vSibling - 1); - - _child->_vertValueTags[vValueIndex] = valTagSemiSharp; - } + if (v0Tag._depSharp && !v1Tag._semiSharp) { + v0Tag._depSharp = false; + } else if (v1Tag._depSharp && !v0Tag._semiSharp) { + v1Tag._depSharp = false; } } } @@ -563,8 +594,8 @@ FVarRefinement::reclassifySemisharpValues() { float FVarRefinement::getFractionalWeight(Index pVert, Sibling pSibling, - Index cVert, Sibling /* cSibling */) const -{ + Index cVert, Sibling /* cSibling */) const { + FVarLevel const& parentFVar = *_parent; Level const& parent = *_refinement._parent; Level const& child = *_refinement._child;