From 2d33a6ebf46392e4b44ae58c3aca8fb07788f724 Mon Sep 17 00:00:00 2001 From: barry Date: Wed, 13 Jul 2016 10:02:01 -0700 Subject: [PATCH] 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 --- .../far/endCapBSplineBasisPatchFactory.cpp | 8 +- .../far/endCapBSplineBasisPatchFactory.h | 2 + .../far/endCapGregoryBasisPatchFactory.cpp | 15 +-- .../far/endCapGregoryBasisPatchFactory.h | 8 +- opensubdiv/far/gregoryBasis.cpp | 93 +++++++------------ opensubdiv/far/gregoryBasis.h | 1 + opensubdiv/far/patchTableFactory.cpp | 9 +- opensubdiv/vtr/level.cpp | 38 ++++++++ opensubdiv/vtr/level.h | 27 +++++- 9 files changed, 127 insertions(+), 74 deletions(-) diff --git a/opensubdiv/far/endCapBSplineBasisPatchFactory.cpp b/opensubdiv/far/endCapBSplineBasisPatchFactory.cpp index 5473eea2..04ab5fe8 100644 --- a/opensubdiv/far/endCapBSplineBasisPatchFactory.cpp +++ b/opensubdiv/far/endCapBSplineBasisPatchFactory.cpp @@ -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 // diff --git a/opensubdiv/far/endCapBSplineBasisPatchFactory.h b/opensubdiv/far/endCapBSplineBasisPatchFactory.h index 7122dc26..fe146268 100644 --- a/opensubdiv/far/endCapBSplineBasisPatchFactory.h +++ b/opensubdiv/far/endCapBSplineBasisPatchFactory.h @@ -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); diff --git a/opensubdiv/far/endCapGregoryBasisPatchFactory.cpp b/opensubdiv/far/endCapGregoryBasisPatchFactory.cpp index 772975a8..e3db5265 100644 --- a/opensubdiv/far/endCapGregoryBasisPatchFactory.cpp +++ b/opensubdiv/far/endCapGregoryBasisPatchFactory.cpp @@ -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; diff --git a/opensubdiv/far/endCapGregoryBasisPatchFactory.h b/opensubdiv/far/endCapGregoryBasisPatchFactory.h index f400bbe8..80502fa1 100644 --- a/opensubdiv/far/endCapGregoryBasisPatchFactory.h +++ b/opensubdiv/far/endCapGregoryBasisPatchFactory.h @@ -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; diff --git a/opensubdiv/far/gregoryBasis.cpp b/opensubdiv/far/gregoryBasis.cpp index 7c3715be..d518380a 100644 --- a/opensubdiv/far/gregoryBasis.cpp +++ b/opensubdiv/far/gregoryBasis.cpp @@ -31,6 +31,7 @@ #include #include #include +#include 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 - 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; i2) { - 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; xGetPatchPoints( - 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(); diff --git a/opensubdiv/vtr/level.cpp b/opensubdiv/vtr/level.cpp index 4148b335..0e93d5ee 100644 --- a/opensubdiv/vtr/level.cpp +++ b/opensubdiv/vtr/level.cpp @@ -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: // diff --git a/opensubdiv/vtr/level.h b/opensubdiv/vtr/level.h index 2d3169e1..6460f417 100644 --- a/opensubdiv/vtr/level.h +++ b/opensubdiv/vtr/level.h @@ -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;