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()
This commit is contained in:
barfowl 2014-10-06 12:07:44 -07:00
parent 639788abbe
commit f706062ab1
5 changed files with 281 additions and 79 deletions

View File

@ -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);
}
}
}

View File

@ -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

View File

@ -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<Sibling>& 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;

View File

@ -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

View File

@ -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: