mirror of
https://github.com/PixarAnimationStudios/OpenSubdiv
synced 2024-12-27 02:10:24 +00:00
Added support for face-varying boundary interpolation options:
- "propagate corners" added as new enumeration to Sdc::Options - topology tags within FVar channel initialized and propagated - face-varying Interpolate() method updated to deal with creases
This commit is contained in:
parent
aacd43a09b
commit
606e8fc1b9
@ -1149,7 +1149,16 @@ TopologyRefiner::faceVaryingInterpolateChildVertsFromVerts(
|
||||
U & vdst = dst[cVertValue];
|
||||
|
||||
vdst.Clear();
|
||||
vdst.AddWithWeight(src[pVertValue], 1.0);
|
||||
if (parentFVar.isValueCorner(parentFVar.getVertexValueIndex(vert, pSibling))) {
|
||||
vdst.AddWithWeight(src[pVertValue], 1.0f);
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,72 +36,71 @@ namespace Sdc {
|
||||
// scheme to affect the shape of the limit surface. These differ from approximations
|
||||
// that may be applied at a higher level, i.e. options to limit the level of feature
|
||||
// adaptive subdivision, options to ignore fractional creasing, or creasing entirely,
|
||||
// etc. These options define a particular limit surface.
|
||||
// etc. These options define the shape of a particular limit surface, including the
|
||||
// "shape" of primitive variable data associated with it.
|
||||
//
|
||||
// The intent is that these sets of options be defined at a high-level and propagated
|
||||
// into the lowest-level computation in support of each subdivision scheme. Ideally
|
||||
// it remains a set of bit-fields (essentially an int) and so remains light weight and
|
||||
// easily passed down by value.
|
||||
//
|
||||
// Questions:
|
||||
// Should the individual enum's be nested within the class or independent?
|
||||
//
|
||||
// Note:
|
||||
// A case can be made that the CreaseMethod enum is better defined as part of the
|
||||
// Crease class, but the goal is to try and put them all in one place. We could define
|
||||
// it there and aggregate it into Options here, but we need to be careful about the
|
||||
// possibility of circular dependencies (nesting types in classes inhibits forward
|
||||
// declaration).
|
||||
// ALPHA NOTES:
|
||||
// Several of these options are being reconsidered in light of the divergence of
|
||||
// OSD 3.0 from Hbr. In some cases the options can be expressed more clearly and free
|
||||
// of any RenderMan legacy for future use. Details are noted below:
|
||||
// "CreasingMethod"
|
||||
// - note the change from the default "Normal" method to "Uniform"
|
||||
// "VVarBoundaryInterpolation"
|
||||
// - both name and enumerations being reconsidered
|
||||
// - the "VVar" prefix was misguided on my part (barfowl)
|
||||
// - "boundary interpolation" is a potential misnomer as it only affects corners
|
||||
// - its effect is to sharpen edges/corners, but edges are always sharpened
|
||||
// - the "None" case serves no purpose (and would be discouraged)
|
||||
// "FVarBoundaryInterpolation" and the bool "propagate corners"
|
||||
// - as above, both name and enumerations being reconsidered
|
||||
// - again "boundary interpolation" a misnomer interior interpolation affected
|
||||
// - consider "FVarInterpolation" or "FVarLinearInterpolation" which by default
|
||||
// (0) is linear throughout and specifies where FVar interpolation is to be
|
||||
// Linear instead of following the assigned scheme. Since the default (0) is
|
||||
// completely (bi)linear, each option successively removes linear features
|
||||
// making the last fully smooth:
|
||||
// FVAR_LINEAR_ALL,
|
||||
// FVAR_LINEAR_EDGE_AND_CORNER,
|
||||
// FVAR_LINEAR_CORNER,
|
||||
// FVAR_LINEAR_CORNER_PROPAGATE (incorporates "propagate corners")
|
||||
// FVAR_LINEAR_NONE,
|
||||
// - the "propgate corners" option only applied to one "interpolation option"
|
||||
// (formerly EDGE_AND_CORNER, now LINEAR_CORNER) and so has been added as a
|
||||
// new fifth choice.
|
||||
// "TriangleSubdivision":
|
||||
// - hoping we can get rid of this due to lack of interest/use
|
||||
// - specific to Catmark and only at level 0
|
||||
// "NonManifoldInterpolation":
|
||||
// - hoping we can get rid of this due to lack of interest/use
|
||||
//
|
||||
class Options {
|
||||
public:
|
||||
|
||||
// XXXX
|
||||
// Manuel suggested "VertexBoundaryInterpolation" here, but when used, that sounded
|
||||
// too much like boundary interpolation specific to a vertex -- I went with the VVar
|
||||
// and FVar naming here instead (abbreviating the FaceVaryingBoundaryInterpolation
|
||||
// that was suggested)...
|
||||
//
|
||||
enum VVarBoundaryInterpolation {
|
||||
VVAR_BOUNDARY_NONE = 0,
|
||||
VVAR_BOUNDARY_EDGE_ONLY,
|
||||
VVAR_BOUNDARY_EDGE_AND_CORNER
|
||||
};
|
||||
|
||||
enum FVarBoundaryInterpolation {
|
||||
FVAR_BOUNDARY_BILINEAR = 0,
|
||||
FVAR_BOUNDARY_EDGE_ONLY,
|
||||
FVAR_BOUNDARY_EDGE_AND_CORNER,
|
||||
FVAR_BOUNDARY_ALWAYS_SHARP
|
||||
FVAR_BOUNDARY_ALWAYS_SHARP,
|
||||
FVAR_BOUNDARY_EDGE_AND_CORNER_PROP
|
||||
};
|
||||
|
||||
//
|
||||
// Tony has expressed a preference of UNIFORM vs NORMAL here, which diverges from
|
||||
// Hbr/RenderMan, but makes a lot more sense as it allows us to distinguish between
|
||||
// uniform and non-uniform creasing computations (with uniform being trivial).
|
||||
//
|
||||
enum CreasingMethod {
|
||||
CREASE_UNIFORM = 0,
|
||||
CREASE_CHAIKIN
|
||||
};
|
||||
|
||||
//
|
||||
// Is it possible to get rid of this entirely? It is specific to Catmark, seems to
|
||||
// be little used and only applies to the first level of subdivision. Getting rid
|
||||
// of the code that supports this (though it is localized) would be a relief...
|
||||
//
|
||||
enum TriangleSubdivision {
|
||||
TRI_SUB_NORMAL = 0,
|
||||
TRI_SUB_OLD,
|
||||
TRI_SUB_NEW
|
||||
};
|
||||
|
||||
//
|
||||
// This is speculative for now and included for illustration purposes -- the simplest
|
||||
// set of interpolation rules for non-manifold features is to make them infinitely
|
||||
// sharp, which fits into existing evaluation schemes. Allowing them to be smooth is
|
||||
// less well-defined and requires additional cases in the masks to properly support.
|
||||
//
|
||||
enum NonManifoldInterpolation {
|
||||
NON_MANIFOLD_NONE = 0,
|
||||
NON_MANIFOLD_SMOOTH,
|
||||
@ -112,11 +111,11 @@ public:
|
||||
|
||||
// Trivial constructor and destructor:
|
||||
Options() : _vvarBoundInterp(VVAR_BOUNDARY_NONE),
|
||||
_fvarBoundInterp(FVAR_BOUNDARY_BILINEAR),
|
||||
_nonManInterp(NON_MANIFOLD_NONE),
|
||||
_creasingMethod(CREASE_UNIFORM),
|
||||
_triangleSub(TRI_SUB_NORMAL),
|
||||
_hbrCompatible(false) { }
|
||||
_fvarBoundInterp(FVAR_BOUNDARY_BILINEAR),
|
||||
_nonManInterp(NON_MANIFOLD_NONE),
|
||||
_creasingMethod(CREASE_UNIFORM),
|
||||
_triangleSub(TRI_SUB_NORMAL),
|
||||
_hbrCompatible(false) { }
|
||||
~Options() { }
|
||||
|
||||
//
|
||||
@ -148,7 +147,7 @@ public:
|
||||
private:
|
||||
// Bitfield members:
|
||||
unsigned int _vvarBoundInterp : 2;
|
||||
unsigned int _fvarBoundInterp : 2;
|
||||
unsigned int _fvarBoundInterp : 3;
|
||||
unsigned int _nonManInterp : 2;
|
||||
unsigned int _creasingMethod : 2;
|
||||
unsigned int _triangleSub : 2;
|
||||
|
@ -49,7 +49,7 @@ namespace Vtr {
|
||||
// Simple (for now) constructor and destructor:
|
||||
//
|
||||
FVarLevel::FVarLevel(Level const& level) :
|
||||
_level(level), _isLinear(false), _valueCount(0) {
|
||||
_level(level), _isLinear(false), _hasSmoothBoundaries(false), _valueCount(0) {
|
||||
}
|
||||
|
||||
FVarLevel::~FVarLevel() {
|
||||
@ -86,111 +86,62 @@ FVarLevel::resizeComponents() {
|
||||
|
||||
|
||||
//
|
||||
// Initialize the component tags once all face-values have been assigned:
|
||||
// - edge traversal:
|
||||
// - finding and testing the values at matching ends of the edge
|
||||
// - marking the bitfields according to the result
|
||||
// - is it worth marking vertices as "mismatch"?
|
||||
// - need more vertex analysis later
|
||||
// - will only be marking discts verts
|
||||
// - no iteration of edge-verts, unlike vert-edges
|
||||
// - (consider) marking incident faces as "mismatch"
|
||||
// - vertex traversal:
|
||||
// - mismatches previously identified in edge traversal:
|
||||
// - should be able to skip vertices that match
|
||||
// - need to identify number/index of values for each
|
||||
// - should be able to identify topology per value during iteration:
|
||||
// - one "in a row" will be a corner, "crease" otherwise
|
||||
// Initialize the component tags once all face-values have been assigned...
|
||||
//
|
||||
// WAIT -- consider the following vertex-oriented alternative...
|
||||
// - vertex traversal:
|
||||
// - iterate through all successive face-vert values:
|
||||
// - each successive mismatch identifies an edge discty at one end
|
||||
// - mark the edge mismatch
|
||||
// - mark the discts end (which is trivially known from local index)
|
||||
// - "spans" of values identifies their topology
|
||||
// - beware disjoint spans -- effectively corners
|
||||
// - set of unique values gathered in process
|
||||
// * NOTE - does not capture the dart/boundary case
|
||||
// - single value consistent around vertex -- no discts edge detected
|
||||
// - remember this is not a Dart but a true boundary, i.e. a Crease
|
||||
// - could mark vertex when marking edge, but order-dependent:
|
||||
// - if just need to mark mismatch, could be sufficient:
|
||||
// - one value but mismatch implies Dart
|
||||
// - single value may be at the center of multiple discts edges:
|
||||
// - value becomes Corner then instead of Crease
|
||||
// Constructing the mapping between vertices and their face-varying values involves:
|
||||
//
|
||||
// Implications:
|
||||
// - both the edge-traversal and the vertex traversal want to compare indices
|
||||
// - pros/cons for vertex traversal:
|
||||
// - pro: 1/2 as many verts as edges
|
||||
// - pro: directly identifies adjacent face-vert values (via local index)
|
||||
// - CON: knowledge of discts edges absent (cts at vertex, discts end of edge)
|
||||
// - pros/cons for edge traversal:
|
||||
// - pro: can mark incident verts and faces mismatched in process
|
||||
// - CON: 2x as many edges as verts
|
||||
// - CON: requires search to find edge in each adjacent face
|
||||
// - vertex traversal seems preferable, but CON is significant:
|
||||
// - could need to test "other end of each edge":
|
||||
// - unfortunate in that pair-wise test succeeding now slowed down
|
||||
// in ALL cases -- even when everything matches
|
||||
// - could mark "other end vertex" when marking any edge discts:
|
||||
// - would result in "revisiting" a vertex
|
||||
// - what are implications of identifying opposite end-splits later?
|
||||
// - currently matched becomes mismatched, crease
|
||||
// - single mismatched becomes mismatched, corner
|
||||
// - multiple mismatched is awkward to deal with:
|
||||
// - spans that may be crease fragmented -- how?
|
||||
// - depending on span size may produce:
|
||||
// - two Creases, two Corners, one of each...
|
||||
// Best of both worlds:
|
||||
// - double vertex traversal:
|
||||
// - first intializes edge tags and marks opposite vertex mismatched
|
||||
// - may also identify value-per-vertex counts
|
||||
// - second (sparse) will analyze local topology
|
||||
// - iteration through all vertices to mark edge discontinuities and classify
|
||||
// - allocation of vectors mapping vertices to their multiple (sibling) values
|
||||
// - iteration through all vertices and their distinct values to tag topologically
|
||||
//
|
||||
// Once values have been identified for each vertex and tagged, refinement propagates
|
||||
// the tags to child values using more simplified logic (child values inherit the
|
||||
// topology of their parent) and no futher analysis is required.
|
||||
//
|
||||
void
|
||||
FVarLevel::completeTopologyFromFaceValues() {
|
||||
|
||||
//
|
||||
// REMEMBER!!!
|
||||
// .... that "mismatches" may also occur where the topology matches, but options
|
||||
// specify different treatment, e.g. boundary and corner interpolation rules. So we
|
||||
// should inspect both the VVar and FVar boundary rules and determine when to mark
|
||||
// such features as mismatched.
|
||||
//
|
||||
// Note that this affects typical exterior boundaries in addition to corners -- a
|
||||
// geometrically smooth disk may end up with piecewise linear UV boundaries.
|
||||
//
|
||||
// Not sure what the precedence is here, particuarly "propagate corner" vs the choice
|
||||
// of "fvar boundary interpolation" -- will need to check with Hbr.
|
||||
// Assign some members and local variables based on the interpolation options (the
|
||||
// members reflect queries that are made elsewhere):
|
||||
//
|
||||
_isLinear = (_options.GetFVarBoundaryInterpolation() == Sdc::Options::FVAR_BOUNDARY_BILINEAR);
|
||||
|
||||
bool geomCornersAreSharp = (_options.GetVVarBoundaryInterpolation() == Sdc::Options::VVAR_BOUNDARY_EDGE_AND_CORNER);
|
||||
bool fvarCornersAreSharp = (_options.GetFVarBoundaryInterpolation() != Sdc::Options::FVAR_BOUNDARY_EDGE_ONLY);
|
||||
bool fvarPropagateCorner = false;
|
||||
_hasSmoothBoundaries = (_options.GetFVarBoundaryInterpolation() != Sdc::Options::FVAR_BOUNDARY_BILINEAR) &&
|
||||
(_options.GetFVarBoundaryInterpolation() != Sdc::Options::FVAR_BOUNDARY_ALWAYS_SHARP);
|
||||
|
||||
fvarCornersAreSharp = fvarPropagateCorner ? geomCornersAreSharp : fvarCornersAreSharp;
|
||||
bool geomCornersAreSmooth = (_options.GetVVarBoundaryInterpolation() != Sdc::Options::VVAR_BOUNDARY_EDGE_AND_CORNER);
|
||||
bool fvarCornersAreSharp = (_options.GetFVarBoundaryInterpolation() != Sdc::Options::FVAR_BOUNDARY_EDGE_ONLY);
|
||||
|
||||
bool mismatchCorners = (fvarCornersAreSharp != geomCornersAreSharp);
|
||||
bool makeCornersSharp = geomCornersAreSmooth && fvarCornersAreSharp;
|
||||
|
||||
bool sharpenAllIfAnyCorner = (_options.GetFVarBoundaryInterpolation() == Sdc::Options::FVAR_BOUNDARY_EDGE_AND_CORNER_PROP);
|
||||
|
||||
//
|
||||
// Iterate through the vertices first to identify discts edges -- this is better done
|
||||
// than iterating through the edges because:
|
||||
// Its awkward and potentially inefficient to try and accomplish everything in one
|
||||
// pass over the vertices...
|
||||
//
|
||||
// - there are typically twice as many edges as vertices
|
||||
// Make a first pass through the vertices to identify discts edges and to determine
|
||||
// the number of values-per-vertex for subsequent allocation. The presence of a
|
||||
// discts edge warrants marking vertices at BOTH ends as having mismatched topology
|
||||
// wrt the vertices (part of why full topological analysis is deferred).
|
||||
//
|
||||
// - the ordering of incident faces of a vertex make retrieval/comparison of
|
||||
// adjacent pairs of face-varying values much more efficient
|
||||
// So this first pass will allocate/initialize the overall structure of the topology.
|
||||
// Given N vertices and M (as yet unknown) sibling values, the first pass achieves
|
||||
// the following:
|
||||
//
|
||||
// Since an edge may be discts at only one end, but the vertex at the cts end still
|
||||
// needs to be aware of such a discontinuity, we mark the opposite vertex for any
|
||||
// edge that is found to be discts.
|
||||
// - assigns the number of siblings for each of the N vertices
|
||||
// - determining the total number of siblings M in the process
|
||||
// - assigns the sibling offsets for each of the N vertices
|
||||
// - assigns the vert-value tags (partially) for the first N vertices (matches or not)
|
||||
// - initializes the vert-face siblings for all N vertices
|
||||
// and
|
||||
// - tags any incident edges as discts
|
||||
//
|
||||
// In the process, we will initialize the "sibling" values associated with both the
|
||||
// face-verts and the vert-faces. These are both 0-initialized and only updated when
|
||||
// discontinuities are encountered.
|
||||
// The second pass initializes remaining members based on the total number of siblings
|
||||
// M after allocating appropriate vectors dependent on M.
|
||||
//
|
||||
// Still looking or opportunities to economize effort between the two passes...
|
||||
//
|
||||
ValueTag valueTagMatch(false);
|
||||
ValueTag valueTagMismatch(true);
|
||||
@ -200,9 +151,9 @@ FVarLevel::completeTopologyFromFaceValues() {
|
||||
|
||||
int const maxValence = _level.getMaxValence();
|
||||
|
||||
Index * indexBuffer = (Index *)alloca(maxValence*sizeof(Index));
|
||||
int * valueBuffer = (int *)alloca(maxValence*sizeof(int));
|
||||
Sibling * siblingBuffer = (Sibling *)alloca(maxValence*sizeof(Sibling));
|
||||
Index * indexBuffer = (Index *)alloca(maxValence*sizeof(Index));
|
||||
int * valueBuffer = (int *)alloca(maxValence*sizeof(int));
|
||||
Sibling * siblingBuffer = (Sibling *)alloca(maxValence*sizeof(Sibling));
|
||||
|
||||
int * uniqueValues = valueBuffer;
|
||||
|
||||
@ -226,7 +177,7 @@ FVarLevel::completeTopologyFromFaceValues() {
|
||||
uniqueValues[uniqueValueCount++] = vValues[0];
|
||||
vSiblings[0] = 0;
|
||||
|
||||
bool vIsBoundary = (vEdges.size() != vFaces.size());
|
||||
bool vIsBoundary = _level._vertTags[vIndex]._boundary;
|
||||
for (int i = vIsBoundary; i < vFaces.size(); ++i) {
|
||||
int iPrev = i ? (i - 1) : (vFaces.size() - 1);
|
||||
|
||||
@ -277,6 +228,21 @@ FVarLevel::completeTopologyFromFaceValues() {
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// While we've tagged the vertex as having mismatched FVar topology in the presence of
|
||||
// any discts edges, we also need to account for different treatment of vertices along
|
||||
// geometric boundaries if the FVar interpolation rules affect them:
|
||||
//
|
||||
if (vIsBoundary && !_vertValueTags[vIndex]._mismatch) {
|
||||
if (vFaces.size() == 1) {
|
||||
if (makeCornersSharp) {
|
||||
_vertValueTags[vIndex]._mismatch = true;
|
||||
}
|
||||
} else if (!_hasSmoothBoundaries) {
|
||||
_vertValueTags[vIndex]._mismatch = true;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Make note of any extra values that will be added after one-per-vertex:
|
||||
//
|
||||
@ -297,16 +263,21 @@ FVarLevel::completeTopologyFromFaceValues() {
|
||||
}
|
||||
|
||||
//
|
||||
// Allocate space for the values -- we already have tags for those corresponding to each
|
||||
// vertex (and those tags have been updated above), so append tags indicating the mismatch
|
||||
// for all of the sibling values detected:
|
||||
// Now that we know the total number of additional sibling values (M values in addition
|
||||
// to the N vertex values) allocate space to accomodate all N + M values. Note that we
|
||||
// we already have tags for the first N partially initialized (matching or not) so just
|
||||
// append tags for the additional M sibling values (all M initialized as mismatched).
|
||||
//
|
||||
_vertValueIndices.resize(totalValueCount);
|
||||
_vertValueTags.resize(totalValueCount, valueTagMismatch);
|
||||
|
||||
if (_hasSmoothBoundaries) {
|
||||
_vertValueCreaseEnds.resize(totalValueCount * 2);
|
||||
}
|
||||
|
||||
//
|
||||
// Now a second pass through the vertices to identify the local face-varying topology
|
||||
// in more detail -- tagging each FVar value associated with the vertex:
|
||||
// Now the second pass through the vertices to identify the local face-varying topology
|
||||
// in more detail -- tagging each FVar value associated with each vertex:
|
||||
//
|
||||
// REMEMBER -- we want a ValueTag for each instance of the value at each vertex, i.e.
|
||||
// per vertex-value. At level 0, given user input, the number of vertex-values may
|
||||
@ -315,33 +286,22 @@ FVarLevel::completeTopologyFromFaceValues() {
|
||||
// of vertex-values is what we need for refinement to "unweld" them, and the tag for
|
||||
// each will indicate refinement rules for its children.
|
||||
//
|
||||
for (int vIndex = 0; vIndex < _level.getNumVertices(); ++vIndex) {
|
||||
ValueTag& vTag = _vertValueTags[vIndex];
|
||||
ValueTag valueTagCorner = valueTagMismatch;
|
||||
valueTagCorner._crease = false;
|
||||
|
||||
ValueTag valueTagCrease = valueTagMismatch;
|
||||
valueTagCrease._crease = true;
|
||||
|
||||
for (int vIndex = 0; vIndex < _level.getNumVertices(); ++vIndex) {
|
||||
IndexArray const vFaces = _level.getVertexFaces(vIndex);
|
||||
LocalIndexArray const vInFace = _level.getVertexFaceLocalIndices(vIndex);
|
||||
|
||||
//
|
||||
// At this point, mismatches have only been determined by the presence of edges
|
||||
// that are discontinuous. We may still have boundary/corner vertices that match
|
||||
// topologically, but need to be treated differently (i.e. they do not match) due
|
||||
// to boundary interpolation rules. So update the "matched" status of boundary
|
||||
// vertices before continuing to the general treatment for all cases:
|
||||
//
|
||||
bool vIsBoundary = _level._vertTags[vIndex]._boundary;
|
||||
if (vIsBoundary && !vTag._mismatch) {
|
||||
bool vIsCorner = (vFaces.size() == 1);
|
||||
if (vIsCorner) {
|
||||
vTag._mismatch = mismatchCorners;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// If the face-varying topology around this vertex matches the vertex topology,
|
||||
// there is little more to do -- except for level 0 where there may not be a
|
||||
// 1-to-1 correspondence between given values and vertex-values:
|
||||
//
|
||||
if (!vTag._mismatch) {
|
||||
if (!_vertValueTags[vIndex]._mismatch) {
|
||||
if (_level.getDepth() == 0) {
|
||||
_vertValueIndices[vIndex] = _faceVertValues[_level.getOffsetOfFaceVertices(vFaces[0]) + vInFace[0]];
|
||||
} else {
|
||||
@ -356,16 +316,8 @@ FVarLevel::completeTopologyFromFaceValues() {
|
||||
// be appended to the set of 1-per-vertex.
|
||||
//
|
||||
// When the topology does not match, we need to gather the unique values around
|
||||
// this vertex and tag each according to its more localized topology.
|
||||
//
|
||||
// Special cases to consider:
|
||||
// - one mismatched value: should be a crease (corner if linear or boundary?)
|
||||
// - one value per face-vert: all will be corners (hard or smooth?)
|
||||
//
|
||||
// The general case will involve identifying "spans" via discts edges. Any value
|
||||
// that has more than one instance split over multiple spans must be a hard corner.
|
||||
//
|
||||
// For now, make each unique value a hard corner to get something working:
|
||||
// this vertex and tag each according to its more localized topology, i.e. is
|
||||
// it a boundary/crease or a corner.
|
||||
//
|
||||
IndexArray const vEdges = _level.getVertexEdges(vIndex);
|
||||
LocalIndexArray const vInEdge = _level.getVertexEdgeLocalIndices(vIndex);
|
||||
@ -373,56 +325,155 @@ FVarLevel::completeTopologyFromFaceValues() {
|
||||
int vSiblingCount = _vertSiblingCounts[vIndex];
|
||||
int vSiblingOffset = _vertSiblingOffsets[vIndex];
|
||||
|
||||
SiblingArray const vFaceSiblings = getVertexFaceSiblings(vIndex);
|
||||
|
||||
//
|
||||
// Gather the unique set of values and relevant topological information for each
|
||||
// as we go...
|
||||
// Assign the value indices for the vertex and all of its siblings and determine
|
||||
// the "valence" for each value (i.e. the number of faces in which it occurs):
|
||||
//
|
||||
int vvCount = 1 + vSiblingCount;
|
||||
int * vvIndices = indexBuffer;
|
||||
//int vvSrcFaces[vvCount];
|
||||
|
||||
Index lastValue = _faceVertValues[_level.getOffsetOfFaceVertices(vFaces[0]) + vInFace[0]];
|
||||
|
||||
vvCount = 1;
|
||||
vvIndices[0] = lastValue;
|
||||
//vvSrcFaces[0] = 0;
|
||||
int * vSiblingValences = indexBuffer;
|
||||
vSiblingValences[0] = 1;
|
||||
for (int i = 1; i <= vSiblingCount; ++i) {
|
||||
vSiblingValences[i] = 0;
|
||||
}
|
||||
|
||||
int vSiblingIndex = 1;
|
||||
Index * vertValueSiblingIndices = &_vertValueIndices[vSiblingOffset];
|
||||
_vertValueIndices[vIndex] = _faceVertValues[_level.getOffsetOfFaceVertices(vFaces[0]) + vInFace[0]];
|
||||
for (int i = 1; i < vFaces.size(); ++i) {
|
||||
Index nextValue = _faceVertValues[_level.getOffsetOfFaceVertices(vFaces[i]) + vInFace[i]];
|
||||
if (vFaceSiblings[i] == vSiblingIndex) {
|
||||
*vertValueSiblingIndices++ = _faceVertValues[_level.getOffsetOfFaceVertices(vFaces[i]) + vInFace[i]];
|
||||
vSiblingIndex++;
|
||||
}
|
||||
vSiblingValences[vFaceSiblings[i]]++;
|
||||
}
|
||||
assert(vSiblingIndex == (1 + vSiblingCount));
|
||||
|
||||
if (lastValue != nextValue) {
|
||||
bool nextValueIsNew = (vvCount == 1) || ((vvCount == 2) && (vvIndices[0] != nextValue)) ||
|
||||
(std::find(vvIndices, vvIndices + vvCount, nextValue) == vvIndices + vvCount);
|
||||
if (nextValueIsNew) {
|
||||
vvIndices[vvCount] = nextValue;
|
||||
//vvSrcFaces[vvCount] = i;
|
||||
vvCount ++;
|
||||
//
|
||||
// Test for conditions that make all vertex values sharp and tag all as corners when so:
|
||||
//
|
||||
bool sharpenAllValuesSinceOneCornerPresent = false;
|
||||
if (sharpenAllIfAnyCorner) {
|
||||
for (int i = 0; i <= vSiblingCount; ++i) {
|
||||
if (vSiblingValences[i] == 1) {
|
||||
sharpenAllValuesSinceOneCornerPresent = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
lastValue = nextValue;
|
||||
}
|
||||
assert(vvCount == (1 + vSiblingCount));
|
||||
bool makeAllVertexValuesSharp = !_hasSmoothBoundaries ||
|
||||
sharpenAllValuesSinceOneCornerPresent ||
|
||||
_level._vertTags[vIndex]._nonManifold;
|
||||
|
||||
if (makeAllVertexValuesSharp) {
|
||||
_vertValueTags[vIndex] = valueTagCorner;
|
||||
for (int i = 0; i < vSiblingCount; ++i) {
|
||||
_vertValueTags[vSiblingOffset + i] = valueTagCorner;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
//
|
||||
// Assign tags to the vertex' primary value and sibling values:
|
||||
// Inspect each vertex value and tag as corner or crease accordingly:
|
||||
//
|
||||
ValueTag valueTag;
|
||||
valueTag._mismatch = true;
|
||||
valueTag._corner = true;
|
||||
valueTag._crease = false;
|
||||
for (int i = 0; i <= vSiblingCount; ++i) {
|
||||
Index valueIndex = (i == 0) ? vIndex : (vSiblingOffset + i - 1);
|
||||
|
||||
_vertValueIndices[vIndex] = vvIndices[0];
|
||||
_vertValueTags[vIndex] = valueTag;
|
||||
ValueTag& valueTag = _vertValueTags[valueIndex];
|
||||
|
||||
for (int i = 0; i < vSiblingCount; ++i) {
|
||||
_vertValueIndices[vSiblingOffset + i] = vvIndices[1 + i];
|
||||
_vertValueTags[vSiblingOffset + i] = valueTag;
|
||||
bool isDisjoint = (vSiblingValences[i] == 0);
|
||||
bool isCorner = (vSiblingValences[i] == 1);
|
||||
if ((isCorner && fvarCornersAreSharp) || isDisjoint) {
|
||||
valueTag = valueTagCorner;
|
||||
continue;
|
||||
}
|
||||
|
||||
//
|
||||
// We have a crease value -- identify the two neighboring values:
|
||||
//
|
||||
// This should really be done more efficiently in a single iteration
|
||||
// in conjunction with determining valence, etc., e.g. identify the
|
||||
// first and last face for each continuous sector. This is only done
|
||||
// once for the cage and the results propagated through refinement,
|
||||
// so revisit if it appears to take more time than it should.
|
||||
//
|
||||
LocalIndex * endFaces = &_vertValueCreaseEnds[2 * valueIndex];
|
||||
|
||||
if (isCorner) {
|
||||
// A smooth corner -- all three values are in the same face:
|
||||
int j;
|
||||
for (j = 0; vFaceSiblings[j] != i; ++j) ;
|
||||
endFaces[0] = j;
|
||||
endFaces[1] = j;
|
||||
} else if (vSiblingCount == 0) {
|
||||
// Single value -- a mismatched boundary or interior dart:
|
||||
if (vEdges.size() > vFaces.size()) {
|
||||
endFaces[0] = 0;
|
||||
endFaces[1] = vFaces.size() - 1;
|
||||
} else {
|
||||
for (int j = 0; j < vEdges.size(); ++j) {
|
||||
if (_edgeTags[vEdges[j]]._mismatch) {
|
||||
endFaces[0] = j;
|
||||
endFaces[1] = (j ? j : vFaces.size()) - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if ((i == 0) && (vFaceSiblings[vFaceSiblings.size() - 1] == 0)) {
|
||||
// The "span" wraps around the start of an interior vertex:
|
||||
int j;
|
||||
for (j = vFaceSiblings.size() - 1; (vFaceSiblings[j] == 0); --j) ;
|
||||
endFaces[0] = j + 1;
|
||||
for (j = 0; (vFaceSiblings[j] == 0); ++j) ;
|
||||
endFaces[1] = j - 1;
|
||||
} else {
|
||||
int j;
|
||||
for (j = 0; vFaceSiblings[j] != i; ++j) ;
|
||||
endFaces[0] = j;
|
||||
for (j = vFaceSiblings.size() - 1; vFaceSiblings[j] != i; --j) ;
|
||||
endFaces[1] = j;
|
||||
}
|
||||
valueTag = valueTagCrease;
|
||||
}
|
||||
}
|
||||
//printf("completed topology...\n");
|
||||
//print();
|
||||
//printf("validating...\n");
|
||||
//assert(validate());
|
||||
// printf("completed fvar topology...\n");
|
||||
// print();
|
||||
// printf("validating...\n");
|
||||
// assert(validate());
|
||||
}
|
||||
|
||||
//
|
||||
// Values tagged as creases have their two "end values" identified relative to the incident
|
||||
// faces of the vertex for compact storage and quick retrieval. This methods identifies the
|
||||
// values for the two ends of such a crease value:
|
||||
//
|
||||
void
|
||||
FVarLevel::getVertexCreaseEndValues(Index vIndex, Sibling vSibling, Index endValues[2]) const
|
||||
{
|
||||
int creaseEndOffset = 2 * getVertexValueIndex(vIndex, vSibling);
|
||||
|
||||
LocalIndex vertFace0 = _vertValueCreaseEnds[creaseEndOffset];
|
||||
LocalIndex vertFace1 = _vertValueCreaseEnds[creaseEndOffset + 1];
|
||||
|
||||
IndexArray const vFaces = _level.getVertexFaces(vIndex);
|
||||
LocalIndexArray const vInFace = _level.getVertexFaceLocalIndices(vIndex);
|
||||
|
||||
LocalIndex endInFace0 = vInFace[vertFace0];
|
||||
LocalIndex endInFace1 = vInFace[vertFace1];
|
||||
if (_level.getDepth() > 0) {
|
||||
endInFace0 = (endInFace0 + 1) % 4;
|
||||
endInFace1 = (endInFace1 + 3) % 4;
|
||||
} else {
|
||||
// Avoid the costly % N for potential N-sided faces at level 0...
|
||||
endInFace0++;
|
||||
if (endInFace0 == _level.getNumFaceVertices(vFaces[vertFace0])) {
|
||||
endInFace0 = 0;
|
||||
}
|
||||
endInFace1 = (endInFace1 ? endInFace1 : _level.getNumFaceVertices(vFaces[vertFace1])) - 1;
|
||||
}
|
||||
|
||||
endValues[0] = _faceVertValues[_level.getOffsetOfFaceVertices(vFaces[vertFace0]) + endInFace0];
|
||||
endValues[1] = _faceVertValues[_level.getOffsetOfFaceVertices(vFaces[vertFace1]) + endInFace1];
|
||||
}
|
||||
|
||||
//
|
||||
@ -562,6 +613,12 @@ FVarLevel::print() const {
|
||||
for (int j = 0; j < sCount; ++j) {
|
||||
printf("%4d", _vertValueIndices[sOffset + j]);
|
||||
}
|
||||
if (sCount) {
|
||||
printf(", crease =%4d", _vertValueTags[i]._crease);
|
||||
for (int j = 0; j < sCount; ++j) {
|
||||
printf("%4d", _vertValueTags[sOffset + j]._crease);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
|
@ -116,20 +116,7 @@ public:
|
||||
typedef unsigned char ValueTagSize;
|
||||
|
||||
ValueTagSize _mismatch : 1; // local FVar topology does not match
|
||||
ValueTagSize _corner : 1; // value is a corner (unlike its vertex)
|
||||
ValueTagSize _crease : 1; // value is a crease (unlike its vertex)
|
||||
|
||||
// Note that the corner/crease distinction will take into account the boundary
|
||||
// interpolation options and other topological factors. For example, a value
|
||||
// on a Crease that is set to have Linear interpolation rules can be set to be a
|
||||
// Corner for interpolation purposes, and conversely a Corner value with smooth
|
||||
// corner rules will be set to be a Crease.
|
||||
|
||||
// Since the Crease is the only non-trivial case, want to store more information
|
||||
// here for Crease so that we can quickly identify the values involved when it
|
||||
// comes time to interpolate (though considering vertex interpolation, the set
|
||||
// of edges is searched to identify the two corresponding to the crease when the
|
||||
// mask is computed, so perhaps this effort is unwarranted).
|
||||
ValueTagSize _crease : 1; // value is a crease, otherwise a corner
|
||||
};
|
||||
|
||||
public:
|
||||
@ -155,18 +142,24 @@ public:
|
||||
// Queries per vertex (and its potential sibling values):
|
||||
bool vertexTopologyMatches(Index vIndex) const { return !_vertValueTags[vIndex]._mismatch; }
|
||||
|
||||
int getNumVertexValues(Index vIndex) const;
|
||||
int getNumVertexValues(Index vIndex) const;
|
||||
Index getVertexValueIndex(Index vIndex, Sibling sibling = 0) const;
|
||||
Index getVertexValue(Index vIndex, Sibling sibling = 0) const;
|
||||
|
||||
// int getNumVertexSiblings(Index vIndex) const;
|
||||
// IndexArray const getVertexSiblingValues(Index vIndex) const;
|
||||
|
||||
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; }
|
||||
|
||||
// Higher-level topological queries, i.e. values in a neighborhood:
|
||||
void getEdgeFaceValues(Index eIndex, int fIncToEdge, Index valuesPerVert[2]) const;
|
||||
void getVertexEdgeValues(Index vIndex, Index valuesPerEdge[]) const;
|
||||
void getVertexCreaseEndValues(Index vIndex, Sibling sibling, Index endValues[2]) const;
|
||||
|
||||
// Currently the sibling value storage and indexing is being reconsidered...
|
||||
// int getNumVertexSiblings(Index vIndex) const;
|
||||
// IndexArray const getVertexSiblingValues(Index vIndex) const;
|
||||
|
||||
// Non-const methods -- modifiers to be protected:
|
||||
//
|
||||
@ -189,128 +182,33 @@ public:
|
||||
public:
|
||||
Level const & _level;
|
||||
|
||||
//
|
||||
// It's a little bit unclear at present how FVarBoundaryInterpolation works.
|
||||
// I would have though the default would be to inherit the same interpolation
|
||||
// rules from the geometry, but I don't see an enumeration for that -- so if
|
||||
// that is desirable, an explicit internal initialization/assignment will be
|
||||
// warranted.
|
||||
//
|
||||
// Remember that the VVarBoundaryInterpolation has three enums that can now
|
||||
// be reduced to two, so some revision to FVarBoundaryInterpolation may also
|
||||
// be considered.
|
||||
//
|
||||
// Options are stored locally here and they may vary between channels. By
|
||||
// default the options member is initialized from whatever contains it --
|
||||
// which may apply a common set to all channels or vary them individually.
|
||||
//
|
||||
// Options vary between channels:
|
||||
Sdc::Options _options;
|
||||
bool _isLinear;
|
||||
|
||||
int _valueCount;
|
||||
bool _isLinear;
|
||||
bool _hasSmoothBoundaries;
|
||||
int _valueCount;
|
||||
|
||||
//
|
||||
// Members that distinguish the face-varying "topology" from the Level to
|
||||
// which the data set is associated.
|
||||
// Vectors recording face-varying topology -- values-per-face, which edges
|
||||
// are discts wrt the FVar data, the one-to-many mapping between vertices and
|
||||
// their sibling values, etc. We use 8-bit "local indices" where possible.
|
||||
//
|
||||
// Like the geometric topology, the face-varying topology is specified by
|
||||
// a set of per-face-vertex indices -- analogous to vertices -- which are
|
||||
// referred to as "values". Additional vectors associated with vertices
|
||||
// are constructed to identify the set of values incident each vertex.
|
||||
// There is typically a single value associated with each vertex but also
|
||||
// a set of "sibling" values when the face-varying values around a vertex
|
||||
// are not all the same. The neighborhood of each vertex is expressed in
|
||||
// terms of these local sibling indices, i.e. local indices typically 0,
|
||||
// 1 or generally very low, and not exceeding the limit we use for vertex
|
||||
// valence (and N-sided faces).
|
||||
//
|
||||
// As the unique values are identified and associated with each vertex,
|
||||
// the local neighborhood is also inspected and each value "tagged" to
|
||||
// indicate its topology. Foremost is a bit indicating whether the value
|
||||
// "matches" the topology of its vertex (which can only occur when there
|
||||
// is only one value for that vertex), but additionally bits are added to
|
||||
// describe the topological neighborhood to indicate how it and all of its
|
||||
// refined descendants should be treated.
|
||||
//
|
||||
// Tags are associated with the "vertex values", i.e. each instance of a
|
||||
// value for each vertex. When a vertex has more than one value associated
|
||||
// with it, the subdivision rules applicable to each are independent and
|
||||
// fixed throughout refinement.
|
||||
//
|
||||
// Level 0 as a special case:
|
||||
// Given that users can specify the input values arbitrarily, it is
|
||||
// necessary to add a little extra to accomodate this.
|
||||
// For subsequent levels of refinement, the values associated with
|
||||
// each vertex are exclusive to that vertex, e.g. if vertex V has values
|
||||
// A and B incident to it, no other vertex will share A and B. Any child
|
||||
// values of A and B are then local to the child of V.
|
||||
// This is not the case in level 0. Considering a quad mesh there
|
||||
// may be only 4 values for the corners of a unit square, and all vertices
|
||||
// share combinations of those values. In the
|
||||
//
|
||||
// Notes on memory usage:
|
||||
// The largest contributor to memory here is the set of face-values,
|
||||
// which matches the size of the geometric face-vertices, i.e. typically
|
||||
// 4 ints per face. It turns out this can be reconstructed from the rest,
|
||||
// so whether it should always be updated/retained vs computed when needed
|
||||
// is up for debate (right now it is constructed from the other members in
|
||||
// a method as the last step in refinement).
|
||||
// The most critical vector stores the sibling index for the values
|
||||
// incident each vertex, i.e. it is the same size as the set of vert-faces
|
||||
// (typically 4 ints per vertex) but a fraction of the size since we use
|
||||
// 8-bit indices for the siblings.
|
||||
// The rest are typically an integer or two per vertex or value and
|
||||
// 8-bit tags per edge and value.
|
||||
// The bulk of the memory usage is in the face-values, and these are
|
||||
// not needed for refinement.
|
||||
//
|
||||
// Memory cost (bytes) for members (N verts, N faces, 2*N edges, M > N value):
|
||||
// 16*N per-face-vert values
|
||||
// 2*N per-edge tags
|
||||
// N per-vertex sibling counts
|
||||
// 4*N per-vertex sibling offsets
|
||||
// 4*N per-vert-face siblings
|
||||
// 4*M per-value indices (redundant after level 0)
|
||||
// M per-value tags
|
||||
// - total: 27*N + 5*M
|
||||
// - consider size of M:
|
||||
// - worst case M = 4*N at level 0, but M -> N as level increases
|
||||
// - typical size may be M ~= 1.5*N, so say M = 8/5 * N
|
||||
// - current examples indicate far less: 1.1*N to 1.2*N
|
||||
// - so M = 6/5 * N may be more realistic
|
||||
// - total = 35*N, i.e. 8-9 ints-per-face (or per-vertex)
|
||||
// * roughly an extra int for each face-vertex index
|
||||
// - bare minimum (*):
|
||||
// - 21*N:
|
||||
// - 16*N face values specified as input
|
||||
// - 4*N for some kind of vertex-to-value index/offset/mapping
|
||||
// - N for some kind of vertex/match indication tag
|
||||
// * assuming face-values retained in user-specified form
|
||||
// - compute on-demand by vert-face-siblings reduces by 12*N
|
||||
// - note typical UV data size of M = N*8/5 values:
|
||||
// - float[2] for each value -> data = 8*M = 13*N
|
||||
// - potentially redundant:
|
||||
// - 6*N (4*M) value indices for level > 0
|
||||
// - possible extras:
|
||||
// - 2*M (3*N) for the 2 ends of each value that is a crease
|
||||
// - allocated for all M vertex-values
|
||||
// - only populated for those tagged as creases
|
||||
// ? how quickly can we look this up instead?
|
||||
//
|
||||
// Per-face:
|
||||
std::vector<Index> _faceVertValues; // matches face-verts of level (16*N)
|
||||
// Per-face (matches face-verts of corresponding level):
|
||||
std::vector<Index> _faceVertValues;
|
||||
|
||||
// Per-edge:
|
||||
std::vector<ETag> _edgeTags; // 1 per edge (2*N)
|
||||
std::vector<ETag> _edgeTags;
|
||||
|
||||
// Per-vertex:
|
||||
std::vector<Sibling> _vertSiblingCounts; // 1 per vertex (1*N)
|
||||
std::vector<int> _vertSiblingOffsets; // 1 per vertex (4*N)
|
||||
std::vector<Sibling> _vertFaceSiblings; // matches face-verts of level (4*N)
|
||||
std::vector<Sibling> _vertSiblingCounts;
|
||||
std::vector<int> _vertSiblingOffsets;
|
||||
std::vector<Sibling> _vertFaceSiblings;
|
||||
|
||||
// Per-value:
|
||||
std::vector<Index> _vertValueIndices; // variable per vertex (4*M>N)
|
||||
std::vector<ValueTag> _vertValueTags; // variable per vertex (1*M>N)
|
||||
std::vector<Index> _vertValueIndices;
|
||||
std::vector<ValueTag> _vertValueTags;
|
||||
std::vector<LocalIndex> _vertValueCreaseEnds;
|
||||
};
|
||||
|
||||
//
|
||||
|
@ -77,8 +77,9 @@ FVarRefinement::applyRefinement() {
|
||||
//
|
||||
// Transfer basic properties from the parent to child level:
|
||||
//
|
||||
_child->_options = _parent->_options;
|
||||
_child->_isLinear = _parent->_isLinear;
|
||||
_child->_options = _parent->_options;
|
||||
_child->_isLinear = _parent->_isLinear;
|
||||
_child->_hasSmoothBoundaries = _parent->_hasSmoothBoundaries;
|
||||
|
||||
//
|
||||
// It's difficult to know immediately how many child values arise from the
|
||||
@ -91,6 +92,9 @@ FVarRefinement::applyRefinement() {
|
||||
|
||||
propagateEdgeTags();
|
||||
propagateValueTags();
|
||||
if (_child->_hasSmoothBoundaries) {
|
||||
propagateValueCreases();
|
||||
}
|
||||
|
||||
//
|
||||
// The refined face-values are technically redundant as they can be constructed
|
||||
@ -102,7 +106,7 @@ FVarRefinement::applyRefinement() {
|
||||
_child->initializeFaceValuesFromFaceVertices();
|
||||
}
|
||||
|
||||
//printf("Refinement to level %d:\n", _child->getDepth());
|
||||
//printf("FVar refinement to level %d:\n", _child->getDepth());
|
||||
//_child->print();
|
||||
|
||||
//printf("Validating refinement to level %d:\n", _child->getDepth());
|
||||
@ -164,6 +168,9 @@ FVarRefinement::trimAndFinalizeChildValues() {
|
||||
_child->_valueCount = _child->getNumVertices() + _childSiblingFromEdgeCount + _childSiblingFromVertCount;
|
||||
|
||||
_child->_vertValueTags.resize(_child->_valueCount);
|
||||
if (_child->_hasSmoothBoundaries) {
|
||||
_child->_vertValueCreaseEnds.resize(2 * _child->_valueCount);
|
||||
}
|
||||
|
||||
_childValueParentSource.resize(_child->_valueCount);
|
||||
|
||||
@ -338,19 +345,32 @@ FVarRefinement::propagateValueTags() {
|
||||
// - if complete, trivially propagated/inherited
|
||||
// - if incomplete, need to map to child subset
|
||||
//
|
||||
|
||||
//
|
||||
// Values from face-vertices -- all match:
|
||||
//
|
||||
FVarLevel::ValueTag valTagMatch(false);
|
||||
FVarLevel::ValueTag valTagMismatch(true);
|
||||
valTagMismatch._corner = true;
|
||||
|
||||
Index cVert = 0;
|
||||
for (cVert = 0; cVert < _refinement._childVertFromFaceCount; ++cVert) {
|
||||
_child->_vertValueTags[cVert] = valTagMatch;
|
||||
}
|
||||
|
||||
//
|
||||
// Values from edge-vertices -- for edges that are split, tag as mismatched and tag
|
||||
// as corner or crease depending on the presence of creases in the parent:
|
||||
//
|
||||
FVarLevel::ValueTag valTagMismatch(true);
|
||||
FVarLevel::ValueTag valTagCrease(true);
|
||||
valTagCrease._crease = true;
|
||||
|
||||
FVarLevel::ValueTag& valTagSplitEdge = _parent->_hasSmoothBoundaries ? valTagCrease : valTagMismatch;
|
||||
|
||||
for (int i = 0; i < _refinement._childVertFromEdgeCount; ++i, ++cVert) {
|
||||
Index pEdge = _refinement.getChildVertexParentIndex(cVert);
|
||||
bool pEdgeIsSplit = _parent->_edgeTags[pEdge]._mismatch;
|
||||
|
||||
FVarLevel::ValueTag const& cValueTag = pEdgeIsSplit ? valTagMismatch : valTagMatch;
|
||||
FVarLevel::ValueTag const& cValueTag = pEdgeIsSplit ? valTagSplitEdge: valTagMatch;
|
||||
|
||||
_child->_vertValueTags[cVert] = cValueTag;
|
||||
|
||||
@ -362,6 +382,11 @@ FVarRefinement::propagateValueTags() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Values from vertex-vertices -- inherit tags from parent values when complete
|
||||
// otherwise (not yet supported) need to identify the parent value for each child:
|
||||
//
|
||||
for (int i = 0; i < _refinement._childVertFromVertCount; ++i, ++cVert) {
|
||||
assert(!_refinement._childVertexTag[cVert]._incomplete);
|
||||
|
||||
@ -380,6 +405,73 @@ FVarRefinement::propagateValueTags() {
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FVarRefinement::propagateValueCreases() {
|
||||
|
||||
assert(_child->_hasSmoothBoundaries);
|
||||
|
||||
// Skip child vertices from faces:
|
||||
Index cVert = _refinement._childVertFromFaceCount;
|
||||
|
||||
//
|
||||
// For each child vertex from an edge that has FVar values and is complete, initialize
|
||||
// the crease-ends for those values tagged as smooth boundaries:
|
||||
//
|
||||
for (int i = 0; i < _refinement._childVertFromEdgeCount; ++i, ++cVert) {
|
||||
if (!_child->_vertValueTags[cVert]._mismatch) continue;
|
||||
if (_refinement._childVertexTag[cVert]._incomplete) continue;
|
||||
|
||||
if (_child->_vertValueTags[cVert]._crease) {
|
||||
LocalIndex * sibling0Ends = &_child->_vertValueCreaseEnds[2 * cVert];
|
||||
sibling0Ends[0] = 0;
|
||||
sibling0Ends[1] = 1;
|
||||
}
|
||||
int vSiblingCount = _child->_vertSiblingCounts[cVert];
|
||||
if (vSiblingCount) {
|
||||
int cOffset = _child->_vertSiblingOffsets[cVert];
|
||||
if (_child->_vertValueTags[cOffset]._crease) {
|
||||
LocalIndex * sibling1Ends = &_child->_vertValueCreaseEnds[2 * cOffset];
|
||||
sibling1Ends[0] = 2;
|
||||
sibling1Ends[1] = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// 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:
|
||||
//
|
||||
for (int i = 0; i < _refinement._childVertFromVertCount; ++i, ++cVert) {
|
||||
if (!_child->_vertValueTags[cVert]._mismatch) continue;
|
||||
if (_refinement._childVertexTag[cVert]._incomplete) continue;
|
||||
|
||||
Index pVert = _refinement.getChildVertexParentIndex(cVert);
|
||||
|
||||
if (_child->_vertValueTags[cVert]._crease) {
|
||||
LocalIndex * pSiblingEnds = &_parent->_vertValueCreaseEnds[2 * pVert];
|
||||
LocalIndex * cSiblingEnds = &_child->_vertValueCreaseEnds[2 * cVert];
|
||||
|
||||
cSiblingEnds[0] = pSiblingEnds[0];
|
||||
cSiblingEnds[1] = pSiblingEnds[1];
|
||||
}
|
||||
int vSiblingCount = _child->_vertSiblingCounts[cVert];
|
||||
if (vSiblingCount) {
|
||||
int cSiblingOffset = _child->_vertSiblingOffsets[cVert];
|
||||
int pSiblingOffset = _parent->_vertSiblingOffsets[pVert];
|
||||
|
||||
LocalIndex * pSiblingEnds = &_parent->_vertValueCreaseEnds[2 * pSiblingOffset];
|
||||
LocalIndex * cSiblingEnds = &_child->_vertValueCreaseEnds[2 * cSiblingOffset];
|
||||
|
||||
for (int j = 0; j < vSiblingCount; ++j, cSiblingEnds += 2, pSiblingEnds += 2) {
|
||||
if (_child->_vertValueTags[cSiblingOffset + j]._crease) {
|
||||
cSiblingEnds[0] = pSiblingEnds[0];
|
||||
cSiblingEnds[1] = pSiblingEnds[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // end namespace Vtr
|
||||
|
||||
} // end namespace OPENSUBDIV_VERSION
|
||||
|
@ -79,6 +79,7 @@ public:
|
||||
|
||||
void propagateEdgeTags();
|
||||
void propagateValueTags();
|
||||
void propagateValueCreases();
|
||||
|
||||
|
||||
public:
|
||||
|
Loading…
Reference in New Issue
Block a user