OpenSubdiv/opensubdiv/far/patchTablesFactory.h
manuelk 10c687ecd5 Release Candidate 1.0 :
- [Feature Adaptive GPU Rendering of Catmull-Clark Surfaces](http://research.microsoft.com/en-us/um/people/cloop/tog2012.pdf).

- New API architecture : we are planning to lock on to this new framework as the basis for backward compatibility, which we will enforce from Release 1.0 onward. Subsequent releases of OpenSubdiv should not break client code.

- DirectX 11 support

- and much more...
2012-12-10 17:15:13 -08:00

1005 lines
40 KiB
C++

//
// Copyright (C) Pixar. All rights reserved.
//
// This license governs use of the accompanying software. If you
// use the software, you accept this license. If you do not accept
// the license, do not use the software.
//
// 1. Definitions
// The terms "reproduce," "reproduction," "derivative works," and
// "distribution" have the same meaning here as under U.S.
// copyright law. A "contribution" is the original software, or
// any additions or changes to the software.
// A "contributor" is any person or entity that distributes its
// contribution under this license.
// "Licensed patents" are a contributor's patent claims that read
// directly on its contribution.
//
// 2. Grant of Rights
// (A) Copyright Grant- Subject to the terms of this license,
// including the license conditions and limitations in section 3,
// each contributor grants you a non-exclusive, worldwide,
// royalty-free copyright license to reproduce its contribution,
// prepare derivative works of its contribution, and distribute
// its contribution or any derivative works that you create.
// (B) Patent Grant- Subject to the terms of this license,
// including the license conditions and limitations in section 3,
// each contributor grants you a non-exclusive, worldwide,
// royalty-free license under its licensed patents to make, have
// made, use, sell, offer for sale, import, and/or otherwise
// dispose of its contribution in the software or derivative works
// of the contribution in the software.
//
// 3. Conditions and Limitations
// (A) No Trademark License- This license does not grant you
// rights to use any contributor's name, logo, or trademarks.
// (B) If you bring a patent claim against any contributor over
// patents that you claim are infringed by the software, your
// patent license from such contributor to the software ends
// automatically.
// (C) If you distribute any portion of the software, you must
// retain all copyright, patent, trademark, and attribution
// notices that are present in the software.
// (D) If you distribute any portion of the software in source
// code form, you may do so only under this license by including a
// complete copy of this license with your distribution. If you
// distribute any portion of the software in compiled or object
// code form, you may only do so under a license that complies
// with this license.
// (E) The software is licensed "as-is." You bear the risk of
// using it. The contributors give no express warranties,
// guarantees or conditions. You may have additional consumer
// rights under your local laws which this license cannot change.
// To the extent permitted under your local laws, the contributors
// exclude the implied warranties of merchantability, fitness for
// a particular purpose and non-infringement.
//
#ifndef FAR_PTACH_TABLES_FACTORY_H
#define FAR_PTACH_TABLES_FACTORY_H
#include "../version.h"
#include "../far/patchTables.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
/// \brief A specialized factory for feature adaptive FarPatchTables
///
/// FarPatchTables contain the lists of vertices for each patch of an adaptive
/// mesh representation. This specialized factory is a private helper for FarMeshFactory.
///
/// Separating the factory allows us to isolate Far data structures from Hbr dependencies.
///
template <class T> class FarPatchTablesFactory {
protected:
template <class X, class Y> friend class FarMeshFactory;
/// Factory constructor
/// @param mesh Hbr mesh to generate tables for
/// @param nfaces Number of faces in the mesh (cached for speed)
/// @param remapTable Vertex remapping table generated by FarMeshFactory
FarPatchTablesFactory( HbrMesh<T> const * mesh, int nfaces, std::vector<int> const & remapTable );
/// Returns a FarPatchTables instance
/// @param maxlevel Highest level of refinement processed
/// @param maxvalence Maximum vertex valence in the mesh
/// @param requirePtexCoordinate Flag for generating ptex coordinate
/// @param requireFVarData Flag for generating face-varying data
FarPatchTables * Create( int maxlevel, int maxvalence, bool requirePtexCoordinate=false,
bool requireFVarData=false );
private:
// Returns true if one of v's neighboring faces has vertices carrying the tag "wasTagged"
static bool vertexHasTaggedNeighbors(HbrVertex<T> * v);
// Returns the rotation for a boundary patch
static unsigned char computeBoundaryPatchRotation( HbrFace<T> * f );
// Returns the rotation for a corner patch
static unsigned char computeCornerPatchRotation( HbrFace<T> * f );
// Populates an array of indices with the "one-ring" vertices for the given face
void getOneRing( HbrFace<T> * f, int ringsize, unsigned int const * remap, unsigned int * result );
// Populates the Gregory patch quad offsets table
static void getQuadOffsets( HbrFace<T> * f, unsigned int * result );
// Hbr mesh accessor
HbrMesh<T> const * getMesh() const { return _mesh; }
// Number of faces in the Hbr mesh (cached for speed)
int getNumFaces() const { return _nfaces; }
template<class X> struct Pointers {
X R_P, // regular patch
B_P[4], // boundary patch (4 rotations)
C_P[4], // corner patch (4 rotations)
G_P[2]; // gregory patch (boundary & corner)
Pointers() { memset(this, 0, sizeof(Pointers<X>)); }
};
typedef Pointers<unsigned int*> IndexPointers;
typedef Pointers<int *> PtexPointers;
typedef Pointers<float *> FVarPointers;
// Sets the FarTable level markers for the given level using index pointers
void setMarkers(int level, FarPatchTables * result, IndexPointers const & fptrs, IndexPointers const * tptrs);
struct Counters {
int R_C, // regular patch
B_C[4], // boundary patch (4 rotations)
C_C[4], // corner patch (4 rotations)
G_C[2]; // gregory patch (boundary & corner)
Counters() { memset(this, 0, sizeof(Counters)); }
};
Counters _fullCtr,
_transitionCtr[5];
HbrMesh<T> const * _mesh;
// Reference to the vertex remapping table generated by FarMeshFactory
std::vector<int> const &_remapTable;
int _nfaces;
};
template <class T> bool
FarPatchTablesFactory<T>::vertexHasTaggedNeighbors(HbrVertex<T> * v) {
assert(v);
HbrHalfedge<T> * start = v->GetIncidentEdge(),
* next=start;
do {
HbrFace<T> * right = next->GetRightFace(),
* left = next->GetLeftFace();
if (right and (not right->hasTaggedVertices()))
return true;
if (left and (not left->hasTaggedVertices()))
return true;
next = v->GetNextEdge(next);
} while (next and next!=start);
return false;
}
// Returns a rotation index for boundary patches (range [0-3])
template <class T> unsigned char
FarPatchTablesFactory<T>::computeBoundaryPatchRotation( HbrFace<T> * f ) {
unsigned char rot=0;
for (unsigned char i=0; i<4;++i) {
if (f->GetVertex(i)->OnBoundary() and
f->GetVertex((i+1)%4)->OnBoundary())
break;
++rot;
}
return rot;
}
// Returns a rotation index for corner patches (range [0-3])
template <class T> unsigned char
FarPatchTablesFactory<T>::computeCornerPatchRotation( HbrFace<T> * f ) {
unsigned char rot=0;
for (unsigned char i=0; i<4; ++i) {
if (not f->GetVertex((i+3)%4)->OnBoundary())
break;
++rot;
}
return rot;
}
template <class T>
FarPatchTablesFactory<T>::FarPatchTablesFactory( HbrMesh<T> const * mesh, int nfaces, std::vector<int> const & remapTable ) :
_mesh(mesh),
_remapTable(remapTable),
_nfaces(nfaces)
{
assert(mesh and nfaces>0);
// First pass : identify transition / watertight-critical
for (int i=0; i<nfaces; ++i) {
HbrFace<T> * f = mesh->GetFace(i);
if (f->_adaptiveFlags.isTagged) {
HbrVertex<T> * v = f->Subdivide();
assert(v);
v->_adaptiveFlags.wasTagged=true;
}
int nv = f->GetNumVertices();
for (int j=0; j<nv; ++j) {
if (f->IsCoarse())
f->GetVertex(j)->_adaptiveFlags.wasTagged=true;
HbrHalfedge<T> * e = f->GetEdge(j);
// Flag transition edge that require a triangulated transition
if (f->_adaptiveFlags.isTagged) {
e->_adaptiveFlags.isTriangleHead=true;
// Both half-edges need to be tagged if an opposite exists
if (e->GetOpposite())
e->GetOpposite()->_adaptiveFlags.isTriangleHead=true;
}
HbrFace<T> * left = e->GetLeftFace(),
* right = e->GetRightFace();
if (not (left and right))
continue;
if (left->_adaptiveFlags.isTagged ^ right->_adaptiveFlags.isTagged) {
e->_adaptiveFlags.isTransition = true;
HbrVertex<T> * child = e->Subdivide();
assert( child );
// These edges will require extra rows of CVs to maintain water-tightness
HbrHalfedge<T> * org = child->GetEdge(e->GetOrgVertex()->Subdivide()),
* dst = child->GetEdge(e->GetDestVertex()->Subdivide());
assert( org and dst );
org->_adaptiveFlags.isWatertightCritical=true;
dst->_adaptiveFlags.isWatertightCritical=true;
}
}
}
// Second pass : count boundaries / identify transition constellation
for (int i=0; i<nfaces; ++i) {
HbrFace<T> * f = mesh->GetFace(i);
if (mesh->GetSubdivision()->FaceIsExtraordinary(mesh,f))
continue;
bool isTagged=0, wasTagged=0, isConnected=0, isWatertightCritical=0, isExtraordinary=0;
int triangleHeads=0, boundaryVerts=0;
int nv = f->GetNumVertices();
for (int j=0; j<nv; ++j) {
HbrVertex<T> * v = f->GetVertex(j);
if (v->OnBoundary()) {
boundaryVerts++;
// Boundary vertices with valence higher than 3 aren't Full Boundary
// patches, they are Gregory Boundary patches.
if (v->IsSingular() or v->GetValence()>3)
isExtraordinary=true;
} else if (v->IsExtraordinary())
isExtraordinary=true;
if (f->GetParent() and (not isWatertightCritical))
isWatertightCritical = vertexHasTaggedNeighbors(v);
if (v->_adaptiveFlags.isTagged)
isTagged=1;
if (v->_adaptiveFlags.wasTagged)
wasTagged=1;
// Count the number of triangle heads to find which transition
// pattern to use.
HbrHalfedge<T> * e = f->GetEdge(j);
if (e->_adaptiveFlags.isTriangleHead) {
++triangleHeads;
if (f->GetEdge((j+1)%4)->_adaptiveFlags.isTriangleHead)
isConnected=true;
}
}
f->_adaptiveFlags.bverts=boundaryVerts;
f->_adaptiveFlags.isCritical=isWatertightCritical;
// Regular Boundary Patch
if (wasTagged)
// XXXX manuelk - need to implement end patches
f->_adaptiveFlags.patchType = HbrFace<T>::kEnd;
if (f->_adaptiveFlags.isTagged)
continue;
assert(f->_adaptiveFlags.rots==0 and nv==4);
if (not isTagged and wasTagged) {
if (triangleHeads==0) {
if (not isExtraordinary and boundaryVerts!=1) {
// Full Patches
f->_adaptiveFlags.patchType = HbrFace<T>::kFull;
switch (boundaryVerts) {
case 0 : { // Regular patch
_fullCtr.R_C++;
} break;
case 2 : { // Boundary patch
f->_adaptiveFlags.rots=computeBoundaryPatchRotation(f);
_fullCtr.B_C[0]++;
} break;
case 3 : { // Corner patch
f->_adaptiveFlags.rots=computeCornerPatchRotation(f);
_fullCtr.C_C[0]++;
} break;
default : break;
}
} else {
// Default to Gregory Patch
f->_adaptiveFlags.patchType = HbrFace<T>::kGregory;
switch (boundaryVerts) {
case 0 : { // Regular Gregory patch
_fullCtr.G_C[0]++;
} break;
default : { // Boundary Gregory patch
_fullCtr.G_C[1]++;
} break;
}
}
} else {
// Transition Patch
// Resolve transition constellation : 5 types (see p.5 fig. 7)
switch (triangleHeads) {
case 1 : { for (unsigned char j=0; j<4; ++j) {
if (f->GetEdge(j)->IsTriangleHead())
break;
f->_adaptiveFlags.rots++;
}
f->_adaptiveFlags.transitionType = HbrFace<T>::kTransition0;
} break;
case 2 : { for (unsigned char j=0; j<4; ++j) {
if (isConnected) {
if (f->GetEdge(j)->IsTriangleHead() and
f->GetEdge((j+3)%4)->IsTriangleHead())
break;
} else {
if (f->GetEdge(j)->IsTriangleHead())
break;
}
f->_adaptiveFlags.rots++;
}
if (isConnected)
f->_adaptiveFlags.transitionType = HbrFace<T>::kTransition1;
else
f->_adaptiveFlags.transitionType = HbrFace<T>::kTransition4;
} break;
case 3 : { for (unsigned char j=0; j<4; ++j) {
if (not f->GetEdge(j)->IsTriangleHead())
break;
f->_adaptiveFlags.rots++;
}
f->_adaptiveFlags.transitionType = HbrFace<T>::kTransition2;
} break;
case 4 : f->_adaptiveFlags.transitionType = HbrFace<T>::kTransition3;
break;
default: break;
}
int tidx = f->_adaptiveFlags.transitionType;
assert(tidx>=0);
// Correct rotations for corners & boundaries
if (not isExtraordinary and boundaryVerts!=1) {
switch (boundaryVerts) {
case 0 : { // regular patch
_transitionCtr[tidx].R_C++;
} break;
case 2 : { // boundary patch
unsigned char rot=computeBoundaryPatchRotation(f);
f->_adaptiveFlags.brots=(4-f->_adaptiveFlags.rots+rot)%4;
f->_adaptiveFlags.rots=rot; // override the transition rotation
_transitionCtr[tidx].B_C[f->_adaptiveFlags.brots]++;
} break;
case 3 : { // corner patch
unsigned char rot=computeCornerPatchRotation(f);
f->_adaptiveFlags.brots=(4-f->_adaptiveFlags.rots+rot)%4;
f->_adaptiveFlags.rots=rot; // override the transition rotation
_transitionCtr[tidx].C_C[f->_adaptiveFlags.brots]++;
} break;
default : assert(0); break;
}
} else {
// Use Gregory Patch transition ?
}
}
}
}
}
// Sets the FarTable markers when all the patches of a given level are processed
template <class T> void
FarPatchTablesFactory<T>::setMarkers(int level, FarPatchTables * result, IndexPointers const & fptrs, IndexPointers const * tptrs) {
result->_full._R_IT.SetMarker(level, fptrs.R_P);
result->_full._B_IT.SetMarker(level, fptrs.B_P[0]);
result->_full._C_IT.SetMarker(level, fptrs.C_P[0]);
result->_full._G_IT.SetMarker(level, fptrs.G_P[0]);
result->_full._G_B_IT.SetMarker(level, fptrs.G_P[1]);
for (unsigned char i=0; i<5; ++i) {
result->_transition[i]._R_IT.SetMarker(level, tptrs[i].R_P);
for (unsigned char j=0; j<4; ++j) {
result->_transition[i]._B_IT[j].SetMarker(level, tptrs[i].B_P[j]);
result->_transition[i]._C_IT[j].SetMarker(level, tptrs[i].C_P[j]);
}
}
}
template <class T> FarPatchTables *
FarPatchTablesFactory<T>::Create( int maxlevel, int maxvalence, bool requirePtexCoordinate,
bool requireFVarData ) {
assert(getMesh() and getNumFaces()>0);
FarPatchTables * result = new FarPatchTables(maxlevel, maxvalence);
static const unsigned int remapRegular [16] = {5,6,10,9,4,0,1,2,3,7,11,15,14,13,12,8};
static const unsigned int remapRegularBoundary[12] = {1,2,6,5,0,3,7,11,10,9,8,4};
static const unsigned int remapRegularCorner [ 9] = {1,2,5,4,0,8,7,6,3};
IndexPointers fptrs, tptrs[5];
PtexPointers fptrsPtx, tptrsPtx[5];
FVarPointers fptrsFvd, tptrsFvd[5];
// Allocate all index tables
// Full Patches
result->_full._R_IT.Resize(_fullCtr.R_C*16);
fptrs.R_P = result->_full._R_IT[0];
// Full Boundary Patches
result->_full._B_IT.Resize(_fullCtr.B_C[0]*12);
fptrs.B_P[0] = result->_full._B_IT[0];
// Full Corner Patches
result->_full._C_IT.Resize(_fullCtr.C_C[0]*9);
fptrs.C_P[0] = result->_full._C_IT[0];
// Full Gregory patches
result->_full._G_IT.Resize(_fullCtr.G_C[0]*4);
fptrs.G_P[0] = result->_full._G_IT[0];
// Full Gregory Boundary patches
result->_full._G_B_IT.Resize(_fullCtr.G_C[1]*4);
fptrs.G_P[1] = result->_full._G_B_IT[0];
// Quad-offsets tables (for Gregory patches)
FarPatchTables::QuadOffsetTable quad_G_C0;
quad_G_C0.resize(_fullCtr.G_C[0]*4);
FarPatchTables::QuadOffsetTable quad_G_C1;
quad_G_C1.resize(_fullCtr.G_C[1]*4);
FarPatchTables::QuadOffsetTable::value_type *quad_G_C0_P = &quad_G_C0[0];
FarPatchTables::QuadOffsetTable::value_type *quad_G_C1_P = &quad_G_C1[0];
// Transition Patches
for (int i=0; i<5; ++i) {
result->_transition[i]._R_IT.Resize(_transitionCtr[i].R_C*16);
tptrs[i].R_P = result->_transition[i]._R_IT[0];
for (int j=0; j<4; ++j) {
result->_transition[i]._B_IT[j].Resize(_transitionCtr[i].B_C[j]*12);
tptrs[i].B_P[j] = result->_transition[i]._B_IT[j][0];
result->_transition[i]._C_IT[j].Resize(_transitionCtr[i].C_C[j]*9);
tptrs[i].C_P[j] = result->_transition[i]._C_IT[j][0];
}
}
// Allocate ptex coordinate table if necessary
if (requirePtexCoordinate) {
result->_full._R_PTX.resize(_fullCtr.R_C*2);
fptrsPtx.R_P = &result->_full._R_PTX[0];
result->_full._B_PTX.resize(_fullCtr.B_C[0]*2);
fptrsPtx.B_P[0] = &result->_full._B_PTX[0];
result->_full._C_PTX.resize(_fullCtr.C_C[0]*2);
fptrsPtx.C_P[0] = &result->_full._C_PTX[0];
result->_full._G_PTX.resize(_fullCtr.G_C[0]*2);
fptrsPtx.G_P[0] = &result->_full._G_PTX[0];
result->_full._G_B_PTX.resize(_fullCtr.G_C[1]*2);
fptrsPtx.G_P[1] = &result->_full._G_B_PTX[0];
for (int i=0; i < 5; ++i) {
result->_transition[i]._R_PTX.resize(_transitionCtr[i].R_C*2);
tptrsPtx[i].R_P = &result->_transition[i]._R_PTX[0];
for (int j=0; j < 4; ++j) {
result->_transition[i]._B_PTX[j].resize(_transitionCtr[i].B_C[j]*2);
tptrsPtx[i].B_P[j] = &result->_transition[i]._B_PTX[j][0];
result->_transition[i]._C_PTX[j].resize(_transitionCtr[i].C_C[j]*2);
tptrsPtx[i].C_P[j] = &result->_transition[i]._C_PTX[j][0];
}
}
}
// Allocate face-varying data table if necessary
if (requireFVarData) {
int width = 4*getMesh()->GetTotalFVarWidth();
result->_full._R_FVD.resize(_fullCtr.R_C*width);
fptrsFvd.R_P = &result->_full._R_FVD[0];
result->_full._B_FVD.resize(_fullCtr.B_C[0]*width);
fptrsFvd.B_P[0] = &result->_full._B_FVD[0];
result->_full._C_FVD.resize(_fullCtr.C_C[0]*width);
fptrsFvd.C_P[0] = &result->_full._C_FVD[0];
result->_full._G_FVD.resize(_fullCtr.G_C[0]*width);
fptrsFvd.G_P[0] = &result->_full._G_FVD[0];
result->_full._G_B_FVD.resize(_fullCtr.G_C[1]*width);
fptrsFvd.G_P[1] = &result->_full._G_B_FVD[0];
for (int i=0; i < 5; ++i) {
result->_transition[i]._R_FVD.resize(_transitionCtr[i].R_C*width);
tptrsFvd[i].R_P = &result->_transition[i]._R_FVD[0];
for (int j=0; j < 4; ++j) {
result->_transition[i]._B_FVD[j].resize(_transitionCtr[i].B_C[j]*width);
tptrsFvd[i].B_P[j] = &result->_transition[i]._B_FVD[j][0];
result->_transition[i]._C_FVD[j].resize(_transitionCtr[i].C_C[j]*width);
tptrsFvd[i].C_P[j] = &result->_transition[i]._C_FVD[j][0];
}
}
}
int currentDepth = 0;
int fvarWidth = getMesh()->GetTotalFVarWidth();
// Populate patch index tables with vertex indices
for (int i=0; i<getNumFaces(); ++i) {
HbrFace<T> * f = getMesh()->GetFace(i);
int depth = f->GetDepth();
if (depth!=currentDepth) {
assert(depth==currentDepth+1);
setMarkers(depth, result, fptrs, tptrs);
currentDepth = depth;
}
if (not f->isTransitionPatch() ) {
// Full / End patches
if (f->_adaptiveFlags.patchType==HbrFace<T>::kFull) {
if (not f->_adaptiveFlags.isExtraordinary and f->_adaptiveFlags.bverts!=1) {
switch (f->_adaptiveFlags.bverts) {
case 0 : { // Regular Patch (16 CVs)
getOneRing(f, 16, remapRegular, fptrs.R_P);
fptrs.R_P+=16;
fptrsPtx.R_P = computePtexCoordinate(f, fptrsPtx.R_P, /*isAdaptive=*/true);
fptrsFvd.R_P = computeFVarData(f, fvarWidth, fptrsFvd.R_P, /*isAdaptive=*/true);
} break;
case 2 : { // Boundary Patch (12 CVs)
f->_adaptiveFlags.brots = (f->_adaptiveFlags.rots+1)%4;
getOneRing(f, 12, remapRegularBoundary, fptrs.B_P[0]);
fptrs.B_P[0]+=12;
fptrsPtx.B_P[0] = computePtexCoordinate(f, fptrsPtx.B_P[0], /*isAdaptive=*/true);
fptrsFvd.B_P[0] = computeFVarData(f, fvarWidth, fptrsFvd.B_P[0], /*isAdaptive=*/true);
} break;
case 3 : { // Corner Patch (9 CVs)
f->_adaptiveFlags.brots = (f->_adaptiveFlags.rots+1)%4;
getOneRing(f, 9, remapRegularCorner, fptrs.C_P[0]);
fptrs.C_P[0]+=9;
fptrsPtx.C_P[0] = computePtexCoordinate(f, fptrsPtx.C_P[0], /*isAdaptive=*/true);
fptrsFvd.C_P[0] = computeFVarData(f, fvarWidth, fptrsFvd.C_P[0], /*isAdaptive=*/true);
} break;
default : assert(0);
}
}
} else if (f->_adaptiveFlags.patchType==HbrFace<T>::kGregory) {
if (f->_adaptiveFlags.bverts==0) {
// Gregory Regular Patch (4 CVs + quad-offsets / valence tables)
for (int j=0; j<4; ++j)
fptrs.G_P[0][j] = _remapTable[f->GetVertex(j)->GetID()];
fptrs.G_P[0]+=4;
getQuadOffsets(f, quad_G_C0_P);
quad_G_C0_P += 4;
fptrsPtx.G_P[0] = computePtexCoordinate(f, fptrsPtx.G_P[0], /*isAdaptive=*/true);
fptrsFvd.G_P[0] = computeFVarData(f, fvarWidth, fptrsFvd.G_P[0], /*isAdaptive=*/true);
} else {
// Gregory Boundary Patch (4 CVs + quad-offsets / valence tables)
for (int j=0; j<4; ++j)
fptrs.G_P[1][j] = _remapTable[f->GetVertex(j)->GetID()];
fptrs.G_P[1]+=4;
getQuadOffsets(f, quad_G_C1_P);
quad_G_C1_P += 4;
fptrsPtx.G_P[1] = computePtexCoordinate(f, fptrsPtx.G_P[1], /*isAdaptive=*/true);
fptrsFvd.G_P[1] = computeFVarData(f, fvarWidth, fptrsFvd.G_P[1], /*isAdaptive=*/true);
}
} else {
// XXXX manuelk - end patches here
}
} else {
// Transition patches
int tcase = f->_adaptiveFlags.transitionType;
assert( tcase>=HbrFace<T>::kTransition0 and tcase<=HbrFace<T>::kTransition4 );
if (not f->_adaptiveFlags.isExtraordinary and f->_adaptiveFlags.bverts!=1) {
switch (f->_adaptiveFlags.bverts) {
case 0 : { // Regular Transition Patch (16 CVs)
getOneRing(f, 16, remapRegular, tptrs[tcase].R_P);
tptrs[tcase].R_P+=16;
tptrsPtx[tcase].R_P = computePtexCoordinate(f, tptrsPtx[tcase].R_P, /*isAdaptive=*/true);
tptrsFvd[tcase].R_P = computeFVarData(f, fvarWidth, tptrsFvd[tcase].R_P, /*isAdaptive=*/true);
} break;
case 2 : { // Boundary Transition Patch (12 CVs)
unsigned rot = f->_adaptiveFlags.brots;
getOneRing(f, 12, remapRegularBoundary, tptrs[tcase].B_P[rot]);
tptrs[tcase].B_P[rot]+=12;
tptrsPtx[tcase].B_P[rot] = computePtexCoordinate(f, tptrsPtx[tcase].B_P[rot], /*isAdaptive=*/true);
tptrsFvd[tcase].B_P[rot] = computeFVarData(f, fvarWidth, tptrsFvd[tcase].B_P[rot], /*isAdaptive=*/true);
} break;
case 3 : { // Corner Transition Patch (9 CVs)
unsigned rot = f->_adaptiveFlags.brots;
getOneRing(f, 9, remapRegularCorner, tptrs[tcase].C_P[rot]);
tptrs[tcase].C_P[rot]+=9;
tptrsPtx[tcase].C_P[rot] = computePtexCoordinate(f, tptrsPtx[tcase].C_P[rot], /*isAdaptive=*/true);
tptrsFvd[tcase].C_P[rot] = computeFVarData(f, fvarWidth, tptrsFvd[tcase].C_P[rot], /*isAdaptive=*/true);
} break;
}
} else
// No transition Gregory patches
assert(false);
}
}
setMarkers(currentDepth+1, result, fptrs, tptrs);
// Build Gregory patches vertex valence indices table
if ((_fullCtr.G_C[0] > 0) or (_fullCtr.G_C[1] > 0)) {
// MAX_VALENCE is a property of hardware shaders and needs to be matched in OSD
const int perVertexValenceSize = 2*maxvalence + 1;
const int nverts = getMesh()->GetNumVertices();
FarPatchTables::VertexValenceTable & table = result->_vertexValenceTable;
table.resize(nverts * perVertexValenceSize);
class GatherNeighborsOperator : public HbrVertexOperator<T> {
public:
HbrVertex<T> * center;
FarPatchTables::VertexValenceTable & table;
int offset, valence;
std::vector<int> const & remap;
GatherNeighborsOperator(FarPatchTables::VertexValenceTable & itable, int ioffset, HbrVertex<T> * v, std::vector<int> const & iremap) :
center(v), table(itable), offset(ioffset), valence(0), remap(iremap) { }
// Operator iterates over neighbor vertices of v and accumulates
// pairs of indices the neighbor and diagonal vertices
//
// Regular case
// Boundary case
// o ------- o D3 o
// D0 N0 | |
// | | o ------- o D2 o
// | | D0 N0 | |
// | | | |
// o ------- o ------- o | |
// N1 | V | N3 | |
// | | o ------- o ------- o
// | | N1 V N2
// | |
// o o ------- o
// D1 N2 D2
//
virtual void operator() (HbrVertex<T> &v) {
table[offset++] = remap[v.GetID()];
HbrVertex<T> * diagonal=&v;
HbrHalfedge<T> * e = center->GetEdge(&v);
if ( e ) {
// If v is on a boundary, there may not be a diagonal vertex
diagonal = e->GetNext()->GetDestVertex();
}
//else {
// diagonal = v.GetQEONext( center );
//}
table[offset++] = remap[diagonal->GetID()];
++valence;
}
};
for (int i=0; i<nverts; ++i) {
HbrVertex<T> * v = getMesh()->GetVertex(i);
int outputVertexID = _remapTable[v->GetID()];
int offset = outputVertexID * perVertexValenceSize;
// "offset+1" : the first table entry is the vertex valence, which
// is gathered by the operator (see note below)
GatherNeighborsOperator op( table, offset+1, v, _remapTable );
v->ApplyOperatorSurroundingVertices( op );
// Valence sign bit used to mark boundary vertices
table[offset] = v->OnBoundary() ? -op.valence : op.valence;
// Note : some topologies can cause v to be singular at certain
// levels of adaptive refinement, which prevents us from using
// the GetValence() function. Fortunately, the GatherNeighbors
// operator above just performed a similar traversal, so it is
// very convenient to use it to accumulate the actionable valence.
}
} else {
result->_vertexValenceTable.clear();
}
// Combine quad offset buffers
result->_quadOffsetTable.resize((_fullCtr.G_C[0]+_fullCtr.G_C[1])*4);
std::copy(quad_G_C0.begin(), quad_G_C0.end(), result->_quadOffsetTable.begin());
std::copy(quad_G_C1.begin(), quad_G_C1.end(), result->_quadOffsetTable.begin()+_fullCtr.G_C[0]*4);
return result;
}
// The One Ring vertices to rule them all !
template <class T> void
FarPatchTablesFactory<T>::getOneRing( HbrFace<T> * f, int ringsize, unsigned int const * remap, unsigned int * result) {
assert( f and f->GetNumVertices()==4 and ringsize >=4 );
int idx=0;
for (unsigned char i=0; i<4; ++i)
result[remap[idx++ % ringsize]] = _remapTable[f->GetVertex( (i+f->_adaptiveFlags.rots)%4 )->GetID()];
if (ringsize==16) {
// Regular case
//
// | | | |
// | 4 | 15 | 14 | 13
// ---- o ---- o ---- o ---- o ----
// | | | |
// | 5 | 0 | 3 | 12
// ---- o ---- o ---- o ---- o ----
// | | | |
// | 6 | 1 | 2 | 11
// ---- o ---- o ---- o ---- o ----
// | | | |
// | 7 | 8 | 9 | 10
// ---- o ---- o ---- o ---- o ----
// | | | |
// | | | |
for (int i=0; i<4; ++i) {
int rot = i+f->_adaptiveFlags.rots;
HbrVertex<T> * v0 = f->GetVertex( rot % 4 ),
* v1 = f->GetVertex( (rot+1) % 4 );
HbrHalfedge<T> * e = v0->GetNextEdge( v0->GetNextEdge( v0->GetEdge(v1) ) );
for (int j=0; j<3; ++j) {
e = e->GetNext();
result[remap[idx++ % ringsize]] = _remapTable[e->GetOrgVertex()->GetID()];
}
}
} else if (ringsize==12) {
// Boundary case
//
// 4 0 3 5
// ---- o ---- o ---- o ---- o ----
// | | | |
// | 11 | 1 | 2 | 6
// ---- o ---- o ---- o ---- o ----
// | | | |
// | 10 | 9 | 8 | 7
// ---- o ---- o ---- o ---- o ----
// | | | |
// | | | |
HbrVertex<T> * v[4];
for (int i=0; i<4; ++i)
v[i] = f->GetVertex( (i+f->_adaptiveFlags.rots)%4 );
HbrHalfedge<T> * e;
e = v[0]->GetIncidentEdge()->GetPrev()->GetOpposite()->GetPrev();
result[remap[idx++ % ringsize]] = _remapTable[e->GetOrgVertex()->GetID()];
e = v[1]->GetIncidentEdge();
result[remap[idx++ % ringsize]] = _remapTable[e->GetDestVertex()->GetID()];
e = v[2]->GetNextEdge( v[2]->GetEdge(v[1]) );
for (int i=0; i<3; ++i) {
e = e->GetNext();
result[remap[idx++ % ringsize]] = _remapTable[e->GetOrgVertex()->GetID()];
}
e = v[3]->GetNextEdge( v[3]->GetEdge(v[2]) );
for (int i=0; i<3; ++i) {
e = e->GetNext();
result[remap[idx++ % ringsize]] = _remapTable[e->GetOrgVertex()->GetID()];
}
} else if (ringsize==9) {
// Corner case
//
// 0 1 4
// o ---- o ---- o ----
// | | |
// | 3 | 2 | 5
// o ---- o ---- o ----
// | | |
// | 8 | 7 | 6
// o ---- o ---- o ----
// | | |
// | | |
HbrVertex<T> * v0 = f->GetVertex( (0+f->_adaptiveFlags.rots)%4 ),
* v2 = f->GetVertex( (2+f->_adaptiveFlags.rots)%4 ),
* v3 = f->GetVertex( (3+f->_adaptiveFlags.rots)%4 );
HbrHalfedge<T> * e;
e = v0->GetIncidentEdge()->GetPrev()->GetOpposite()->GetPrev();
result[remap[idx++ % ringsize]] = _remapTable[e->GetOrgVertex()->GetID()];
e = v2->GetIncidentEdge();
result[remap[idx++ % ringsize]] = _remapTable[e->GetDestVertex()->GetID()];
e = v3->GetNextEdge( v3->GetEdge(v2) );
for (int i=0; i<3; ++i) {
e = e->GetNext();
result[remap[idx++ % ringsize]] = _remapTable[e->GetOrgVertex()->GetID()];
}
}
assert(idx==ringsize);
}
// Populate the quad-offsets table used by Gregory patches
template <class T> void
FarPatchTablesFactory<T>::getQuadOffsets( HbrFace<T> * f, unsigned int * result ) {
assert( f and f->GetNumVertices()==4 );
// Builds a table of value pairs for each vertex of the patch.
//
// o
// N0 |
// |
// |
// o ------ o ------ o
// N1 V | .... M3
// | .......
// | .......
// o .......
// N2
//
// [...] [N2 - N3] [...]
//
// Each value pair is composed of 2 index values in range [0-4[ pointing
// to the 2 neighbor vertices to the vertex that belong to the Gregory patch.
// Neighbor ordering is valence counter-clockwise and must match the winding
// used to build the vertexValenceTable.
//
class GatherOffsetsOperator : public HbrVertexOperator<T> {
public:
HbrVertex<T> ** verts; int offsets[2]; int index; int count;
GatherOffsetsOperator(HbrVertex<T> ** iverts) : verts(iverts) { }
void reset() {
index=count=offsets[0]=offsets[1]=0;
}
virtual void operator() (HbrVertex<T> &v) {
// Resolve which 2 neighbor vertices of v belong to the Gregory patch
for (unsigned char i=0; i<4; ++i)
if (&v==verts[i]) {
assert(count<3);
offsets[count++]=index;
break;
}
++index;
}
};
// 4 central CVs of the Gregory patch
HbrVertex<T> * fvs[4] = { f->GetVertex(0),
f->GetVertex(1),
f->GetVertex(2),
f->GetVertex(3) };
// Hbr vertex operator that iterates over neighbor vertices
GatherOffsetsOperator op( fvs );
for (unsigned char i=0; i<4; ++i) {
op.reset();
fvs[i]->ApplyOperatorSurroundingVertices( op );
if (op.offsets[1] - op.offsets[0] != 1)
std::swap(op.offsets[0], op.offsets[1]);
// Pack the 2 indices in 16 bits
result[i] = (op.offsets[0] | (op.offsets[1] << 8));
}
}
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* FAR_VERTEX_EDIT_TABLES_H */