From f706062ab1dc60cfa5154ed8abb0400c87f8eea9 Mon Sep 17 00:00:00 2001 From: barfowl Date: Mon, 6 Oct 2014 12:07:44 -0700 Subject: [PATCH] Added support for semi-sharp features along face-varying boundaries: - added semi-sharp tag to FVar ValueTags and applied in base FVarLevel - re-assess status of tagged semi-sharp values in each FVarRefinement - detect and apply fractional weighting in InterpolateFaceVarying() --- opensubdiv/far/topologyRefiner.h | 25 ++++- opensubdiv/vtr/fvarLevel.cpp | 180 +++++++++++++++++++----------- opensubdiv/vtr/fvarLevel.h | 22 +++- opensubdiv/vtr/fvarRefinement.cpp | 126 ++++++++++++++++++++- opensubdiv/vtr/fvarRefinement.h | 7 ++ 5 files changed, 281 insertions(+), 79 deletions(-) diff --git a/opensubdiv/far/topologyRefiner.h b/opensubdiv/far/topologyRefiner.h index 3b5bb9da..f57b4b2d 100644 --- a/opensubdiv/far/topologyRefiner.h +++ b/opensubdiv/far/topologyRefiner.h @@ -1148,6 +1148,9 @@ TopologyRefiner::faceVaryingInterpolateChildVertsFromVerts( // // Each FVar value associated with a vertex will be either a corner or a crease, // or potentially in transition from corner to crease: + // - if the CHILD is a corner, we have no transition but a corner + // - otherwise if the PARENT is a crease too, we have no transition but a creas + // - otherwise the parent must be a corner and the child a crease. // for (int cSibling = 0; cSibling < childFVar.getNumVertexValues(cVert); ++cSibling) { int pSibling = refineFVar.getChildValueParentSource(cVert, cSibling); @@ -1159,15 +1162,29 @@ TopologyRefiner::faceVaryingInterpolateChildVertsFromVerts( U & vdst = dst[cVertValue]; vdst.Clear(); - if (parentFVar.isValueCorner(parentFVar.getVertexValueIndex(vert, pSibling))) { + if (childFVar.isValueCorner(childFVar.getVertexValueIndex(cVert, cSibling))) { vdst.AddWithWeight(src[pVertValue], 1.0f); } else { + // + // We have either a crease or a transition from corner to crease -- in + // either case, we need the end values for the crease: + // Index pEndValues[2]; parentFVar.getVertexCreaseEndValues(vert, pSibling, pEndValues); - vdst.AddWithWeight(src[pEndValues[0]], 0.125f); - vdst.AddWithWeight(src[pEndValues[1]], 0.125f); - vdst.AddWithWeight(src[pVertValue], 0.75f); + float vWeight = 0.75f; + float eWeight = 0.125f; + + if (parentFVar.isValueSemiSharp(parentFVar.getVertexValueIndex(vert, pSibling))) { + float wCorner = refineFVar.getFractionalWeight(vert, pSibling, cVert, cSibling); + float wCrease = 1.0f - wCorner; + + vWeight = wCrease * 0.75f + wCorner; + eWeight = wCrease * 0.125f; + } + vdst.AddWithWeight(src[pEndValues[0]], eWeight); + vdst.AddWithWeight(src[pEndValues[1]], eWeight); + vdst.AddWithWeight(src[pVertValue], vWeight); } } } diff --git a/opensubdiv/vtr/fvarLevel.cpp b/opensubdiv/vtr/fvarLevel.cpp index 2c425267..bd0e9aaa 100644 --- a/opensubdiv/vtr/fvarLevel.cpp +++ b/opensubdiv/vtr/fvarLevel.cpp @@ -311,7 +311,12 @@ FVarLevel::completeTopologyFromFaceValues() { // vertex and to inspect local face-varying topology in more detail when necessary: // ValueTag valueTagCrease = valueTagMismatch; - valueTagCrease._crease = true; + valueTagCrease._crease = true; + valueTagCrease._semiSharp = false; + + ValueTag valueTagSemiSharp = valueTagMismatch; + valueTagSemiSharp._crease = false; + valueTagSemiSharp._semiSharp = true; for (int vIndex = 0; vIndex < _level.getNumVertices(); ++vIndex) { IndexArray const vFaces = _level.getVertexFaces(vIndex); @@ -350,9 +355,16 @@ FVarLevel::completeTopologyFromFaceValues() { // further inspection there may be other cases where all are determined to be // sharp, but use what information we can now to avoid that inspection: // - bool vIsBoundary = _level._vertTags[vIndex]._boundary; + // Regarding sharpness of the vertex itself, its vertex tags reflect the inf- + // or semi-sharp nature of the vertex and edges around it, so be careful not + // to assume too much from say, the presence of an incident inf-sharp edge. + // We can make clear decisions based on the sharpness of the vertex itself. + // + bool vIsBoundary = _level._vertTags[vIndex]._boundary; + float vSharpness = _level._vertSharpness[vIndex]; bool allCornersAreSharp = !_hasSmoothBoundaries || + Sdc::Crease::IsInfinite(vSharpness) || (sharpenAllIfMoreThan2 && (vValueCount > 2)) || (sharpenDarts && (vValueCount == 1) && !vIsBoundary) || _level._vertTags[vIndex]._nonManifold; @@ -363,69 +375,15 @@ FVarLevel::completeTopologyFromFaceValues() { // // Values may be a mix of sharp corners and smooth boundaries... // - // Gather information about the "span" of faces for each value. The "size" (number - // of faces in which each value occurs), is most immediately useful in determining - // whether a value is a corner or smooth boundary, while other properties such as - // the first face and whether or not the span is interrupted by a discts edge (and - // so made "disjoint") are useful to fully qualify 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... // - struct ValueSpan { - LocalIndex _size; - LocalIndex _start; - LocalIndex _disjoint; - }; - assert(sizeof(ValueSpan) < sizeof(int)); + assert(sizeof(ValueSpan) <= sizeof(int)); ValueSpan * vValueSpans = (ValueSpan *) indexBuffer; memset(vValueSpans, 0, vValueCount * sizeof(ValueSpan)); - IndexArray const vEdges = _level.getVertexEdges(vIndex); - LocalIndexArray const vInEdge = _level.getVertexEdgeLocalIndices(vIndex); + gatherValueSpans(vIndex, vValueSpans); - if (vValueCount == 1) { - // Mark an interior dart disjoint if more than one discts edge: - for (int i = 0; i < vEdges.size(); ++i) { - if (_edgeTags[vEdges[i]]._mismatch) { - if (vValueSpans[0]._size) { - vValueSpans[0]._disjoint = true; - break; - } else { - vValueSpans[0]._size = (LocalIndex) vFaces.size(); - vValueSpans[0]._start = (LocalIndex) i; - } - } - } - } else { - // Walk around the vertex and accumulate span info for each value -- be - // careful about the span for the first value "wrapping" around: - vValueSpans[0]._size = 1; - vValueSpans[0]._start = 0; - if (_edgeTags[vEdges[0]]._mismatch && !vIsBoundary) { - vValueSpans[0]._disjoint = (vFaceSiblings[vFaces.size() - 1] == 0); - } - for (int i = 1; i < vFaces.size(); ++i) { - if (_edgeTags[vEdges[i]]._mismatch) { - if (vFaceSiblings[i] == vFaceSiblings[i-1]) { - ++ vValueSpans[vFaceSiblings[i]]._disjoint; - } else { - // If we have already set the span for this value, mark disjoint - if (vValueSpans[vFaceSiblings[i]]._size > 0) { - ++ vValueSpans[vFaceSiblings[i]]._disjoint; - } - vValueSpans[vFaceSiblings[i]]._start = (LocalIndex) i; - } - } - ++ vValueSpans[vFaceSiblings[i]]._size; - } - // If the span for value 0 has wrapped around, decrement the disjoint added - // at the interior edge where it started the closing part of the span: - if ((vFaceSiblings[vFaces.size() - 1] == 0) && !vIsBoundary) { - -- vValueSpans[0]._disjoint; - } - } - - // - // Last chance for all values to be made sharp via interpolation options... - // bool testForSmoothBoundaries = true; if (sharpenAllIfAnyCorner) { for (int i = 0; i < vValueCount; ++i) { @@ -435,18 +393,25 @@ FVarLevel::completeTopologyFromFaceValues() { } } } + + // + // 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: + // if (testForSmoothBoundaries) { - // - // Inspect each vertex value -- if not disjoint and not a sharp corner, tag as a - // smooth boundary and identify its ends: - // 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); - _vertValueTags[valueIndex] = valueTagCrease; + if ((vSpan._semiSharp > 0) || (vSharpness > 0)) { + _vertValueTags[valueIndex] = valueTagSemiSharp; + } else { + _vertValueTags[valueIndex] = valueTagCrease; + } LocalIndex * endFaces = &_vertValueCreaseEnds[2 * valueIndex]; @@ -642,6 +607,10 @@ FVarLevel::print() const { for (int j = 0; j < sCount; ++j) { printf("%4d", _vertValueTags[sOffset + j]._crease); } + printf(", semi-sharp =%2d", (int)_vertValueTags[i]._semiSharp); + for (int j = 0; j < sCount; ++j) { + printf("%2d", _vertValueTags[sOffset + j]._semiSharp); + } } printf("\n"); } @@ -868,6 +837,87 @@ printf(" edge[] tag mismatch = %d\n", _edgeTags[eIndex]._mismatch); } } +// +// Gather information about the "span" of faces for each value: +// +// This method is only invoked when the spans for values may be smooth boundaries and +// other criteria that make all sharp (e.g. a non-manifold vertex) have been considered. +// +// The "size" (number of faces in which each value occurs), is most immediately useful +// in determining whether a value is a corner or smooth boundary, while other properties +// such as the first face and whether or not the span is interrupted by a discts edge +// (and so made "disjoint") or semi-sharp or infinite edges, are useful to fully qualify +// smooth boundaries by the caller. +// +void +FVarLevel::gatherValueSpans(Index vIndex, ValueSpan * vValueSpans) const { + + IndexArray const vEdges = _level.getVertexEdges(vIndex); + IndexArray const vFaces = _level.getVertexFaces(vIndex); + + SiblingArray const vFaceSiblings = getVertexFaceSiblings(vIndex); + + bool vHasSingleValue = (_vertSiblingCounts[vIndex] == 0); + bool vIsBoundary = vEdges.size() > vFaces.size(); + + if (vHasSingleValue) { + // Mark an interior dart disjoint if more than one discts edge: + for (int i = 0; i < vEdges.size(); ++i) { + if (_edgeTags[vEdges[i]]._mismatch) { + if (vValueSpans[0]._size) { + vValueSpans[0]._disjoint = true; + break; + } else { + vValueSpans[0]._size = (LocalIndex) vFaces.size(); + vValueSpans[0]._start = (LocalIndex) i; + } + } else if (_level._edgeTags[vEdges[i]]._infSharp) { + vValueSpans[0]._disjoint = true; + break; + } else if (_level._edgeTags[vEdges[i]]._semiSharp) { + ++ vValueSpans[0]._semiSharp; + } + } + } else { + // Walk around the vertex and accumulate span info for each value -- be + // careful about the span for the first value "wrapping" around: + vValueSpans[0]._size = 1; + vValueSpans[0]._start = 0; + if (!vIsBoundary && (vFaceSiblings[vFaces.size() - 1] == 0)) { + if (_edgeTags[vEdges[0]]._mismatch) { + vValueSpans[0]._disjoint = true; + } else if (_level._edgeTags[vEdges[0]]._infSharp) { + vValueSpans[0]._disjoint = true; + } else if (_level._edgeTags[vEdges[0]]._semiSharp) { + ++ vValueSpans[0]._semiSharp; + } + } + for (int i = 1; i < vFaces.size(); ++i) { + if (vFaceSiblings[i] == vFaceSiblings[i-1]) { + if (_edgeTags[vEdges[i]]._mismatch) { + ++ vValueSpans[vFaceSiblings[i]]._disjoint; + } else if (_level._edgeTags[vEdges[i]]._infSharp) { + vValueSpans[vFaceSiblings[i]]._disjoint = true; + } else if (_level._edgeTags[vEdges[i]]._semiSharp) { + ++ vValueSpans[vFaceSiblings[i]]._semiSharp; + } + } else { + // If we have already set the span for this value, mark disjoint + if (vValueSpans[vFaceSiblings[i]]._size > 0) { + ++ vValueSpans[vFaceSiblings[i]]._disjoint; + } + vValueSpans[vFaceSiblings[i]]._start = (LocalIndex) i; + } + ++ vValueSpans[vFaceSiblings[i]]._size; + } + // If the span for value 0 has wrapped around, decrement the disjoint added + // at the interior edge where it started the closing part of the span: + if ((vFaceSiblings[vFaces.size() - 1] == 0) && !vIsBoundary) { + -- vValueSpans[0]._disjoint; + } + } +} + } // end namespace Vtr } // end namespace OPENSUBDIV_VERSION diff --git a/opensubdiv/vtr/fvarLevel.h b/opensubdiv/vtr/fvarLevel.h index 51db8dc1..14b06c05 100644 --- a/opensubdiv/vtr/fvarLevel.h +++ b/opensubdiv/vtr/fvarLevel.h @@ -115,8 +115,9 @@ public: typedef unsigned char ValueTagSize; - ValueTagSize _mismatch : 1; // local FVar topology does not match - ValueTagSize _crease : 1; // value is a crease, otherwise a corner + 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 }; public: @@ -149,8 +150,12 @@ public: SiblingArray const getVertexFaceSiblings(Index faceIndex) const; // Queries specific to values: - bool isValueCrease(Index valueIndex) const { return _vertValueTags[valueIndex]._crease; } - bool isValueCorner(Index valueIndex) const { return !_vertValueTags[valueIndex]._crease; } + bool isValueCrease(Index valueIndex) const { return _vertValueTags[valueIndex]._crease; } + bool isValueCorner(Index valueIndex) const { return !_vertValueTags[valueIndex]._crease; } + bool isValueSemiSharp(Index valueIndex) const { return _vertValueTags[valueIndex]._semiSharp; } + bool isValueInfSharp(Index valueIndex) const { return !_vertValueTags[valueIndex]._semiSharp && + !_vertValueTags[valueIndex]._crease; } + // Higher-level topological queries, i.e. values in a neighborhood: void getEdgeFaceValues(Index eIndex, int fIncToEdge, Index valuesPerVert[2]) const; @@ -176,6 +181,15 @@ public: void initializeFaceValuesFromVertexFaceSiblings(int firstVertex = 0); void buildFaceVertexSiblingsFromVertexFaceSiblings(std::vector& fvSiblings) const; + // Information about the "span" for a value: + struct ValueSpan { + LocalIndex _size; + LocalIndex _start; + LocalIndex _disjoint; + LocalIndex _semiSharp; + }; + void gatherValueSpans(Index vIndex, ValueSpan * vValueSpans) const; + bool validate() const; void print() const; diff --git a/opensubdiv/vtr/fvarRefinement.cpp b/opensubdiv/vtr/fvarRefinement.cpp index 8cb9010c..cb19e121 100644 --- a/opensubdiv/vtr/fvarRefinement.cpp +++ b/opensubdiv/vtr/fvarRefinement.cpp @@ -94,6 +94,7 @@ FVarRefinement::applyRefinement() { propagateValueTags(); if (_child->_hasSmoothBoundaries) { propagateValueCreases(); + reclassifySemisharpValues(); } // @@ -361,8 +362,12 @@ FVarRefinement::propagateValueTags() { // as corner or crease depending on the presence of creases in the parent: // FVarLevel::ValueTag valTagMismatch(true); + valTagMismatch._crease = false; + valTagMismatch._semiSharp = false; + FVarLevel::ValueTag valTagCrease(true); - valTagCrease._crease = true; + valTagCrease._crease = true; + valTagCrease._semiSharp = false; FVarLevel::ValueTag& valTagSplitEdge = _parent->_hasSmoothBoundaries ? valTagCrease : valTagMismatch; @@ -421,7 +426,7 @@ FVarRefinement::propagateValueCreases() { if (!_child->_vertValueTags[cVert]._mismatch) continue; if (_refinement._childVertexTag[cVert]._incomplete) continue; - if (_child->_vertValueTags[cVert]._crease) { + if (!_child->isValueInfSharp(cVert)) { LocalIndex * sibling0Ends = &_child->_vertValueCreaseEnds[2 * cVert]; sibling0Ends[0] = 0; sibling0Ends[1] = 1; @@ -429,7 +434,7 @@ FVarRefinement::propagateValueCreases() { int vSiblingCount = _child->_vertSiblingCounts[cVert]; if (vSiblingCount) { int cOffset = _child->_vertSiblingOffsets[cVert]; - if (_child->_vertValueTags[cOffset]._crease) { + if (!_child->isValueInfSharp(cOffset)) { LocalIndex * sibling1Ends = &_child->_vertValueCreaseEnds[2 * cOffset]; sibling1Ends[0] = 2; sibling1Ends[1] = 3; @@ -439,7 +444,8 @@ FVarRefinement::propagateValueCreases() { // // For each child vertex from a vertex that has FVar values and is complete, initialize - // the crease-ends for those values tagged as smooth boundaries: + // the crease-ends for those values tagged as smooth or semi-sharp (to become smooth + // eventually): // for (int i = 0; i < _refinement._childVertFromVertCount; ++i, ++cVert) { if (!_child->_vertValueTags[cVert]._mismatch) continue; @@ -447,7 +453,7 @@ FVarRefinement::propagateValueCreases() { Index pVert = _refinement.getChildVertexParentIndex(cVert); - if (_child->_vertValueTags[cVert]._crease) { + if (!_child->isValueInfSharp(cVert)) { LocalIndex * pSiblingEnds = &_parent->_vertValueCreaseEnds[2 * pVert]; LocalIndex * cSiblingEnds = &_child->_vertValueCreaseEnds[2 * cVert]; @@ -463,7 +469,7 @@ FVarRefinement::propagateValueCreases() { LocalIndex * cSiblingEnds = &_child->_vertValueCreaseEnds[2 * cSiblingOffset]; for (int j = 0; j < vSiblingCount; ++j, cSiblingEnds += 2, pSiblingEnds += 2) { - if (_child->_vertValueTags[cSiblingOffset + j]._crease) { + if (!_child->isValueInfSharp(cSiblingOffset + j)) { cSiblingEnds[0] = pSiblingEnds[0]; cSiblingEnds[1] = pSiblingEnds[1]; } @@ -472,6 +478,114 @@ FVarRefinement::propagateValueCreases() { } } +void +FVarRefinement::reclassifySemisharpValues() { + + // + // Reclassify the tags of semi-sharp vertex values to smooth creases according to + // changes in sharpness: + // + // Vertex values introduced on edge-verts can never be semi-sharp as they will be + // introduced on discts edges, which are implicitly infinitely sharp, so we can + // skip them entirely. + // + // So we just need to deal with those values descended from parent vertices that + // were semi-sharp. The child values will have inherited the semi-sharp tag from + // their parent values -- we will be able to clear it in many simple cases but + // ultimately will need to inspect each value: + // + FVarLevel::ValueTag valTagCrease(true); + valTagCrease._crease = true; + valTagCrease._semiSharp = false; + + FVarLevel::ValueTag valTagSemiSharp(true); + valTagSemiSharp._crease = false; + valTagSemiSharp._semiSharp = true; + + Index cVert = _refinement._childVertFromFaceCount + _refinement._childVertFromEdgeCount; + + for (int i = 0; i < _refinement._childVertFromVertCount; ++i, ++cVert) { + if (!_child->_vertValueTags[cVert]._mismatch) continue; + if (_refinement._childVertexTag[cVert]._incomplete) continue; + + // Remember the child vertex may no longer be semisharp when the parent was: + Index pVert = _refinement.getChildVertexParentIndex(cVert); + if (!_refinement._parent->_vertTags[pVert]._semiSharp) continue; + + // If the child vertex sharpness is non-zero, all values remain unaffected: + if (!Sdc::Crease::IsSmooth(_refinement._child->_vertSharpness[cVert])) continue; + + // + // 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: + // + int vSiblingOffset = _child->_vertSiblingOffsets[cVert]; + int vSiblingCount = _child->_vertSiblingCounts[cVert]; + int vValueCount = 1 + vSiblingCount; + for (int j = 0; j < vValueCount; ++j) { + int vValueIndex = (j == 0) ? cVert : (vSiblingOffset + j - 1); + + if (_child->isValueSemiSharp(vValueIndex)) { + _child->_vertValueTags[vValueIndex] = 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: + // + SiblingArray const vFaceSiblings = _child->getVertexFaceSiblings(cVert); + + 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; + } + } + } + } +} + +float +FVarRefinement::getFractionalWeight(Index pVert, Sibling /* pSibling */, + Index cVert, Sibling /* cSibling */) const +{ + // Should only be called when the parent was semi-sharp but this child vertex + // value (not necessarily the child vertex as a whole) is no longer semi-sharp: + assert(_refinement._parent->_vertTags[pVert]._semiSharp); + + float pVertSharpness = _refinement._parent->_vertSharpness[pVert]; + float cVertSharpness = _refinement._child->_vertSharpness[cVert]; + + // + // Need to identify sharpness values for edges within the spans for both the + // parent and child... + // + int numValueEdges = 0; + float * pEdgeSharpness = 0; + float * cEdgeSharpness = 0; + + if (Sdc::Crease::IsSmooth(pVertSharpness)) { + printf("Warning -- FVarRefinement::getFractionalWeight() currently ignores edge sharpness...\n"); + // Consider fully sharp until we gather and average the edge sharpness values... + return 1.0; + } + return Sdc::Crease(_refinement._schemeOptions).ComputeFractionalWeightAtVertex( + pVertSharpness, cVertSharpness, numValueEdges, pEdgeSharpness, cEdgeSharpness); +} + } // end namespace Vtr } // end namespace OPENSUBDIV_VERSION diff --git a/opensubdiv/vtr/fvarRefinement.h b/opensubdiv/vtr/fvarRefinement.h index 6a95c911..ee82db54 100644 --- a/opensubdiv/vtr/fvarRefinement.h +++ b/opensubdiv/vtr/fvarRefinement.h @@ -56,6 +56,9 @@ namespace Vtr { // class FVarRefinement { +public: + typedef FVarLevel::Sibling Sibling; + typedef FVarLevel::SiblingArray SiblingArray; public: FVarRefinement(Refinement const& refinement, FVarLevel& parent, FVarLevel& child); @@ -80,6 +83,10 @@ public: void propagateEdgeTags(); void propagateValueTags(); void propagateValueCreases(); + void reclassifySemisharpValues(); + + float getFractionalWeight(Index pVert, Sibling pSibling, + Index cVert, Sibling cSibling) const; public: