// // Copyright (C) Pixar. All rights reserved. // // This license governs use of the accompanying software. If you // use the software, you accept this license. If you do not accept // the license, do not use the software. // // 1. Definitions // The terms "reproduce," "reproduction," "derivative works," and // "distribution" have the same meaning here as under U.S. // copyright law. A "contribution" is the original software, or // any additions or changes to the software. // A "contributor" is any person or entity that distributes its // contribution under this license. // "Licensed patents" are a contributor's patent claims that read // directly on its contribution. // // 2. Grant of Rights // (A) Copyright Grant- Subject to the terms of this license, // including the license conditions and limitations in section 3, // each contributor grants you a non-exclusive, worldwide, // royalty-free copyright license to reproduce its contribution, // prepare derivative works of its contribution, and distribute // its contribution or any derivative works that you create. // (B) Patent Grant- Subject to the terms of this license, // including the license conditions and limitations in section 3, // each contributor grants you a non-exclusive, worldwide, // royalty-free license under its licensed patents to make, have // made, use, sell, offer for sale, import, and/or otherwise // dispose of its contribution in the software or derivative works // of the contribution in the software. // // 3. Conditions and Limitations // (A) No Trademark License- This license does not grant you // rights to use any contributor's name, logo, or trademarks. // (B) If you bring a patent claim against any contributor over // patents that you claim are infringed by the software, your // patent license from such contributor to the software ends // automatically. // (C) If you distribute any portion of the software, you must // retain all copyright, patent, trademark, and attribution // notices that are present in the software. // (D) If you distribute any portion of the software in source // code form, you may do so only under this license by including a // complete copy of this license with your distribution. If you // distribute any portion of the software in compiled or object // code form, you may only do so under a license that complies // with this license. // (E) The software is licensed "as-is." You bear the risk of // using it. The contributors give no express warranties, // guarantees or conditions. You may have additional consumer // rights under your local laws which this license cannot change. // To the extent permitted under your local laws, the contributors // exclude the implied warranties of merchantability, fitness for // a particular purpose and non-infringement. // #ifndef FAR_MESH_FACTORY_H #define FAR_MESH_FACTORY_H #include #include "../version.h" #include "../hbr/mesh.h" #include "../hbr/bilinear.h" #include "../hbr/catmark.h" #include "../hbr/loop.h" #include "../far/mesh.h" #include "../far/dispatcher.h" #include "../far/bilinearSubdivisionTablesFactory.h" #include "../far/catmarkSubdivisionTablesFactory.h" #include "../far/loopSubdivisionTablesFactory.h" #include "../far/vertexEditTablesFactory.h" namespace OpenSubdiv { namespace OPENSUBDIV_VERSION { /// \brief Instantiates a FarMesh from an HbrMesh. /// /// FarMeshFactory requires a 2 steps process : /// 1. Instantiate a FarMeshFactory object from an HbrMesh /// 2. Call "Create" to obtain the FarMesh instance /// /// This tiered factory approach offers client-code the opportunity to access /// useful transient information tied to the lifespan of the factory instance. /// Specifically, regression code needs to access the remapping tables that /// tie HbrMesh vertices to their FarMesh counterparts for comparison. template class FarMeshFactory { public: // Constructor for the factory : analyzes the HbrMesh and stores // transient data used to create the adaptive patch representation. // Once the new rep has been instantiated with 'Create', this factory // object can be deleted safely. FarMeshFactory(HbrMesh * mesh, int maxlevel); /// Create a table-based mesh representation FarMesh * Create( FarDispatcher * dispatch=0 ); /// Maximum level of subidivision supported by this factory int GetMaxLevel() const { return _maxlevel; } /// Total number of face vertices up to 'level' int GetNumFaceVerticesTotal(int level) const { return sumList *>(_faceVertsList, level); } /// Total number of edge vertices up to 'level' int GetNumEdgeVerticesTotal(int level) const { return sumList *>(_edgeVertsList, level); } /// Total number of vertex vertices up to 'level' int GetNumVertexVerticesTotal(int level) const { return sumList *>(_vertVertsList, level); } /// Valence summation up to 'level' int GetNumAdjacentVertVerticesTotal(int level) const; /// Total number of faces across up to a level int GetNumFacesTotal(int level) const { return sumList *>(_facesList, level); } /// Return the corresponding index of the HbrVertex in the new mesh int GetVertexID( HbrVertex * v ); /// Returns a the mapping between HbrVertex->GetID() and Far vertices indices std::vector const & GetRemappingTable( ) const { return _remapTable; } private: friend struct FarBilinearSubdivisionTablesFactory; friend struct FarCatmarkSubdivisionTablesFactory; friend struct FarLoopSubdivisionTablesFactory; friend struct FarVertexEditTablesFactory; // Non-copyable, so these are not implemented: FarMeshFactory( FarMeshFactory const & ); FarMeshFactory & operator=(FarMeshFactory const &); static bool isBilinear(HbrMesh * mesh); static bool isCatmark(HbrMesh * mesh); static bool isLoop(HbrMesh * mesh); void generatePtexCoordinates( std::vector & vec, int level ); void copyTopology( std::vector & vec, int level ); static bool compareVertices( HbrVertex const *x, HbrVertex const *y ); static void refine( HbrMesh * mesh, int maxlevel ); template static int sumList( std::vector > const & list, int level ); private: HbrMesh * _hbrMesh; int _maxlevel, _numVertices, _numFaces; // per-level counters and offsets for each type of vertex (face,edge,vert) std::vector _faceVertIdx, _edgeVertIdx, _vertVertIdx; // number of indices required for the vertex iteration table at each level std::vector _vertVertsListSize; // remapping table to translate vertex ID's between Hbr indices and the // order of the same vertices in the tables std::vector _remapTable; // lists of vertices sorted by type and level std::vector *> > _faceVertsList, _edgeVertsList, _vertVertsList; // list of faces sorted by level std::vector *> > _facesList; }; template template int FarMeshFactory::sumList( std::vector > const & list, int level) { level = std::min(level, (int)list.size()-1); int total = 0; for (int i=0; i<=level; ++i) total += (int)list[i].size(); return total; } template int FarMeshFactory::GetNumAdjacentVertVerticesTotal(int level) const { level = std::min(level, GetMaxLevel()); int total = 0; for (int i=0; i<=level; ++i) total += _vertVertsListSize[i]; return total; } template void FarMeshFactory::refine( HbrMesh * mesh, int maxlevel ) { for (int l=0; lGetNumFaces(); for (int i=0; i * f = mesh->GetFace(i); if (f->GetDepth()==l) f->Refine(); } } } // Compare the weight masks of 2 vertices using the following ordering table. // // Assuming 2 computer kernels : // - A handles the k_Crease and K_Corner rules // - B handles the K_Smooth and K_Dart rules // The vertices should be sorted so as to minimize the number execution calls of // these kernels to match the 2 pass interpolation scheme used in Hbr. template bool FarMeshFactory::compareVertices( HbrVertex const * x, HbrVertex const * y ) { // Masks of the parent vertex decide for the current vertex. HbrVertex * px=x->GetParentVertex(), * py=y->GetParentVertex(); assert( (FarSubdivisionTables::getMaskRanking(px->GetMask(false), px->GetMask(true) )!=0xFF) and (FarSubdivisionTables::getMaskRanking(py->GetMask(false), py->GetMask(true) )!=0xFF) ); return FarSubdivisionTables::getMaskRanking(px->GetMask(false), px->GetMask(true) ) < FarSubdivisionTables::getMaskRanking(py->GetMask(false), py->GetMask(true) ); } // Assumption : the order of the vertices in the HbrMesh could be set in any // random order, so the builder runs 2 passes over the entire vertex list to // gather the counters needed to generate the indexing tables. template FarMeshFactory::FarMeshFactory( HbrMesh * mesh, int maxlevel ) : _hbrMesh(mesh), _maxlevel(maxlevel), _numVertices(-1), _numFaces(-1), _faceVertIdx(maxlevel+1,0), _edgeVertIdx(maxlevel+1,0), _vertVertIdx(maxlevel+1,0), _vertVertsListSize(maxlevel+1,0), _faceVertsList(maxlevel+1), _edgeVertsList(maxlevel+1), _vertVertsList(maxlevel+1), _facesList(maxlevel+1) { // non-adaptive subdivision of the Hbr mesh up to maxlevel refine( mesh, maxlevel); int numVertices = mesh->GetNumVertices(); int numFaces = mesh->GetNumFaces(); std::vector faceCounts(maxlevel+1,0), edgeCounts(maxlevel+1,0), vertCounts(maxlevel+1,0); // First pass (vertices) : count the vertices of each type for each depth // up to maxlevel (values are dependent on topology). int maxvertid=-1; for (int i=0; i * v = mesh->GetVertex(i); assert(v); int depth = v->GetFace()->GetDepth(); if (depth>maxlevel) continue; if (depth==0 ) vertCounts[depth]++; if (v->GetID()>maxvertid) maxvertid = v->GetID(); if (not v->OnBoundary()) _vertVertsListSize[depth] += v->GetValence(); else if (v->GetValence()!=2) _vertVertsListSize[depth] ++; if (v->GetParentFace()) faceCounts[depth]++; else if (v->GetParentEdge()) edgeCounts[depth]++; else if (v->GetParentVertex()) vertCounts[depth]++; } // Per-level offset to the first vertex of each type in the global vertex map _vertVertsList[0].reserve( vertCounts[0] ); for (int l=1; l<(maxlevel+1); ++l) { _faceVertIdx[l]= _vertVertIdx[l-1]+vertCounts[l-1]; _edgeVertIdx[l]= _faceVertIdx[l]+faceCounts[l]; _vertVertIdx[l]= _edgeVertIdx[l]+edgeCounts[l]; _faceVertsList[l].reserve( faceCounts[l] ); _edgeVertsList[l].reserve( edgeCounts[l] ); _vertVertsList[l].reserve( vertCounts[l] ); } // reset counters faceCounts.assign(maxlevel+1,0); edgeCounts.assign(maxlevel+1,0); _remapTable.resize( maxvertid+1, -1); // Second pass (vertices) : calculate the starting indices of the sub-tables // (face, edge, verts...) and populate the remapping table. for (int i=0; i * v = mesh->GetVertex(i); assert(v); int depth = v->GetFace()->GetDepth(); if (depth>maxlevel) continue; assert( _remapTable[ v->GetID() ] = -1 ); if (depth==0) { _vertVertsList[ depth ].push_back( v ); _remapTable[ v->GetID() ] = v->GetID(); } else if (v->GetParentFace()) { _remapTable[ v->GetID() ]=_faceVertIdx[depth]+faceCounts[depth]++; _faceVertsList[ depth ].push_back( v ); } else if (v->GetParentEdge()) { _remapTable[ v->GetID() ]=_edgeVertIdx[depth]+edgeCounts[depth]++; _edgeVertsList[ depth ].push_back( v ); } else if (v->GetParentVertex()) { // vertices need to be sorted separately based on compute kernel : // the remapping step is done just after this _vertVertsList[ depth ].push_back( v ); } } // Sort the the vertices that are the child of a vertex based on their weight // mask. The masks combinations are ordered so as to minimize the compute // kernel switching. for (size_t i=1; i<_vertVertsList.size(); ++i) std::sort(_vertVertsList[i].begin(), _vertVertsList[i].end(),compareVertices); // These vertices still need a remapped index for (int l=1; l<(maxlevel+1); ++l) for (size_t i=0; i<_vertVertsList[l].size(); ++i) _remapTable[ _vertVertsList[l][i]->GetID() ]=_vertVertIdx[l]+(int)i; // Third pass (faces) : populate the face lists. int fsize=0; for (int i=0; i * f = mesh->GetFace(i); assert(f); if (f->GetDepth()==0) fsize += mesh->GetSubdivision()->GetFaceChildrenCount( f->GetNumVertices() ); } _facesList[0].reserve(mesh->GetNumCoarseFaces()); _facesList[1].reserve(fsize); for (int l=2; l<=maxlevel; ++l) _facesList[l].reserve( _facesList[l-1].capacity()*4 ); for (int i=0; i * f = mesh->GetFace(i); if (f->GetDepth()<=maxlevel) _facesList[ f->GetDepth() ].push_back(f); } _numFaces = GetNumFacesTotal(maxlevel); _numVertices = GetNumFaceVerticesTotal(maxlevel) + GetNumEdgeVerticesTotal(maxlevel) + GetNumVertexVerticesTotal(maxlevel); } template bool FarMeshFactory::isBilinear(HbrMesh * mesh) { return typeid(*(mesh->GetSubdivision()))==typeid(HbrBilinearSubdivision); } template bool FarMeshFactory::isCatmark(HbrMesh * mesh) { return typeid(*(mesh->GetSubdivision()))==typeid(HbrCatmarkSubdivision); } template bool FarMeshFactory::isLoop(HbrMesh * mesh) { return typeid(*(mesh->GetSubdivision()))==typeid(HbrLoopSubdivision); } template void FarMeshFactory::copyTopology( std::vector & vec, int level ) { assert( _hbrMesh ); int nv=-1; if ( isCatmark(_hbrMesh) or isBilinear(_hbrMesh) ) nv=4; else if ( isLoop(_hbrMesh) ) nv=3; assert(nv>0); vec.resize( nv * _facesList[level].size(), -1 ); for (int i=0; i<(int)_facesList[level].size(); ++i) { HbrFace * f = _facesList[level][i]; assert( f and f->GetNumVertices()==nv); for (int j=0; jGetNumVertices(); ++j) vec[nv*i+j]=_remapTable[f->GetVertex(j)->GetID()]; } } template void copyVertex( T & dest, U const & src ) { } template void copyVertex( T & dest, T const & src ) { dest = src; } // XXXX : this currently only supports Catmark / Bilinear schemes. template void FarMeshFactory::generatePtexCoordinates( std::vector & vec, int level ) { assert( _hbrMesh ); if (_facesList[level][0]->GetPtexIndex() == -1) return; vec.resize( _facesList[level].size()*2, -1 ); for (int i=0; i<(int)_facesList[level].size(); ++i) { HbrFace const * f = _facesList[level][i]; assert(f); short u,v; unsigned short ofs = 1, depth; bool quad = true; // track upwards towards coarse face, accumulating u,v indices HbrFace const * p = f->GetParent(); for ( u=v=depth=0; p!=NULL; depth++ ) { int nverts = p->GetNumVertices(); if ( nverts != 4 ) { // non-quad coarse face : stop accumulating offsets quad = false; // invert the coarse face index break; } for (unsigned char i=0; iGetChild( i )==f ) { switch ( i ) { case 0 : break; case 1 : { u+=ofs; } break; case 2 : { u+=ofs; v+=ofs; } break; case 3 : { v+=ofs; } break; } break; } ofs = ofs << 1; f = p; p = f->GetParent(); } vec[2*i] = quad ? f->GetPtexIndex() : -f->GetPtexIndex(); vec[2*i+1] = (int)u << 16; vec[2*i+1] += v; } } template FarMesh * FarMeshFactory::Create( FarDispatcher * dispatch ) { assert( _hbrMesh ); if (_maxlevel<1) return 0; FarMesh * result = new FarMesh(); if (dispatch) result->_dispatcher = dispatch; else result->_dispatcher = & FarDispatcher::_DefaultDispatcher; if ( isBilinear( _hbrMesh ) ) { result->_subdivisionTables = FarBilinearSubdivisionTablesFactory::Create( this, result, _maxlevel ); } else if ( isCatmark( _hbrMesh ) ) { result->_subdivisionTables = FarCatmarkSubdivisionTablesFactory::Create( this, result, _maxlevel ); } else if ( isLoop(_hbrMesh) ) { result->_subdivisionTables = FarLoopSubdivisionTablesFactory::Create( this, result, _maxlevel ); } else assert(0); assert(result->_subdivisionTables); result->_numCoarseVertices = (int)_vertVertsList[0].size(); // Copy the data of the coarse vertices into the vertex buffer. // XXXX : we should figure out a test to make sure that the vertex // class is not an empty placeholder (ex. non-interleaved data) result->_vertices.resize( _numVertices ); for (int i=0; iGetNumCoarseVertices(); ++i) copyVertex(result->_vertices[i], _hbrMesh->GetVertex(i)->GetData()); // Populate topology (face verts indices) // XXXX : only k_BilinearQuads support for now - adaptive bicubic patches to come result->_patchtype = FarMesh::k_BilinearQuads; // XXXX : we should let the client control what to copy, most of this may be irrelevant result->_faceverts.resize(_maxlevel+1); for (int l=1; l<=_maxlevel; ++l) copyTopology(result->_faceverts[l], l); result->_ptexcoordinates.resize(_maxlevel+1); for (int l=1; l<=_maxlevel; ++l) generatePtexCoordinates(result->_ptexcoordinates[l], l); // Create VertexEditTables if necessary if (_hbrMesh->HasVertexEdits()) { result->_vertexEditTables = FarVertexEditTablesFactory::Create( this, result, _maxlevel ); assert(result->_vertexEditTables); } return result; } template int FarMeshFactory::GetVertexID( HbrVertex * v ) { assert( v and (v->GetID() < _remapTable.size()) ); return _remapTable[ v->GetID() ]; } } // end namespace OPENSUBDIV_VERSION using namespace OPENSUBDIV_VERSION; } // end namespace OpenSubdiv #endif /* FAR_MESH_FACTORY_H */