// // 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_STENCILTABLE_FACTORY_H #define FAR_STENCILTABLE_FACTORY_H #include "../version.h" #include "../hbr/allocator.h" #include "../hbr/mesh.h" #include "../hbr/catmark.h" #include "../far/stencilTables.h" #include #include namespace OpenSubdiv { namespace OPENSUBDIV_VERSION { class FarVertexStencil; class FarStencilFactoryVertex; /// \brief A factory for FarStencilTables /// /// The FarStencilTablesFactory is used to generate FarStencilTables. Currently /// this factory can only accumulate stencil tables for the Catmark subdivision /// scheme. /// /// Client-code must first select a face index in the source Hbr mesh (and /// possibly a sub-face quadrant if the coarse face is not a quad). If the call /// is successful, then an arbitrary number of stencils corresponding to (u,v) /// sample locations can be computed using the AppendStencils method. These /// stencils are self-contained and can be appended to any FarStencilTables. /// /// Each stencil can be pushed to an selected level of feature isolation. /// Surface approximation beyond this level of isolation is obtained by pushing /// volatile vertices to their limits and bilinearly interpolating the resulting /// quad sub-face containing the sample location (no Gregory patch approximation). /// /// The current stencils accumulated are for limit positions only. Additional /// methods to provide limit stencils on vertex locations should be fairly /// easy to implement. /// /// \note Hierarchical edits are not supported yet (XXXX) /// /// \note Face-varying boundaries are not implemented yet (XXXX) /// /// \note The FarStencilTablesFactory processes are currently not re-entrant: /// every invokation of SetCurrentFace causes the HbrMesh to be unrefined to /// a default starting position. /// template class FarStencilTablesFactory { public: /// \brief Constructor /// /// \note this factory *will* modify the state of the HbrMesh /// /// \note hierarchical vertex edits are not supported yet /// FarStencilTablesFactory( HbrMesh * mesh ); /// \brief Returns the HbrMesh used by the factory HbrMesh const * GetMesh() { return _mesh; } /// \brief Sets the current Hbr face where stencils will be added /// /// @param id A valid Hbr coarse face ID /// /// @param quadrant If the face is extraordinary, specify which (u,v) /// coordinates quadrant (aka sub-face) to use. /// /// @return True upon success /// bool SetCurrentFace( int id, unsigned int quadrant=0 ); /// \brief Append stencils for the given UV's to the FarStencilTables /// /// @param stencilTables The table of stencils to add the results to /// /// @param nsamples The number of uv locations /// /// @param u Array of u-paramater locations /// /// @param v Array of v-paramater locations /// /// @param reflevel Max level of feature isolation /// /// @return The number of stencils added to the array /// int AppendStencils( FarStencilTables * stencilTables, int nsamples, float const * u, float const * v, int reflevel ); /// \brief Returns the maximum valence of a vertex allowed in the coarse /// mesh topology. Higher valences will generate incorrect limit tangents. int GetMaxValenceSupported(); private: friend class FarVertexStencil; friend class FarStencilFactoryVertex; // Reserve space for stencils of a set size at the end of a stencil table void _AddNewStencils( FarStencilTables * tables, int nstencils, int stencilsize); HbrMesh * _mesh; int _numCoarseVertices; // A private helper class that caches computations for the current face class Patch; Patch _patch; // Cache for the current face }; // Constructor template FarStencilTablesFactory::FarStencilTablesFactory( HbrMesh * mesh ) : _mesh(mesh) { _numCoarseVertices = mesh->GetNumVertices(); } // Sets the current Hbr face where stencils will be added template bool FarStencilTablesFactory::SetCurrentFace(int id, unsigned int quadrant) { assert(_mesh); HbrFace * f = GetMesh()->GetFace(id); // XXXX Vertex edits are not supported yet if ((f and (not f->IsCoarse())) or GetMesh()->HasVertexEdits()) return false; // Extraordinary faces don't have simple (u,v) parameterization so we need // to specify the parametric quadrant if (GetMesh()->GetSubdivision()->FaceIsExtraordinary(GetMesh(), f)) { if ( quadrant > (unsigned int)(f->GetNumVertices()-1) ) return false; } else { // face is a quad: lock quadrant to 0 quadrant = 0; } _mesh->Unrefine(_numCoarseVertices, GetMesh()->GetNumCoarseFaces()); _patch.SetupControlStencils(f, quadrant); return true; } // Append stencils for the given UV's to the FarStencilTables template int FarStencilTablesFactory::AppendStencils( FarStencilTables * stencilTables, int nsamples, float const * u, float const * v, int reflevel ) { assert(stencilTables); int result=0; if ((not nsamples) or (not _patch.GetCurrentFace())) return result; int stencilsize = _patch.GetStencilSize(); _AddNewStencils( stencilTables, nsamples, stencilsize ); FarStencil stencil = stencilTables->GetStencil( stencilTables->GetNumStencils()-nsamples ); std::vector const & indices = _patch.GetControlVertexIndices(); for (int i=0; i * edge = _patch.GetCurrentFace()->GetEdge(_patch.GetCurrentQuadrant()); // Copy stencil weights for sample result += _patch.GetStencilsAtUV( edge, u[i], v[i], reflevel, stencil._point, stencil._uderiv, stencil._vderiv ); // Advance to the next stencil stencil.Increment(); } return result; } template void FarStencilTablesFactory::_AddNewStencils( FarStencilTables * tables, int nstencils, int stencilsize) { int size = tables->GetNumStencils(); // Append sizes tables->_sizes.insert(tables->_sizes.end(), nstencils, stencilsize); // Append offsets tables->_offsets.resize(size+nstencils); int * offsPtr = &(tables->_offsets[size]), h = (int)tables->_point.size(); for (int i=0; i_point.size() + (nstencils*stencilsize); tables->_indices.resize(newsize); tables->_point.resize(newsize); tables->_uderiv.resize(newsize); tables->_vderiv.resize(newsize); } class FarVertexStencilAllocator; /// \brief A container for vertex stencil weight coefficients /// class FarVertexStencil { public: /// \brief Constructor FarVertexStencil() { } /// \brief Destructor ~FarVertexStencil() { } /// \brief Next available instance in the pool allocator FarVertexStencil * & GetNext() { return _next; } /// \brief Returns a pointer to the vertex stencils pool allocator FarVertexStencilAllocator * GetAllocator() const { return _allocator; } /// \brief Returns a pointer to the first stencil weight coefficient float const * GetData() const { return _data; } /// \brief Resets the values of the weight coefficients: dst = val /// /// @param dst Pointer to the weight coefficients that will be reset /// /// @param val Value the weights will be reset to /// /// @param stencilsize Number of weight coefficients in the stencil /// static void Reset( float * dst, float val, int stencilsize ); /// \brief Resets the values of the weight coefficients: this = val /// /// @param val Value the weights will be reset to /// void Reset( float val ); /// \brief Copies the values of the weight coefficients: dst = other /// /// @param dst Pointer to the destination weight coefficients /// /// @param other Source stencil to copy the weights from /// static void Copy( float * dst , FarVertexStencil const * other ); /// \brief Adds the coefficients from a stencil /// /// @param dst Pointer to the destination weight coefficients /// /// @param other Source stencil to add the weights from /// static void Add( float * dst, FarVertexStencil const * other ); /// \brief Subtracts the coefficients from a stencil dst = (a - b) /// /// @param dst Pointer to the destination weight coefficients /// /// @param sa 'A' source stencil /// /// @param sb 'B' source stencil /// static void Subtract( float * dst, FarVertexStencil const * sa, FarVertexStencil const * sb ); /// \brief Scales the coefficients from a stencil: dst *= val /// /// @param dst Pointer to the destination weight coefficients /// /// @param val Value the weights will be scaled with /// /// @param stencilsize Number of weight coefficients in the stencil /// static void Scale( float * dst , float val, int stencilsize ); /// \brief Adds and scales the coefficients of the stencil: dst += other * val /// /// @param dst Pointer to the destination weight coefficients /// /// @param other Stencil to be added /// /// @param val Value to weigh 'other' with /// static void AddScaled( float * dst, FarVertexStencil const * other, float val ); /// \brief Adds and scales the coefficients of the stencil: this += other * val /// /// @param other Stencil to be added /// /// @param val Value to weigh 'other' with /// void AddScaled( FarVertexStencil const & other, float val ); /// \brief Combines coefficients: this = a*sa + b*sb /// /// @param a 'A' weight /// /// @param sa 'A' stencil /// /// @param b 'B' weight /// /// @param sb 'B' stencil /// void Combine( float a, FarVertexStencil const & sa, float b, FarVertexStencil const & sb ); /// \brief Prints the weight coefficients void Print() const; private: template friend class FarStencilTablesFactory::Patch; friend class FarVertexStencilAllocator; float * _GetData() { return _data; } FarVertexStencilAllocator * _allocator; FarVertexStencil * _next; float _data[1]; }; /// \brief A pool allocator for FarVertexStencil /// class FarVertexStencilAllocator { public: FarVertexStencilAllocator(int stencilsize) : _stencilSize(stencilsize), _allocator(&_memory, 256, 0, 0, sizeof(FarVertexStencil)+(stencilsize-1)*sizeof(float)) { assert(stencilsize>=1); } // Returns the number of coefficients in the the stencil int GetStencilSize() const { return _stencilSize; } // Returns the total memory used by the allocator size_t GetMemoryUsed( ) const { return _memory; } // Allocates a FarVertexStencil from the pool FarVertexStencil * Allocate( ) { FarVertexStencil * result = _allocator.Allocate(); result->_allocator = this; return result; } // Deallocates a FarVertexStencil from the pool void Deallocate( FarVertexStencil * v) { _allocator.Deallocate(v); } private: int _stencilSize; size_t _memory; HbrAllocator _allocator; }; inline void FarVertexStencil::Reset( float val ) { Reset( _GetData(), val, GetAllocator()->GetStencilSize() ); } inline void FarVertexStencil::Reset( float * dst, float val, int stencilsize ) { float * end = dst + stencilsize; while (dst < end) (*dst++) = val; } inline void FarVertexStencil::Copy( float * dst, FarVertexStencil const * other ) { assert(other and other->GetAllocator()); memcpy( dst, other->GetData(), other->GetAllocator()->GetStencilSize()*sizeof(float) ); } inline void FarVertexStencil::Add( float * dst, FarVertexStencil const * other ) { assert(other and other->GetAllocator()); float * end = dst + other->GetAllocator()->GetStencilSize(); float const * src = other->GetData(); while (dst < end) (*dst++) += (*src++); } inline void FarVertexStencil::Subtract( float * dst, FarVertexStencil const * sa, FarVertexStencil const * sb ) { assert(sa and sb and sa->GetAllocator()); float * end = dst + sa->GetAllocator()->GetStencilSize(); float const * aptr = sa->GetData(), * bptr = sb->GetData(); while (dst < end) (*dst++) = (*aptr++) - (*bptr++); } inline void FarVertexStencil::Scale( float * dst , float val, int stencilsize ) { float * end = dst + stencilsize; while (dst < end) (*dst++) *= val; } inline void FarVertexStencil::AddScaled( FarVertexStencil const & other, float val ) { AddScaled( _GetData(), &other, val ); } inline void FarVertexStencil::AddScaled( float * dst, FarVertexStencil const * other, float val ) { assert(other and other->GetAllocator()); float * end = dst + other->GetAllocator()->GetStencilSize(); float const * src = other->GetData(); while (dst < end) (*dst++) += (*src++) * val; } inline void FarVertexStencil::Combine( float a, FarVertexStencil const & sa, float b, FarVertexStencil const & sb ) { assert(GetAllocator()); float * dst = _data, * end = dst + GetAllocator()->GetStencilSize(); float const * aptr = sa.GetData(), * bptr = sb.GetData(); while (dst < end) (*dst++) = a*(*aptr++) + b*(*bptr++); } inline void FarVertexStencil::Print() const { printf("{ "); for (int i=0; i<_allocator->GetStencilSize(); ++i) printf("%.5f ", _data[i] ); printf("}"); } /// \brief A dedicated vertex class for the FarStencilTablesFactory /// class FarStencilFactoryVertex { public: /// \brief Constructor FarStencilFactoryVertex( ) : _stencil(0) { } FarStencilFactoryVertex(int) : _stencil(0) { } /// \brief Destructor ~FarStencilFactoryVertex( ) { } FarVertexStencil const * GetStencil() const { return _stencil; } /// \brief Hbr template vertex class API : resets the vertex void Clear() { if (_stencil) _stencil->Reset(0.0f); } /// \brief Hbr template vertex class API: interpolate vertex data with 'src' /// vertex (this += src * weight) /// /// @param src The source vertex /// /// @param weight The interpolation weight /// void AddWithWeight(FarStencilFactoryVertex const & src, float weight, void * =0 ) { if (not _stencil) { // Allocation is lazy, but 'src' will have a valid stencil unless that // vertex is singular if (src.GetStencil()) { _stencil = src.GetStencil()->GetAllocator()->Allocate(); _stencil->Reset(0.0f); } else return; } _stencil->AddScaled( *src.GetStencil(), weight ); } /// \brief Hbr template vertex class API: interpolate varying data with 'src' /// vertex (this += src * weight) /// void AddVaryingWithWeight(FarStencilFactoryVertex const & /* src */, float /* weight */, void * =0 ) { } /// \brief Hbr template vertex class API: edits are not supported yet /// void ApplyVertexEdit(OpenSubdiv::HbrVertexEdit const & /* edit */) { } /// \brief Hbr template vertex class API: edits are not supported yet /// void ApplyMovingVertexEdit(const OpenSubdiv::HbrMovingVertexEdit &) { } private: template friend class FarStencilTablesFactory::Patch; FarVertexStencil * _GetStencil() { return _stencil; } void _SetStencil( FarVertexStencil * stencil) { _stencil = stencil; } FarVertexStencil * _stencil; }; // A private helper class that caches computations for the current face template class FarStencilTablesFactory::Patch { public: // Constructor Patch() : _face(0), _quadrant(0), _allocator(0) { } // Returns the current coarse face HbrFace const * GetCurrentFace() const { return _face; } // Sub-quadrant of the current face if it's not a quad int GetCurrentQuadrant() const { return _quadrant; } // Returns the number of weights in the stencils for this face int GetStencilSize() const { return (int)_allocator->GetStencilSize(); } // Return the indices of the control vertices in the HbrMesh std::vector const & GetControlVertexIndices() const { return _vertIndices; } // Gathers all the coarse control vertices for an arbitrary patch void SetupControlStencils( HbrFace * f, int quadrant ); // Appends stencil weight coefficients for the given u & v bool GetStencilsAtUV( HbrHalfedge * e, float u, float v, int reflevel, float *point, float *deriv1, float *deriv2 ); private: // True if the vertex has a BSpline limit static bool _IsaBSpline( HbrVertex * v ); // True if the edge has a BSpline limit static bool _IsaBSpline( HbrHalfedge * e ); // True if the face has a BSpline limit static bool _IsaBSpline( HbrFace * f ); // Computes the limit stencils void _GetLimitStencils( HbrVertex * v, float *point ); // Computes derivative limit stencils void _GetTangentLimitStencils( HbrHalfedge * e, float *uderiv, float *vderiv ); // Computes BSpline weights static void _GetBSplineWeights( float t, float *cubicWeights, float *quadraticWeights ); // Updates the cached BSpline stencils void _UpdateBSplineStencils( HbrFace const * f ); // Scale tangent stencils so that magnitudes are consistent across // isolation levels. static void _ScaleTangentStencil( HbrFace const * f, int stencilsize, float *uderiv, float *vderiv ); // Computes BSpline stencil weights at (u,v) void _GetBSplineStencilsAtUV( float u, float v, float *point, float *deriv1, float *deriv2 ); HbrFace * _face; // current face int _quadrant, // current quadrant (if _face is not a quad) _sharpedge; // index of the sharp edge (if any) std::vector _vertIndices; // indices of the control vertices HbrFace const * _bsplineFace; // current b-spline sub-face FarVertexStencil * _bsplineStencils[16], // control stencils for current * _reflectedStencils[4]; // b-spline sub-face FarVertexStencilAllocator * _allocator; }; // Gathers all the coarse control vertices for an arbitrary patch template void FarStencilTablesFactory::Patch::SetupControlStencils( HbrFace * f, int quadrant ) { assert(f and f->IsCoarse()); // same face and same quadrant: control stencil and cached bspline patch // stay the same if (quadrant==GetCurrentQuadrant() and f==GetCurrentFace()) return; // new face or new quadrant: control stencil may still be the same, but // cached bspline patch must be invalidated _quadrant = quadrant; _bsplineFace = NULL; if (f==GetCurrentFace()) return; // new coarse face: control stencil is invalidated and must be recomputed _face = f; HbrMesh * mesh = f->GetMesh(); // reset stencil pointers in coarse vertices (if any) for (int i=0; i<(int)_vertIndices.size(); ++i) { mesh->GetVertex(_vertIndices[i])->GetData()._SetStencil(0); } // new face will require a new set of coarse vertices _vertIndices.clear(); // most stencils should use 16 CVs : 64 should be enough for most faces _vertIndices.reserve( 16*4 ); // Gather operator that collects all the 1-ring control vertices around a // face's corner // // R2 // o // / . // / . // / . R1 R0 // R3 o o ----------- o ---- // \ | | // \ | | // \ | Corner | // o ---- o ----------- o ---- // R4 | | ........... | // | | ........... | // | | .. Face .. | // | | ........... | // o ---- o ----------- o ---- // R5 | | | // | | | // class GatherOperator : public HbrFaceOperator { HbrFace const * _face; HbrVertex const * _corner; std::vector & _verts; public: GatherOperator( HbrFace const * face, HbrVertex const * corner, std::vector & verts ) : _face(face), _corner(corner), _verts(verts) { } ~GatherOperator() { } virtual void operator() (HbrFace &face) { HbrMesh * mesh = face.GetMesh(); assert(mesh); if (&face==_face) return; int nv = face.GetNumVertices(); for (int i=0; i * e = face.GetEdge(i); // skip edges along original 0-ring face if (e and (e->GetRightFace()==_face or e->GetLeftFace()==_face)) continue; // skip original corner vertex if (e->GetDestVertex(mesh)!=_corner) _verts.push_back(e->GetDestVertexID()); } } }; for (int i=0; iGetNumVertices(); ++i) { HbrVertex * corner = f->GetVertex(i); // save the the 0-ring corner vertices _vertIndices.push_back( corner->GetID() ); // gather the 1-ring vertices GatherOperator op( f, corner, _vertIndices ); corner->ApplyOperatorSurroundingFaces( op ); } std::sort( _vertIndices.begin(), _vertIndices.end()); std::vector::const_iterator last = std::unique(_vertIndices.begin(), _vertIndices.end()); int ssize = (int)(last-_vertIndices.begin()); _vertIndices.resize(ssize); // At this point _vertIndices contains the IDs of all base vertices that // control the limit surface patch corresponding to the given face. // Now we need to create and cache a base stencil for each of these // control vertices. if (_allocator) { delete _allocator; } for (int i=0; i<4; ++i) { _reflectedStencils[i] = NULL; } _allocator = new FarVertexStencilAllocator(ssize); for (int i=0; iAllocate(); stencil->Reset(0.0f); // Set the i-th coefficient of this stencil to 1.0 stencil->_GetData()[i] = 1.0f; // Assign the stencil to the control vertex mesh->GetVertex(_vertIndices[i])->GetData()._SetStencil(stencil); } } // Evaluate the surface for the quad face left of edge at local coordinates // (u,v), computing point and tangent stencils for non-null corresponding // stencil pointers. // // In most cases only approximate evaluation is done by linearly interpolating // between limit values after refLevel subdivision steps. // template bool FarStencilTablesFactory::Patch::GetStencilsAtUV( HbrHalfedge * e, float u, float v, int reflevel, float *point, float *uderiv, float *vderiv ) { assert( _allocator ); HbrFace * f = e->GetLeftFace(); if (f->IsHole()) return false; // non-quad ? get corresponding quadrant sub-face if (f->GetMesh()->GetSubdivision()->FaceIsExtraordinary(f->GetMesh(), f)) { f->Refine(); f = f->GetChild(GetCurrentQuadrant()); } // We cache the B-spline stencils that we compute for a face, // so the first thing we do is check whether the face that was given // to us is the same one for which we last computed the B-spline // stencils. If this is so, we can simply return the pointer. if (f==_bsplineFace) { _GetBSplineStencilsAtUV( u, v, point, uderiv, vderiv ); return true; } else if (_IsaBSpline(f)) { _UpdateBSplineStencils( f ); _GetBSplineStencilsAtUV( u, v, point, uderiv, vderiv ); return true; } // We reached the maximum recursion : give up ! if (reflevel==0) { assert(not f->IsCoarse()); FarVertexStencil * pt = _allocator->Allocate(), * utan = _allocator->Allocate(), * vtan = _allocator->Allocate(); // Compute the bi-linear weights corresponding to the 4 face corners: float weights[4]; weights[0] = (1.0f - u)*(1.0f - v); weights[1] = u * (1.0f - v); weights[2] = u * v; weights[3] = (1.0f - u) * v; // Iterate over the 4 corners for(int i = 0; i < 4; ++i) { if(weights[i] == 0) continue; HbrHalfedge * elimit = f->GetEdge(i); HbrVertex * vlimit = f->GetVertex(i); _GetLimitStencils( vlimit, pt->_GetData() ); FarVertexStencil::AddScaled( point, pt, weights[i] ); if (uderiv and vderiv) { _GetTangentLimitStencils( elimit, utan->_GetData(), vtan->_GetData() ); // Tangent vectors must compensate for the CCW rotation of elimit switch (i) { case 0: { FarVertexStencil::AddScaled( uderiv, utan, weights[i] ); FarVertexStencil::AddScaled( vderiv, vtan, weights[i] ); } break; case 1: { FarVertexStencil::AddScaled( uderiv, vtan, -weights[i] ); FarVertexStencil::AddScaled( vderiv, utan, weights[i] ); } break; case 2: { FarVertexStencil::AddScaled( uderiv, utan, -weights[i] ); FarVertexStencil::AddScaled( vderiv, vtan, -weights[i] ); } break; case 3: { FarVertexStencil::AddScaled( uderiv, vtan, weights[i] ); FarVertexStencil::AddScaled( vderiv, utan, -weights[i] ); } break; } } } _allocator->Deallocate(pt); _allocator->Deallocate(utan); _allocator->Deallocate(vtan); return true; } else { f->Refine(); int quadrant=-1; if (u<=0.5f and v<=0.5f) { quadrant = 0; } else if (u> 0.5f and v<=0.5f) { quadrant = 1; u-=0.5f; } else if (u> 0.5f and v> 0.5f) { quadrant = 2; u-=0.5f; v-=0.5f; } else if (u<=0.5f and v> 0.5f) { quadrant = 3; v-=0.5f; } else assert(0); assert(quadrant>-1 and quadrant<4); HbrVertex * a = f->GetVertex(quadrant)->Subdivide(), * b = f->GetEdge(quadrant)->Subdivide(); HbrHalfedge * subedge = a->GetEdge(b); GetStencilsAtUV( subedge, 2.0f*u, 2.0f*v, reflevel-1, point, uderiv, vderiv ); } return true; } // True if the vertex has a BSpline limit template bool FarStencilTablesFactory::Patch::_IsaBSpline( HbrVertex * v ) { if (v->IsExtraordinary() or v->GetMask(true)) return false; HbrHalfedge const * start = v->GetIncidentEdge(), * e = start; if (e) do { HbrFace * f = e->GetFace(); // Adjacent face must be regular if (f->GetNumVertices() != 4) return false; // If the adjacent face has hierarchical edits which don't // terminate at this level, we can't subdivide if (HbrHierarchicalEdit ** edits = f->GetHierarchicalEdits()) { while (HbrHierarchicalEdit const * edit = *edits) { if (not edit->IsRelevantToFace(f)) break; if (edit->GetNSubfaces() > f->GetDepth()) return false; edits++; } } HbrHalfedge const * next = v->GetNextEdge(e); if (!next) { // We encountered a break in the cycle. This means the // vertex is on a boundary! return false; } else { e = next; } } while (e != start); // If there is facevarying data, we check whether this is a // facevarying boundary vertex as well. if (v->GetMesh()->GetFVarCount() and v->GetMesh()->GetFVarInterpolateBoundaryMethod() != HbrMesh::k_InterpolateBoundaryNone) { if (not v->IsFVarAllSmooth()) return false; } return true; } // True if the edge has a BSpline limit template bool FarStencilTablesFactory::Patch::_IsaBSpline( HbrHalfedge * e ) { // Edge operator checking sharpness of next edge in cycle struct _SharpOnext { HbrHalfedge const * operator () (HbrHalfedge const * e0) const { HbrVertex const * v = e0->GetOrgVertex(); HbrHalfedge const * e = v->GetNextEdge(e0); if (e) do { if (e->GetSharpness()) break; e = v->GetNextEdge(e); } while (e and e != e0); return (e == e0 ? 0 : e); } }; e->GetOrgVertex()->GuaranteeNeighbors(); if (e->GetSharpness() and e->GetSharpness() < HbrHalfedge::k_InfinitelySharp) return false; HbrMesh const * mesh = e->GetLeftFace() ? e->GetLeftFace()->GetMesh() : e->GetRightFace()->GetMesh(); // currently the B-spline code does not handle facevarying sharp edges correctly if (mesh->GetFVarCount() and mesh->GetFVarInterpolateBoundaryMethod() != HbrHalfedge::k_InterpolateBoundaryNone) { for(int j = 0; j < mesh->GetFVarCount(); ++j) if(e->GetFVarSharpness(j)) return false; } if (e->GetSharpness()) { if (e->GetOrgVertex()->GetMask(true) != HbrVertex::k_Crease or e->GetDestVertex()->GetMask(true) != HbrVertex::k_Crease) return false; if (!e->GetOpposite()) return false; HbrHalfedge const * e1 = _SharpOnext(e), * e2 = _SharpOnext(e->GetOpposite()); if ( (not e1) or (not e2) or e1->GetSharpness() < HbrHalfedge::k_InfinitelySharp or e2->GetSharpness() < HbrHalfedge::k_InfinitelySharp ) return false; } else { if ((not _IsaBSpline(e->GetOrgVertex())) or (not _IsaBSpline(e->GetDestVertex()))) { return false; } } return true; } // True if the face has a BSpline limit template bool FarStencilTablesFactory::Patch::_IsaBSpline( HbrFace * f ) { int nv = f->GetNumVertices(); if (nv != 4) return false; if (f->IsHole()) return false; HbrMesh const * mesh = f->GetMesh(); HbrHalfedge * sharpEdge = 0, * edge = f->GetFirstEdge(); for (int i = 0; i < 4; ++i) { edge->GetOrgVertex()->GuaranteeNeighbors(); // currently the BSpline code does not handle facevarying // sharp edges correctly if (mesh->GetFVarCount() and mesh->GetFVarInterpolateBoundaryMethod() != HbrMesh::k_InterpolateBoundaryNone) { for(int j = 0; j < mesh->GetFVarCount(); ++j) if(edge->GetFVarSharpness(j)) return false; } // ensure that the face isn't adjacent to more than one sharp edge if (edge->GetSharpness() >= HbrHalfedge::k_InfinitelySharp) { if (sharpEdge) { return false; } else { sharpEdge = edge; } } edge = edge->GetNext(); } if (sharpEdge) { // Make sure the two vertices on the sharp edge are creases // and the other two vertices are bspline verts if ( sharpEdge->GetOrgVertex()->GetMask(true)!=HbrVertex::k_Crease or sharpEdge->GetDestVertex()->GetMask(true)!=HbrVertex::k_Crease or (not _IsaBSpline(sharpEdge->GetNext()->GetDestVertex())) or (not _IsaBSpline(sharpEdge->GetPrev()->GetOrgVertex()))) { return false; } // Make sure the hard edge continues being a hard edge on adjacent // two faces edge = sharpEdge->GetNext()->GetOpposite(); if (!edge) { return false; } if (edge->GetNext()->GetSharpness() != HbrHalfedge::k_InfinitelySharp) { return false; } edge = sharpEdge->GetPrev()->GetOpposite(); if ((not edge) or edge->GetPrev()->GetSharpness() != HbrHalfedge::k_InfinitelySharp) { return false; } } else { // All four vertices must be spline verts edge = f->GetFirstEdge(); for (int i = 0; i < 4; ++i) { if (!_IsaBSpline(edge->GetOrgVertex())) return false; edge = edge->GetNext(); } } return true; } // Updates the cached BSpline stencils template void FarStencilTablesFactory::Patch::_UpdateBSplineStencils( HbrFace const * f ) { assert( f->GetNumVertices()==4 ); _bsplineFace = f; // Fill out the array of stencils assuming this is a simple B-Spline // case, and no stencil reflection is necessary. static size_t indices[16] = {5, 4, 0, 1, 6, 2, 3, 7, 10, 11, 15, 14, 9, 13, 12, 8}; int sharpedge = -1; for (int i=0; i<4; ++i) { HbrHalfedge const * e = f->GetEdge(i); // First check if this edge is sharp, and if so remember it. // Also, if it's sharp we don't want all of the four stencils // whose numbers are given by indices[4*i]..indices[4*i+3]. // Rather, we need just the first two (the remaining two // will be computed by reflection later in this function). // // | | | // | | | --- : regular edge // | | | // | (4*i+1) | (4*i) | === : sharp edge // X --------- X ========= o ------ // e1 e X : accumulated vertices // if (e->GetSharpness() >= HbrHalfedge::k_InfinitelySharp) { sharpedge=i; HbrHalfedge * e1 = e->GetPrev()->GetOpposite()->GetPrev(); _bsplineStencils[indices[4*i ]] = e->GetOrgVertex()->GetData()._GetStencil(); _bsplineStencils[indices[4*i+1]] = e1->GetOrgVertex()->GetData()._GetStencil(); continue; } // Also must check if the previous edge was sharp. If it was, then // once again, we only need two stencils instead of four. In this // case those are the stencils numbered indices[4*i] and // indices[4*i+3]. // // | || | // | || | --- : regular edge // | || | // | || (4*i) | || : sharp edge // X --------- X --------- o ------ // | e | X : accumulated vertices // | | // | // | (4*i+3) // ---- X ---- // if (e->GetPrev()->GetSharpness() >= HbrHalfedge::k_InfinitelySharp) { _bsplineStencils[indices[4*i ]] = e->GetOrgVertex()->GetData()._GetStencil(); _bsplineStencils[indices[4*i+3]] = e->GetOpposite()->GetNext()->GetDestVertex()->GetData()._GetStencil(); continue; } // Both the current edge, and the previous one are smooth, // so we should get all four stencils in this case. HbrHalfedge * e2 = e->GetOpposite()->GetNext()->GetOpposite()->GetNext(); for (int j=0; j<4; ++j) { assert(e2); _bsplineStencils[indices[4*i+j]] = e2->GetOrgVertex()->GetData()._GetStencil(); e2 = e2->GetNext(); } } if (sharpedge<0) return; if (not _reflectedStencils[0]) { _reflectedStencils[0] = _allocator->Allocate(); _reflectedStencils[1] = _allocator->Allocate(); _reflectedStencils[2] = _allocator->Allocate(); _reflectedStencils[3] = _allocator->Allocate(); } // There is a sharp edge around the face, pointed to by sharpEdge. // We must override the corresponding 4 outer control stencils by // reflecting 4 internal control stencils across this edge. // We'll make use of the following three static index arrays. // reflect[k] lists the indices of those stencils who must be reflected // if the sharp edge is the k-th edge ccw around the face. static int reflect[4][4] = {{8, 9, 10, 11},{1, 5, 9, 13},{7, 6, 5, 4},{14, 10, 6, 2}}; // via[k] lists the indices of those stencils in the row or column // corresponding to the k-th sharp edge (ccw around the face). static int via[4][4] = {{4, 5, 6, 7}, {2, 6, 10, 14}, {11, 10, 9, 8}, {13, 9, 5, 1}}; // dest[k] lists the destination of the reflected stencils // corresponding to the k-th sharp edge (ccw around the face). static int dest[4][4] = {{0, 1, 2, 3}, {3, 7, 11, 15}, {15, 14, 13, 12}, {12, 8, 4, 0}}; // We must reflect the stencil reflect[sharpEdgeIndex][j] via // stencil via[sharpEdgeIndex][j], overriding the stencil // dest[sharpEdgeIndex][j] for (int i=0; i<4; ++i) { _reflectedStencils[i]->Combine( 2.0, *(_bsplineStencils[ via[sharpedge][i]]), -1.0, *(_bsplineStencils[reflect[sharpedge][i]]) ); _bsplineStencils[dest[sharpedge][i]] = _reflectedStencils[i]; } return; } // Computes BSpline weights template void FarStencilTablesFactory::Patch::_GetBSplineWeights( float t, float *cubicWeights, float *quadraticWeights) { // The weights for the four uniform cubic B-Spline basis functions are: // (1/6)(1 - t)^3 // (1/6)(3t^3 - 6t^2 + 4) // (1/6)(-3t^3 + 3t^2 + 3t + 1) // (1/6)t^3 float t2 = t*t; float t3 = 3*t2*t; float w0 = 1 - t; cubicWeights[0] = (w0*w0*w0) / 6.0f; cubicWeights[1] = (t3 - 6.0f*t2 + 4.0f) / 6.0f; cubicWeights[2] = (3.0f*t2 - t3 + 3.0f*t + 1.0f) / 6.0f; cubicWeights[3] = t3 / 18.0f; // The weights for the three uniform quadratic basis functions are: // (1/2)(1-t)^2 // (1/2)(1 + 2t - 2t^2) // (1/2)t^2 quadraticWeights[0] = 0.5f * w0 * w0; quadraticWeights[1] = 0.5f + t - t2; quadraticWeights[2] = 0.5f * t2; } // Computes BSpline stencil weights at (u,v) template void FarStencilTablesFactory::Patch::_GetBSplineStencilsAtUV( float u, float v, float *point, float *deriv1, float *deriv2 ) { float uWeights[4], vWeights[4], duWeights[3], dvWeights[3]; _GetBSplineWeights(u, uWeights, duWeights); _GetBSplineWeights(v, vWeights, dvWeights); if (point) { FarVertexStencil::Reset(point, 0.0f, GetStencilSize()); // Compute the tensor product weight corresponding to each control // vertex, and accumulate the weighted control stencils. for (int i = 0; i < 4; ++i) { for (int j = 0; j < 4; ++j) { float weight = uWeights[j] * vWeights[i]; FarVertexStencil::AddScaled(point, _bsplineStencils[4*i+j], weight); } } } if (deriv1 and deriv2) { // Compute the du tangent stencil. This is done by taking the tensor // product between the quadratic weights computed for u and the cubic // weights computed for v. The stencil is constructed using // differences between consecutive vertices in each row (i.e. // in the u direction). FarVertexStencil::Reset(deriv1, 0.0f, GetStencilSize()); for (int i = 0; i < 4; ++i) { float prevWeight = 0.0f; for (int j = 0; j < 3; ++j) { float weight = duWeights[j]*vWeights[i]; FarVertexStencil::AddScaled(deriv1, _bsplineStencils[4*i+j], prevWeight - weight); prevWeight = weight; } FarVertexStencil::AddScaled(deriv1, _bsplineStencils[4*i+3], prevWeight); } FarVertexStencil::Reset(deriv2, 0.0f, GetStencilSize()); for (int j = 0; j < 4; ++j) { float prevWeight = 0.0f; for (int i = 0; i < 3; ++i) { float weight = uWeights[j]*dvWeights[i]; FarVertexStencil::AddScaled(deriv2, _bsplineStencils[4*i+j], prevWeight - weight); prevWeight = weight; } FarVertexStencil::AddScaled(deriv2, _bsplineStencils[12+j], prevWeight); } _ScaleTangentStencil(_bsplineFace, GetStencilSize(), deriv1, deriv2); } } // Computes the limit stencils template void FarStencilTablesFactory::Patch::_GetLimitStencils( HbrVertex * v, float *point ) { v->GuaranteeNeighbors(); // Refine vertex until stable while (v->IsVolatile()) { v->Refine(); HbrVertex * vfine = v->Subdivide(); assert(vfine); v=vfine; } assert(v->GetMask(false) == v->GetMask(true)); switch(v->GetMask(false)) { case HbrVertex::k_Smooth: case HbrVertex::k_Dart: { assert(not v->OnBoundary()); FarVertexStencil::Reset(point, 0.0f, GetStencilSize()); int n = v->GetValence(); // // o ------- o o // b0*1 | a0*4 | b3*1 // | | // | | // | | // o ------- o ------- o // a1*4 | point | a3*4 // | | // | | // | | // o o ------- o // b1*1 a2*4 b2*1 // // point = (a0+a1+a2+a3)*4 + (b0+b1+b2+b3) // class SmoothVertexOperator : public HbrHalfedgeOperator { public: SmoothVertexOperator(HbrVertex *v, float * point) : _vertex(v), _point(point) { } ~SmoothVertexOperator() { } virtual void operator() (HbrHalfedge &e) { HbrVertex * a = e.GetDestVertex(); HbrHalfedge * next = e.GetNext(); if (a==_vertex) { a = e.GetOrgVertex(); next = e.GetPrev(); } HbrVertex * b = next->GetDestVertex(); FarVertexStencil::AddScaled( _point, a->GetData().GetStencil(), 4.0f ); FarVertexStencil::Add( _point, b->GetData().GetStencil() ); } private: HbrVertex * _vertex; float * _point; }; SmoothVertexOperator op( v, point ); v->ApplyOperatorSurroundingEdges(op); float s = 1.0f / float(n*(n+5.0f)); FarVertexStencil::Scale(point, s, GetStencilSize()); FarVertexStencil::AddScaled(point, v->GetData().GetStencil(), s*n*n ); } break; case HbrVertex::k_Crease: { FarVertexStencil::Reset(point, 0.0f, GetStencilSize()); // // | | | // | | | // | | | --- : regular edge // ---- o ======= o ======= o --- // a1 | point | | a0 === : sharp edge // | | | // | | | // // point = a0 + a1 // class CreaseEdgeOperator : public HbrHalfedgeOperator { public: CreaseEdgeOperator(HbrVertex *v, float * point) : _vertex(v), _point(point), _count(0) { } ~CreaseEdgeOperator() { } virtual void operator() (HbrHalfedge &e) { if (_count<2 and e.IsSharp(false)) { HbrVertex * a = e.GetDestVertex(); if (a==_vertex) a = e.GetOrgVertex(); FarVertexStencil::Add( _point, a->GetData().GetStencil() ); ++_count; } } private: HbrVertex * _vertex; float * _point; int _count; }; CreaseEdgeOperator op( v, point ); v->ApplyOperatorSurroundingEdges(op); FarVertexStencil::Scale( point, 1.0f/6.0f, GetStencilSize() ); FarVertexStencil::AddScaled( point, v->GetData().GetStencil(), 2.0f/3.0f ); } break; case HbrVertex::k_Corner: { FarVertexStencil::Copy( point, v->GetData().GetStencil() ); } break; } } // Scale tangent stencils so that magnitudes are consistent across // isolation levels. template void FarStencilTablesFactory::Patch::_ScaleTangentStencil( HbrFace const * f, int stencilsize, float *uderiv, float *vderiv ) { float scale = float (1 << f->GetDepth()); FarVertexStencil::Scale(uderiv, scale, stencilsize); FarVertexStencil::Scale(vderiv, scale, stencilsize); } // Computes derivative limit stencils template void FarStencilTablesFactory::Patch::_GetTangentLimitStencils( HbrHalfedge * e, float * uderiv, float * vderiv ) { // Boundary vertex tangent stencil table generated using the python script: // opensubdiv/tools/tangentStencils/tangentStencils.py #define FAR_LIMITTANGENT_MAXVALENCE 20 static float creaseK[][40] = { { -0.662085f, -0.082761f, 0.662085f, -0.248282f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.165521f, 0.165521f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, }, { 0.430450f, 0.385279f, -0.430450f, -0.430450f, 0.475622f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, -0.107613f, -0.215225f, -0.107613f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, }, { -0.263966f, -0.557805f, 0.263966f, 0.373304f, 0.263966f, -0.530084f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.065992f, 0.159318f, 0.159318f, 0.065992f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, }, { 0.173071f, 0.574415f, -0.173071f, -0.280034f, -0.280034f, -0.173071f, 0.611831f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, -0.043268f, -0.113276f, -0.140017f, -0.113276f, -0.043268f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, }, { 0.110310f, 0.858370f, -0.110310f, -0.191063f, -0.220620f, -0.191063f, -0.110310f, 0.266369f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, -0.027578f, -0.075343f, -0.102921f, -0.102921f, -0.075343f, -0.027578f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, }, { 0.059499f, -0.108652f, -0.059499f, -0.107213f, -0.133693f, -0.133693f, -0.107213f, -0.059499f, 0.950367f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, -0.014875f, -0.041678f, -0.060226f, -0.066846f, -0.060226f, -0.041678f, -0.014875f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, }, { -0.069034f, -0.643889f, 0.069034f, 0.127558f, 0.166663f, 0.180394f, 0.166663f, 0.127558f, 0.069034f, -0.647432f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.017258f, 0.049148f, 0.073555f, 0.086764f, 0.086764f, 0.073555f, 0.049148f, 0.017258f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, }, { 0.054360f, 0.565883f, -0.054360f, -0.102164f, -0.137645f, -0.156524f, -0.156524f, -0.137645f, -0.102164f, -0.054360f, 0.731835f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, -0.013590f, -0.039131f, -0.059952f, -0.073542f, -0.078262f, -0.073542f, -0.059952f, -0.039131f, -0.013590f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, }, { -0.044000f, -0.755024f, 0.044000f, 0.083693f, 0.115193f, 0.135418f, 0.142386f, 0.135418f, 0.115193f, 0.083693f, 0.044000f, -0.549465f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.011000f, 0.031923f, 0.049721f, 0.062653f, 0.069451f, 0.069451f, 0.062653f, 0.049721f, 0.031923f, 0.011000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, }, { 0.010145f, -0.497027f, -0.010145f, -0.019467f, -0.027213f, -0.032754f, -0.035642f, -0.035642f, -0.032754f, -0.027213f, -0.019467f, -0.010145f, 0.862545f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, -0.002536f, -0.007403f, -0.011670f, -0.014992f, -0.017099f, -0.017821f, -0.017099f, -0.014992f, -0.011670f, -0.007403f, -0.002536f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, }, { -0.029547f, -0.419056f, 0.029547f, 0.057081f, 0.080725f, 0.098867f, 0.110272f, 0.114162f, 0.110272f, 0.098867f, 0.080725f, 0.057081f, 0.029547f, -0.852117f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.007387f, 0.021657f, 0.034451f, 0.044898f, 0.052285f, 0.056109f, 0.056109f, 0.052285f, 0.044898f, 0.034451f, 0.021657f, 0.007387f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, }, { 0.022520f, 0.197157f, -0.022520f, -0.043731f, -0.062400f, -0.077443f, -0.087986f, -0.093415f, -0.093415f, -0.087986f, -0.077443f, -0.062400f, -0.043731f, -0.022520f, 0.942807f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, -0.005630f, -0.016563f, -0.026533f, -0.034961f, -0.041357f, -0.045350f, -0.046707f, -0.045350f, -0.041357f, -0.034961f, -0.026533f, -0.016563f, -0.005630f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, }, { -0.022403f, -0.513093f, 0.022403f, 0.043683f, 0.062773f, 0.078714f, 0.090709f, 0.098155f, 0.100680f, 0.098155f, 0.090709f, 0.078714f, 0.062773f, 0.043683f, 0.022403f, -0.804837f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.005601f, 0.016522f, 0.026614f, 0.035372f, 0.042356f, 0.047216f, 0.049709f, 0.049709f, 0.047216f, 0.042356f, 0.035372f, 0.026614f, 0.016522f, 0.005601f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, }, { -0.019877f, -0.743782f, 0.019877f, 0.038885f, 0.056194f, 0.071047f, 0.082795f, 0.090924f, 0.095079f, 0.095079f, 0.090924f, 0.082795f, 0.071047f, 0.056194f, 0.038885f, 0.019877f, -0.600744f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.004969f, 0.014691f, 0.023770f, 0.031810f, 0.038460f, 0.043430f, 0.046501f, 0.047540f, 0.046501f, 0.043430f, 0.038460f, 0.031810f, 0.023770f, 0.014691f, 0.004969f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, }, { 0.015725f, 0.289165f, -0.015725f, -0.030845f, -0.044780f, -0.056994f, -0.067018f, -0.074466f, -0.079053f, -0.080602f, -0.079053f, -0.074466f, -0.067018f, -0.056994f, -0.044780f, -0.030845f, -0.015725f, 0.922656f, 0.000000f, 0.000000f, 0.000000f, -0.003931f, -0.011642f, -0.018906f, -0.025444f, -0.031003f, -0.035371f, -0.038380f, -0.039914f, -0.039914f, -0.038380f, -0.035371f, -0.031003f, -0.025444f, -0.018906f, -0.011642f, -0.003931f, 0.000000f, 0.000000f, 0.000000f, }, { 0.015399f, 0.556833f, -0.015399f, -0.030273f, -0.044117f, -0.056458f, -0.066877f, -0.075018f, -0.080605f, -0.083446f, -0.083446f, -0.080605f, -0.075018f, -0.066877f, -0.056458f, -0.044117f, -0.030273f, -0.015399f, 0.784351f, 0.000000f, 0.000000f, -0.003850f, -0.011418f, -0.018598f, -0.025144f, -0.030834f, -0.035474f, -0.038906f, -0.041013f, -0.041723f, -0.041013f, -0.038906f, -0.035474f, -0.030834f, -0.025144f, -0.018598f, -0.011418f, -0.003850f, 0.000000f, 0.000000f, }, { -0.006900f, -0.951532f, 0.006900f, 0.013591f, 0.019869f, 0.025543f, 0.030441f, 0.034414f, 0.037341f, 0.039134f, 0.039737f, 0.039134f, 0.037341f, 0.034414f, 0.030441f, 0.025543f, 0.019869f, 0.013591f, 0.006900f, 0.277130f, 0.000000f, 0.001725f, 0.005123f, 0.008365f, 0.011353f, 0.013996f, 0.016214f, 0.017939f, 0.019119f, 0.019718f, 0.019718f, 0.019119f, 0.017939f, 0.016214f, 0.013996f, 0.011353f, 0.008365f, 0.005123f, 0.001725f, 0.000000f, }, { 0.010264f, 0.154214f, -0.010264f, -0.020248f, -0.029680f, -0.038302f, -0.045879f, -0.052205f, -0.057107f, -0.060451f, -0.062146f, -0.062146f, -0.060451f, -0.057107f, -0.052205f, -0.045879f, -0.038302f, -0.029680f, -0.020248f, -0.010264f, 0.964364f, -0.002566f, -0.007628f, -0.012482f, -0.016995f, -0.021045f, -0.024521f, -0.027328f, -0.029389f, -0.030649f, -0.031073f, -0.030649f, -0.029389f, -0.027328f, -0.024521f, -0.021045f, -0.016995f, -0.012482f, -0.007628f, -0.002566f, }, }; HbrVertex * v = e->GetOrgVertex(); while (v->IsVolatile()) { v->Refine(); v = v->Subdivide(); e = v->GetEdge(e->Subdivide()); assert(e); } assert(v->GetMask(false) == v->GetMask(true)); switch(v->GetMask(false)) { case HbrVertex::k_Smooth: case HbrVertex::k_Dart: { FarVertexStencil::Reset(uderiv, 0.0f, GetStencilSize()); FarVertexStencil::Reset(vderiv, 0.0f, GetStencilSize()); int n = v->GetValence(); float alpha = 2.0f * static_cast(M_PI) / (float) n, c0 = 2.0f * cosf(alpha), c1 = 1.0f, A = 1.0f + c0 + sqrtf(18.0f + c0) * cosf(0.5f * alpha); int i = 0; float d = 0.0f; HbrHalfedge * e0=e; if (e0) do { HbrHalfedge * onext = v->GetNextEdge(e0); assert(onext); HbrVertex * w1 = e0->GetDestVertex(), * f1 = e0->GetNext()->GetDestVertex(), * w2 = onext->GetDestVertex(), * f2 = onext->GetNext()->GetDestVertex(); c0 = c1; c1 = cosf((i+1.0f) * alpha); float K1 = A * c0, K2 = c0 + c1; d += fabsf(K1) + fabsf(K2); // Accumulate FarVertexStencil::AddScaled(uderiv, w1->GetData().GetStencil(), K1); FarVertexStencil::AddScaled(uderiv, f1->GetData().GetStencil(), K2); FarVertexStencil::AddScaled(vderiv, w2->GetData().GetStencil(), K1); FarVertexStencil::AddScaled(vderiv, f2->GetData().GetStencil(), K2); e0 = onext; i++; } while (e0 != e); // XXXX prman scales deriv1 and deriv2 by 1.0/d // Why? Do we need to do this as well? float invd = 1.0f/d; FarVertexStencil::Scale(uderiv, invd, GetStencilSize()); FarVertexStencil::Scale(vderiv, invd, GetStencilSize()); } break; case HbrVertex::k_Crease: { std::list *> edges; v->GetSurroundingEdges(std::back_inserter(edges)); std::list *> vertices; v->GetSurroundingVertices(std::back_inserter(vertices)); assert(edges.size()==vertices.size()); // Circle the lists around so that we start processing at the edge // after 'e' while (*edges.rbegin() != e) { edges.push_back(edges.front()); edges.pop_front(); vertices.push_back(vertices.front()); vertices.pop_front(); } // Look for the two sharp edges int idx=0, e1i=-1, e2i=-1; typename std::list *>::iterator ei; typename std::list *>::iterator vi, v1i, v2i; for (ei=edges.begin(), vi=vertices.begin(); ei!=edges.end(); ++ei, ++vi, ++idx) { if ((*ei)->IsSharp(false)) { if (e2i<0) { e2i = idx; v2i = vi; } else { e1i = idx; v1i = vi; break; } } } // We expected (at least) two edges to be on a crease. Instead, there // are zero or 1. We're not really sure what to do in that case, but // the easiest thing to do is to ignore the crease, which fixes the // coredump at the very least. The code here is exactly the same as // the default case. if (e1i<0 or e2i<0) { e = e->GetPrev(); HbrVertex * v1 = e->GetDestVertex(), * v2 = e->GetNext()->GetDestVertex(); FarVertexStencil::Subtract(uderiv, v1->GetData().GetStencil(), v->GetData().GetStencil()); FarVertexStencil::Subtract(vderiv, v2->GetData().GetStencil(), v->GetData().GetStencil()); break; } // Count the number of edges between e1 and e2 going clockwise. // Since e1 is AFTER e2 (see above), this just requires some math on // the edge indices int n = (int)edges.size() - e1i + e2i + 1; assert(n >= 2); // creaseK table has 11 entries : max valence is 10 // XXXX error should be reported if (n >= FAR_LIMITTANGENT_MAXVALENCE) { break; } // Math on the two crease vertices HbrVertex * v1 = *v1i, * v2 = *v2i; FarVertexStencil::Subtract(uderiv, v1->GetData().GetStencil(), v2->GetData().GetStencil()); FarVertexStencil::Scale(uderiv, 0.5f, GetStencilSize()); FarVertexStencil::Reset(vderiv, 0.0f, GetStencilSize()); FarVertexStencil::AddScaled(vderiv, v->GetData().GetStencil(), creaseK[n][0]); FarVertexStencil::AddScaled(vderiv, v1->GetData().GetStencil(), creaseK[n][1]); FarVertexStencil::AddScaled(vderiv, v2->GetData().GetStencil(), creaseK[n][2]); // Math on vertices between the two creases float d = fabsf(creaseK[n][0]) + fabsf(creaseK[n][1]) + fabsf(creaseK[n][2]); idx = 3; vi = v1i; if ((++vi)==vertices.end()) { vi = vertices.begin(); } while (vi!=v2i) { FarVertexStencil::AddScaled(vderiv, (*vi)->GetData().GetStencil(), creaseK[n][idx]); d += fabsf(creaseK[n][idx]); ++idx; if (++vi==vertices.end()) { vi = vertices.begin(); } } FarVertexStencil::Scale(vderiv, -2.0f/d, GetStencilSize()); } break; case HbrVertex::k_Corner: { HbrVertex * v1 = e->GetDestVertex(), * v2 = v->GetQEONext(v1); FarVertexStencil::Subtract(uderiv, v1->GetData().GetStencil(), v->GetData().GetStencil()); FarVertexStencil::Subtract(vderiv, v2->GetData().GetStencil(), v->GetData().GetStencil()); } break; } _ScaleTangentStencil(e->GetFace(), GetStencilSize(), uderiv, vderiv); } template int FarStencilTablesFactory::GetMaxValenceSupported() { return FAR_LIMITTANGENT_MAXVALENCE; } } // end namespace OPENSUBDIV_VERSION using namespace OPENSUBDIV_VERSION; } // end namespace OpenSubdiv #endif // FAR_STENCILTABLE_FACTORY_H