first pass at face-varying interpolation for CpuEvalLimit - still

a couple of kinks to be worked out...
This commit is contained in:
manuelk 2013-06-06 18:07:46 -07:00
parent f0be0b5799
commit c18cf5bff2
7 changed files with 295 additions and 121 deletions

View File

@ -139,7 +139,8 @@ std::vector<float> g_coarseEdgeSharpness;
std::vector<float> g_coarseVertexSharpness;
enum DrawMode { kUV=0,
kVARYING=1 };
kVARYING=1,
kFACEVARYING=2 };
int g_running = 1,
g_width = 1024,
@ -387,7 +388,9 @@ OsdCpuEvalLimitController g_evalCtrl;
OsdVertexBufferDescriptor g_idesc( /*offset*/ 0, /*legnth*/ 3, /*stride*/ 3 ),
g_odesc( /*offset*/ 0, /*legnth*/ 3, /*stride*/ 6 ),
g_vdesc( /*offset*/ 3, /*legnth*/ 3, /*stride*/ 6 );
g_vdesc( /*offset*/ 3, /*legnth*/ 3, /*stride*/ 6 ),
g_fvidesc( /*offset*/ 0, /*legnth*/ 2, /*stride*/ 2 ),
g_fvodesc( /*offset*/ 3, /*legnth*/ 2, /*stride*/ 6 );
std::vector<OsdEvalCoords> g_coords;
@ -444,9 +447,15 @@ updateGeom() {
// The varying data ends-up interleaved in the same g_Q output buffer because
// g_Q has a stride of 6 and g_vdesc sets the offset to 3, while g_odesc sets
// the offset to 0
if (g_drawMode==kVARYING) {
g_evalCtx->BindVaryingBuffers( g_idesc, g_varyingData, g_vdesc, g_Q );
}
switch (g_drawMode) {
case kVARYING : g_evalCtx->BindVaryingBuffers( g_idesc, g_varyingData, g_vdesc, g_Q ); break;
case kFACEVARYING : g_evalCtx->BindFaceVaryingBuffers( g_fvidesc, g_fvodesc, g_Q );
case kUV :
default : g_evalCtx->UnbindVaryingBuffers(); break;
}
#define USE_OPENMP
#if defined(OPENSUBDIV_HAS_OPENMP) and defined(USE_OPENMP)
@ -457,16 +466,19 @@ updateGeom() {
int n = g_evalCtrl.EvalLimitSample<OsdCpuVertexBuffer,OsdCpuGLVertexBuffer>( g_coords[i], g_evalCtx, i );
if (n) {
// point colors
switch (g_drawMode) {
case kUV : { float * color = g_Q->BindCpuBuffer() + i * 6 + 3;
color[0] = g_coords[i].u;
color[1] = 0.0f;
color[2] = g_coords[i].v; } break;
// point colors
switch (g_drawMode) {
case kUV : { float * color = g_Q->BindCpuBuffer() + i * 6 + 3;
color[0] = g_coords[i].u;
color[1] = 0.0f;
color[2] = g_coords[i].v; } break;
case kVARYING :
default : break;
}
case kVARYING : break;
case kFACEVARYING : { g_Q->BindCpuBuffer()[i*6 + 5] = 0.1f;
} break;
default : break;
}
#if defined(OPENSUBDIV_HAS_OPENMP) and defined(USE_OPENMP)
#pragma omp atomic
#endif
@ -488,7 +500,7 @@ static void
createOsdMesh( const std::string &shape, int level, Scheme scheme=kCatmark ) {
// Create HBR mesh
OsdHbrMesh * hmesh = simpleHbr<OsdVertex>(shape.c_str(), scheme, g_orgPositions);
OsdHbrMesh * hmesh = simpleHbr<OsdVertex>(shape.c_str(), scheme, g_orgPositions, true);
g_positions.resize(g_orgPositions.size(),0.0f);
@ -504,7 +516,7 @@ createOsdMesh( const std::string &shape, int level, Scheme scheme=kCatmark ) {
OsdFarMeshFactory factory( hmesh, level, /*adaptive*/ true);
delete g_fmesh;
g_fmesh = factory.Create(/*fvar*/ false);
g_fmesh = factory.Create(/*fvar*/ true);
int nverts = g_fmesh->GetNumVertices();
@ -514,17 +526,13 @@ createOsdMesh( const std::string &shape, int level, Scheme scheme=kCatmark ) {
delete g_vertexData;
g_vertexData = OsdCpuVertexBuffer::Create(3, nverts);
// Create v-buffer & populate w/ colors
delete g_varyingData;
// Create primvar v-buffer & populate w/ colors or (u,v) data
delete g_varyingData; g_varyingData = 0;
if (g_drawMode==kVARYING) {
g_varyingData = OsdCpuVertexBuffer::Create(3, nverts);
g_varyingData->UpdateData( &g_varyingColors[0], 0, nverts);
} else {
g_varyingData = 0;
}
// Create a Compute context, used to "pose" the vertices
delete g_computeCtx;
g_computeCtx = OsdCpuComputeContext::Create(g_fmesh);
@ -535,7 +543,7 @@ createOsdMesh( const std::string &shape, int level, Scheme scheme=kCatmark ) {
// Create eval context & data buffers
delete g_evalCtx;
g_evalCtx = OsdCpuEvalLimitContext::Create(g_fmesh);
g_evalCtx = OsdCpuEvalLimitContext::Create(g_fmesh, /*requireFVarData*/ true);
delete g_Q;
g_Q = OsdCpuGLVertexBuffer::Create(6,nsamples);
@ -780,7 +788,7 @@ drawSamples() {
glBindVertexArray(g_samplesVAO);
glPointSize(1.0f);
glDrawArrays( GL_POINTS, 0, (int)g_coords.size() );
glDrawArrays( GL_POINTS, 0, g_nsamplesFound);
glPointSize(1.0f);
glBindVertexArray(0);
@ -1044,6 +1052,7 @@ initHUD()
g_hud.AddRadioButton(0, "(u,v)", true, 200, 10, callbackDisplayVaryingColors, kUV, 'k');
g_hud.AddRadioButton(0, "varying", false, 200, 30, callbackDisplayVaryingColors, kVARYING, 'k');
g_hud.AddRadioButton(0, "face-varying", false, 200, 50, callbackDisplayVaryingColors, kFACEVARYING, 'k');
for (int i = 1; i < 11; ++i) {
char level[16];

View File

@ -67,7 +67,7 @@ namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
OsdCpuEvalLimitContext *
OsdCpuEvalLimitContext::Create(FarMesh<OsdVertex> const * farmesh) {
OsdCpuEvalLimitContext::Create(FarMesh<OsdVertex> const * farmesh, bool requireFVarData) {
assert(farmesh);
@ -75,7 +75,7 @@ OsdCpuEvalLimitContext::Create(FarMesh<OsdVertex> const * farmesh) {
if (not farmesh->GetPatchTables())
return NULL;
return new OsdCpuEvalLimitContext(farmesh);
return new OsdCpuEvalLimitContext(farmesh, requireFVarData);
}
void
@ -85,7 +85,15 @@ OsdCpuEvalLimitContext::EvalData::Unbind() {
_inQ=0;
_outDesc.Reset();
_outQ = _outdQu = _outdQv = 0;
_outQ = 0;
}
void
OsdCpuEvalLimitContext::EvalVertexData::Unbind() {
EvalData::Unbind();
_outdQu = _outdQv = 0;
}
void
@ -98,7 +106,12 @@ OsdCpuEvalLimitContext::UnbindVaryingBuffers() {
_varyingData.Unbind();
}
OsdCpuEvalLimitContext::OsdCpuEvalLimitContext(FarMesh<OsdVertex> const * farmesh) :
void
OsdCpuEvalLimitContext::UnbindFaceVaryingBuffers() {
_faceVaryingData.Unbind();
}
OsdCpuEvalLimitContext::OsdCpuEvalLimitContext(FarMesh<OsdVertex> const * farmesh, bool requireFVarData) :
OsdEvalLimitContext(farmesh) {
FarPatchTables const * patchTables = farmesh->GetPatchTables();
@ -137,6 +150,12 @@ OsdCpuEvalLimitContext::OsdCpuEvalLimitContext(FarMesh<OsdVertex> const * farmes
}
}
if (requireFVarData) {
_fvarwidth = farmesh->GetTotalFVarWidth();
if (_fvarwidth>0) {
_fvarData = patchTables->GetFVarDataTable();
}
}
_patchMap = new FarPatchTables::PatchMap( *patchTables );
}

View File

@ -71,11 +71,18 @@ namespace OPENSUBDIV_VERSION {
class OsdCpuEvalLimitContext : public OsdEvalLimitContext {
public:
/// \brief Factory
/// Returns an EvalLimitContext from the given farmesh.
/// Note : the farmesh is expected to be feature-adaptive and have ptex
/// coordinates tables.
static OsdCpuEvalLimitContext * Create(FarMesh<OsdVertex> const * farmesh);
///
/// @param farmesh a pointer to an initialized farmesh
///
/// @param requireFVarData flag for generating face-varying data
///
static OsdCpuEvalLimitContext * Create(FarMesh<OsdVertex> const * farmesh,
bool requireFVarData=false);
/// Destructor
virtual ~OsdCpuEvalLimitContext();
@ -99,12 +106,14 @@ public:
return _outQ + index * _outDesc.stride;
}
float const * GetOutputDU(int index=0) const {
return _outdQu + index * _outDesc.stride;
template <class BUFFER>
void BindInputData( BUFFER * inQ ) {
_inQ = inQ ? inQ->BindCpuBuffer() : 0;
}
float const * GetOutputDV(int index=0) const {
return _outdQv + index * _outDesc.stride;
template <class BUFFER>
void BindOutputData( BUFFER * outQ ) {
_outQ = outQ ? outQ->BindCpuBuffer() : 0;
}
bool IsBound() const {
@ -114,46 +123,47 @@ public:
private:
friend class OsdCpuEvalLimitContext;
EvalData() : _inQ(0), _outQ(0) { }
OsdVertexBufferDescriptor _inDesc; // input data
float * _inQ;
OsdVertexBufferDescriptor _outDesc; // output data
float * _outQ,
* _outdQu, // U derivative of output data
* _outdQv; // V derivative of output data
/// Binds the data buffers.
///
/// @param inDesc vertex / varying data descriptor shared by all input data buffers
///
/// @param inQ input subidivision data
///
/// @param outDesc vertex buffer data descriptor shared by all output data buffers
///
/// @param outQ output vertex data
///
/// @param outdQu optional output derivative along "u" of the vertex data
///
/// @param outdQv optional output derivative along "v" of the vertex data
///
template<class INPUT_BUFFER, class OUTPUT_BUFFER>
void Bind( OsdVertexBufferDescriptor const & inDesc, INPUT_BUFFER *inQ,
OsdVertexBufferDescriptor const & outDesc, OUTPUT_BUFFER *outQ,
OUTPUT_BUFFER *outdQu=0,
OUTPUT_BUFFER *outdQv=0);
float * _outQ;
/// Resets the descriptors & pointers
void Unbind();
};
EvalData const & GetVertexData() const {
return _vertexData;
}
/// Limit evaluation data descriptor with derivatives
class EvalVertexData : public EvalData {
public:
float const * GetOutputDU(int index=0) const {
return _outdQu + index * _outDesc.stride;
}
EvalData const & GetVaryingData() const {
return _varyingData;
}
float const * GetOutputDV(int index=0) const {
return _outdQv + index * _outDesc.stride;
}
template <class BUFFER>
void BindOutputDerivData( BUFFER * outdQu, BUFFER * outdQv ) {
_outdQu = outdQu ? outdQu->BindCpuBuffer() : 0;
_outdQv = outdQv ? outdQv->BindCpuBuffer() : 0;
}
private:
friend class OsdCpuEvalLimitContext;
EvalVertexData() : _outdQu(0), _outdQv(0) { }
/// Resets the descriptors & pointers
void Unbind();
float * _outdQu, // U derivative of output data
* _outdQv; // V derivative of output data
};
/// Binds the vertex-interpolated data buffers.
///
@ -174,12 +184,24 @@ public:
OsdVertexBufferDescriptor const & outDesc, OUTPUT_BUFFER *outQ,
OUTPUT_BUFFER *outdQu=0,
OUTPUT_BUFFER *outdQv=0) {
_vertexData.Bind( inDesc, inQ, outDesc, outQ, outdQu, outdQv );
_vertexData._inDesc = inDesc;
_vertexData.BindInputData( inQ );
_vertexData._outDesc = outDesc;
_vertexData.BindOutputData( outQ );
_vertexData.BindOutputDerivData( outdQu, outdQv );
}
/// Unbind the vertex data buffers
void UnbindVertexBuffers();
/// Returns an Eval data descriptor of the vertex-interpolated data currently
/// bound to this EvalLimitContext.
EvalVertexData const & GetVertexData() const {
return _vertexData;
}
/// Binds the varying-interpolated data buffers.
///
/// @param inDesc varying buffer data descriptor shared by all input data buffers
@ -190,19 +212,59 @@ public:
///
/// @param outQ output varying data
///
/// @param outdQu optional output derivative along "u" of the varying data
///
/// @param outdQv optional output derivative along "v" of the varying data
///
template<class VARYING_BUFFER, class OUTPUT_BUFFER>
void BindVaryingBuffers( OsdVertexBufferDescriptor const & inDesc, VARYING_BUFFER *inQ,
OsdVertexBufferDescriptor const & outDesc, OUTPUT_BUFFER *outQ) {
_varyingData.Bind( inDesc, inQ, outDesc, outQ );
_varyingData._inDesc = inDesc;
_varyingData.BindInputData( inQ );
_varyingData._outDesc = outDesc;
_varyingData.BindOutputData( outQ );
}
/// Unbind the varying data buffers
void UnbindVaryingBuffers();
/// Returns an Eval data descriptor of the varying-interpolated data currently
/// bound to this EvalLimitContext.
EvalData const & GetVaryingData() const {
return _varyingData;
}
/// Binds the face-varying-interpolated data buffers.
///
/// Note : currently we only support bilinear boundary interpolation rules
/// for face-varying data. Although Hbr supports 3 addition smooth rule sets,
/// the feature-adaptive patch interpolation code currently does not support
/// them, and neither does this EvalContext
///
/// @param inDesc varying buffer data descriptor shared by all input data buffers
///
/// @param inQ input varying data
///
/// @param outDesc varying buffer data descriptor shared by all output data buffers
///
/// @param outQ output varying data
///
template<class OUTPUT_BUFFER>
void BindFaceVaryingBuffers( OsdVertexBufferDescriptor const & inDesc,
OsdVertexBufferDescriptor const & outDesc, OUTPUT_BUFFER *outQ) {
_faceVaryingData._inDesc = inDesc;
_faceVaryingData._outDesc = outDesc;
_faceVaryingData.BindOutputData( outQ );
}
/// Unbind the varying data buffers
void UnbindFaceVaryingBuffers();
/// Returns an Eval data descriptor of the face-varying-interpolated data
/// currently bound to this EvalLimitContext.
EvalData const & GetFaceVaryingData() const {
return _faceVaryingData;
}
/// Returns the vector of patch arrays
const FarPatchTables::PatchArrayVector & GetPatchArrayVector() const {
@ -228,6 +290,16 @@ public:
const unsigned int *GetQuadOffsetBuffer() const {
return &_quadOffsetBuffer[0];
}
/// Returns the face-varying data patch table
FarPatchTables::FVarDataTable const & GetFVarData() const {
return _fvarData;
}
/// Returns the number of floats in a datum of the face-varying data table
int GetFVarWidth() const {
return _fvarwidth;
}
/// Returns a map object that can connect a faceId to a list of children patches
const FarPatchTables::PatchMap * GetPatchesMap() const {
@ -240,7 +312,7 @@ public:
}
protected:
explicit OsdCpuEvalLimitContext(FarMesh<OsdVertex> const * farmesh);
explicit OsdCpuEvalLimitContext(FarMesh<OsdVertex> const * farmesh, bool requireFVarData);
private:
@ -252,32 +324,18 @@ private:
FarPatchTables::VertexValenceTable _vertexValenceBuffer; // extra Gregory patch data buffers
FarPatchTables::QuadOffsetTable _quadOffsetBuffer;
FarPatchTables::FVarDataTable _fvarData;
FarPatchTables::PatchMap * _patchMap; // map of the sub-patches given a face index
EvalData _vertexData,
_varyingData;
EvalVertexData _vertexData; // vertex-interpolated data descriptor
EvalData _varyingData, // varying-interpolated data descriptor
_faceVaryingData; // face-varying-interpolated data descriptor
int _maxValence;
int _maxValence,
_fvarwidth;
};
template<class INPUT_BUFFER, class OUTPUT_BUFFER> void
OsdCpuEvalLimitContext::EvalData::Bind( OsdVertexBufferDescriptor const & inDesc,
INPUT_BUFFER *inQ,
OsdVertexBufferDescriptor const & outDesc,
OUTPUT_BUFFER *outQ,
OUTPUT_BUFFER *outdQu,
OUTPUT_BUFFER *outdQv) {
_inDesc = inDesc;
_inQ = inQ ? inQ->BindCpuBuffer() : 0;
_outDesc = outDesc;
_outQ = outQ ? outQ->BindCpuBuffer() : 0 ;
_outdQu = outdQu ? outdQu->BindCpuBuffer() : 0 ;
_outdQv = outdQv ? outdQv->BindCpuBuffer() : 0 ;
}
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;

View File

@ -104,9 +104,8 @@ OsdCpuEvalLimitController::_EvalLimitSample( OpenSubdiv::OsdEvalCoords const & c
unsigned int const * cvs = &context->GetControlVertices()[ parray.GetVertIndex() + handle.vertexOffset ];
OsdCpuEvalLimitContext::EvalData const & vertexData = context->GetVertexData(),
& varyingData = context->GetVaryingData();
OsdCpuEvalLimitContext::EvalVertexData const & vertexData = context->GetVertexData();
// Position lookup pointers at the indexed vertex
float const * inQ = vertexData.GetInputData();
float * outQ = const_cast<float *>(vertexData.GetOutputData(index));
@ -167,6 +166,8 @@ OsdCpuEvalLimitController::_EvalLimitSample( OpenSubdiv::OsdEvalCoords const & c
default:
assert(0);
}
OsdCpuEvalLimitContext::EvalData const & varyingData = context->GetVaryingData();
if (varyingData.IsBound()) {
static int indices[5][4] = { {5, 6,10, 9}, // regular
@ -182,13 +183,37 @@ OsdCpuEvalLimitController::_EvalLimitSample( OpenSubdiv::OsdEvalCoords const & c
cvs[indices[type][2]],
cvs[indices[type][3]] };
evalVarying( v, u, zeroRing,
varyingData.GetInputDesc(),
varyingData.GetInputData(),
varyingData.GetOutputDesc(),
const_cast<float *>(varyingData.GetOutputData(index)) );
evalBilinear( v, u, zeroRing,
varyingData.GetInputDesc(),
varyingData.GetInputData(),
varyingData.GetOutputDesc(),
const_cast<float *>(varyingData.GetOutputData(index)) );
}
// Note : currently we only support bilinear boundary interpolation rules
// for face-varying data. Although Hbr supports 3 addition smooth rule sets,
// the feature-adaptive patch interpolation code currently does not support
// them, and neither does this EvalContext
OsdCpuEvalLimitContext::EvalData const & faceVaryingData = context->GetFaceVaryingData();
if (faceVaryingData.GetOutputData()) {
FarPatchTables::FVarDataTable const & fvarData = context->GetFVarData();
if (not fvarData.empty()) {
float const * fvar = &fvarData[ handle.serialIndex * 4 * context->GetFVarWidth() ];
static unsigned int zeroRing[4] = {0,1,2,3};
evalBilinear( v, u, zeroRing,
faceVaryingData.GetInputDesc(),
fvar,
faceVaryingData.GetOutputDesc(),
const_cast<float *>(faceVaryingData.GetOutputData(index)) );
}
}
return 1;
}

View File

@ -69,12 +69,12 @@ namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
void
evalVarying(float u, float v,
unsigned int const * vertexIndices,
OsdVertexBufferDescriptor const & inDesc,
float const * inQ,
OsdVertexBufferDescriptor const & outDesc,
float * outQ) {
evalBilinear(float u, float v,
unsigned int const * vertexIndices,
OsdVertexBufferDescriptor const & inDesc,
float const * inQ,
OsdVertexBufferDescriptor const & outDesc,
float * outQ) {
assert( inDesc.length <= (outDesc.stride-outDesc.offset) );

View File

@ -65,12 +65,12 @@ namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
void
evalVarying(float u, float v,
unsigned int const * vertexIndices,
OsdVertexBufferDescriptor const & inDesc,
float const * inQ,
OsdVertexBufferDescriptor const & outDesc,
float * outQ);
evalBilinear(float u, float v,
unsigned int const * vertexIndices,
OsdVertexBufferDescriptor const & inDesc,
float const * inQ,
OsdVertexBufferDescriptor const & outDesc,
float * outQ);
void
evalBSpline(float u, float v,

View File

@ -703,7 +703,7 @@ hbrToObj( OpenSubdiv::HbrMesh<T> * mesh ) {
//------------------------------------------------------------------------------
template <class T> OpenSubdiv::HbrMesh<T> *
createMesh( Scheme scheme=kCatmark) {
createMesh( Scheme scheme=kCatmark, int fvarwidth=0) {
OpenSubdiv::HbrMesh<T> * mesh = 0;
@ -711,10 +711,32 @@ createMesh( Scheme scheme=kCatmark) {
static OpenSubdiv::HbrLoopSubdivision<T> _loop;
static OpenSubdiv::HbrCatmarkSubdivision<T> _catmark;
static int indices[1] = { 0 },
widths[1] = { 2 };
int const fvarcount = fvarwidth > 0 ? 1 : 0,
* fvarindices = fvarwidth > 0 ? indices : NULL,
* fvarwidths = fvarwidth > 0 ? widths : NULL;
switch (scheme) {
case kBilinear : mesh = new OpenSubdiv::HbrMesh<T>( &_bilinear ); break;
case kLoop : mesh = new OpenSubdiv::HbrMesh<T>( &_loop ); break;
case kCatmark : mesh = new OpenSubdiv::HbrMesh<T>( &_catmark ); break;
case kBilinear : mesh = new OpenSubdiv::HbrMesh<T>( &_bilinear,
fvarcount,
fvarindices,
fvarwidths,
fvarwidth ); break;
case kLoop : mesh = new OpenSubdiv::HbrMesh<T>( &_loop,
fvarcount,
fvarindices,
fvarwidths,
fvarwidth ); break;
case kCatmark : mesh = new OpenSubdiv::HbrMesh<T>( &_catmark,
fvarcount,
fvarindices,
fvarwidths,
fvarwidth ); break;
}
return mesh;
@ -840,19 +862,55 @@ createTopology( shape const * sh, OpenSubdiv::HbrMesh<T> * mesh, Scheme scheme)
mesh->Finish();
}
//------------------------------------------------------------------------------
template <class T> void
createFaceVaryingUV( shape const * sh, OpenSubdiv::HbrMesh<T> * mesh) {
if (sh->uvs.empty() or sh->faceuvs.empty())
return;
for (int i=0, idx=0; i<sh->getNfaces(); ++i ) {
OpenSubdiv::HbrFace<T> * f = mesh->GetFace(i);
int nv = sh->nvertsPerFace[i];
OpenSubdiv::HbrHalfedge<T> * e = f->GetFirstEdge();
for (int j=0; j<nv; ++j, e=e->GetNext()) {
OpenSubdiv::HbrFVarData<T> & fvt = e->GetOrgVertex()->GetFVarData(f);
float const * fvdata = &sh->uvs[ sh->faceuvs[idx++] ];
if (not fvt.IsInitialized()) {
fvt.SetAllData(2, fvdata);
} else if (not fvt.CompareAll(2, fvdata)) {
OpenSubdiv::HbrFVarData<T> & nfvt = e->GetOrgVertex()->NewFVarData(f);
nfvt.SetAllData(2, fvdata);
}
}
}
}
//------------------------------------------------------------------------------
template <class T> OpenSubdiv::HbrMesh<T> *
simpleHbr(char const * shapestr, Scheme scheme, std::vector<float> * verts=0) {
simpleHbr(char const * shapestr, Scheme scheme, std::vector<float> * verts=0, bool fvar=false) {
shape * sh = shape::parseShape( shapestr );
OpenSubdiv::HbrMesh<T> * mesh = createMesh<T>(scheme);
int fvarwidth = fvar ? 2 : 0;
OpenSubdiv::HbrMesh<T> * mesh = createMesh<T>(scheme, fvarwidth);
createVertices<T>(sh, mesh, verts);
createTopology<T>(sh, mesh, scheme);
if (fvar)
createFaceVaryingUV<T>(sh, mesh);
if(verts)
if (verts)
copyVertexPositions<T>(sh,mesh,*verts);
delete sh;
@ -862,16 +920,21 @@ simpleHbr(char const * shapestr, Scheme scheme, std::vector<float> * verts=0) {
//------------------------------------------------------------------------------
template <class T> OpenSubdiv::HbrMesh<T> *
simpleHbr(char const * shapestr, Scheme scheme, std::vector<float> & verts) {
simpleHbr(char const * shapestr, Scheme scheme, std::vector<float> & verts, bool fvar=false) {
shape * sh = shape::parseShape( shapestr );
OpenSubdiv::HbrMesh<T> * mesh = createMesh<T>(scheme);
int fvarwidth = fvar ? 2 : 0;
OpenSubdiv::HbrMesh<T> * mesh = createMesh<T>(scheme, fvarwidth);
createVertices<T>(sh, mesh, verts);
createTopology<T>(sh, mesh, scheme);
if (fvar)
createFaceVaryingUV<T>(sh, mesh);
copyVertexPositions<T>(sh,mesh,verts);
delete sh;