mirror of
https://github.com/PixarAnimationStudios/OpenSubdiv
synced 2024-12-26 09:41:08 +00:00
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
This commit is contained in:
parent
5f810a0f8e
commit
69e6da8fab
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
//
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user