OpenSubdiv/opensubdiv/far/meshFactory.h
manuelk a52c70ab8b First pass implementation of holes :
- make sure HBR passes down the hole tag to children when subdividing faces
- minor API modification : allow to unset the hole flag on a face
- modify uniform / adaptive FarMeshFactory to be aware of the flag
- make the FarSubdivisionTableFactory assert when finding unconnected HBR vertices (as it should)

* Uniform subdivision :
    The refinement scheme only creates faces & vertices necessary
    to maintain the one-ring around the edges of a hole, so this solution
    is quite efficient.

* Adaptive subdivision :
    At the moment we are still performing full topological analysis on holes and
    only skipping patches associated to holes. This is sub-optimal in 2 ways :
        1. the topological analysis can potentially be cranking on a lot of unnecessary
           geometry
        2. even though we may not be drawing the patches, the compute stage is still
           applying kernels on all the control vertices of these patches.
    We will have to revisit feature adaptive subdivision & holes, so keep the issue active.

fixes #78
2013-02-13 14:34:33 -08:00

835 lines
28 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_MESH_FACTORY_H
#define FAR_MESH_FACTORY_H
#include "../version.h"
// Activate Hbr feature adaptive tagging : in order to process the HbrMesh
// adaptively, some tag data is added to HbrFace, HbrVertex and HbrHalfedge.
// While small, these tags incur some performance costs and are by default
// disabled.
#define HBR_ADAPTIVE
#include "../hbr/mesh.h"
#include "../hbr/bilinear.h"
#include "../hbr/catmark.h"
#include "../hbr/loop.h"
#include "../far/mesh.h"
#include "../far/dispatcher.h"
#include "../far/bilinearSubdivisionTablesFactory.h"
#include "../far/catmarkSubdivisionTablesFactory.h"
#include "../far/loopSubdivisionTablesFactory.h"
#include "../far/patchTablesFactory.h"
#include "../far/vertexEditTablesFactory.h"
#include <typeinfo>
#include <set>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
/// \brief Instantiates a FarMesh from an HbrMesh.
///
/// FarMeshFactory requires a 2 steps process :
/// 1. Instantiate a FarMeshFactory object from an HbrMesh
/// 2. Call "Create" to obtain the FarMesh instance
///
/// This tiered factory approach offers client-code the opportunity to access
/// useful transient information tied to the lifespan of the factory instance.
/// Specifically, regression code needs to access the remapping tables that
/// tie HbrMesh vertices to their FarMesh counterparts for comparison.
template <class T, class U=T> class FarMeshFactory {
public:
/// \brief Constructor for the factory.
/// Analyzes the HbrMesh and stores transient data used to create the
/// adaptive patch representation. Once the new rep has been instantiated
/// with 'Create', this factory object can be deleted safely.
FarMeshFactory(HbrMesh<T> * mesh, int maxlevel, bool adaptive=false);
/// Create a table-based mesh representation
FarMesh<U> * Create( bool requirePtexCoordinate=false, // XXX yuck.
bool requireFVarData=false );
/// The Hbr mesh that this factory is converting
HbrMesh<T> const * GetHbrMesh() const { return _hbrMesh; }
/// Maximum level of subidivision supported by this factory
int GetMaxLevel() const { return _maxlevel; }
/// The number of coarse vertices found in the HbrMesh before refinement
int GetNumCoarseVertices() const { return _numCoarseVertices; }
/// Total number of faces across up to a level
int GetNumFacesTotal(int level) const {
return sumList<HbrFace<T> *>(_facesList, level);
}
/// Return the corresponding index of the HbrVertex<T> in the new mesh
int GetVertexID( HbrVertex<T> * v );
/// Returns a the mapping between HbrVertex<T>->GetID() and Far vertices indices
std::vector<int> const & GetRemappingTable( ) const { return _remapTable; }
private:
friend class FarBilinearSubdivisionTablesFactory<T,U>;
friend class FarCatmarkSubdivisionTablesFactory<T,U>;
friend class FarLoopSubdivisionTablesFactory<T,U>;
friend class FarSubdivisionTablesFactory<T,U>;
friend class FarVertexEditTablesFactory<T,U>;
// Non-copyable, so these are not implemented:
FarMeshFactory( FarMeshFactory const & );
FarMeshFactory<T,U> & operator=(FarMeshFactory<T,U> const &);
// True if the HbrMesh applies the bilinear subdivision scheme
static bool isBilinear(HbrMesh<T> const * mesh);
// True if the HbrMesh applies the Catmull-Clark subdivision scheme
static bool isCatmark(HbrMesh<T> const * mesh);
// True if the HbrMesh applies the Loop subdivision scheme
static bool isLoop(HbrMesh<T> const * mesh);
// True if the factory is refining adaptively
bool isAdaptive() { return _adaptive; }
// False if v prevents a face from being represented with a BSpline
static bool vertexIsBSpline( HbrVertex<T> * v, bool next );
// True if a vertex is a regular boundary
static bool vertexIsRegularBoundary( HbrVertex<T> * v );
// Non-const accessor to the remapping table
std::vector<int> & getRemappingTable( ) { return _remapTable; }
template <class Type> static int sumList( std::vector<std::vector<Type> > const & list, int level );
// Returns the highest level of subdivision required to adaptively refine the mesh
static int computeAdaptiveMaxLevel( HbrMesh<T> * mesh, int nfaces, int maxIsolate );
// Calls Hbr to refines the neighbors of v
static void refineVertexNeighbors(HbrVertex<T> * v);
// Densely refine the Hbr mesh
static void refine( HbrMesh<T> * mesh, int maxlevel );
// Adaptively refine the Hbr mesh
int refineAdaptive( HbrMesh<T> * mesh, int maxIsolate );
// Generates local sub-face coordinates for Ptex textures
void generatePtexCoordinates( std::vector<int> & vec, int level );
// Generates local sub-face face-varying UV coordinates
void generateFVarData( std::vector<float> & vec, int level );
// Generates non-adaptive quad topology
// XXXX manuelk we should introduce an equivalent to FarPatchTables for
// non-adaptive stuff
void generateQuadsTopology( std::vector<int> & vec, int level );
private:
HbrMesh<T> * _hbrMesh;
bool _adaptive;
int _maxlevel,
_numVertices,
_numCoarseVertices,
_numFaces,
_maxValence;
// remapping table to translate vertex ID's between Hbr indices and the
// order of the same vertices in the tables
std::vector<int> _remapTable;
// list of faces sorted by level
std::vector<std::vector< HbrFace<T> *> > _facesList;
};
template <class T, class U>
template <class Type> int
FarMeshFactory<T,U>::sumList( std::vector<std::vector<Type> > const & list, int level) {
level = std::min(level, (int)list.size()-1);
int total = 0;
for (int i=0; i<=level; ++i)
total += (int)list[i].size();
return total;
}
// Refines non-adaptively an Hbr mesh
template <class T, class U> void
FarMeshFactory<T,U>::refine( HbrMesh<T> * mesh, int maxlevel ) {
for (int level=0, firstface=0; level<maxlevel; ++level ) {
int nfaces = mesh->GetNumFaces();
for (int i=firstface; i<nfaces; ++i) {
HbrFace<T> * f = mesh->GetFace(i);
if (f->GetDepth()==level) {
if (not f->IsHole()) {
f->Refine();
} else {
// Hole faces need to maintain the 1-ring of vertices so we
// have to create an extra row of children faces around the
// hole.
HbrHalfedge<T> * e = f->GetFirstEdge();
for (int i=0; i<f->GetNumVertices(); ++i) {
assert(e);
if ((not e->IsBoundary()) and (not e->GetOpposite()->GetFace()->IsHole())) {
// RefineFaceAtVertex only creates a single child face
// centered on the passed vertex
HbrSubdivision<T> * s = mesh->GetSubdivision();
s->RefineFaceAtVertex(mesh,f,e->GetOrgVertex());
s->RefineFaceAtVertex(mesh,f,e->GetDestVertex());
}
e = e->GetNext();
}
}
}
}
// Hbr allocates faces sequentially, so there is no need to iterate over
// faces that have already been refined.
firstface = nfaces;
}
}
// Scan the faces of a mesh and compute the max level of subdivision required
template <class T, class U> int
FarMeshFactory<T,U>::computeAdaptiveMaxLevel( HbrMesh<T> * mesh, int nfaces, int maxIsolate ) {
assert(mesh);
int editmax=0;
float sharpmax=0.0f;
for( unsigned int i=0 ; i<(unsigned int)nfaces ; ++i ) {
HbrFace<T> * f = mesh->GetFace(i);
assert( f->IsCoarse() );
// Check for edits
if (f->HasVertexEdits()) {
HbrVertexEdit<T> ** edits = (HbrVertexEdit<T>**)f->GetHierarchicalEdits();
while (HbrVertexEdit<T> * edit = *edits++)
editmax = std::max( editmax , edit->GetNSubfaces() );
}
// Check for sharpness
for (int j=0; j<f->GetNumVertices(); ++j) {
HbrHalfedge<T> * e = f->GetEdge(j);
if (not e->IsBoundary())
sharpmax = std::max( sharpmax, f->GetEdge(j)->GetSharpness() );
HbrVertex<T> * v = f->GetVertex(j);
if (not v->OnBoundary())
sharpmax = std::max( sharpmax, f->GetVertex(j)->GetSharpness() );
}
}
int maxlevel = std::max( maxIsolate+1, (int)ceil(sharpmax)+1 );
maxlevel = std::max( maxlevel, editmax+1 );
maxlevel = std::min( maxlevel, (int)HbrHalfedge<T>::k_InfinitelySharp );
return maxlevel;
}
// True if a vertex is a regular boundary
template <class T, class U> bool
FarMeshFactory<T,U>::vertexIsRegularBoundary( HbrVertex<T> * v ) {
int valence = v->GetValence();
return (v->OnBoundary() and (valence==2 or valence==3));
}
// True if the vertex can be incorporated into a B-spline patch
template <class T, class U> bool
FarMeshFactory<T,U>::vertexIsBSpline( HbrVertex<T> * v, bool next ) {
int valence = v->GetValence();
bool isRegBoundary = v->OnBoundary() and (valence==3);
// Extraordinary vertices that are not on a regular boundary
if (v->IsExtraordinary() and not isRegBoundary )
return false;
// Irregular boundary vertices (high valence)
if (v->OnBoundary() and (valence>3))
return false;
// Creased vertices that aren't corner / boundaries
if (v->IsSharp(next) and not v->OnBoundary())
return false;
return true;
}
// Calls Hbr to refines the neighbors of v
template <class T, class U> void
FarMeshFactory<T,U>::refineVertexNeighbors(HbrVertex<T> * v) {
assert(v);
HbrHalfedge<T> * start = v->GetIncidentEdge(),
* next=start;
do {
if (next->GetRightFace())
next->GetRightFace()->_adaptiveFlags.isTagged=true;
if (next->GetLeftFace())
next->GetLeftFace()->_adaptiveFlags.isTagged=true;
HbrHalfedge<T> * istart = next,
* inext = istart;
do {
inext->GetOrgVertex()->Refine();
inext = inext->GetNext();
} while (istart != inext);
next = v->GetNextEdge( next );
} while (next and next!=start);
}
// XXXX manuelk : std::sets are slow.
// with the "isTagged" flag on verts we can prob. ditch verts / nextverts !!!
template <class T> struct VertCompare {
bool operator()(HbrVertex<T> const * v1, HbrVertex<T> const * v2 ) const {
//return v1->GetID() < v2->GetID();
return (void*)(v1) < (void*)(v2);
}
};
// Refines an Hbr mesh adaptively around extraordinary features
template <class T, class U> int
FarMeshFactory<T,U>::refineAdaptive( HbrMesh<T> * mesh, int maxIsolate ) {
int ncoarsefaces = mesh->GetNumCoarseFaces(),
ncoarseverts = mesh->GetNumVertices();
// XXX manuelk : disabling guesstimate of the max. isolate level for now
//int maxlevel = computeAdaptiveMaxLevel(mesh, ncoarsefaces, maxIsolate);
int maxlevel = maxIsolate+1;
// First pass : tag coarse vertices & faces that need refinement
typedef std::set<HbrVertex<T> *,VertCompare<T> > VertSet;
VertSet verts, nextverts;
for (int i=0; i<ncoarseverts; ++i) {
HbrVertex<T> * v = mesh->GetVertex(i);
// Tag non-BSpline vertices for refinement
if (not vertexIsBSpline(v, false)) {
v->_adaptiveFlags.isTagged=true;
nextverts.insert(v);
}
}
for (int i=0; i<ncoarsefaces; ++i) {
HbrFace<T> * f = mesh->GetFace(i);
for (int j=0; j<f->GetNumVertices(); ++j) {
HbrHalfedge<T> * e = f->GetEdge(j);
assert(e);
// Tag sharp edges for refinement
if (e->IsSharp(true) and (not e->IsBoundary())) {
nextverts.insert(e->GetOrgVertex());
nextverts.insert(e->GetDestVertex());
e->GetOrgVertex()->_adaptiveFlags.isTagged=true;
e->GetDestVertex()->_adaptiveFlags.isTagged=true;
}
// Tag extraordinary (non-quad) faces for refinement
if (mesh->GetSubdivision()->FaceIsExtraordinary(mesh,f) or f->HasVertexEdits()) {
HbrVertex<T> * v = f->GetVertex(j);
v->_adaptiveFlags.isTagged=true;
nextverts.insert(v);
}
}
}
// Second pass : refine adaptively around singularities
for (int level=0; level<maxlevel-1; ++level) {
verts = nextverts;
nextverts.clear();
// Refine vertices
for (typename VertSet::iterator i=verts.begin(); i!=verts.end(); ++i) {
HbrVertex<T> * v = *i;
assert(v);
if (level>0)
v->_adaptiveFlags.isTagged=true;
else
v->_adaptiveFlags.wasTagged=true;
refineVertexNeighbors(v);
// Tag non-BSpline vertices for refinement
if (not vertexIsBSpline(v, true))
nextverts.insert(v->Subdivide());
// Refine edges with creases or edits
int valence = v->GetValence();
_maxValence = std::max(_maxValence, valence);
HbrHalfedge<T> * e = v->GetIncidentEdge();
for (int j=0; j<valence; ++j) {
if (e->IsSharp(false) and (not e->IsBoundary())) {
nextverts.insert( e->Subdivide() );
nextverts.insert( e->GetOrgVertex()->Subdivide() );
nextverts.insert( e->GetDestVertex()->Subdivide() );
}
HbrHalfedge<T> * next = v->GetNextEdge(e);
e = next ? next : e->GetPrev();
}
// Flag verts with hierarchical edits for neighbor refinement at the next level
HbrVertex<T> * childvert = v->Subdivide();
HbrHalfedge<T> * childedge = childvert->GetIncidentEdge();
assert( childvert->GetValence()==valence);
for (int j=0; j<valence; ++j) {
HbrFace<T> * f = childedge->GetFace();
if (f->HasVertexEdits()) {
int nv = f->GetNumVertices();
for (int k=0; k<nv; ++k)
nextverts.insert( f->GetVertex(k) );
}
if (not (childedge = childvert->GetNextEdge(childedge)))
break;
}
}
// Add coarse verts from extraordinary faces
if (level==0) {
for (int i=0; i<ncoarsefaces; ++i) {
HbrFace<T> * f = mesh->GetFace(i);
assert (f->IsCoarse());
if (mesh->GetSubdivision()->FaceIsExtraordinary(mesh,f))
nextverts.insert( f->Subdivide() );
}
}
}
return maxlevel-1;
}
// Assumption : the order of the vertices in the HbrMesh could be set in any
// random order, so the builder runs 2 passes over the entire vertex list to
// gather the counters needed to generate the indexing tables.
template <class T, class U>
FarMeshFactory<T,U>::FarMeshFactory( HbrMesh<T> * mesh, int maxlevel, bool adaptive ) :
_hbrMesh(mesh),
_adaptive(adaptive),
_maxlevel(maxlevel),
_numVertices(-1),
_numCoarseVertices(-1),
_numFaces(-1),
_maxValence(4),
_facesList(maxlevel+1)
{
_numCoarseVertices = mesh->GetNumVertices();
// Subdivide the Hbr mesh up to maxlevel.
//
// Note : using a placeholder vertex class 'T' can greatly speed up the
// topological analysis if the interpolation results are not used.
if (adaptive)
_maxlevel=refineAdaptive( mesh, maxlevel );
else
refine( mesh, maxlevel);
_numFaces = mesh->GetNumFaces();
_numVertices = mesh->GetNumVertices();
if (not adaptive) {
// Populate the face lists
int fsize=0;
for (int i=0; i<_numFaces; ++i) {
HbrFace<T> * f = mesh->GetFace(i);
assert(f);
if (f->GetDepth()==0 and (not f->IsHole()))
fsize += mesh->GetSubdivision()->GetFaceChildrenCount( f->GetNumVertices() );
}
_facesList[0].reserve(mesh->GetNumCoarseFaces());
_facesList[1].reserve(fsize);
for (int l=2; l<=maxlevel; ++l)
_facesList[l].reserve( _facesList[l-1].capacity()*4 );
for (int i=0; i<_numFaces; ++i) {
HbrFace<T> * f = mesh->GetFace(i);
if (f->GetDepth()<=maxlevel and (not f->IsHole()))
_facesList[ f->GetDepth() ].push_back(f);
}
}
}
template <class T, class U> bool
FarMeshFactory<T,U>::isBilinear(HbrMesh<T> const * mesh) {
return typeid(*(mesh->GetSubdivision()))==typeid(HbrBilinearSubdivision<T>);
}
template <class T, class U> bool
FarMeshFactory<T,U>::isCatmark(HbrMesh<T> const * mesh) {
return typeid(*(mesh->GetSubdivision()))==typeid(HbrCatmarkSubdivision<T>);
}
template <class T, class U> bool
FarMeshFactory<T,U>::isLoop(HbrMesh<T> const * mesh) {
return typeid(*(mesh->GetSubdivision()))==typeid(HbrLoopSubdivision<T>);
}
template <class T, class U> void
FarMeshFactory<T,U>::generateQuadsTopology( std::vector<int> & vec, int level ) {
assert( GetHbrMesh() );
int nv=-1;
if ( isCatmark(GetHbrMesh()) or isBilinear(GetHbrMesh()) )
nv=4;
else if ( isLoop(GetHbrMesh()) )
nv=3;
assert(nv>0);
vec.resize( nv * _facesList[level].size(), -1 );
for (int i=0; i<(int)_facesList[level].size(); ++i) {
HbrFace<T> * f = _facesList[level][i];
assert( f and f->GetNumVertices()==nv);
for (int j=0; j<f->GetNumVertices(); ++j)
vec[nv*i+j]=_remapTable[f->GetVertex(j)->GetID()];
}
}
template <class T, class U> void
copyVertex( T & dest, U const & src ) {
}
template <class T> void
copyVertex( T & dest, T const & src ) {
dest = src;
}
// Computes per-face or per-patch local ptex texture coordinates.
//
// int 1 :
// Adaptive : [face index (13 bits)] [rotation (2bits)] [non-quad (1bit)]
//
// Non-adaptive : [non-quad (sign bit)] [face index)]
// int 2 :
// [ u (16 bits) ] [ v (16 bits) ]
//
template <class T> int *
computePtexCoordinate(HbrFace<T> const *f, int *coord, bool isAdaptive) {
short u,v;
unsigned short ofs = 1, depth;
bool nonquad = false;
if (coord == NULL) return NULL;
int 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();
}
// bit0 : non-quad bit
// bit1,2 : rotation bit
if (isAdaptive)
coord[0] = (f->GetPtexIndex() << 3) | (rots << 1) | (nonquad ? 1 : 0);
else
coord[0] = nonquad ? -f->GetPtexIndex() : f->GetPtexIndex();
coord[1] = (int)u << 16;
coord[1] += v;
return coord+2;
}
// This currently only supports the Catmark / Bilinear schemes. Loop
template <class T, class U> void
FarMeshFactory<T,U>::generatePtexCoordinates( std::vector<int> & vec, int level ) {
assert( _hbrMesh );
if (_facesList[0].empty() or _facesList[level][0]->GetPtexIndex() == -1)
return;
vec.resize( _facesList[level].size()*2, -1 );
int *p = &vec[0];
for (int i=0; i<(int)_facesList[level].size(); ++i) {
HbrFace<T> const * f = _facesList[level][i];
assert(f);
p = computePtexCoordinate(f, p, /*isAdaptive=*/false);
}
}
template <class T> float *
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;
}
// This currently only supports the Catmark / Bilinear schemes. Loop
template <class T, class U> void
FarMeshFactory<T,U>::generateFVarData( std::vector<float> & vec, int level ) {
assert( _hbrMesh );
if (_facesList[0].empty())
return;
// initialize coordinate vector: numFaces*4verts*numFVarDatumPerVert
int totalFVarWidth = _hbrMesh->GetTotalFVarWidth();
vec.resize( _facesList[level].size()*4 * totalFVarWidth, -1.0 );
// pointer will be advanced through vector as we go through faces
float *p = &vec[0];
for (int i=0; i<(int)_facesList[level].size(); ++i) {
HbrFace<T> const * f = _facesList[level][i];
assert(f);
p = computeFVarData(f, totalFVarWidth, p, /*isAdaptive=*/false);
}
}
template <class T, class U> FarMesh<U> *
FarMeshFactory<T,U>::Create( bool requirePtexCoordinate, // XXX yuck.
bool requireFVarData ) {
assert( GetHbrMesh() );
// Note : we cannot create a Far rep of level 0 (coarse mesh)
if (GetMaxLevel()<1)
return 0;
FarMesh<U> * result = new FarMesh<U>();
if ( isBilinear( GetHbrMesh() ) ) {
result->_subdivisionTables = FarBilinearSubdivisionTablesFactory<T,U>::Create(this, result);
} else if ( isCatmark( GetHbrMesh() ) ) {
result->_subdivisionTables = FarCatmarkSubdivisionTablesFactory<T,U>::Create(this, result);
} else if ( isLoop(GetHbrMesh()) ) {
result->_subdivisionTables = FarLoopSubdivisionTablesFactory<T,U>::Create(this, result);
} else
assert(0);
assert(result->_subdivisionTables);
const_cast<FarSubdivisionTables<U> *>(result->GetSubdivisionTables())->_numCoarseVertices=GetNumCoarseVertices();
// If the vertex classes aren't place-holders, copy the data of the coarse
// vertices into the vertex buffer.
result->_vertices.resize( _numVertices );
if (sizeof(U)>1) {
for (int i=0; i<GetNumCoarseVertices(); ++i)
copyVertex(result->_vertices[i], GetHbrMesh()->GetVertex(i)->GetData());
}
// Create the element indices tables (patches for adaptive, quads for non-adaptive)
if (isAdaptive()) {
FarPatchTablesFactory<T> factory(GetHbrMesh(), _numFaces, _remapTable);
// XXXX: currently PatchGregory shader supports up to 29 valence
result->_patchTables = factory.Create(GetMaxLevel()+1, _maxValence, requirePtexCoordinate,
requireFVarData);
assert( result->_patchTables );
if (requireFVarData) {
result->_totalFVarWidth = _hbrMesh->GetTotalFVarWidth();
}
} else {
// XXXX : we should let the client control what to copy, most of this may be irrelevant
result->_faceverts.resize(GetMaxLevel()+1);
for (int l=1; l<=GetMaxLevel(); ++l)
generateQuadsTopology(result->_faceverts[l], l);
if (requirePtexCoordinate) {
// Generate Ptex coordinates
result->_ptexcoordinates.resize(GetMaxLevel()+1);
for (int l=1; l<=GetMaxLevel(); ++l)
generatePtexCoordinates(result->_ptexcoordinates[l], l);
}
if (requireFVarData) {
// Generate fvar data
result->_totalFVarWidth = _hbrMesh->GetTotalFVarWidth();
result->_fvarData.resize(GetMaxLevel()+1);
for (int l=1; l<=GetMaxLevel(); ++l)
generateFVarData(result->_fvarData[l], l);
}
}
// Create VertexEditTables if necessary
if (GetHbrMesh()->HasVertexEdits()) {
result->_vertexEditTables = FarVertexEditTablesFactory<T,U>::Create( this, result, GetMaxLevel() );
assert(result->_vertexEditTables);
}
return result;
}
template <class T, class U> int
FarMeshFactory<T,U>::GetVertexID( HbrVertex<T> * v ) {
assert( v and (v->GetID() < _remapTable.size()) );
return _remapTable[ v->GetID() ];
}
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* FAR_MESH_FACTORY_H */