Adding VSpan to standardize representation of partial rings around vertices:

- added definition and gathering method to Vtr::Level
    - extended Far::EndCap...PatchFactories with VSpan[4] for patch corners
    - extended Far::EndCapGregoryPatchFactory to avoid last level assumption
    - adapted Far::PatchTableFactory to use above extensions
    - extended Far::GregoryBasis to recognize VSpan corners
    - simplified Far::GregoryBasis treatement of boundaries
    - fixed bug in Far::GregoryBasis related to smooth corners
This commit is contained in:
barry 2016-07-13 10:02:01 -07:00
parent fc9bbb154b
commit 2d33a6ebf4
9 changed files with 127 additions and 74 deletions

View File

@ -77,6 +77,7 @@ EndCapBSplineBasisPatchFactory::EndCapBSplineBasisPatchFactory(
ConstIndexArray
EndCapBSplineBasisPatchFactory::GetPatchPoints(
Vtr::internal::Level const * level, Index thisFace,
Vtr::internal::Level::VSpan const cornerSpans[],
PatchTableFactory::PatchFaceTag const *levelPatchTags,
int levelVertOffset) {
@ -85,7 +86,7 @@ EndCapBSplineBasisPatchFactory::GetPatchPoints(
// if it's boundary, fallback to use GregoryBasis
if (patchTag._boundaryCount > 0) {
return getPatchPointsFromGregoryBasis(
level, thisFace, facePoints, levelVertOffset);
level, thisFace, cornerSpans, facePoints, levelVertOffset);
}
// there's a short-cut when the face contains only 1 extraordinary vertex.
@ -99,7 +100,7 @@ EndCapBSplineBasisPatchFactory::GetPatchPoints(
// more than one extraoridinary vertices.
// fallback to use GregoryBasis
return getPatchPointsFromGregoryBasis(
level, thisFace, facePoints, levelVertOffset);
level, thisFace, cornerSpans, facePoints, levelVertOffset);
}
irregular = i;
}
@ -113,6 +114,7 @@ EndCapBSplineBasisPatchFactory::GetPatchPoints(
ConstIndexArray
EndCapBSplineBasisPatchFactory::getPatchPointsFromGregoryBasis(
Vtr::internal::Level const * level, Index thisFace,
Vtr::internal::Level::VSpan const cornerSpans[],
ConstIndexArray facePoints, int levelVertOffset) {
// XXX: For now, always create new 16 indices for each patch.
@ -124,7 +126,7 @@ EndCapBSplineBasisPatchFactory::getPatchPointsFromGregoryBasis(
_patchPoints.push_back(_numVertices + offset);
++_numVertices;
}
GregoryBasis::ProtoBasis basis(*level, thisFace, levelVertOffset, -1);
GregoryBasis::ProtoBasis basis(*level, thisFace, cornerSpans, levelVertOffset, -1);
// XXX: temporary hack. we should traverse topology and find existing
// vertices if available
//

View File

@ -80,12 +80,14 @@ public:
///
ConstIndexArray GetPatchPoints(
Vtr::internal::Level const * level, Index faceIndex,
Vtr::internal::Level::VSpan const cornerSpans[],
PatchTableFactory::PatchFaceTag const * levelPatchTags,
int levelVertOffset);
private:
ConstIndexArray getPatchPointsFromGregoryBasis(
Vtr::internal::Level const * level, Index thisFace,
Vtr::internal::Level::VSpan const cornerSpans[],
ConstIndexArray facePoints,
int levelVertOffset);

View File

@ -77,7 +77,8 @@ EndCapGregoryBasisPatchFactory::Create(TopologyRefiner const & refiner,
// Gregory patches are end-caps: they only exist on max-level
Vtr::internal::Level const & level = refiner.getLevel(refiner.GetMaxLevel());
GregoryBasis::ProtoBasis basis(level, faceIndex, 0, fvarChannel);
// Is this method used/supported? If so, needs corner spans (and vert offset?)...
GregoryBasis::ProtoBasis basis(level, faceIndex, 0, 0, fvarChannel);
GregoryBasis * result = new GregoryBasis;
basis.Copy(result);
@ -86,16 +87,14 @@ EndCapGregoryBasisPatchFactory::Create(TopologyRefiner const & refiner,
}
bool
EndCapGregoryBasisPatchFactory::addPatchBasis(Index faceIndex,
EndCapGregoryBasisPatchFactory::addPatchBasis(Vtr::internal::Level const & level, Index faceIndex,
Vtr::internal::Level::VSpan const cornerSpans[],
bool verticesMask[4][5],
int levelVertOffset) {
// Gregory patches only exist on the hight
Vtr::internal::Level const & level = _refiner->getLevel(_refiner->GetMaxLevel());
// Gather the CVs that influence the Gregory patch and their relative
// weights in a basis
GregoryBasis::ProtoBasis basis(level, faceIndex, levelVertOffset, -1);
GregoryBasis::ProtoBasis basis(level, faceIndex, cornerSpans, levelVertOffset, -1);
for (int i = 0; i < 4; ++i) {
if (verticesMask[i][0]) {
@ -131,8 +130,10 @@ EndCapGregoryBasisPatchFactory::addPatchBasis(Index faceIndex,
ConstIndexArray
EndCapGregoryBasisPatchFactory::GetPatchPoints(
Vtr::internal::Level const * level, Index faceIndex,
Vtr::internal::Level::VSpan const cornerSpans[],
PatchTableFactory::PatchFaceTag const * levelPatchTags,
int levelVertOffset) {
// allocate indices (awkward)
// assert(Vtr::INDEX_INVALID==0xFFFFFFFF);
for (int i = 0; i < 20; ++i) {
@ -227,7 +228,7 @@ EndCapGregoryBasisPatchFactory::GetPatchPoints(
_faceIndices.push_back(faceIndex);
// add basis
addPatchBasis(faceIndex, newVerticesMask, levelVertOffset);
addPatchBasis(*level, faceIndex, cornerSpans, newVerticesMask, levelVertOffset);
++_numGregoryBasisPatches;

View File

@ -98,6 +98,8 @@ public:
/// @param level vtr refinement level
///
/// @param faceIndex vtr faceIndex at the level
//
/// @param cornerSpans information about topology for each corner of patch
///
/// @param levelPatchTags Array of patchTags for all faces in the level
///
@ -105,6 +107,7 @@ public:
///
ConstIndexArray GetPatchPoints(
Vtr::internal::Level const * level, Index faceIndex,
Vtr::internal::Level::VSpan const cornerSpans[],
PatchTableFactory::PatchFaceTag const * levelPatchTags,
int levelVertOffset);
@ -112,8 +115,9 @@ private:
/// Creates a basis for the vertices specified in mask on the face and
/// accumates it
bool addPatchBasis(Index faceIndex, bool newVerticesMask[4][5],
int levelVertOffset);
bool addPatchBasis(Vtr::internal::Level const & level, Index faceIndex,
Vtr::internal::Level::VSpan const cornerSpans[],
bool newVerticesMask[4][5], int levelVertOffset);
StencilTable *_vertexStencils;
StencilTable *_varyingStencils;

View File

@ -31,6 +31,7 @@
#include <cassert>
#include <cmath>
#include <cstring>
#include <cstdio>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
@ -100,6 +101,7 @@ inline float computeCoefficient(int valence) {
GregoryBasis::ProtoBasis::ProtoBasis(
Vtr::internal::Level const & level, Index faceIndex,
Vtr::internal::Level::VSpan const cornerSpans[],
int levelVertOffset, int fvarChannel) {
// XXX: This function is subject to refactor in 3.1
@ -110,8 +112,7 @@ GregoryBasis::ProtoBasis::ProtoBasis(
assert(facePoints.size()==4);
int maxvalence = level.getMaxValence(),
valences[4],
zerothNeighbors[4];
valences[4];
// XXX: a temporary hack for the performance issue
// ensure Point has a capacity for the neighborhood of
@ -140,29 +141,32 @@ GregoryBasis::ProtoBasis::ProtoBasis(
// the first phase
for (int vid=0; vid<4; ++vid) {
// save for varying stencils
varyingIndex[vid] = facePoints[vid] + levelVertOffset;
int ringSize =
level.gatherQuadRegularRingAroundVertex(
facePoints[vid], manifoldRings[vid], fvarChannel);
int ringSize = 0;
if (cornerSpans[vid]._numFaces == 0) {
ringSize = level.gatherQuadRegularRingAroundVertex( facePoints[vid],
manifoldRings[vid], fvarChannel);
} else {
ringSize = level.gatherQuadRegularPartialRingAroundVertex( facePoints[vid],
cornerSpans[vid],
manifoldRings[vid], fvarChannel);
}
int valence;
// when the corner vertex is on a boundary (ring-size is odd), valence is
// negated and the ring is padded to replicate the last entry
if (ringSize & 1) {
// boundary vertex
manifoldRings[vid][ringSize] = manifoldRings[vid][ringSize-1];
++ringSize;
valence = -ringSize/2;
valences[vid] = -ringSize/2;
} else {
valence = ringSize/2;
valences[vid] = ringSize/2;
}
int ivalence = abs(valence);
valences[vid] = valence;
Index boundaryEdgeNeighbors[2],
currentNeighbor = 0,
zerothNeighbor=0,
ibefore=0;
int valence = valences[vid];
int ivalence = abs(valence);
for (int i=0; i<ivalence; ++i) {
@ -175,35 +179,13 @@ GregoryBasis::ProtoBasis::ProtoBasis(
idx_neighbor_m = (manifoldRings[vid][2*im + 0]),
idx_diagonal_m = (manifoldRings[vid][2*im + 1]);
bool boundaryNeighbor = (level.getVertexEdges(idx_neighbor).size() >
level.getVertexFaces(idx_neighbor).size());
if (fvarChannel>=0) {
// XXXX manuelk need logic to check for boundary in fvar
boundaryNeighbor = false;
}
if (boundaryNeighbor) {
if (currentNeighbor<2) {
boundaryEdgeNeighbors[currentNeighbor] = idx_neighbor;
}
++currentNeighbor;
if (currentNeighbor==1) {
ibefore = zerothNeighbor = i;
} else {
if (i-ibefore==1) {
std::swap(boundaryEdgeNeighbors[0], boundaryEdgeNeighbors[1]);
zerothNeighbor = i;
}
}
}
float d = float(ivalence)+5.0f;
f[i].Clear(4);
f[i].AddWithWeight(facePoints[vid], float(ivalence)/d);
f[i].AddWithWeight(idx_neighbor_p, 2.0f/d);
f[i].AddWithWeight(idx_neighbor, 2.0f/d);
f[i].AddWithWeight(idx_diagonal, 1.0f/d);
P[vid].AddWithWeight(f[i], 1.0f/float(ivalence));
int rid = vid * maxvalence + i;
@ -214,11 +196,6 @@ GregoryBasis::ProtoBasis::ProtoBasis(
r[rid].AddWithWeight(idx_diagonal_m, -1.0f/6.0f);
}
zerothNeighbors[vid] = zerothNeighbor;
if (currentNeighbor == 1) {
boundaryEdgeNeighbors[1] = boundaryEdgeNeighbors[0];
}
for (int i=0; i<ivalence; ++i) {
int im = (i+ivalence-1)%ivalence;
float c0 = 0.5f * csf(ivalence-3, 2*i);
@ -235,14 +212,14 @@ GregoryBasis::ProtoBasis::ProtoBasis(
// Boundary gregory case:
if (valence < 0) {
Index boundaryEdgeNeighbors[2] = { manifoldRings[vid][0],
manifoldRings[vid][ringSize-1] };
P[vid].Clear(stencilCapacity);
if (ivalence>2) {
P[vid].AddWithWeight(boundaryEdgeNeighbors[0], 1.0f/6.0f);
P[vid].AddWithWeight(boundaryEdgeNeighbors[1], 1.0f/6.0f);
P[vid].AddWithWeight(facePoints[vid], 4.0f/6.0f);
} else {
P[vid].AddWithWeight(facePoints[vid], 1.0f);
}
P[vid].AddWithWeight(boundaryEdgeNeighbors[0], 1.0f/6.0f);
P[vid].AddWithWeight(boundaryEdgeNeighbors[1], 1.0f/6.0f);
P[vid].AddWithWeight(facePoints[vid], 4.0f/6.0f);
float k = float(float(ivalence) - 1.0f); //k is the number of faces
float c = cosf(float(M_PI)/k);
float s = sinf(float(M_PI)/k);
@ -250,7 +227,7 @@ GregoryBasis::ProtoBasis::ProtoBasis(
float alpha_0k = -((1.0f+2.0f*c)*sqrtf(1.0f+c))/((3.0f*k+c)*sqrtf(1.0f-c));
float beta_0 = s/(3.0f*k + c);
int idx_diagonal = manifoldRings[vid][2*zerothNeighbor + 1];
int idx_diagonal = manifoldRings[vid][1];
e0[vid].Clear(stencilCapacity);
e0[vid].AddWithWeight(boundaryEdgeNeighbors[0], 1.0f/6.0f);
@ -264,13 +241,11 @@ GregoryBasis::ProtoBasis::ProtoBasis(
for (int x=1; x<ivalence-1; ++x) {
Index curri = ((x + zerothNeighbor)%ivalence);
float alpha = (4.0f*sinf((float(M_PI) * float(x))/k))/(3.0f*k+c),
beta = (sinf((float(M_PI) * float(x))/k) + sinf((float(M_PI) * float(x+1))/k))/(3.0f*k+c);
Index idx_neighbor = manifoldRings[vid][2*curri + 0],
idx_diagonal = manifoldRings[vid][2*curri + 1];
Index idx_neighbor = manifoldRings[vid][2*x + 0],
idx_diagonal = manifoldRings[vid][2*x + 1];
e1[vid].AddWithWeight(idx_neighbor, alpha);
e1[vid].AddWithWeight(idx_diagonal, beta);
@ -316,7 +291,7 @@ GregoryBasis::ProtoBasis::ProtoBasis(
Point Ep_im = P[im];
if (valences[ip]<-2) {
Index j = (np + prev_p - zerothNeighbors[ip]) % np;
Index j = (np + prev_p) % np;
Em_ip.AddWithWeight(e0[ip], cosf((float(M_PI)*j)/float(np-1)));
Em_ip.AddWithWeight(e1[ip], sinf((float(M_PI)*j)/float(np-1)));
} else {
@ -325,7 +300,7 @@ GregoryBasis::ProtoBasis::ProtoBasis(
}
if (valences[im]<-2) {
Index j = (nm + start_m - zerothNeighbors[im]) % nm;
Index j = (nm + start_m) % nm;
Ep_im.AddWithWeight(e0[im], cosf((float(M_PI)*j)/float(nm-1)));
Ep_im.AddWithWeight(e1[im], sinf((float(M_PI)*j)/float(nm-1)));
} else {
@ -371,8 +346,8 @@ GregoryBasis::ProtoBasis::ProtoBasis(
Fm[vid].AddWithWeight(rp[prev], -1.0f/3.0f);
} else if (valences[vid] < -2) {
Index jp = (ivalence + start - zerothNeighbors[vid]) % ivalence,
jm = (ivalence + prev - zerothNeighbors[vid]) % ivalence;
Index jp = (ivalence + start) % ivalence,
jm = (ivalence + prev) % ivalence;
float s1 = 3-2*csf(n-3,2)-csf(np-3,2),
s2 = 2*csf(n-3,2),

View File

@ -189,6 +189,7 @@ public:
ProtoBasis(Vtr::internal::Level const & level,
Vtr::Index faceIndex,
Vtr::internal::Level::VSpan const cornerSpans[],
int levelVertOffset,
int fvarChannel);

View File

@ -1223,13 +1223,18 @@ PatchTableFactory::populateAdaptivePatches(
// emit end patch. end patch should be in the max level (until we implement DFAS)
assert(i==refiner.GetMaxLevel());
// identify relevant spans around the corner vertices for the irregular patches
// (this is just a stub for now -- leaving the span "size" to zero, as constructed,
// indicates to use the full neighborhood)...
Vtr::internal::Level::VSpan cornerSpans[4];
// switch endcap patchtype by option
switch(context.options.GetEndCapType()) {
case Options::ENDCAP_GREGORY_BASIS:
{
// note: this call will be moved into vtr::level.
ConstIndexArray cvs = endCapGregoryBasis->GetPatchPoints(
level, faceIndex, levelPatchTags, levelVertOffset);
level, faceIndex, cornerSpans, levelPatchTags, levelVertOffset);
for (int j = 0; j < cvs.size(); ++j) iptrs.GP[j] = cvs[j];
iptrs.GP += cvs.size();
@ -1244,7 +1249,7 @@ PatchTableFactory::populateAdaptivePatches(
case Options::ENDCAP_BSPLINE_BASIS:
{
ConstIndexArray cvs = endCapBSpline->GetPatchPoints(
level, faceIndex, levelPatchTags, levelVertOffset);
level, faceIndex, cornerSpans, levelPatchTags, levelVertOffset);
for (int j = 0; j < cvs.size(); ++j) iptrs.R[j] = cvs[j];
iptrs.R += cvs.size();

View File

@ -650,6 +650,44 @@ Level::gatherQuadRegularRingAroundVertex(
return ringIndex;
}
int
Level::gatherQuadRegularPartialRingAroundVertex(
Index vIndex, VSpan const & span, int ringPoints[], int fvarChannel) const {
Level const& level = *this;
assert(! level.isVertexNonManifold(vIndex));
ConstIndexArray vFaces = level.getVertexFaces(vIndex);
ConstLocalIndexArray vInFaces = level.getVertexFaceLocalIndices(vIndex);
int nFaces = span._numFaces;
int leadingEdge = span._leadingVertEdge;
int ringIndex = 0;
for (int i = 0; i < nFaces; ++i) {
//
// For every incident quad, we want the two vertices clockwise in each face, i.e.
// the vertex at the end of the leading edge and the vertex opposite this one:
//
int fIncident = (leadingEdge + i) % vFaces.size();
ConstIndexArray fPoints = (fvarChannel < 0)
? level.getFaceVertices(vFaces[fIncident])
: level.getFaceFVarValues(vFaces[fIncident], fvarChannel);
int vInThisFace = vInFaces[fIncident];
ringPoints[ringIndex++] = fPoints[fastMod4(vInThisFace + 1)];
ringPoints[ringIndex++] = fPoints[fastMod4(vInThisFace + 2)];
if (i == nFaces - 1) {
ringPoints[ringIndex++] = fPoints[fastMod4(vInThisFace + 3)];
}
}
return ringIndex;
}
//
// Gathering the 4 vertices of a quad:
//

View File

@ -149,6 +149,28 @@ public:
ETag getFaceCompositeETag(ConstIndexArray & faceEdges) const;
// Additional simple struct to identify a "span" around a vertex, i.e. a
// subset of the faces around a vertex delimited by some property (e.g. a
// face-varying discontinuity, an inf-sharp edge, etc.)
//
// The span requires an "origin", i.e. a leading edge or face and a "size"
// to fully define its extent. Use of the size is preferred over leading
// and trailing edges/faces so that valence is available since for a non-
// manifold vertex that cannot be determined from the two extremeties.
// There is also a subtle but marginal advantage in using a leading edge
// rather than face, but it may be worth using the leading face with the
// face count for consistency (both properties defined in terms of faces).
//
// Currently setting the size to 0 is an indication to use the full
// neighborhood rather than a subset -- may want to revisit that choice...
//
struct VSpan {
VSpan() : _numFaces(0), _leadingVertEdge(0) { }
LocalIndex _numFaces;
LocalIndex _leadingVertEdge;
};
public:
Level();
~Level();
@ -284,7 +306,10 @@ public:
int gatherQuadRegularCornerPatchPoints( Index fIndex, Index patchPoints[], int cornerVertInFace,
int fvarChannel = -1) const;
int gatherQuadRegularRingAroundVertex(Index vIndex, Index ringPoints[], int fvarChannel = -1) const;
int gatherQuadRegularRingAroundVertex(Index vIndex, Index ringPoints[],
int fvarChannel = -1) const;
int gatherQuadRegularPartialRingAroundVertex(Index vIndex, VSpan const & span, Index ringPoints[],
int fvarChannel = -1) const;
// WIP -- for future use, need to extend for face-varying...
int gatherTriRegularInteriorPatchPoints( Index fIndex, Index patchVerts[], int rotation = 0) const;