From c237ab00fc884b4cb7a355900f95f14a9cdbf522 Mon Sep 17 00:00:00 2001 From: manuelk Date: Tue, 4 Nov 2014 11:14:21 -0800 Subject: [PATCH] WIP check-point for Gregory basis factorization - adding support for StencilTables creation from a Gregory basis - fix a bug in the prot-stencil allocator (slow memory pool was not being cleared properly) --- examples/vtrViewer/gl_mesh.cpp | 2 +- examples/vtrViewer/vtrViewer.cpp | 104 +++++++------ opensubdiv/far/gregoryBasis.cpp | 164 +++++++++++++++----- opensubdiv/far/gregoryBasis.h | 59 +++++-- opensubdiv/far/protoStencil.h | 258 +++++++++++++++++++------------ opensubdiv/far/stencilTables.h | 20 +-- 6 files changed, 398 insertions(+), 209 deletions(-) diff --git a/examples/vtrViewer/gl_mesh.cpp b/examples/vtrViewer/gl_mesh.cpp index 71414caf..9564276e 100644 --- a/examples/vtrViewer/gl_mesh.cpp +++ b/examples/vtrViewer/gl_mesh.cpp @@ -156,7 +156,7 @@ GLMesh::initializeVertexComponentBuffer(float const * vertData, int nverts) { //------------------------------------------------------------------------------ void -GLMesh::Initialize(Options options, +GLMesh::Initialize(Options /* options */, int nverts, int nfaces, int * vertsperface, int * faceverts, float const * vertexData) { diff --git a/examples/vtrViewer/vtrViewer.cpp b/examples/vtrViewer/vtrViewer.cpp index da32223f..50c68549 100644 --- a/examples/vtrViewer/vtrViewer.cpp +++ b/examples/vtrViewer/vtrViewer.cpp @@ -115,6 +115,7 @@ int g_displayPatchColor = 1, g_VtrDrawPtexIDs = false, g_VtrDrawEdgeSharpness = false, g_numPatches = 0, + g_maxValence = 0, g_currentPatch = 0, g_Adaptive = true; @@ -546,68 +547,68 @@ static GLMesh gregoryWire; static void createGregoryBasis(OpenSubdiv::Far::TopologyRefiner const & refiner, - std::vector const & vertexBuffer) { + OpenSubdiv::Far::StencilTables const & stencils, int maxvalence, + std::vector const & vertexBuffer) { int level = refiner.GetMaxLevel(), nfaces = refiner.GetNumFaces(level); - int vertOffset = 0; - for (int i=0; i gpatches(nfaces); + for (int face=0; face vertsperedge, edgeindices; - std::vector edgeverts; + int npatches = (int)gpatches.size(); - for (int face=0; face vertsperedge(nedges), edgeindices(nedges*2); + std::vector edgeverts(npatches*20); + gstencils->UpdateValues(&vertexBuffer[0], &edgeverts[0]); - int nedges = (int)vertsperedge.size(); - - vertsperedge.resize(nedges+20); - edgeindices.resize(nedges*2+40); + for (int patch=0; patch gverts(20); - gbasis->Evaluate(&vertexBuffer[vertOffset], &gverts[0]); + int offset = patch * 20, + * vpe = &vertsperedge[offset], + * indices = &edgeindices[patch * 40]; + for (int i=0; i<20; ++i) { + vpe[i] = 2; + indices[i*2] = basisedges[i*2] + offset; + indices[i*2+1] =basisedges[i*2+1] + offset; + } - static char buf[16]; - for (int i=0; i<4; ++i) { - int vid = i * 5; - snprintf(buf, 16, " P%d", i); - g_font->Print3D(gverts[vid].GetPos(), buf, 3); - snprintf(buf, 16, " Ep%d", i); - g_font->Print3D(gverts[vid+1].GetPos(), buf, 3); - snprintf(buf, 16, " Em%d", i); - g_font->Print3D(gverts[vid+2].GetPos(), buf, 3); - snprintf(buf, 16, " Fp%d", i); - g_font->Print3D(gverts[vid+3].GetPos(), buf, 3); - snprintf(buf, 16, " Fm%d", i); - g_font->Print3D(gverts[vid+4].GetPos(), buf, 3); - } - delete gbasis; - - edgeverts.insert(edgeverts.end(), gverts.begin(), gverts.end()); + Vertex const * verts = &edgeverts[offset]; + static char buf[16]; + for (int i=0; i<4; ++i) { + int vid = i * 5; + snprintf(buf, 16, " P%d", i); + g_font->Print3D(verts[vid].GetPos(), buf, 3); + snprintf(buf, 16, " Ep%d", i); + g_font->Print3D(verts[vid+1].GetPos(), buf, 3); + snprintf(buf, 16, " Em%d", i); + g_font->Print3D(verts[vid+2].GetPos(), buf, 3); + snprintf(buf, 16, " Fp%d", i); + g_font->Print3D(verts[vid+3].GetPos(), buf, 3); + snprintf(buf, 16, " Fm%d", i); + g_font->Print3D(verts[vid+4].GetPos(), buf, 3); } } @@ -697,6 +698,7 @@ createVtrMesh(Shape * shape, int maxlevel) { patchTables = OpenSubdiv::Far::PatchTablesFactory::Create(*refiner); g_numPatches = patchTables->GetNumPatchesTotal(); + g_maxValence = patchTables->GetMaxValence(); } else { refiner->RefineUniform(maxlevel, /*fullTopology*/true); } @@ -726,12 +728,13 @@ createVtrMesh(Shape * shape, int maxlevel) { //printf(" %f ms (total)\n", float(s.GetTotalElapsed())*1000.0f); } #else + OpenSubdiv::Far::StencilTables const * stencilTables = 0; { OpenSubdiv::Far::StencilTablesFactory::Options options; options.generateOffsets=true; options.generateIntermediateLevels=true; - OpenSubdiv::Far::StencilTables const * stencilTables = + stencilTables = OpenSubdiv::Far::StencilTablesFactory::Create(*refiner, options); stencilTables->UpdateValues(verts, verts + ncoarseverts); @@ -755,7 +758,7 @@ createVtrMesh(Shape * shape, int maxlevel) { } if (g_Adaptive and g_drawGregoryBasis) { - createGregoryBasis(*refiner, vertexBuffer); + createGregoryBasis(*refiner, *stencilTables, g_maxValence, vertexBuffer); } createEdgeNumbers(*refiner, vertexBuffer, g_VtrDrawEdgeIDs!=0, g_VtrDrawEdgeSharpness!=0); @@ -780,6 +783,7 @@ createVtrMesh(Shape * shape, int maxlevel) { delete refiner; delete patchTables; + delete stencilTables; } //------------------------------------------------------------------------------ diff --git a/opensubdiv/far/gregoryBasis.cpp b/opensubdiv/far/gregoryBasis.cpp index adc9b2bd..3df3f6db 100644 --- a/opensubdiv/far/gregoryBasis.cpp +++ b/opensubdiv/far/gregoryBasis.cpp @@ -32,6 +32,26 @@ namespace OpenSubdiv { namespace OPENSUBDIV_VERSION { +// Builds a table of local indices pairs for each vertex of the patch. +// +// o +// N0 | +// | .... +// | .... : Gregory patch +// 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 of the vertex 'V' belonging to the Gregory patch. +// Neighbor ordering is valence CCW and must match the winding of the 1-ring +// vertices. +// static void getQuadOffsets(Vtr::Level const& level, Vtr::Index fIndex, Vtr::Index offsets[]) { @@ -61,10 +81,12 @@ getQuadOffsets(Vtr::Level const& level, Vtr::Index fIndex, Vtr::Index offsets[]) } } +#define GetNumMaxElems( maxvalence ) \ + 16 + maxvalence - 3 + // limit valence of 30 because we use a pre-computed closed-form 'ef' table static const int MAX_VALENCE=30, - MAX_ELEMS = 16 + MAX_VALENCE - 3; - + MAX_ELEMS = GetNumMaxElems(MAX_VALENCE); namespace Far { @@ -72,7 +94,7 @@ namespace Far { // Basis point // // Implements arithmetic operators to manipulate the influence of the -// control vertices supporting the patch basis +// 1-ring control vertices supporting the patch basis // class Point { @@ -93,11 +115,11 @@ public: int GetSize() const { return _size; } - + Index const * GetIndices() const { return _indices; } - + float const * GetWeights() const { return _weights; } @@ -152,6 +174,12 @@ public: Point p(*this); return p-=other; } + void OffsetIndices(Index offset) { + for (int i=0; i<_size; ++i) { + _indices[i] += offset; + } + } + void Copy(int ** size, Index ** indices, float ** weights) const; private: @@ -196,12 +224,17 @@ Point::Copy(int ** size, Index ** indices, float ** weights) const { // // ProtoBasis // +// Given a Vtr::Level and a face index, gathers all the influences of the 1-ring +// that supports the 20 CVs of a Gregory patch basis. +// struct ProtoBasis { ProtoBasis(Vtr::Level const & level, Index faceIndex); int GetNumElements() const; + void OffsetIndices(Index offset); + void Copy(int * sizes, Index * indices, float * weights) const; // Control Vertices based on : @@ -233,7 +266,6 @@ struct ProtoBasis { int ProtoBasis::GetNumElements() const { - int nelems=0; for (int vid=0; vid<4; ++vid) { nelems += P[vid].GetSize(); @@ -244,10 +276,18 @@ ProtoBasis::GetNumElements() const { } return nelems; } - +void +ProtoBasis::OffsetIndices(Index offset) { + for (int vid=0; vid<4; ++vid) { + P[vid].OffsetIndices(offset); + Ep[vid].OffsetIndices(offset); + Em[vid].OffsetIndices(offset); + Fp[vid].OffsetIndices(offset); + Fm[vid].OffsetIndices(offset); + } +} void ProtoBasis::Copy(int * sizes, Index * indices, float * weights) const { - for (int vid=0; vid<4; ++vid) { P[vid].Copy(&sizes, &indices, &weights); Ep[vid].Copy(&sizes, &indices, &weights); @@ -256,7 +296,6 @@ ProtoBasis::Copy(int * sizes, Index * indices, float * weights) const { Fm[vid].Copy(&sizes, &indices, &weights); } } - inline float csf(Index n, Index j) { if (j%2 == 0) { return cosf((2.0f * float(M_PI) * float(float(j-0)/2.0f))/(float(n)+3.0f)); @@ -264,7 +303,6 @@ inline float csf(Index n, Index j) { return sinf((2.0f * float(M_PI) * float(float(j-1)/2.0f))/(float(n)+3.0f)); } } - ProtoBasis::ProtoBasis(Vtr::Level const & level, Index faceIndex) { static float ef[MAX_VALENCE-3] = { @@ -502,18 +540,21 @@ ProtoBasis::ProtoBasis(Vtr::Level const & level, Index faceIndex) { } } +int GregoryBasisFactory::GetMaxValence() { + return MAX_VALENCE; +} // -// GregoryBasisFactory +// Stateless GregoryBasisFactory // - GregoryBasis const * GregoryBasisFactory::Create(TopologyRefiner const & refiner, Index faceIndex) { - // Gregory patches only exist on the hight + // Gregory patches are end-caps: they only exist on max-level Vtr::Level const & level = refiner.getLevel(refiner.GetMaxLevel()); - if (level.getMaxValence()>MAX_VALENCE) { + if (level.getMaxValence()>GetMaxValence()) { + // The proto-basis closed-form table limits valence to 'MAX_VALENCE' return 0; } @@ -536,48 +577,97 @@ GregoryBasisFactory::Create(TopologyRefiner const & refiner, Index faceIndex) { return result; } -GregoryBasisFactory::GregoryBasisFactory(StencilTables const & stencils, - int numpatches, int maxvalence) : - _currentStencil(0), _stencils(stencils), _alloc(16 + maxvalence - 3) { - +// +// GregoryBasisFactory for StencilTables +// +GregoryBasisFactory::GregoryBasisFactory(TopologyRefiner const & refiner, + StencilTables const & stencils, int numpatches, int maxvalence) : + _currentStencil(0), _refiner(refiner), + _stencils(stencils), _alloc(GetNumMaxElems(maxvalence)) { _alloc.Resize(numpatches * 20); } +inline void +factorizeBasisVertex(StencilTables const & stencils, Point const & p, ProtoStencil dst) { + // Use the Allocator to factorize the Gregory patch influence CVs with the + // supporting CVs from the stencil tables. + dst.Clear(); + for (int j=0; jMAX_VALENCE) { + if (level.getMaxValence()>GetMaxValence()) { + // The proto-basis closed-form table limits valence to 'MAX_VALENCE' return false; } - - int voffset = 0; - for (int i=0; i_numControlVertices = _refiner.GetNumVertices(0); + + result->resize(nstencils, nelems); + + Stencil dst(&result->_sizes.at(0), + &result->_indices.at(0), &result->_weights.at(0)); + + for (int i=0; i<_alloc.GetNumStencils(); ++i) { + *dst._size = _alloc.CopyStencil(i, dst._indices, dst._weights); + dst.Next(); } - return true; + result->generateOffsets(); + + return result; } + } // end namespace Far } // end namespace OPENSUBDIV_VERSION diff --git a/opensubdiv/far/gregoryBasis.h b/opensubdiv/far/gregoryBasis.h index e6ae2428..bb4e6ac6 100644 --- a/opensubdiv/far/gregoryBasis.h +++ b/opensubdiv/far/gregoryBasis.h @@ -40,8 +40,18 @@ class GregoryBasis { public: - template - void Evaluate(T const * controlValues, T values[20]) const { + /// \brief Updates point values based on the control values + /// + /// \note The destination buffers are assumed to have allocated at least + /// \c GetNumStencils() elements. + /// + /// @param controlValues Buffer with primvar data for the control vertices + /// + /// @param values Destination buffer for the interpolated primvar + /// data + /// + template + void Evaluate(T const & controlValues, U values[20]) const { Index const * indices = &_indices.at(0); float const * weights = &_weights.at(0); @@ -71,19 +81,50 @@ class GregoryBasisFactory { public: + // + // Single patch GregoryBasis basis factory + // + + /// \brief Instantiates a GregoryBasis from a TopologyRefiner that has been + /// refined adaptively for a given face. + /// + /// @param refiner The TopologyRefiner containing the topology + /// + /// @param faceIndex The index of the face (level is assumed to be MaxLevel) + /// static GregoryBasis const * Create(TopologyRefiner const & refiner, Index faceIndex); + /// \brief Returns the maximum valence of a vertex in the mesh that the + /// Gregory patches can handle + static int GetMaxValence(); + +public: + + // + // Multi-patch Gregory stencils factory + // + + // This factory accumulates Gregory patch basis into StencilTables + // + // Note: the TopologyRefiner and StencilTables references are held for the + // lifespan of the factory - neither can be deleted or modified while + // this factory is active. + // + GregoryBasisFactory(TopologyRefiner const & refiner, + StencilTables const & stencils, int numpatches, int maxvalence); + + // Creates a basis for the face and adds it to the stencil pool allocator + bool AddPatchBasis(Index faceIndex); + + // After all the patches have been collected, create the final table + StencilTables const * CreateStencilTables(); + private: - GregoryBasisFactory(StencilTables const & stencils, int numpatches, int maxvalence); - - bool AddBasis(TopologyRefiner const & refiner, Index faceIndex); - - friend class Point; - friend struct ProtoBasis; - int _currentStencil; + TopologyRefiner const & _refiner; // XXXX these should be smart pointers ! + StencilTables const & _stencils; StencilAllocator _alloc; }; diff --git a/opensubdiv/far/protoStencil.h b/opensubdiv/far/protoStencil.h index 51ec7ed4..16f529d7 100644 --- a/opensubdiv/far/protoStencil.h +++ b/opensubdiv/far/protoStencil.h @@ -40,17 +40,19 @@ namespace Far { // Proto-stencil Pool Allocator classes // // Strategy: allocate up-front a data pool for supporting PROTOSTENCILS of a size -// slightly above average. For the (rare) BIG_PROTOSTENCILS that require more support -// vertices, switch to (slow) heap allocation. +// (maxsize) slightly above average. For the (rare) BIG_PROTOSTENCILS that +// require more support vertices, switch to (slow) heap allocation. // template class Allocator { public: + // Constructor Allocator(int maxSize, bool interpolateVarying=false) : _maxsize(maxSize), _interpolateVarying(interpolateVarying) { } + // Returns the number of stencils in the allocator int GetNumStencils() const { return (int)_sizes.size(); } @@ -64,6 +66,8 @@ public: return nverts; } + // Returns true if the pool allocator executes AddVaryingWithWeight + // factorization bool GetInterpolateVarying() const { return _interpolateVarying; } @@ -81,10 +85,10 @@ public: // Adds the contribution of a supporting vertex that was not yet // in the stencil - void PushBackVertex(Index stencil, Index vert, float weight) { + void PushBackVertex(Index protoStencil, Index vert, float weight) { assert(weight!=0.0f); - unsigned char & size = _sizes[stencil]; - Index idx = stencil*_maxsize; + unsigned char & size = _sizes[protoStencil]; + Index idx = protoStencil*_maxsize; if (size < (_maxsize-1)) { idx += size; _indices[idx] = vert; @@ -93,11 +97,11 @@ public: BIG_PROTOSTENCIL * dst = 0; if (size==(_maxsize-1)) { dst = new BIG_PROTOSTENCIL(size, &_indices[idx], &_weights[idx]); - assert(_bigStencils.find(stencil)==_bigStencils.end()); - _bigStencils[stencil] = dst; + assert(_bigStencils.find(protoStencil)==_bigStencils.end()); + _bigStencils[protoStencil] = dst; } else { - assert(_bigStencils.find(stencil)!=_bigStencils.end()); - dst = _bigStencils[stencil]; + assert(_bigStencils.find(protoStencil)!=_bigStencils.end()); + dst = _bigStencils[protoStencil]; } dst->_indices.push_back(vert); dst->_weights.push_back(weight); @@ -105,14 +109,11 @@ public: ++size; } - unsigned char GetSize(Index stencil) const { - assert(stencil<(int)_sizes.size()); - return _sizes[stencil]; - } - - Index FindVertex(Index stencil, Index vert) { - int size = _sizes[stencil]; - Index const * indices = GetIndices(stencil); + // Returns the local index in 'stencil' of a given vertex index, or + // INDEX_INVALID if the stencil does not contain this vertex + int FindVertex(Index protoStencil, Index vert) { + int size = _sizes[protoStencil]; + Index const * indices = GetIndices(protoStencil); for (int i=0; i=_maxsize; + // Returns true of the stencil does not fit in the pool allocator and + // has been moved to the 'big' (slow) allocation pool + bool IsBigStencil(Index protoStencil) const { + assert(protoStencil<(int)_sizes.size()); + return _sizes[protoStencil]>=_maxsize; } - Index * GetIndices(Index stencil) { - if (not IsBigStencil(stencil)) { - return &_indices[stencil*_maxsize]; + // Returns the size of a given proto-stencil + unsigned char GetSize(Index protoStencil) const { + assert(protoStencil<(int)_sizes.size()); + return _sizes[protoStencil]; + } + + // Resolve memory pool and return a pointer to the indices of a given + // proto-stencil + Index * GetIndices(Index protoStencil) { + if (not IsBigStencil(protoStencil)) { + return &_indices[protoStencil*_maxsize]; } else { - assert(_bigStencils.find(stencil)!=_bigStencils.end()); - return &_bigStencils[stencil]->_indices[0]; + assert(_bigStencils.find(protoStencil)!=_bigStencils.end()); + return &_bigStencils[protoStencil]->_indices[0]; } } - float * GetWeights(Index stencil) { - if (not IsBigStencil(stencil)) { - return &_weights[stencil*_maxsize]; + // Resolve memory pool and return a pointer to the weights of a given + // proto-stencil + float * GetWeights(Index protoStencil) { + if (not IsBigStencil(protoStencil)) { + return &_weights[protoStencil*_maxsize]; } else { - assert(_bigStencils.find(stencil)!=_bigStencils.end()); - return &_bigStencils[stencil]->_weights[0]; + assert(_bigStencils.find(protoStencil)!=_bigStencils.end()); + return &_bigStencils[protoStencil]->_weights[0]; } } - PROTOSTENCIL operator[] (Index i) { + // Returns the proto-stencil at a given index + PROTOSTENCIL operator[] (Index protoStencil) { // If the allocator is empty, AddWithWeight() expects a coarse control // vertex instead of a stencil and we only need to pass the index - return PROTOSTENCIL(i, this->GetNumStencils()>0 ? this : 0); + return PROTOSTENCIL(protoStencil, this->GetNumStencils()>0 ? this : 0); } - PROTOSTENCIL operator[] (Index i) const { + // Returns the proto-stencil at a given index + PROTOSTENCIL operator[] (Index protoStencil) const { // If the allocator is empty, AddWithWeight() expects a coarse control // vertex instead of a stencil and we only need to pass the index - return PROTOSTENCIL(i, this->GetNumStencils()>0 ? + return PROTOSTENCIL(protoStencil, this->GetNumStencils()>0 ? const_cast *>(this) : 0); } - void ClearStencil(Index stencil) { - memset(GetWeights(stencil), 0, _sizes[stencil]*sizeof(float)); - } - - unsigned char CopyStencil(Index i, Index * indices, float * weights) { - unsigned char size = GetSize(i); - memcpy(indices, this->GetIndices(i), size*sizeof(Index)); - memcpy(weights, this->GetWeights(i), size*sizeof(float)); + // Copy the proto-stencil out of the pool + unsigned char CopyStencil(Index protoStencil, + Index * indices, float * weights) { + unsigned char size = GetSize(protoStencil); + memcpy(indices, this->GetIndices(protoStencil), size*sizeof(Index)); + memcpy(weights, this->GetWeights(protoStencil), size*sizeof(float)); return size; } protected: + // delete 'slow' memory pool void clearBigStencils() { typename BigStencilMap::iterator it; for (it=_bigStencils.begin(); it!=_bigStencils.end(); ++it) { delete it->second; } + _bigStencils.clear(); } protected: - int _maxsize; + int _maxsize; // max size of stencil that fits in the 'fast' pool - bool _interpolateVarying; + bool _interpolateVarying; // true for varying interpolation - std::vector _sizes; + std::vector _sizes; // 'fast' memory pool std::vector _indices; std::vector _weights; typedef std::map BigStencilMap; - BigStencilMap _bigStencils; + BigStencilMap _bigStencils; // 'slow' memory pool }; +// +// Specialization of the Allocator for stencils with tangents that require +// additional derivative weights. +// template class LimitAllocator : public Allocator { public: - LimitAllocator(int maxSize) : Allocator(maxSize) { } + // Constructor + LimitAllocator(int maxSize) : + Allocator(maxSize) { } void Resize(int size) { Allocator::Resize(size); @@ -205,11 +226,11 @@ public: _tan2Weights.resize(nelems); } - void PushBackVertex(Index stencil, + void PushBackVertex(Index protoStencil, Index vert, float weight, float tan1Weight, float tan2Weight) { assert(weight!=0.0f); - unsigned char & size = this->_sizes[stencil]; - Index idx = stencil*this->_maxsize; + unsigned char & size = this->_sizes[protoStencil]; + Index idx = protoStencil*this->_maxsize; if (size < (this->_maxsize-1)) { idx += size; this->_indices[idx] = vert; @@ -219,13 +240,14 @@ public: } else { BIG_PROTOSTENCIL * dst = 0; if (size==(this->_maxsize-1)) { - dst = new BIG_PROTOSTENCIL(size, &this->_indices[idx], - &this->_weights[idx], &this->_tan1Weights[idx], &this->_tan2Weights[idx]); - assert(this->_bigStencils.find(stencil)==this->_bigStencils.end()); - this->_bigStencils[stencil] = dst; + dst = new BIG_PROTOSTENCIL(size, + &this->_indices[idx], &this->_weights[idx], + &this->_tan1Weights[idx], &this->_tan2Weights[idx]); + assert(this->_bigStencils.find(protoStencil)==this->_bigStencils.end()); + this->_bigStencils[protoStencil] = dst; } else { - assert(this->_bigStencils.find(stencil)!=this->_bigStencils.end()); - dst = this->_bigStencils[stencil]; + assert(this->_bigStencils.find(protoStencil)!=this->_bigStencils.end()); + dst = this->_bigStencils[protoStencil]; } dst->_indices.push_back(vert); dst->_weights.push_back(weight); @@ -235,41 +257,42 @@ public: ++size; } - float * GetTan1Weights(Index stencil) { - if (not this->IsBigStencil(stencil)) { - return &_tan1Weights[stencil*this->_maxsize]; + float * GetTan1Weights(Index protoStencil) { + if (not this->IsBigStencil(protoStencil)) { + return &_tan1Weights[protoStencil*this->_maxsize]; } else { - assert(this->_bigStencils.find(stencil)!=this->_bigStencils.end()); - return &this->_bigStencils[stencil]->_tan1Weights[0]; + assert(this->_bigStencils.find(protoStencil)!=this->_bigStencils.end()); + return &this->_bigStencils[protoStencil]->_tan1Weights[0]; } } - float * GetTan2Weights(Index stencil) { - if (not this->IsBigStencil(stencil)) { - return &_tan2Weights[stencil*this->_maxsize]; + float * GetTan2Weights(Index protoStencil) { + if (not this->IsBigStencil(protoStencil)) { + return &_tan2Weights[protoStencil*this->_maxsize]; } else { - assert(this->_bigStencils.find(stencil)!=this->_bigStencils.end()); - return &this->_bigStencils[stencil]->_tan2Weights[0]; + assert(this->_bigStencils.find(protoStencil)!=this->_bigStencils.end()); + return &this->_bigStencils[protoStencil]->_tan2Weights[0]; } } - PROTOSTENCIL operator[] (Index i) { + PROTOSTENCIL operator[] (Index protoStencil) { assert(this->GetNumStencils()>0); - return PROTOSTENCIL(i, this); + return PROTOSTENCIL(protoStencil, this); } - void ClearStencil(Index stencil) { - Allocator::ClearStencil(stencil); - memset(GetTan1Weights(stencil), 0, this->_sizes[stencil]*sizeof(float)); - memset(GetTan2Weights(stencil), 0, this->_sizes[stencil]*sizeof(float)); + void ClearStencil(Index protoStencil) { + Allocator::ClearStencil(protoStencil); + memset(GetTan1Weights(protoStencil), 0, this->_sizes[protoStencil]*sizeof(float)); + memset(GetTan2Weights(protoStencil), 0, this->_sizes[protoStencil]*sizeof(float)); } - unsigned char CopyLimitStencil(Index i, Index * indices, - float * weights, float * tan1Weights, float * tan2Weights) { + unsigned char CopyLimitStencil(Index protoStencil, + Index * indices, float * weights, float * tan1Weights, float * tan2Weights) { unsigned char size = - Allocator::CopyStencil(i, indices, weights); - memcpy(tan1Weights, this->GetTan1Weights(i), size*sizeof(Index)); - memcpy(tan2Weights, this->GetTan2Weights(i), size*sizeof(float)); + Allocator::CopyStencil( + protoStencil, indices, weights); + memcpy(tan1Weights, this->GetTan1Weights(protoStencil), size*sizeof(Index)); + memcpy(tan2Weights, this->GetTan2Weights(protoStencil), size*sizeof(float)); return size; } @@ -281,8 +304,8 @@ private: // // 'Big' Proto stencil classes // -// When proto-stencils exceed _maxsize, fall back to dynamically -// allocated "BigStencils" +// When proto-stencils exceed _maxsize, fall back to dynamically allocated +// "BigStencils" (with 'Limit' specialization to handle tangents) // struct BigStencil { @@ -299,9 +322,11 @@ struct BigStencil { }; struct BigLimitStencil : public BigStencil { - BigLimitStencil(unsigned char size, Index const * indices, - float const * weights, float const * tan1Weights, float const * tan2Weights) : - BigStencil(size, indices, weights) { + BigLimitStencil(unsigned char size, + Index const * indices, float const * weights, + float const * tan1Weights, float const * tan2Weights) : + BigStencil(size, indices, weights) { + _tan1Weights.reserve(size+5); _tan1Weights.resize(size); memcpy(&_tan1Weights.at(0), tan1Weights, size*sizeof(float)); _tan2Weights.reserve(size+5); _tan2Weights.resize(size); @@ -327,9 +352,11 @@ public: _id(id), _alloc(alloc) { } void Clear() { - _alloc->ClearStencil(_id); + // Clear() can only ever be called on an empty stencil: nothing to do + assert(_alloc->GetSize(_id)==0); } + // Factorize from a proto-stencil allocator void AddWithWeight(ProtoStencil const & src, float weight) { if(weight==0.0f) { @@ -342,24 +369,7 @@ public: Index const * srcIndices = src._alloc->GetIndices(src._id); float const * srcWeights = src._alloc->GetWeights(src._id); - for (unsigned char i=0; iFindVertex(_id, vertIndex); - if (Vtr::IndexIsValid(n)) { - _alloc->GetWeights(_id)[n] += w; - assert(_alloc->GetWeights(_id)[n]!=0.0f); - } else { - _alloc->PushBackVertex(_id, vertIndex, w); - } - } + addWithWeight(weight, srcSize, srcIndices, srcWeights); } else { // Coarse vertex contribution Index n = _alloc->FindVertex(_id, src._id); @@ -372,16 +382,56 @@ public: } } + // Factorize from a finished stencil table + void AddWithWeight(StencilTables const & table, Index idx, float weight) { + + assert(idxGetInterpolateVarying()) { AddWithWeight(src, weight); } } -private: +protected: friend class ProtoLimitStencil; + void addWithWeight(float weight, unsigned char srcSize, + Index const * srcIndices, float const * srcWeights) { + + for (unsigned char i=0; iFindVertex(_id, vertIndex); + if (Vtr::IndexIsValid(n)) { + _alloc->GetWeights(_id)[n] += w; + assert(_alloc->GetWeights(_id)[n]!=0.0f); + } else { + _alloc->PushBackVertex(_id, vertIndex, w); + } + } + } + Index _id; Allocator * _alloc; }; @@ -395,12 +445,14 @@ typedef Allocator StencilAllocator; class ProtoLimitStencil { public: + ProtoLimitStencil(Index id, LimitAllocator * alloc) : _id(id), _alloc(alloc) { } void Clear() { - _alloc->ClearStencil(_id); + // Clear() can only ever be called on an empty stencil: nothing to do + assert(_alloc->GetSize(_id)==0); } void AddWithWeight(Stencil const & src, diff --git a/opensubdiv/far/stencilTables.h b/opensubdiv/far/stencilTables.h index 0ab7eb79..246b85c9 100644 --- a/opensubdiv/far/stencilTables.h +++ b/opensubdiv/far/stencilTables.h @@ -100,6 +100,7 @@ public: } protected: + friend class GregoryBasisFactory; friend class StencilTablesFactory; friend class LimitStencilTablesFactory; @@ -135,7 +136,7 @@ public: } /// \brief Returns a Stencil at index i in the tables - Stencil GetStencil(int i) const; + Stencil GetStencil(Index i) const; /// \brief Returns the number of control vertices of each stencil in the table std::vector const & GetSizes() const { @@ -158,12 +159,12 @@ public: } /// \brief Returns the stencil at index i in the tables - Stencil operator[] (int index) const; + Stencil operator[] (Index index) const; /// \brief Updates point values based on the control values /// - /// \note The destination buffers ('uderivs' & 'vderivs') are assumed to - /// have allocated at least \c GetNumStencils() elements. + /// \note The destination buffers are assumed to have allocated at least + /// \c GetNumStencils() elements. /// /// @param controlValues Buffer with primvar data for the control vertices /// @@ -175,7 +176,7 @@ public: /// @param end Index of last value to update /// template - void UpdateValues(T const *controlValues, T *values, int start=-1, int end=-1) const { + void UpdateValues(T const *controlValues, T *values, Index start=-1, Index end=-1) const { update(controlValues, values, _weights, start, end); } @@ -193,7 +194,7 @@ protected: // Update values by appling cached stencil weights to new control values template void update( T const *controlValues, T *values, - std::vector const & valueWeights, int start, int end) const; + std::vector const & valueWeights, Index start, Index end) const; // Populate the offsets table from the stencil sizes in _sizes (factory helper) void generateOffsets(); @@ -204,6 +205,7 @@ protected: protected: friend class StencilTablesFactory; + friend class GregoryBasisFactory; int _numControlVertices; // number of control vertices @@ -335,7 +337,7 @@ private: // Update values by appling cached stencil weights to new control values template void StencilTables::update(T const *controlValues, T *values, - std::vector const &valueWeights, int start, int end) const { + std::vector const &valueWeights, Index start, Index end) const { unsigned char const * sizes = &_sizes.at(0); Index const * indices = &_indices.at(0); @@ -387,7 +389,7 @@ StencilTables::resize(int nstencils, int nelems) { // Returns a Stencil at index i in the table inline Stencil -StencilTables::GetStencil(int i) const { +StencilTables::GetStencil(Index i) const { assert((not _offsets.empty()) and i<(int)_offsets.size()); @@ -399,7 +401,7 @@ StencilTables::GetStencil(int i) const { } inline Stencil -StencilTables::operator[] (int index) const { +StencilTables::operator[] (Index index) const { return GetStencil(index); }