mirror of
https://github.com/PixarAnimationStudios/OpenSubdiv
synced 2024-11-30 15:20:07 +00:00
1542 lines
57 KiB
C++
Executable File
1542 lines
57 KiB
C++
Executable File
//
|
|
// Copyright 2013 Pixar
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "Apache License")
|
|
// with the following modification; you may not use this file except in
|
|
// compliance with the Apache License and the following modification to it:
|
|
// Section 6. Trademarks. is deleted and replaced with:
|
|
//
|
|
// 6. Trademarks. This License does not grant permission to use the trade
|
|
// names, trademarks, service marks, or product names of the Licensor
|
|
// and its affiliates, except as required to comply with Section 4(c) of
|
|
// the License and to reproduce the content of the NOTICE file.
|
|
//
|
|
// You may obtain a copy of the Apache License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the Apache License with the above modification is
|
|
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
// KIND, either express or implied. See the Apache License for the specific
|
|
// language governing permissions and limitations under the Apache License.
|
|
//
|
|
|
|
#ifndef FAR_PATCH_TABLES_FACTORY_H
|
|
#define FAR_PATCH_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 {
|
|
|
|
public:
|
|
typedef std::vector<FarMesh<T> const *> FarMeshVector;
|
|
typedef std::vector<FarPatchTables::PatchArrayVector> MultiPatchArrayVector;
|
|
|
|
/// \brief Splices patch tables from multiple meshes.
|
|
/// if non-null multPatchArrays is given, it returns subsets of patcharrays such that
|
|
/// corresponding input meshes are separately expressed.
|
|
/// Client code is responsible for deallocation.
|
|
static FarPatchTables *Splice(FarMeshVector const &meshes,
|
|
MultiPatchArrayVector *multiPatchArrays);
|
|
|
|
protected:
|
|
template <class X, class Y> friend class FarMeshFactory;
|
|
|
|
/// \brief Factory constructor for feature-adaptive meshes
|
|
///
|
|
/// @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 );
|
|
|
|
/// \brief Returns a feature-adaptive FarPatchTables instance
|
|
///
|
|
/// @param maxlevel Highest level of refinement processed
|
|
///
|
|
/// @param maxvalence Maximum vertex valence in the mesh
|
|
///
|
|
/// @param numPtexFaces Number of ptex faces
|
|
///
|
|
/// @param fvarWidth The width of the interleaved face-varying data
|
|
///
|
|
/// @return A new instance of FarPatchTables
|
|
///
|
|
FarPatchTables * Create( int maxlevel, int maxvalence, int numPtexFaces=0, int fvarWidth=0 );
|
|
|
|
|
|
typedef std::vector<std::vector< HbrFace<T> *> > FacesList;
|
|
|
|
/// \brief Factory constructor for uniform meshes
|
|
///
|
|
/// @param mesh Hbr mesh to generate tables for
|
|
///
|
|
/// @param flist Vectors of pointers to HbrFace<T> for each level
|
|
/// of subdivision
|
|
///
|
|
/// @param remapTable Vertex remapping table generated by FarMeshFactory
|
|
///
|
|
/// @param firstLevel First level of subdivision to use when building the
|
|
/// PatchArrayVector (default -1 means only generate
|
|
/// a single patch array for the highest level of
|
|
/// subdivision)
|
|
///
|
|
/// @param patchType The type of patch to create: QUADS or TRIANGLES
|
|
///
|
|
/// @param numPtexFaces Number of ptex faces
|
|
///
|
|
/// @param fvarWidth The width of the interleaved face-varying data
|
|
///
|
|
/// @return A new instance of FarPatchTables
|
|
///
|
|
static FarPatchTables * Create( HbrMesh<T> const * mesh,
|
|
FacesList const & flist,
|
|
std::vector<int> const & remapTable,
|
|
int firstLevel=-1,
|
|
FarPatchTables::Type patchType=FarPatchTables::QUADS,
|
|
int numPtexFaces=0,
|
|
int fvarWidth=0 );
|
|
|
|
private:
|
|
|
|
typedef FarPatchTables::Descriptor Descriptor;
|
|
|
|
// 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 the face-varying data buffer 'coord' for the given face and
|
|
// returns a pointer to the next entry in the table
|
|
static float * computeFVarData(HbrFace<T> const *f, const int width, float *coord, bool isAdaptive);
|
|
|
|
// Populates the patch parametrization descriptor 'coord' for the given face
|
|
// returns a pointer to the next descriptor
|
|
static FarPatchParam * computePatchParam(HbrFace<T> const *f, FarPatchParam *coord);
|
|
|
|
// Populates an array of indices with the "one-ring" vertices for the given face
|
|
unsigned int * getOneRing(HbrFace<T> const * f, int ringsize, unsigned int const * remap, unsigned int * result) const;
|
|
|
|
// Populates the Gregory patch quad offsets table
|
|
static void getQuadOffsets(HbrFace<T> const * f, unsigned int * result);
|
|
|
|
// Iterates through the faces of an HbrMesh and tags the _adaptiveFlags on faces and vertices
|
|
void tagAdaptivePatches( HbrMesh<T> const * mesh, int nfaces );
|
|
|
|
// 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; }
|
|
|
|
// The number of patch arrays in the mesh
|
|
int getNumPatchArrays() const;
|
|
|
|
// The number of patches in the mesh
|
|
static int getNumPatches( FarPatchTables::PatchArrayVector const & parrays );
|
|
|
|
// Reserves tables based on the contents of the PatchArrayVector
|
|
static void allocateTables( FarPatchTables * tables, int nlevels, int fvarwidth );
|
|
|
|
// A convenience container for the different types of feature adaptive patches
|
|
template<class TYPE> struct PatchTypes {
|
|
|
|
static const int NUM_TRANSITIONS=6,
|
|
NUM_ROTATIONS=4;
|
|
|
|
TYPE R[NUM_TRANSITIONS], // regular patch
|
|
B[NUM_TRANSITIONS][NUM_ROTATIONS], // boundary patch (4 rotations)
|
|
C[NUM_TRANSITIONS][NUM_ROTATIONS], // corner patch (4 rotations)
|
|
G, // gregory patch
|
|
GB; // gregory boundary patch
|
|
|
|
PatchTypes() { memset(this, 0, sizeof(PatchTypes<TYPE>)); }
|
|
|
|
// Returns the number of patches based on the patch type in the descriptor
|
|
TYPE & getValue( FarPatchTables::Descriptor desc );
|
|
|
|
// Counts the number of arrays required to store each type of patch used
|
|
// in the primitive
|
|
int getNumPatchArrays() const;
|
|
};
|
|
|
|
typedef PatchTypes<unsigned int*> CVPointers;
|
|
typedef PatchTypes<FarPatchParam *> ParamPointers;
|
|
typedef PatchTypes<float *> FVarPointers;
|
|
typedef PatchTypes<int> Counter;
|
|
|
|
// Creates a PatchArray and appends it to a vector and keeps track of both
|
|
// vertex and patch offsets
|
|
void pushPatchArray( FarPatchTables::Descriptor desc,
|
|
FarPatchTables::PatchArrayVector & parray,
|
|
int npatches, int * voffset, int * poffset, int * qoffset );
|
|
|
|
Counter _patchCtr; // counters for full and transition patches
|
|
|
|
HbrMesh<T> const * _mesh;
|
|
|
|
// Reference to the vertex remapping table generated by FarMeshFactory
|
|
std::vector<int> const &_remapTable;
|
|
|
|
int _nfaces;
|
|
};
|
|
|
|
template <class T>
|
|
template <class TYPE> TYPE &
|
|
FarPatchTablesFactory<T>::PatchTypes<TYPE>::getValue( FarPatchTables::Descriptor desc ) {
|
|
|
|
switch (desc.GetType()) {
|
|
case FarPatchTables::REGULAR : return R[desc.GetPattern()];
|
|
case FarPatchTables::BOUNDARY : return B[desc.GetPattern()][desc.GetRotation()];
|
|
case FarPatchTables::CORNER : return C[desc.GetPattern()][desc.GetRotation()];
|
|
case FarPatchTables::GREGORY : return G;
|
|
case FarPatchTables::GREGORY_BOUNDARY : return GB;
|
|
default : assert(0);
|
|
}
|
|
// can't be reached (suppress compiler warning)
|
|
return R[0];
|
|
}
|
|
|
|
template <class T>
|
|
template <class TYPE> int
|
|
FarPatchTablesFactory<T>::PatchTypes<TYPE>::getNumPatchArrays() const {
|
|
|
|
int result=0;
|
|
|
|
for (int i=0; i<6; ++i) {
|
|
|
|
if (R[i]) ++result;
|
|
|
|
for (int j=0; j<4; ++j) {
|
|
if (B[i][j]) ++result;
|
|
if (C[i][j]) ++result;
|
|
|
|
}
|
|
}
|
|
|
|
if (G) ++result;
|
|
if (GB) ++result;
|
|
|
|
return result;
|
|
}
|
|
|
|
// True if the surrounding faces are "tagged" (unsupported feature : watertight
|
|
// critical patches)
|
|
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;
|
|
}
|
|
|
|
// Reserves tables based on the contents of the PatchArrayVector
|
|
template <class T> void
|
|
FarPatchTablesFactory<T>::allocateTables( FarPatchTables * tables, int nlevels, int fvarwidth ) {
|
|
|
|
int nverts = tables->GetNumControlVertices(),
|
|
npatches = getNumPatches(tables->GetPatchArrayVector());
|
|
|
|
if (nverts==0 or npatches==0)
|
|
return;
|
|
|
|
tables->_patches.resize( nverts );
|
|
|
|
tables->_paramTable.resize( npatches );
|
|
|
|
if (fvarwidth>0) {
|
|
FarPatchTables::PatchArrayVector const & parrays = tables->GetPatchArrayVector();
|
|
int nfvarverts = 0;
|
|
for (int i=0; i<(int)parrays.size(); ++i) {
|
|
nfvarverts += parrays[i].GetNumPatches() *
|
|
(parrays[i].GetDescriptor().GetType() == FarPatchTables::TRIANGLES ? 3 : 4);
|
|
}
|
|
|
|
tables->_fvarData._data.resize( nfvarverts * fvarwidth );
|
|
|
|
if (nlevels >1) {
|
|
tables->_fvarData._offsets.resize( nlevels );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Uniform mesh factory (static function because it requires no cached state)
|
|
template <class T> FarPatchTables *
|
|
FarPatchTablesFactory<T>::Create( HbrMesh<T> const * mesh, FacesList const & flist, std::vector<int> const & remapTable, int firstLevel, FarPatchTables::Type patchType, int numPtexFaces, int fvarwidth ) {
|
|
|
|
assert(patchType == FarPatchTables::QUADS || patchType == FarPatchTables::TRIANGLES);
|
|
|
|
if (flist.size()<2)
|
|
return 0;
|
|
|
|
FarPatchTables * result = new FarPatchTables(0);
|
|
|
|
bool isLoop = FarMeshFactory<T,T>::isLoop(mesh);
|
|
if (isLoop)
|
|
patchType = FarPatchTables::TRIANGLES;
|
|
|
|
bool triangulateQuads = !isLoop && patchType == FarPatchTables::TRIANGLES;
|
|
|
|
int nverts = patchType == FarPatchTables::TRIANGLES ? 3 : 4;
|
|
|
|
int firstArray = firstLevel > -1 ? firstLevel : (int)flist.size()-1,
|
|
nlevels = (int)flist.size()-firstArray;
|
|
|
|
// Populate the patch array descriptors
|
|
|
|
FarPatchTables::PatchArrayVector & parray = result->_patchArrays;
|
|
parray.reserve( (int)flist.size() - firstArray );
|
|
|
|
Descriptor desc( patchType, FarPatchTables::NON_TRANSITION, 0 );
|
|
|
|
for (int i=1, poffset=0, voffset=0; i<(int)flist.size(); ++i) {
|
|
|
|
int npatches = (int)flist[i].size();
|
|
if (triangulateQuads)
|
|
npatches *= 2;
|
|
|
|
if (i>=firstArray) {
|
|
parray.push_back( FarPatchTables::PatchArray(desc, voffset, poffset, npatches, 0 ) );
|
|
|
|
voffset += npatches * nverts;
|
|
poffset += npatches;
|
|
}
|
|
}
|
|
|
|
result->_fvarData._fvarWidth = fvarwidth;
|
|
result->_numPtexFaces = numPtexFaces;
|
|
|
|
// Populate the patch / param / fvar tables
|
|
|
|
allocateTables( result, nlevels, fvarwidth );
|
|
|
|
unsigned int * iptr = &result->_patches[0];
|
|
FarPatchParam * pptr = &result->_paramTable[0];
|
|
float * fptr = fvarwidth>0 ? &result->_fvarData._data[0] : 0;
|
|
|
|
for (int level=firstArray, fvarOffset=0; level<(int)flist.size(); ++level) {
|
|
|
|
for (int i=0; i<(int)flist[level].size(); ++i) {
|
|
HbrFace<T> * f = flist[level][i];
|
|
assert( f and (f->GetNumVertices() == (isLoop ? 3 : 4)));
|
|
|
|
for (int j=0; j<f->GetNumVertices(); ++j) {
|
|
*iptr++ = remapTable[f->GetVertex(j)->GetID()];
|
|
}
|
|
|
|
pptr = computePatchParam(f, pptr);
|
|
|
|
if (fvarwidth>0)
|
|
fptr = computeFVarData(f, fvarwidth, fptr, /*isAdaptive=*/false);
|
|
|
|
if (triangulateQuads) {
|
|
// Triangulate the quadrilateral: {v0,v1,v2,v3} -> {v0,v1,v2},{v3,v0,v2}.
|
|
*iptr = *(iptr - 4); // copy v0 index
|
|
++iptr;
|
|
*iptr = *(iptr - 3); // copy v2 index
|
|
++iptr;
|
|
|
|
*pptr = *(pptr - 1); // copy first patch param
|
|
++pptr;
|
|
|
|
for (int j = 0; j < fvarwidth; ++j, ++fptr) {
|
|
*fptr = *(fptr - 4 * fvarwidth); // copy v0 fvar data
|
|
}
|
|
for (int j = 0; j < fvarwidth; ++j, ++fptr) {
|
|
*fptr = *(fptr - 3 * fvarwidth); // copy v2 fvar data
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fvarwidth>0 and (not result->_fvarData._offsets.empty())) {
|
|
result->_fvarData._offsets[level-firstArray] = (fvarOffset+=(int)flist[level].size()*nverts*fvarwidth);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// PatchTables Factory
|
|
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 and (not f->IsHole())) {
|
|
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;
|
|
|
|
// a tagged edge w/ no children is inside a hole
|
|
if (e->HasChild() and (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
|
|
// Note : vertices inside holes have no children
|
|
if (e->GetOrgVertex()->HasChild()) {
|
|
HbrHalfedge<T> * org = child->GetEdge(e->GetOrgVertex()->Subdivide());
|
|
if (org)
|
|
org->_adaptiveFlags.isWatertightCritical=true;
|
|
}
|
|
|
|
if (e->GetDestVertex()->HasChild()) {
|
|
HbrHalfedge<T> * dst = child->GetEdge(e->GetDestVertex()->Subdivide());
|
|
if (dst)
|
|
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;
|
|
|
|
if (f->IsHole())
|
|
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
|
|
_patchCtr.R[FarPatchTables::NON_TRANSITION]++;
|
|
} break;
|
|
|
|
case 2 : { // Boundary patch
|
|
f->_adaptiveFlags.rots=computeBoundaryPatchRotation(f);
|
|
_patchCtr.B[FarPatchTables::NON_TRANSITION][0]++;
|
|
} break;
|
|
|
|
case 3 : { // Corner patch
|
|
f->_adaptiveFlags.rots=computeCornerPatchRotation(f);
|
|
_patchCtr.C[FarPatchTables::NON_TRANSITION][0]++;
|
|
} break;
|
|
|
|
default : break;
|
|
}
|
|
} else {
|
|
|
|
// Default to Gregory Patch
|
|
f->_adaptiveFlags.patchType = HbrFace<T>::kGregory;
|
|
|
|
switch (boundaryVerts) {
|
|
|
|
case 0 : { // Regular Gregory patch
|
|
_patchCtr.G++;
|
|
} break;
|
|
|
|
|
|
default : { // Boundary Gregory patch
|
|
_patchCtr.GB++;
|
|
} 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 pattern = f->_adaptiveFlags.transitionType;
|
|
assert(pattern>=0);
|
|
|
|
// Correct rotations for corners & boundaries
|
|
if (not isExtraordinary and boundaryVerts!=1) {
|
|
|
|
switch (boundaryVerts) {
|
|
|
|
case 0 : { // regular patch
|
|
_patchCtr.R[pattern+1]++;
|
|
} 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
|
|
|
|
_patchCtr.B[pattern+1][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
|
|
|
|
_patchCtr.C[pattern+1][f->_adaptiveFlags.brots]++;
|
|
} break;
|
|
|
|
default : assert(0); break;
|
|
}
|
|
} else {
|
|
// Use Gregory Patch transition ?
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
template <class T> int
|
|
FarPatchTablesFactory<T>::getNumPatchArrays() const {
|
|
|
|
return _patchCtr.getNumPatchArrays();
|
|
}
|
|
|
|
template <class T> int
|
|
FarPatchTablesFactory<T>::getNumPatches( FarPatchTables::PatchArrayVector const & parrays ) {
|
|
|
|
int result=0;
|
|
for (int i=0; i<(int)parrays.size(); ++i) {
|
|
result += parrays[i].GetNumPatches();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
template <class T> void
|
|
FarPatchTablesFactory<T>::pushPatchArray( FarPatchTables::Descriptor desc,
|
|
FarPatchTables::PatchArrayVector & parray,
|
|
int npatches, int * voffset, int * poffset, int * qoffset ) {
|
|
|
|
if (npatches>0) {
|
|
parray.push_back( FarPatchTables::PatchArray(desc, *voffset, *poffset, npatches, *qoffset) );
|
|
|
|
*voffset += npatches * desc.GetNumControlVertices();
|
|
*poffset += npatches;
|
|
*qoffset += (desc.GetType() == FarPatchTables::GREGORY) ? npatches * desc.GetNumControlVertices() : 0;
|
|
}
|
|
}
|
|
|
|
// Uniform mesh factory
|
|
template <class T> FarPatchTables *
|
|
FarPatchTablesFactory<T>::Create( int maxlevel, int maxvalence, int numPtexFaces, int fvarwidth ) {
|
|
|
|
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};
|
|
|
|
assert(getMesh() and getNumFaces()>0);
|
|
|
|
FarPatchTables * result = new FarPatchTables(maxvalence);
|
|
|
|
// Populate the patch array descriptors
|
|
FarPatchTables::PatchArrayVector & parray = result->_patchArrays;
|
|
parray.reserve( getNumPatchArrays() );
|
|
|
|
int voffset=0, poffset=0, qoffset=0;
|
|
|
|
|
|
for (Descriptor::iterator it=Descriptor::begin(Descriptor::FEATURE_ADAPTIVE_CATMARK);
|
|
it!=Descriptor::end(); ++it) {
|
|
|
|
pushPatchArray( *it, parray, _patchCtr.getValue(*it), &voffset, &poffset, &qoffset );
|
|
}
|
|
|
|
result->_fvarData._fvarWidth = fvarwidth;
|
|
result->_numPtexFaces = numPtexFaces;
|
|
|
|
// Allocate various tables
|
|
allocateTables( result, 0, fvarwidth );
|
|
|
|
if ((_patchCtr.G > 0) or (_patchCtr.GB > 0)) { // Quad-offsets tables (for Gregory patches)
|
|
result->_quadOffsetTable.resize( _patchCtr.G*4 + _patchCtr.GB*4 );
|
|
}
|
|
|
|
// Setup convenience pointers at the beginning of each patch array for each
|
|
// table (patches, ptex, fvar)
|
|
CVPointers iptrs;
|
|
ParamPointers pptrs;
|
|
FVarPointers fptrs;
|
|
|
|
for (Descriptor::iterator it=Descriptor::begin(Descriptor::FEATURE_ADAPTIVE_CATMARK);
|
|
it!=Descriptor::end(); ++it) {
|
|
|
|
FarPatchTables::PatchArray * pa = result->findPatchArray(*it);
|
|
|
|
if (not pa)
|
|
continue;
|
|
|
|
iptrs.getValue( *it ) = &result->_patches[pa->GetVertIndex()];
|
|
pptrs.getValue( *it ) = &result->_paramTable[pa->GetPatchIndex()];
|
|
|
|
if (fvarwidth>0)
|
|
fptrs.getValue( *it ) = &result->_fvarData._data[pa->GetPatchIndex() * 4 * fvarwidth];
|
|
}
|
|
|
|
FarPatchTables::QuadOffsetTable::value_type *quad_G_C0_P = _patchCtr.G>0 ? &result->_quadOffsetTable[0] : 0;
|
|
FarPatchTables::QuadOffsetTable::value_type *quad_G_C1_P = _patchCtr.GB>0 ? &result->_quadOffsetTable[_patchCtr.G*4] : 0;
|
|
|
|
// Populate patch index tables with vertex indices
|
|
for (int i=0; i<getNumFaces(); ++i) {
|
|
|
|
HbrFace<T> * f = getMesh()->GetFace(i);
|
|
|
|
if (not f->isTransitionPatch() ) {
|
|
|
|
// Full / End patches
|
|
|
|
if (f->_adaptiveFlags.patchType==HbrFace<T>::kFull) {
|
|
if (not f->_adaptiveFlags.isExtraordinary and f->_adaptiveFlags.bverts!=1) {
|
|
|
|
int pattern = FarPatchTables::NON_TRANSITION,
|
|
rot = 0;
|
|
|
|
switch (f->_adaptiveFlags.bverts) {
|
|
case 0 : { // Regular Patch (16 CVs)
|
|
iptrs.R[pattern] = getOneRing(f, 16, remapRegular, iptrs.R[0]);
|
|
pptrs.R[pattern] = computePatchParam(f, pptrs.R[0]);
|
|
fptrs.R[pattern] = computeFVarData(f, fvarwidth, fptrs.R[0], /*isAdaptive=*/true);
|
|
} break;
|
|
|
|
case 2 : { // Boundary Patch (12 CVs)
|
|
f->_adaptiveFlags.brots = (f->_adaptiveFlags.rots+1)%4;
|
|
iptrs.B[pattern][rot] = getOneRing(f, 12, remapRegularBoundary, iptrs.B[0][0]);
|
|
pptrs.B[pattern][rot] = computePatchParam(f, pptrs.B[0][0]);
|
|
fptrs.B[pattern][rot] = computeFVarData(f, fvarwidth, fptrs.B[0][0], /*isAdaptive=*/true);
|
|
} break;
|
|
|
|
case 3 : { // Corner Patch (9 CVs)
|
|
f->_adaptiveFlags.brots = (f->_adaptiveFlags.rots+1)%4;
|
|
iptrs.C[pattern][rot] = getOneRing(f, 9, remapRegularCorner, iptrs.C[0][0]);
|
|
pptrs.C[pattern][rot] = computePatchParam(f, pptrs.C[0][0]);
|
|
fptrs.C[pattern][rot] = computeFVarData(f, fvarwidth, fptrs.C[0][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)
|
|
iptrs.G[j] = _remapTable[f->GetVertex(j)->GetID()];
|
|
iptrs.G+=4;
|
|
getQuadOffsets(f, quad_G_C0_P);
|
|
quad_G_C0_P += 4;
|
|
pptrs.G = computePatchParam(f, pptrs.G);
|
|
fptrs.G = computeFVarData(f, fvarwidth, fptrs.G, /*isAdaptive=*/true);
|
|
} else {
|
|
|
|
// Gregory Boundary Patch (4 CVs + quad-offsets / valence tables)
|
|
for (int j=0; j<4; ++j)
|
|
iptrs.GB[j] = _remapTable[f->GetVertex(j)->GetID()];
|
|
iptrs.GB+=4;
|
|
getQuadOffsets(f, quad_G_C1_P);
|
|
quad_G_C1_P += 4;
|
|
pptrs.GB = computePatchParam(f, pptrs.GB);
|
|
fptrs.GB = computeFVarData(f, fvarwidth, fptrs.GB, /*isAdaptive=*/true);
|
|
}
|
|
} else {
|
|
// XXXX manuelk - end patches here
|
|
}
|
|
} else {
|
|
|
|
// Transition patches
|
|
|
|
int pattern = f->_adaptiveFlags.transitionType;
|
|
assert( pattern>=HbrFace<T>::kTransition0 and pattern<=HbrFace<T>::kTransition4 );
|
|
++pattern; // TransitionPattern begin with NON_TRANSITION
|
|
|
|
if (not f->_adaptiveFlags.isExtraordinary and f->_adaptiveFlags.bverts!=1) {
|
|
|
|
switch (f->_adaptiveFlags.bverts) {
|
|
case 0 : { // Regular Transition Patch (16 CVs)
|
|
iptrs.R[pattern] = getOneRing(f, 16, remapRegular, iptrs.R[pattern]);
|
|
pptrs.R[pattern] = computePatchParam(f, pptrs.R[pattern]);
|
|
fptrs.R[pattern] = computeFVarData(f, fvarwidth, fptrs.R[pattern], /*isAdaptive=*/true);
|
|
} break;
|
|
|
|
case 2 : { // Boundary Transition Patch (12 CVs)
|
|
unsigned rot = f->_adaptiveFlags.brots;
|
|
iptrs.B[pattern][rot] = getOneRing(f, 12, remapRegularBoundary, iptrs.B[pattern][rot]);
|
|
pptrs.B[pattern][rot] = computePatchParam(f, pptrs.B[pattern][rot]);
|
|
fptrs.B[pattern][rot] = computeFVarData(f, fvarwidth, fptrs.B[pattern][rot], /*isAdaptive=*/true);
|
|
} break;
|
|
|
|
case 3 : { // Corner Transition Patch (9 CVs)
|
|
unsigned rot = f->_adaptiveFlags.brots;
|
|
iptrs.C[pattern][rot] = getOneRing(f, 9, remapRegularCorner, iptrs.C[pattern][rot]);
|
|
pptrs.C[pattern][rot] = computePatchParam(f, pptrs.C[pattern][rot]);
|
|
fptrs.C[pattern][rot] = computeFVarData(f, fvarwidth, fptrs.C[pattern][rot], /*isAdaptive=*/true);
|
|
} break;
|
|
}
|
|
} else
|
|
// No transition Gregory patches
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
// Build Gregory patches vertex valence indices table
|
|
if ((_patchCtr.G > 0) or (_patchCtr.GB > 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) { }
|
|
|
|
~GatherNeighborsOperator() { }
|
|
|
|
// 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;
|
|
|
|
// feature adaptive refinement can generate un-connected face-vertices
|
|
// that have a valence of 0
|
|
if (not v->IsConnected()) {
|
|
//assert( v->GetParentFace() );
|
|
table[offset] = 0;
|
|
continue;
|
|
}
|
|
|
|
// "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();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// The One Ring vertices to rule them all !
|
|
template <class T> unsigned int *
|
|
FarPatchTablesFactory<T>::getOneRing(HbrFace<T> const * f,
|
|
int ringsize, unsigned int const * remap, unsigned int * result) const {
|
|
|
|
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()];
|
|
}
|
|
}
|
|
|
|
result += 16;
|
|
|
|
} 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()];
|
|
}
|
|
|
|
result += 12;
|
|
|
|
} 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()];
|
|
}
|
|
|
|
result += 9;
|
|
|
|
}
|
|
assert(idx==ringsize);
|
|
return result;
|
|
}
|
|
|
|
// Populate the quad-offsets table used by Gregory patches
|
|
template <class T> void
|
|
FarPatchTablesFactory<T>::getQuadOffsets(HbrFace<T> const * f, unsigned int * result) {
|
|
|
|
assert(result and 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) { }
|
|
|
|
~GatherOffsetsOperator() { }
|
|
|
|
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));
|
|
}
|
|
}
|
|
|
|
// Computes per-face or per-patch local ptex texture coordinates.
|
|
template <class T> FarPatchParam *
|
|
FarPatchTablesFactory<T>::computePatchParam(HbrFace<T> const * f, FarPatchParam *coord) {
|
|
|
|
short u,v;
|
|
unsigned short ofs = 1;
|
|
unsigned char depth;
|
|
bool nonquad = false;
|
|
|
|
if (coord == NULL) return NULL;
|
|
|
|
// save the rotation state of the coarse face
|
|
unsigned char rots = f->_adaptiveFlags.rots;
|
|
|
|
// track upwards towards coarse parent face, accumulating u,v indices
|
|
HbrFace<T> const * p = f->GetParent();
|
|
for ( u=v=depth=0; p!=NULL; depth++ ) {
|
|
|
|
int nverts = p->GetNumVertices();
|
|
if ( nverts != 4 ) { // non-quad coarse face : stop accumulating offsets
|
|
nonquad = true; // set non-quad bit
|
|
break;
|
|
}
|
|
|
|
for (unsigned char i=0; i<nverts; ++i) {
|
|
if ( p->GetChild( i )==f ) {
|
|
switch ( i ) {
|
|
case 0 : break;
|
|
case 1 : { u+=ofs; } break;
|
|
case 2 : { u+=ofs; v+=ofs; } break;
|
|
case 3 : { v+=ofs; } break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
ofs = ofs << 1;
|
|
f = p;
|
|
p = f->GetParent();
|
|
}
|
|
|
|
coord->Set( f->GetPtexIndex(), u, v, rots, depth, nonquad );
|
|
|
|
return ++coord;
|
|
}
|
|
|
|
// Populates the face-varying data buffer 'coord' for the given face
|
|
template <class T> float *
|
|
FarPatchTablesFactory<T>::computeFVarData(
|
|
HbrFace<T> const *f, const int width, float *coord, bool isAdaptive) {
|
|
|
|
if (coord == NULL) return NULL;
|
|
|
|
if (isAdaptive) {
|
|
|
|
int rots = f->_adaptiveFlags.rots;
|
|
int nverts = f->GetNumVertices();
|
|
assert(nverts==4);
|
|
|
|
for ( int j=0; j < nverts; ++j ) {
|
|
|
|
HbrVertex<T> *v = f->GetVertex((j+rots)%4);
|
|
float *fvdata = v->GetFVarData(f).GetData(0);
|
|
|
|
for ( int k=0; k<width; ++k ) {
|
|
(*coord++) = fvdata[k];
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
// for each face vertex copy face-varying data into coord pointer
|
|
int nverts = f->GetNumVertices();
|
|
for ( int j=0; j < nverts; ++j ) {
|
|
|
|
HbrVertex<T> *v = f->GetVertex(j);
|
|
float *fvdata = v->GetFVarData(f).GetData(0);
|
|
|
|
for ( int k=0; k<width; ++k ) {
|
|
(*coord++) = fvdata[k];
|
|
}
|
|
}
|
|
}
|
|
|
|
// pass back pointer to next destination
|
|
return coord;
|
|
}
|
|
|
|
// splicing functions
|
|
template <typename V, typename IT> static IT
|
|
copyWithOffset(IT dst_iterator, V const &src, int start, int count, int offset) {
|
|
return std::transform(src.begin()+start, src.begin()+start+count, dst_iterator,
|
|
std::bind2nd(std::plus<typename V::value_type>(), offset));
|
|
}
|
|
|
|
template <typename V, typename IT> static IT
|
|
copyWithOffsetVertexValence(IT dst_iterator, V const &src, int srcMaxValence, int dstMaxValence, int offset) {
|
|
for (typename V::const_iterator it = src.begin(); it != src.end(); ) {
|
|
int valence = *it++;
|
|
*dst_iterator++ = valence;
|
|
valence = abs(valence);
|
|
for (int i = 0; i < 2*dstMaxValence; ++i) {
|
|
if (i < 2*srcMaxValence) {
|
|
*dst_iterator++ = (i < 2*valence) ? *it + offset : 0;
|
|
++it;
|
|
} else {
|
|
*dst_iterator++ = 0;
|
|
}
|
|
}
|
|
}
|
|
return dst_iterator;
|
|
}
|
|
|
|
template <class T> static FarPatchTables::PTable::iterator
|
|
splicePatch(FarPatchTables::Descriptor desc,
|
|
std::vector<FarMesh<T> const *> const &meshes,
|
|
FarPatchTables::PatchArrayVector &result,
|
|
std::vector<FarPatchTables::PatchArrayVector> *multiArrayResult,
|
|
FarPatchTables::PTable::iterator dstIndexIt,
|
|
int *voffset, int *poffset, int *qoffset,
|
|
std::vector<int> const &vertexOffsets) {
|
|
|
|
for (size_t i = 0; i < meshes.size(); ++i) {
|
|
FarPatchTables const *patchTables = meshes[i]->GetPatchTables();
|
|
FarPatchTables::PatchArray const *srcPatchArray = patchTables->GetPatchArray(desc);
|
|
if (not srcPatchArray) continue;
|
|
|
|
// create new patcharray with offset
|
|
int vindex = srcPatchArray->GetVertIndex();
|
|
int npatch = srcPatchArray->GetNumPatches();
|
|
int nvertex = npatch * desc.GetNumControlVertices();
|
|
|
|
FarPatchTables::PatchArray patchArray(desc,
|
|
*voffset,
|
|
*poffset,
|
|
npatch,
|
|
*qoffset);
|
|
// append patch array
|
|
result.push_back(patchArray);
|
|
|
|
// also store into multiPatchArrays, will be used for partial drawing
|
|
// XXX: can be stored as indices. revisit here later
|
|
if (multiArrayResult) {
|
|
(*multiArrayResult)[i].push_back(patchArray);
|
|
}
|
|
|
|
// increment offset
|
|
*voffset += nvertex;
|
|
*poffset += npatch;
|
|
*qoffset += (desc.GetType() == FarPatchTables::GREGORY ||
|
|
desc.GetType() == FarPatchTables::GREGORY_BOUNDARY) ? npatch * 4 : 0;
|
|
|
|
// copy index arrays [vindex, vindex+nvertex]
|
|
dstIndexIt = copyWithOffset(dstIndexIt,
|
|
patchTables->GetPatchTable(),
|
|
vindex,
|
|
nvertex,
|
|
vertexOffsets[i]);
|
|
}
|
|
return dstIndexIt;
|
|
}
|
|
|
|
template <class T> FarPatchTables *
|
|
FarPatchTablesFactory<T>::Splice(FarMeshVector const &meshes,
|
|
MultiPatchArrayVector *multiPatchArrays) {
|
|
|
|
int totalQuadOffset0 = 0;
|
|
int totalQuadOffset1 = 0;
|
|
int totalFVarData = 0;
|
|
int fvarWidth = 0;
|
|
|
|
std::vector<int> vertexOffsets;
|
|
std::vector<int> gregoryQuadOffsets;
|
|
std::vector<int> numGregoryPatches;
|
|
int vertexOffset = 0;
|
|
int maxValence = 0;
|
|
int numTotalIndices = 0;
|
|
|
|
//result->_patchCounts.reserve(meshes.size());
|
|
//FarPatchCount totalCount;
|
|
typedef FarPatchTables::Descriptor Descriptor;
|
|
|
|
// note: see FarPatchTablesFactory<T>::Create
|
|
// feature adaptive refinement can generate un-connected face-vertices
|
|
// that have a valence of 0. The spliced vertex valence tables
|
|
// needs to be resized including such un-connected face-vertices.
|
|
int numVerticesInVertexValence = 0;
|
|
|
|
// count how many patches exist on each mesh
|
|
for (size_t i = 0; i < meshes.size(); ++i) {
|
|
const FarPatchTables *ptables = meshes[i]->GetPatchTables();
|
|
assert(ptables);
|
|
|
|
vertexOffsets.push_back(vertexOffset);
|
|
vertexOffset += meshes[i]->GetNumVertices();
|
|
|
|
// need to align maxvalence with the highest value
|
|
maxValence = std::max(maxValence, ptables->_maxValence);
|
|
|
|
FarPatchTables::PatchArray const *gregory =
|
|
ptables->GetPatchArray(Descriptor(FarPatchTables::GREGORY,
|
|
FarPatchTables::NON_TRANSITION, /*rot*/ 0));
|
|
FarPatchTables::PatchArray const *gregoryBoundary =
|
|
ptables->GetPatchArray(Descriptor(FarPatchTables::GREGORY_BOUNDARY,
|
|
FarPatchTables::NON_TRANSITION, /*rot*/ 0));
|
|
|
|
int nGregory = gregory ? gregory->GetNumPatches() : 0;
|
|
int nGregoryBoundary = gregoryBoundary ? gregoryBoundary->GetNumPatches() : 0;
|
|
totalQuadOffset0 += nGregory * 4;
|
|
totalQuadOffset1 += nGregoryBoundary * 4;
|
|
numGregoryPatches.push_back(nGregory);
|
|
gregoryQuadOffsets.push_back(totalQuadOffset0);
|
|
|
|
totalFVarData += (int)ptables->GetFVarData()._data.size();
|
|
numTotalIndices += ptables->GetNumControlVertices();
|
|
|
|
// note: some prims may not have vertex valence table, but still need a space
|
|
// in order to fill following prim's data at appropriate location.
|
|
numVerticesInVertexValence += ptables->_vertexValenceTable.empty()
|
|
? (int)meshes[i]->GetNumVertices()
|
|
: (int)ptables->_vertexValenceTable.size()/(2*ptables->_maxValence+1);
|
|
|
|
// fvarWidth has to be same for all meshes.
|
|
fvarWidth = meshes[i]->GetPatchTables()->GetFVarData().GetFVarWidth();
|
|
}
|
|
|
|
FarPatchTables *result = new FarPatchTables(maxValence);
|
|
|
|
// Allocate full patches
|
|
result->_patches.resize(numTotalIndices);
|
|
|
|
// Allocate vertex valence table, quad offset table
|
|
if (totalQuadOffset0 + totalQuadOffset1 > 0) {
|
|
result->_vertexValenceTable.resize((2*maxValence+1) * numVerticesInVertexValence);
|
|
result->_quadOffsetTable.resize(totalQuadOffset0 + totalQuadOffset1);
|
|
}
|
|
|
|
// Allocate fvardata table
|
|
result->_fvarData._data.resize(totalFVarData);
|
|
|
|
// splice tables
|
|
// assuming input farmeshes have dense patchtables
|
|
|
|
if (multiPatchArrays)
|
|
multiPatchArrays->resize(meshes.size());
|
|
|
|
int voffset = 0, poffset = 0, qoffset = 0;
|
|
FarPatchTables::PTable::iterator dstIndexIt = result->_patches.begin();
|
|
|
|
// splice patches : iterate over all descriptors, including points, lines, quads, etc.
|
|
for (Descriptor::iterator it=Descriptor::begin(Descriptor::ANY); it!=Descriptor::end(); ++it) {
|
|
dstIndexIt = splicePatch(*it,
|
|
meshes,
|
|
result->_patchArrays,
|
|
multiPatchArrays,
|
|
dstIndexIt,
|
|
&voffset,
|
|
&poffset,
|
|
&qoffset,
|
|
vertexOffsets);
|
|
}
|
|
|
|
// merge vertexvalence and quadoffset tables
|
|
FarPatchTables::QuadOffsetTable::iterator Q0_IT = result->_quadOffsetTable.begin();
|
|
FarPatchTables::QuadOffsetTable::iterator Q1_IT = Q0_IT + totalQuadOffset0;
|
|
|
|
FarPatchTables::VertexValenceTable::iterator VV_IT = result->_vertexValenceTable.begin();
|
|
for (size_t i = 0; i < meshes.size(); ++i) {
|
|
const FarPatchTables *ptables = meshes[i]->GetPatchTables();
|
|
|
|
// merge vertex valence
|
|
// note: some prims may not have vertex valence table, but still need a space
|
|
// in order to fill following prim's data at appropriate location.
|
|
copyWithOffsetVertexValence(VV_IT,
|
|
ptables->_vertexValenceTable,
|
|
ptables->_maxValence,
|
|
maxValence,
|
|
vertexOffsets[i]);
|
|
|
|
VV_IT += meshes[i]->GetNumVertices() * (2 * maxValence + 1);
|
|
|
|
// merge quad offsets
|
|
int nGregoryQuads = numGregoryPatches[i] * 4;
|
|
if (nGregoryQuads > 0) {
|
|
Q0_IT = std::copy(ptables->_quadOffsetTable.begin(),
|
|
ptables->_quadOffsetTable.begin()+nGregoryQuads,
|
|
Q0_IT);
|
|
}
|
|
if (nGregoryQuads < (int)ptables->_quadOffsetTable.size()) {
|
|
Q1_IT = std::copy(ptables->_quadOffsetTable.begin()+nGregoryQuads,
|
|
ptables->_quadOffsetTable.end(),
|
|
Q1_IT);
|
|
}
|
|
}
|
|
|
|
// merge ptexCoord table
|
|
for (FarPatchTables::Descriptor::iterator it =
|
|
FarPatchTables::Descriptor::begin(FarPatchTables::Descriptor::ANY);
|
|
it != FarPatchTables::Descriptor::end(); ++it) {
|
|
|
|
int ptexFaceOffset = 0;
|
|
for (size_t i = 0; i < meshes.size(); ++i) {
|
|
FarPatchTables const *ptables = meshes[i]->GetPatchTables();
|
|
FarPatchTables::PatchArray const *parray = ptables->GetPatchArray(*it);
|
|
if (parray) {
|
|
copyWithPtexFaceOffset(std::back_inserter(result->_paramTable),
|
|
ptables->_paramTable,
|
|
parray->GetPatchIndex(),
|
|
parray->GetNumPatches(), ptexFaceOffset);
|
|
}
|
|
ptexFaceOffset += ptables->GetNumPtexFaces();
|
|
}
|
|
}
|
|
|
|
// count total num ptex faces
|
|
int numPtexFaces = 0;
|
|
for (size_t i = 0; i < meshes.size(); ++i) {
|
|
numPtexFaces += meshes[i]->GetPatchTables()->GetNumPtexFaces();
|
|
}
|
|
result->_numPtexFaces = numPtexFaces;
|
|
|
|
// merge fvardata table
|
|
if (fvarWidth > 0) {
|
|
|
|
std::vector<float>::iterator FV_IT = result->_fvarData._data.begin();
|
|
|
|
for (FarPatchTables::Descriptor::iterator it =
|
|
FarPatchTables::Descriptor::begin(FarPatchTables::Descriptor::ANY);
|
|
it != FarPatchTables::Descriptor::end(); ++it) {
|
|
|
|
for (size_t i = 0; i < meshes.size(); ++i) {
|
|
|
|
FarPatchTables const *ptables = meshes[i]->GetPatchTables();
|
|
FarPatchTables::PatchArray const *parray = ptables->GetPatchArray(*it);
|
|
FarSubdivisionTables::Scheme scheme = meshes[i]->GetSubdivisionTables()->GetScheme();
|
|
|
|
if (parray) {
|
|
|
|
int nv = (scheme == FarSubdivisionTables::LOOP) ? 3 : 4;
|
|
int width = ptables->GetFVarData().GetFVarWidth() * nv; // for each quads or tris
|
|
|
|
std::vector<float>::const_iterator begin =
|
|
ptables->_fvarData._data.begin() + parray->GetPatchIndex() * width;
|
|
|
|
std::vector<float>::const_iterator end =
|
|
begin + parray->GetNumPatches() * width;
|
|
|
|
FV_IT = std::copy(begin, end, FV_IT);
|
|
}
|
|
}
|
|
}
|
|
|
|
// set fvarwidth
|
|
result->_fvarData._fvarWidth = fvarWidth;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
|
|
} // end namespace OPENSUBDIV_VERSION
|
|
using namespace OPENSUBDIV_VERSION;
|
|
|
|
} // end namespace OpenSubdiv
|
|
|
|
#endif /* FAR_PATCH_TABLES_FACTORY_H */
|