// // 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_PROTOSTENCIL_H #define FAR_PROTOSTENCIL_H #include "../far/stencilTables.h" #include #include #include namespace OpenSubdiv { namespace OPENSUBDIV_VERSION { namespace Far { // // Proto-stencil Pool Allocator classes // // Strategy: allocate up-front a data pool for supporting PROTOSTENCILS of a size // (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) { } ~Allocator() { clearBigStencils(); } // Returns the number of stencils in the allocator int GetNumStencils() const { return (int)_sizes.size(); } // Returns the total number of control vertices used by the all the stencils int GetNumVerticesTotal() const { int nverts=0; for (int i=0; i_indices.push_back(vert); dst->_weights.push_back(weight); } ++size; } // 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 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(protoStencil)!=_bigStencils.end()); return &_bigStencils[protoStencil]->_indices[0]; } } // 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(protoStencil)!=_bigStencils.end()); return &_bigStencils[protoStencil]->_weights[0]; } } // 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(protoStencil, this->GetNumStencils()>0 ? this : 0); } // 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(protoStencil, this->GetNumStencils()>0 ? const_cast *>(this) : 0); } // 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; // max size of stencil that fits in the 'fast' pool bool _interpolateVarying; // true for varying interpolation std::vector _sizes; // 'fast' memory pool std::vector _indices; std::vector _weights; typedef std::map BigStencilMap; BigStencilMap _bigStencils; // 'slow' memory pool }; // // Specialization of the Allocator for stencils with tangents that require // additional derivative weights. // template class LimitAllocator : public Allocator { public: // Constructor LimitAllocator(int maxSize) : Allocator(maxSize) { } void Resize(int size) { Allocator::Resize(size); int nelems = (int)this->_weights.size(); _tan1Weights.resize(nelems); _tan2Weights.resize(nelems); } void PushBackVertex(Index protoStencil, Index vert, float weight, float tan1Weight, float tan2Weight) { assert(weight!=0.0f or tan1Weight!=0.0f or tan2Weight!=0.0f); unsigned char & size = this->_sizes[protoStencil]; Index idx = protoStencil*this->_maxsize; if (size < (this->_maxsize-1)) { idx += size; this->_indices[idx] = vert; this->_weights[idx] = weight; this->_tan1Weights[idx] = tan1Weight; this->_tan2Weights[idx] = tan2Weight; } 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(protoStencil)==this->_bigStencils.end()); this->_bigStencils[protoStencil] = dst; } else { assert(this->_bigStencils.find(protoStencil)!=this->_bigStencils.end()); dst = this->_bigStencils[protoStencil]; } dst->_indices.push_back(vert); dst->_weights.push_back(weight); dst->_tan1Weights.push_back(tan1Weight); dst->_tan2Weights.push_back(tan2Weight); } ++size; } float * GetTan1Weights(Index protoStencil) { if (not this->IsBigStencil(protoStencil)) { return &_tan1Weights[protoStencil*this->_maxsize]; } else { assert(this->_bigStencils.find(protoStencil)!=this->_bigStencils.end()); return &this->_bigStencils[protoStencil]->_tan1Weights[0]; } } float * GetTan2Weights(Index protoStencil) { if (not this->IsBigStencil(protoStencil)) { return &_tan2Weights[protoStencil*this->_maxsize]; } else { assert(this->_bigStencils.find(protoStencil)!=this->_bigStencils.end()); return &this->_bigStencils[protoStencil]->_tan2Weights[0]; } } PROTOSTENCIL operator[] (Index protoStencil) { assert(this->GetNumStencils()>0); return PROTOSTENCIL(protoStencil, this); } 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 protoStencil, Index * indices, float * weights, float * tan1Weights, float * tan2Weights) { unsigned char size = Allocator::CopyStencil( protoStencil, indices, weights); memcpy(tan1Weights, this->GetTan1Weights(protoStencil), size*sizeof(Index)); memcpy(tan2Weights, this->GetTan2Weights(protoStencil), size*sizeof(float)); return size; } private: std::vector _tan1Weights, _tan2Weights; }; // // 'Big' Proto stencil classes // // When proto-stencils exceed _maxsize, fall back to dynamically allocated // "BigStencils" (with 'Limit' specialization to handle tangents) // struct BigStencil { BigStencil(unsigned char size, Index const * indices, float const * weights) { _indices.reserve(size+5); _indices.resize(size); memcpy(&_indices.at(0), indices, size*sizeof(int)); _weights.reserve(size+5); _weights.resize(size); memcpy(&_weights.at(0), weights, size*sizeof(float)); } std::vector _indices; std::vector _weights; }; struct BigLimitStencil : public BigStencil { 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); memcpy(&_tan2Weights.at(0), tan2Weights, size*sizeof(float)); } std::vector _tan1Weights, _tan2Weights; }; // // ProtoStencils // // Proto-stencils are used to interpolate stencils from supporting vertices. // These stencils are backed by a pool allocator to allow for fast push-back // of contributing control-vertices weights & indices as they are discovered. // class ProtoStencil { public: ProtoStencil(Index id, Allocator * alloc) : _id(id), _alloc(alloc) { } void Clear() { // 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) { return; } if (src._alloc) { // Stencil contribution unsigned char srcSize = src._alloc->GetSize(src._id); Index const * srcIndices = src._alloc->GetIndices(src._id); float const * srcWeights = src._alloc->GetWeights(src._id); addWithWeight(weight, srcSize, srcIndices, srcWeights); } else { // Coarse vertex contribution Index n = _alloc->FindVertex(_id, src._id); if (Vtr::IndexIsValid(n)) { _alloc->GetWeights(_id)[n] += weight; assert(_alloc->GetWeights(_id)[n]>0.0f); } else { _alloc->PushBackVertex(_id, src._id, weight); } } } // Factorize from a finished stencil table void AddWithWeight(StencilTables const & table, Index idx, float weight) { assert(idxGetInterpolateVarying()) { AddWithWeight(src, weight); } } 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; }; typedef Allocator StencilAllocator; // // ProtoLimitStencil // class ProtoLimitStencil { public: ProtoLimitStencil(Index id, LimitAllocator * alloc) : _id(id), _alloc(alloc) { } void Clear() { // Clear() can only ever be called on an empty stencil: nothing to do assert(_alloc->GetSize(_id)==0); } void AddWithWeight(Stencil const & src, float weight, float tan1Weight, float tan2Weight) { if(weight==0.0f and tan1Weight==0.0f and tan2Weight==0.0f) { return; } unsigned char srcSize = *src.GetSizePtr(); Index const * srcIndices = src.GetVertexIndices(); float const * srcWeights = src.GetWeights(); for (unsigned char i=0; iFindVertex(_id, vertIndex); if (Vtr::IndexIsValid(n)) { _alloc->GetWeights(_id)[n] += weight*w; _alloc->GetTan1Weights(_id)[n] += tan1Weight*w; _alloc->GetTan2Weights(_id)[n] += tan2Weight*w; } else { _alloc->PushBackVertex(_id, vertIndex, weight*w, tan1Weight*w, tan2Weight*w); } } } private: Index _id; LimitAllocator * _alloc; }; typedef LimitAllocator LimitStencilAllocator; } // end namespace Far } // end namespace OPENSUBDIV_VERSION } // end namespace OpenSubdiv #endif // FAR_PROTOSTENCIL_H