diff --git a/opensubdiv/far/catmarkSubdivisionTablesFactory.h b/opensubdiv/far/catmarkSubdivisionTablesFactory.h index 4d60d4bc..bd188b7f 100644 --- a/opensubdiv/far/catmarkSubdivisionTablesFactory.h +++ b/opensubdiv/far/catmarkSubdivisionTablesFactory.h @@ -26,6 +26,7 @@ #define FAR_CATMARK_SUBDIVISION_TABLES_FACTORY_H #include +#include #include #include "../version.h" @@ -48,6 +49,9 @@ template class FarCatmarkSubdivisionTablesFactory { protected: template friend class FarMeshFactory; + typedef std::vector VertexList; + typedef std::map VertexPermutation; + /// \brief Creates a FarSubdivisiontables instance with Catmark scheme. /// /// @param meshFactory a valid FarMeshFactory instance @@ -60,6 +64,90 @@ protected: // Compares vertices based on their topological configuration // (see subdivisionTables::GetMaskRanking for more details) static bool CompareVertices( HbrVertex const *x, HbrVertex const *y ); + + /// \brief Duplicates vertices in a kernel batch + /// + /// @param subdivisionTables the subdivision tables to modify + /// + /// @param kernelBatch kernel batch at the finest subdivision level + /// + /// @param vertexList the list of vertices to duplicate + /// + static void DuplicateVertices( FarSubdivisionTables * subdivisionTables, + FarKernelBatch &kernelBatch, + VertexList const &vertexList ); + + /// \brief Rearranges vertices in a kernel batch to process them in a + /// \brief specific order + /// + /// @param subdivisionTables the subdivision tables to modify + /// + /// @param kernelBatch the kernel batch + /// + /// @param vertexPermutation permutation of the vertices + /// + static bool PermuteVertices( FarSubdivisionTables * subdivisionTables, + FarKernelBatch const &kernelBatch, + VertexPermutation const &vertexPermutation ); + + /// \brief Remaps the vertices in a kernel batch + /// + /// @param subdivisionTables the subdivision tables to modify + /// + /// @param kernelBatch the kernel batch + /// + /// @param vertexPermutation permutation of the vertices + /// + static void RemapVertices( FarSubdivisionTables * subdivisionTables, + FarKernelBatch const &kernelBatch, + VertexPermutation const& vertexPermutation); + + /// \brief Shifts the vertices in a kernel batch + /// + /// @param subdivisionTables the subdivision tables to modify + /// + /// @param kernelBatch the kernel batch + /// + /// @param expandedKernelBatch the kernel batch whose range was expanded + /// + /// @param numVertices the number of vertices to shift + /// + static void ShiftVertices( FarSubdivisionTables * subdivisionTables, + FarKernelBatch &kernelBatch, + FarKernelBatch const &expandedKernelBatch, + int numVertices ); + +private: + /// \brief Duplicates vertices in an edge-vertex kernel batch + static void duplicateEdgeVertexKernelBatch( FarSubdivisionTables * subdivisionTables, + FarKernelBatch &kernelBatch, + VertexList const &vertexList ); + + /// \brief Duplicates vertices in a vertex-vertex kernel batch + static void duplicateVertexVertexKernelBatch( FarSubdivisionTables * subdivisionTables, + FarKernelBatch &kernelBatch, + VertexList const &vertexList ); + + /// \brief Rearranges vertices in an edge-vertex kernel batch + static void permuteEdgeVertexKernelBatch( FarSubdivisionTables * subdivisionTables, + FarKernelBatch const &kernelBatch, + VertexPermutation const &inversePermutation ); + + /// \brief Rearranges vertices in a face-vertex kernel batch + static void permuteFaceVertexKernelBatch( FarSubdivisionTables * subdivisionTables, + FarKernelBatch const &kernelBatch, + VertexPermutation const &inversePermutation ); + + /// \brief Rearranges vertices in a vertex-vertex kernel batch + static void permuteVertexVertexKernelBatch( FarSubdivisionTables * subdivisionTables, + FarKernelBatch const &kernelBatch, + VertexPermutation const &inversePermutation ); + + /// \brief Remaps a vertex index + static void remapVertex( VertexPermutation const& vertexPermutation, int& vertex ); + + /// \brief Remaps a vertex index + static void remapVertex( VertexPermutation const& vertexPermutation, unsigned int& vertex ); }; // This factory walks the Hbr vertices and accumulates the weights and adjacency @@ -466,6 +554,535 @@ FarCatmarkSubdivisionTablesFactory::CompareVertices( HbrVertex const * x return rankx < ranky; } +template void +FarCatmarkSubdivisionTablesFactory::DuplicateVertices( + FarSubdivisionTables * subdivisionTables, + FarKernelBatch &kernelBatch, VertexList const &vertexList ) +{ + switch (kernelBatch.GetKernelType()) { + case FarKernelBatch::CATMARK_EDGE_VERTEX: + case FarKernelBatch::CATMARK_RESTRICTED_EDGE_VERTEX: + duplicateEdgeVertexKernelBatch(subdivisionTables, kernelBatch, + vertexList); + break; + + case FarKernelBatch::CATMARK_VERT_VERTEX_A1: + case FarKernelBatch::CATMARK_VERT_VERTEX_B: + case FarKernelBatch::CATMARK_RESTRICTED_VERT_VERTEX_A: + case FarKernelBatch::CATMARK_RESTRICTED_VERT_VERTEX_B1: + case FarKernelBatch::CATMARK_RESTRICTED_VERT_VERTEX_B2: + duplicateVertexVertexKernelBatch(subdivisionTables, kernelBatch, + vertexList); + break; + } + + // Update the number of vertices in the subdivision tables. + subdivisionTables->_vertsOffsets.back() += (int)vertexList.size(); +} + +template void +FarCatmarkSubdivisionTablesFactory::duplicateEdgeVertexKernelBatch( + FarSubdivisionTables * subdivisionTables, + FarKernelBatch &kernelBatch, VertexList const &vertexList ) +{ + // Duplicate vertices in the edge vertices tables. + std::vector& srcE_IT = subdivisionTables->_E_IT; + std::vector& srcE_W = subdivisionTables->_E_W; + std::vector dstE_IT; + std::vector dstE_W; + + int kernelBatchSize = kernelBatch.GetEnd() - kernelBatch.GetStart(); + int tableOffset = kernelBatch.GetTableOffset(); + int vertexOffset = kernelBatch.GetVertexOffset(); + for (int i = 0; i < (int)vertexList.size(); ++i) { + int srcVertex = vertexList[i]; + int srcTableOffset = tableOffset + srcVertex - vertexOffset; + + for (int j = 0; j < 4; ++j) { + dstE_IT.push_back(srcE_IT[srcTableOffset * 4 + j]); + } + + if ((int)srcE_W.size() > srcTableOffset) { + for (int j = 0; j < 2; ++j) { + dstE_W.push_back(srcE_W[srcTableOffset * 2 + j]); + } + } + } + + // Rewrite the edge-vertices tables. + srcE_IT.insert(srcE_IT.begin() + (tableOffset + kernelBatchSize) * 4, + dstE_IT.begin(), dstE_IT.end()); + if (!dstE_W.empty()) { + srcE_W.insert(srcE_W.begin() + (tableOffset + kernelBatchSize) * 2, + dstE_W.begin(), dstE_W.end()); + } + + // Replace the kernel batch. + int numDuplicates = (int)vertexList.size(); + kernelBatch = FarKernelBatch(kernelBatch.GetKernelType(), + kernelBatch.GetLevel(), kernelBatch.GetTableIndex(), + kernelBatch.GetStart(), kernelBatch.GetEnd() + numDuplicates, + kernelBatch.GetTableOffset(), kernelBatch.GetVertexOffset(), + kernelBatch.GetMeshIndex()); +} + +template void +FarCatmarkSubdivisionTablesFactory::duplicateVertexVertexKernelBatch( + FarSubdivisionTables * subdivisionTables, + FarKernelBatch &kernelBatch, VertexList const &vertexList ) +{ + // Duplicate vertices in the vertex-vertices tables. + std::vector& srcV_ITa = subdivisionTables->_V_ITa; + std::vector& srcV_IT = subdivisionTables->_V_IT; + std::vector& srcV_W = subdivisionTables->_V_W; + std::vector dstV_ITa; + std::vector dstV_IT; + std::vector dstV_W; + + int kernelBatchEnd = kernelBatch.GetEnd(); + int tableOffset = kernelBatch.GetTableOffset(); + int vertexOffset = kernelBatch.GetVertexOffset(); + int lastVertexOffset = srcV_ITa[(tableOffset + kernelBatchEnd - 1) * 5 + 0]; + int lastValence = srcV_ITa[(tableOffset + kernelBatchEnd - 1) * 5 + 1]; + int dstVertexOffset = lastVertexOffset + lastValence * 2; + for (int i = 0; i < (int)vertexList.size(); ++i) { + int srcVertex = vertexList[i]; + int srcTableOffset = tableOffset + srcVertex - vertexOffset; + int srcVertexOffset = srcV_ITa[srcTableOffset * 5 + 0]; + int valence = srcV_ITa[srcTableOffset * 5 + 1]; + int parentVertex = srcV_ITa[srcTableOffset * 5 + 2]; + int edgeVertex1 = srcV_ITa[srcTableOffset * 5 + 3]; + int edgeVertex2 = srcV_ITa[srcTableOffset * 5 + 4]; + + dstV_ITa.push_back(dstVertexOffset); + dstV_ITa.push_back(valence); + dstV_ITa.push_back(parentVertex); + dstV_ITa.push_back(edgeVertex1); + dstV_ITa.push_back(edgeVertex2); + dstVertexOffset += valence * 2; + + for (int j = 0; j < valence * 2; ++j) { + dstV_IT.push_back(srcV_IT[srcVertexOffset + j]); + } + + if ((int)srcV_W.size() > srcTableOffset) { + dstV_W.push_back(srcV_W[srcTableOffset]); + } + } + + // Rewrite the vertex-vertices tables. + srcV_ITa.insert(srcV_ITa.begin() + (tableOffset + kernelBatchEnd) * 5, + dstV_ITa.begin(), dstV_ITa.end()); + srcV_IT.insert(srcV_IT.begin() + lastVertexOffset + lastValence * 2, + dstV_IT.begin(), dstV_IT.end()); + if (!dstV_W.empty()) { + srcV_W.insert(srcV_W.begin() + tableOffset + kernelBatchEnd, + dstV_W.begin(), dstV_W.end()); + } + + // Replace the kernel batch. + int numDuplicates = (int)vertexList.size(); + kernelBatch = FarKernelBatch(kernelBatch.GetKernelType(), + kernelBatch.GetLevel(), kernelBatch.GetTableIndex(), + kernelBatch.GetStart(), kernelBatch.GetEnd() + numDuplicates, + kernelBatch.GetTableOffset(), kernelBatch.GetVertexOffset(), + kernelBatch.GetMeshIndex()); +} + +template bool +FarCatmarkSubdivisionTablesFactory::PermuteVertices( + FarSubdivisionTables * subdivisionTables, + FarKernelBatch const &kernelBatch, + VertexPermutation const &vertexPermutation ) +{ + // Create the inverse permutation. + VertexPermutation inversePermutation; + int kernelBatchSize = kernelBatch.GetEnd() - kernelBatch.GetStart(); + int vertexOffset = kernelBatch.GetVertexOffset(); + int firstVertex = vertexOffset + kernelBatch.GetStart(); + int lastVertex = vertexOffset + kernelBatch.GetEnd(); + for (int i = 0; i < kernelBatchSize; ++i) { + unsigned int oldVertex = firstVertex + i; + VertexPermutation::const_iterator j = vertexPermutation.find(oldVertex); + if (j == vertexPermutation.end()) + continue; + + int newVertex = j->second; + + // Guarantee that the inverse map is a permutation. + assert(newVertex >= firstVertex && newVertex < lastVertex); + assert(inversePermutation.count(newVertex) == 0); + + inversePermutation[newVertex] = oldVertex; + } + + if (inversePermutation.empty()) + return false; // the vertices of the kernel batch are not permuted + + // Guarantee that the inverse map is a bijection. + assert((int)inversePermutation.size() == kernelBatchSize); + + switch (kernelBatch.GetKernelType()) { + case FarKernelBatch::CATMARK_EDGE_VERTEX: + case FarKernelBatch::CATMARK_RESTRICTED_EDGE_VERTEX: + permuteEdgeVertexKernelBatch(subdivisionTables, kernelBatch, + inversePermutation); + break; + + case FarKernelBatch::CATMARK_FACE_VERTEX: + case FarKernelBatch::CATMARK_QUAD_FACE_VERTEX: + case FarKernelBatch::CATMARK_TRI_QUAD_FACE_VERTEX: + permuteFaceVertexKernelBatch(subdivisionTables, kernelBatch, + inversePermutation); + break; + + case FarKernelBatch::CATMARK_VERT_VERTEX_A1: + case FarKernelBatch::CATMARK_VERT_VERTEX_B: + case FarKernelBatch::CATMARK_RESTRICTED_VERT_VERTEX_A: + case FarKernelBatch::CATMARK_RESTRICTED_VERT_VERTEX_B1: + case FarKernelBatch::CATMARK_RESTRICTED_VERT_VERTEX_B2: + permuteVertexVertexKernelBatch(subdivisionTables, kernelBatch, + inversePermutation); + break; + } + + return true; +} + +template void +FarCatmarkSubdivisionTablesFactory::permuteEdgeVertexKernelBatch( + FarSubdivisionTables * subdivisionTables, + FarKernelBatch const &kernelBatch, + VertexPermutation const &inversePermutation ) +{ + std::vector& oldE_IT = subdivisionTables->_E_IT; + std::vector& oldE_W = subdivisionTables->_E_W; + std::vector newE_IT; + std::vector newE_W; + + // Rearrange the edge-vertices tables. + int tableOffset = kernelBatch.GetTableOffset(); + int vertexOffset = kernelBatch.GetVertexOffset(); + for (int i = kernelBatch.GetStart(); i < kernelBatch.GetEnd(); ++i) { + int newVertex = i + vertexOffset; + int oldVertex = inversePermutation.find(newVertex)->second; + int oldTableOffset = tableOffset + oldVertex - vertexOffset; + + for (int j = 0; j < 4; ++j) { + newE_IT.push_back(oldE_IT[oldTableOffset * 4 + j]); + } + + if ((int)oldE_W.size() > oldTableOffset) { + for (int j = 0; j < 2; ++j) { + newE_W.push_back(oldE_W[oldTableOffset * 2 + j]); + } + } + } + + // Rewrite the edge-vertices tables. + std::copy(newE_IT.begin(), newE_IT.end(), oldE_IT.begin() + + tableOffset * 4); + if (!newE_W.empty()) { + std::copy(newE_W.begin(), newE_W.end(), + oldE_W.begin() + tableOffset * 2); + } +} + +template void +FarCatmarkSubdivisionTablesFactory::permuteFaceVertexKernelBatch( + FarSubdivisionTables * subdivisionTables, + FarKernelBatch const &kernelBatch, + VertexPermutation const &inversePermutation ) +{ + bool isCatmarkFaceVertex = kernelBatch.GetKernelType() == + FarKernelBatch::CATMARK_FACE_VERTEX; + + std::vector& oldF_ITa = subdivisionTables->_F_ITa; + std::vector& oldF_IT = subdivisionTables->_F_IT; + std::vector newF_ITa; + std::vector newF_IT; + + // Rearrange the face-vertices tables. + int tableOffset = kernelBatch.GetTableOffset(); + int vertexOffset = kernelBatch.GetVertexOffset(); + int firstVertexOffset, newVertexOffset; + + if (isCatmarkFaceVertex) { + firstVertexOffset = oldF_ITa[tableOffset * 2]; + newVertexOffset = firstVertexOffset; + } else { + firstVertexOffset = tableOffset; + } + + newVertexOffset = firstVertexOffset; + for (int i = kernelBatch.GetStart(); i < kernelBatch.GetEnd(); ++i) { + int newVertex = i + vertexOffset; + int oldVertex = inversePermutation.find(newVertex)->second; + int oldVertexOffset, valence; + + if (isCatmarkFaceVertex) { + int oldTableOffset = tableOffset + oldVertex - vertexOffset; + oldVertexOffset = oldF_ITa[oldTableOffset * 2 + 0]; + valence = oldF_ITa[oldTableOffset * 2 + 1]; + + newF_ITa.push_back(newVertexOffset); + newF_ITa.push_back(valence); + newVertexOffset += valence; + } else { + oldVertexOffset = tableOffset + 4 * (oldVertex - vertexOffset); + valence = 4; + } + + for (int j = 0; j < valence; ++j) { + newF_IT.push_back(oldF_IT[oldVertexOffset + j]); + } + } + + // Rewrite the face-vertices tables. + std::copy(newF_IT.begin(), newF_IT.end(), oldF_IT.begin() + + firstVertexOffset); + if (!newF_ITa.empty()) { + std::copy(newF_ITa.begin(), newF_ITa.end(), oldF_ITa.begin() + + tableOffset * 2); + } +} + +template void +FarCatmarkSubdivisionTablesFactory::permuteVertexVertexKernelBatch( + FarSubdivisionTables * subdivisionTables, + FarKernelBatch const &kernelBatch, + VertexPermutation const &inversePermutation ) +{ + std::vector& oldV_ITa = subdivisionTables->_V_ITa; + std::vector& oldV_IT = subdivisionTables->_V_IT; + std::vector& oldV_W = subdivisionTables->_V_W; + std::vector newV_ITa; + std::vector newV_IT; + std::vector newV_W; + + // Rearrange the vertex-vertices tables. + int kernelBatchStart = kernelBatch.GetStart(); + int kernelBatchEnd = kernelBatch.GetEnd(); + int tableOffset = kernelBatch.GetTableOffset(); + int vertexOffset = kernelBatch.GetVertexOffset(); + int firstVertexOffset = oldV_ITa[(tableOffset + kernelBatchStart) * 5 + 0]; + int newVertexOffset = firstVertexOffset; + for (int i = kernelBatchStart; i < kernelBatchEnd; ++i) { + int newVertex = i + vertexOffset; + int oldVertex = inversePermutation.find(newVertex)->second; + int oldTableOffset = tableOffset + oldVertex - vertexOffset; + int oldVertexOffset = oldV_ITa[oldTableOffset * 5 + 0]; + int valence = oldV_ITa[oldTableOffset * 5 + 1]; + int parentVertex = oldV_ITa[oldTableOffset * 5 + 2]; + int edgeVertex1 = oldV_ITa[oldTableOffset * 5 + 3]; + int edgeVertex2 = oldV_ITa[oldTableOffset * 5 + 4]; + + newV_ITa.push_back(newVertexOffset); + newV_ITa.push_back(valence); + newV_ITa.push_back(parentVertex); + newV_ITa.push_back(edgeVertex1); + newV_ITa.push_back(edgeVertex2); + newVertexOffset += valence * 2; + + for (int j = 0; j < valence * 2; ++j) { + newV_IT.push_back(oldV_IT[oldVertexOffset + j]); + } + + if ((int)oldV_W.size() > oldTableOffset) { + newV_W.push_back(oldV_W[oldTableOffset]); + } + } + + // Rewrite the vertex-vertices tables. + std::copy(newV_ITa.begin(), newV_ITa.end(), oldV_ITa.begin() + + (tableOffset + kernelBatchStart) * 5); + std::copy(newV_IT.begin(), newV_IT.end(), oldV_IT.begin() + + firstVertexOffset); + if (!newV_W.empty()) { + std::copy(newV_W.begin(), newV_W.end(), oldV_W.begin() + tableOffset + + kernelBatchStart); + } +} + +template void +FarCatmarkSubdivisionTablesFactory::RemapVertices( + FarSubdivisionTables * subdivisionTables, + FarKernelBatch const &kernelBatch, + VertexPermutation const& vertexPermutation ) +{ + switch (kernelBatch.GetKernelType()) { + case FarKernelBatch::CATMARK_FACE_VERTEX: + { + // Remap the face-vertices tables. + const std::vector& F_ITa = subdivisionTables->_F_ITa; + std::vector& F_IT = subdivisionTables->_F_IT; + int tableOffset = kernelBatch.GetTableOffset(); + for (int i = kernelBatch.GetStart(); i < kernelBatch.GetEnd(); ++i) + { + int vertexOffset = F_ITa[(tableOffset + i) * 2]; + int valence = F_ITa[(tableOffset + i) * 2 + 1]; + for (int j = 0; j < valence; ++j) { + remapVertex(vertexPermutation, F_IT[vertexOffset + j]); + } + } + } + break; + + case FarKernelBatch::CATMARK_QUAD_FACE_VERTEX: + case FarKernelBatch::CATMARK_TRI_QUAD_FACE_VERTEX: + { + // Remap the face-vertices tables. + std::vector& F_IT = subdivisionTables->_F_IT; + int tableOffset = kernelBatch.GetTableOffset(); + for (int i = kernelBatch.GetStart(); i < kernelBatch.GetEnd(); ++i) + { + for (int j = 0; j < 4; ++j) { + remapVertex(vertexPermutation, + F_IT[tableOffset + 4 * i + j]); + } + } + } + break; + + case FarKernelBatch::CATMARK_EDGE_VERTEX: + case FarKernelBatch::CATMARK_RESTRICTED_EDGE_VERTEX: + { + // Remap the edge-vertices indexing table. + std::vector& E_IT = subdivisionTables->_E_IT; + int tableOffset = kernelBatch.GetTableOffset(); + for (int i = kernelBatch.GetStart(); i < kernelBatch.GetEnd(); ++i) + { + int vertexOffset = (tableOffset + i) * 4; + for (int j = 0; j < 4; ++j) { + remapVertex(vertexPermutation, E_IT[vertexOffset + j]); + } + } + } + break; + + case FarKernelBatch::CATMARK_VERT_VERTEX_A1: + case FarKernelBatch::CATMARK_VERT_VERTEX_B: + case FarKernelBatch::CATMARK_RESTRICTED_VERT_VERTEX_A: + case FarKernelBatch::CATMARK_RESTRICTED_VERT_VERTEX_B1: + case FarKernelBatch::CATMARK_RESTRICTED_VERT_VERTEX_B2: + { + // Remap the vertex-vertices tables. + std::vector& V_ITa = subdivisionTables->_V_ITa; + std::vector& V_IT = subdivisionTables->_V_IT; + int tableOffset = kernelBatch.GetTableOffset(); + for (int i = kernelBatch.GetStart(); i < kernelBatch.GetEnd(); ++i) + { + int vertexOffset = V_ITa[(tableOffset + i) * 5]; + int valence = V_ITa[(tableOffset + i) * 5 + 1]; + int& parentVertex = V_ITa[(tableOffset + i) * 5 + 2]; + int& edgeVertex1 = V_ITa[(tableOffset + i) * 5 + 3]; + int& edgeVertex2 = V_ITa[(tableOffset + i) * 5 + 4]; + remapVertex(vertexPermutation, parentVertex); + remapVertex(vertexPermutation, edgeVertex1); + remapVertex(vertexPermutation, edgeVertex2); + + for (int j = 0; j < valence; ++j) { + remapVertex(vertexPermutation, V_IT[vertexOffset + j * 2]); + remapVertex(vertexPermutation, V_IT[vertexOffset + j * 2 + 1]); + } + } + } + break; + } +} + +template inline void +FarCatmarkSubdivisionTablesFactory::remapVertex( + VertexPermutation const& vertexPermutation, int& vertex ) +{ + if (vertex < 0) + return; // do not remap negative indices + + VertexPermutation::const_iterator i = vertexPermutation.find(vertex); + if (i != vertexPermutation.end()) + vertex = i->second; +} + +template inline void +FarCatmarkSubdivisionTablesFactory::remapVertex( + VertexPermutation const& vertexPermutation, unsigned int& vertex ) +{ + VertexPermutation::const_iterator i = vertexPermutation.find(vertex); + if (i != vertexPermutation.end()) + vertex = i->second; +} + +template void +FarCatmarkSubdivisionTablesFactory::ShiftVertices( + FarSubdivisionTables * subdivisionTables, FarKernelBatch &kernelBatch, + FarKernelBatch const &expandedKernelBatch, + int numVertices ) +{ + int start = kernelBatch.GetStart(); + int end = kernelBatch.GetEnd(); + int tableOffset = kernelBatch.GetTableOffset(); + int vertexOffset = kernelBatch.GetVertexOffset(); + int expandedKernelType = expandedKernelBatch.GetKernelType(); + + switch (kernelBatch.GetKernelType()) { + case FarKernelBatch::CATMARK_EDGE_VERTEX: + case FarKernelBatch::CATMARK_RESTRICTED_EDGE_VERTEX: + if (expandedKernelType == FarKernelBatch::CATMARK_EDGE_VERTEX || + expandedKernelType == + FarKernelBatch::CATMARK_RESTRICTED_EDGE_VERTEX) + { + tableOffset += numVertices; + vertexOffset += numVertices; + } + break; + + case FarKernelBatch::CATMARK_VERT_VERTEX_A1: + case FarKernelBatch::CATMARK_VERT_VERTEX_B: + case FarKernelBatch::CATMARK_RESTRICTED_VERT_VERTEX_A: + case FarKernelBatch::CATMARK_RESTRICTED_VERT_VERTEX_B1: + case FarKernelBatch::CATMARK_RESTRICTED_VERT_VERTEX_B2: + if (expandedKernelType == FarKernelBatch::CATMARK_EDGE_VERTEX || + expandedKernelType == + FarKernelBatch::CATMARK_RESTRICTED_EDGE_VERTEX) + { + vertexOffset += numVertices; + } else if (expandedKernelType == + FarKernelBatch::CATMARK_VERT_VERTEX_A1 || + expandedKernelType == FarKernelBatch::CATMARK_VERT_VERTEX_B || + expandedKernelType == + FarKernelBatch::CATMARK_RESTRICTED_VERT_VERTEX_A || + expandedKernelType == + FarKernelBatch::CATMARK_RESTRICTED_VERT_VERTEX_B1 || + expandedKernelType == + FarKernelBatch::CATMARK_RESTRICTED_VERT_VERTEX_B2) + { + start += numVertices; + end += numVertices; + + // Remap the vertex-vertices tables. + std::vector& V_ITa = subdivisionTables->_V_ITa; + int lastVertexOffset = V_ITa[(tableOffset + start - 1) * 5 + 0]; + int lastValence = V_ITa[(tableOffset + start - 1) * 5 + 1]; + int oldVertexOffset = V_ITa[(tableOffset + start) * 5 + 0]; + int newVertexOffset = lastVertexOffset + lastValence * 2; + for (int i = start; i < end; ++i) { + int& vertexOffset = V_ITa[(tableOffset + i) * 5 + 0]; + vertexOffset += newVertexOffset - oldVertexOffset; + } + } + break; + + default: + assert(!"kernel type is not supported"); + break; + } + + // Replace the kernel batch. + kernelBatch = FarKernelBatch(kernelBatch.GetKernelType(), + kernelBatch.GetLevel(), kernelBatch.GetTableIndex(), start, end, + tableOffset, vertexOffset, kernelBatch.GetMeshIndex()); +} + } // end namespace OPENSUBDIV_VERSION using namespace OPENSUBDIV_VERSION; diff --git a/opensubdiv/far/meshFactory.h b/opensubdiv/far/meshFactory.h index 56cd5cd8..6eb54bb9 100644 --- a/opensubdiv/far/meshFactory.h +++ b/opensubdiv/far/meshFactory.h @@ -167,12 +167,40 @@ public: /// /// @return true if the kernel type is supported /// - bool IsKernelTypeSupported(int kernelType) const { + bool IsKernelTypeSupported( int kernelType ) const { assert(kernelType >= FarKernelBatch::FIRST_KERNEL_TYPE and kernelType < FarKernelBatch::NUM_KERNEL_TYPES); return _supportedKernelTypes[kernelType]; } + typedef std::vector VertexList; + typedef std::map VertexPermutation; + typedef std::vector SplitTable; + + /// \brief Duplicates vertices at the finest subdivision level + /// + /// @param mesh the mesh to modify + /// + /// @param vertexList the list of vertices to duplicate + /// + static void DuplicateVertices( FarMesh * mesh, VertexList const &vertexList); + + /// \brief Rearranges vertices to process them in a specific order + /// + /// @param mesh the mesh to modify + /// + /// @param vertexPermutation permutation of the vertices in a kernel batch + /// + static void PermuteVertices( FarMesh * mesh, VertexPermutation const &vertexPermutation); + + /// \brief Splits patch control vertices that have been duplicated + /// + /// @param mesh the mesh to modify + /// + /// @param splitTable a table of offsets for each patch control vertex + /// + static void SplitVertices( FarMesh * mesh, SplitTable const &splitTable ); + private: friend class FarBilinearSubdivisionTablesFactory; friend class FarCatmarkSubdivisionTablesFactory; @@ -812,6 +840,110 @@ FarMeshFactory::GetVertexID( HbrVertex * v ) { return _remapTable[ v->GetID() ]; } +template void +FarMeshFactory::DuplicateVertices( FarMesh * mesh, + VertexList const &vertexList ) +{ + FarKernelBatchVector& kernelBatchVector = mesh->_batches; + FarPatchTables* patchTables = mesh->_patchTables; + FarSubdivisionTables* subdivisionTables = mesh->_subdivisionTables; + assert(subdivisionTables->GetScheme() == FarSubdivisionTables::CATMARK); + + VertexList sortedVertexList(vertexList); + std::sort(sortedVertexList.begin(), sortedVertexList.end()); + + for (FarKernelBatchVector::iterator i = kernelBatchVector.begin(); + i != kernelBatchVector.end(); ++i) + { + FarKernelBatch& kernelBatch = *i; + + VertexList::iterator begin = + std::lower_bound(sortedVertexList.begin(), + sortedVertexList.end(), + kernelBatch.GetVertexOffset() + kernelBatch.GetStart()); + VertexList::iterator end = + std::upper_bound(sortedVertexList.begin(), + sortedVertexList.end(), + kernelBatch.GetVertexOffset() + kernelBatch.GetEnd() - 1); + if (begin == sortedVertexList.end() || + (int)*begin >= kernelBatch.GetVertexOffset() + kernelBatch.GetEnd()) + { + continue; // the vertices of the kernel batch are not duplicated + } + + // Guarantee that the kernel batch is at the finest subdivision level. + assert(kernelBatch.GetLevel() == subdivisionTables->GetMaxLevel() - 1); + + // Duplicate the vertices in this kernel batch. + FarCatmarkSubdivisionTablesFactory::DuplicateVertices( + subdivisionTables, kernelBatch, VertexList(begin, end)); + + // Shift the affected kernel batches. + FarKernelBatchVector::iterator first = i; + FarKernelBatchVector::iterator last = kernelBatchVector.end(); + for (++first; first != last; ++first) { + FarCatmarkSubdivisionTablesFactory::ShiftVertices( + subdivisionTables, *first, kernelBatch, + std::distance(begin, end)); + } + + // Shift the control vertices in the patch tables. + FarPatchTablesFactory::ShiftVertices(patchTables, kernelBatch, + std::distance(begin, end)); + } +} + +template void +FarMeshFactory::PermuteVertices( FarMesh * mesh, + VertexPermutation const &vertexPermutation ) +{ + FarKernelBatchVector& kernelBatchVector = mesh->_batches; + FarPatchTables* patchTables = mesh->_patchTables; + FarSubdivisionTables* subdivisionTables = mesh->_subdivisionTables; + assert(subdivisionTables->GetScheme() == FarSubdivisionTables::CATMARK); + + for (FarKernelBatchVector::const_iterator i = kernelBatchVector.begin(); + i != kernelBatchVector.end(); ++i) + { + const FarKernelBatch& kernelBatch = *i; + + // Permute the vertices in this kernel batch. + if (not FarCatmarkSubdivisionTablesFactory::PermuteVertices( + subdivisionTables, kernelBatch, vertexPermutation)) + { + continue; + } + + // Find the range of kernel batches affected by the vertex permutation. + FarKernelBatchVector::const_iterator first = i; + FarKernelBatchVector::const_iterator last = kernelBatchVector.end(); + for (FarKernelBatchVector::const_iterator j = first; j != last; ++j) { + if (j->GetLevel() > kernelBatch.GetLevel() + 1) { + // The vertex permutation does not affect this level. + last = j; + break; + } + } + + // Remap the vertices in the affected kernel batches. + for (++first; first != last; ++first) { + FarCatmarkSubdivisionTablesFactory::RemapVertices( + subdivisionTables, *first, vertexPermutation); + } + + // Remap the patch tables. + FarPatchTablesFactory::RemapVertices(patchTables, vertexPermutation); + } +} + +template void +FarMeshFactory::SplitVertices( FarMesh * mesh, + SplitTable const &splitTable ) +{ + FarPatchTables* patchTables = mesh->_patchTables; + FarPatchTablesFactory::SplitVertices(patchTables, splitTable); +} + } // end namespace OPENSUBDIV_VERSION using namespace OPENSUBDIV_VERSION; diff --git a/opensubdiv/far/patchTablesFactory.h b/opensubdiv/far/patchTablesFactory.h index cbc50a37..ece4bf2f 100644 --- a/opensubdiv/far/patchTablesFactory.h +++ b/opensubdiv/far/patchTablesFactory.h @@ -110,6 +110,40 @@ protected: int numPtexFaces=0, int fvarWidth=0 ); + typedef std::vector VertexList; + typedef std::map VertexPermutation; + typedef std::vector SplitTable; + + /// \brief Remaps the vertices in the patch tables + /// + /// @param patchTables the patch tables to modify + /// + /// @param vertexPermutation permutation of the vertices + /// + static void RemapVertices( FarPatchTables * patchTables, + VertexPermutation const &vertexPermutation ); + + /// \brief Shifts the vertices in a kernel batch + /// + /// @param patchTables the patch tables to modify + /// + /// @param kernelBatch the kernel batch + /// + /// @param numVertices the number of vertices to shift + /// + static void ShiftVertices( FarPatchTables * patchTables, + FarKernelBatch const &kernelBatch, + int numVertices ); + + /// \brief Splits patch control vertices that have been duplicated + /// + /// @param patchTables the patch tables to modify + /// + /// @param splitTable a table of offsets for each patch control vertex + /// + static void SplitVertices( FarPatchTables * patchTables, + SplitTable const &splitTable ); + private: typedef FarPatchTables::Descriptor Descriptor; @@ -1527,8 +1561,69 @@ FarPatchTablesFactory::Splice(FarMeshVector const &meshes, return result; } +template void +FarPatchTablesFactory::RemapVertices( FarPatchTables * patchTables, + VertexPermutation const& vertexPermutation ) +{ + // Remap the patch control vertex table. + FarPatchTables::PTable& patches = patchTables->_patches; + for (FarPatchTables::PTable::iterator i = patches.begin(); + i != patches.end(); ++i) + { + unsigned int& vertex = *i; + VertexPermutation::const_iterator iterator = + vertexPermutation.find(vertex); + if (iterator != vertexPermutation.end()) + vertex = iterator->second; + } + // Remap the vertex valence table. + FarPatchTables::VertexValenceTable& vertexValenceTable = + patchTables->_vertexValenceTable; + if (!vertexValenceTable.empty()) { + int maxValence = patchTables->GetMaxValence(); + for (int i = 0; i < (int)vertexValenceTable.size(); i += maxValence) { + int vertexValence = vertexValenceTable[i]; + for (int j = 0; j < 2 * vertexValence; ++j) { + int& vertex = vertexValenceTable[i + j + 1]; + VertexPermutation::const_iterator iterator = + vertexPermutation.find(vertex); + if (iterator != vertexPermutation.end()) + vertex = iterator->second; + } + } + } +} +template void +FarPatchTablesFactory::ShiftVertices( FarPatchTables * patchTables, + FarKernelBatch const &kernelBatch, int numVertices ) +{ + unsigned int insertVertex = kernelBatch.GetVertexOffset() + + kernelBatch.GetEnd() - numVertices; + + // Remap the patch control vertex table. + FarPatchTables::PTable& patchTable = patchTables->_patches; + for (FarPatchTables::PTable::iterator i = patchTable.begin(); + i != patchTable.end(); ++i) + { + unsigned int& vertex = *i; + if (vertex >= insertVertex) + vertex += numVertices; + } +} + +template void +FarPatchTablesFactory::SplitVertices( FarPatchTables * patchTables, + SplitTable const &splitTable ) +{ + FarPatchTables::PTable& patchTable = patchTables->_patches; + assert(splitTable.size() == patchTable.size()); + + for (int i = 0; i < (int)patchTable.size(); ++i) { + patchTable[i] += splitTable[i]; + } +} } // end namespace OPENSUBDIV_VERSION using namespace OPENSUBDIV_VERSION; diff --git a/opensubdiv/osdutil/CMakeLists.txt b/opensubdiv/osdutil/CMakeLists.txt index a7957efa..3e48cd15 100644 --- a/opensubdiv/osdutil/CMakeLists.txt +++ b/opensubdiv/osdutil/CMakeLists.txt @@ -39,7 +39,8 @@ set(PUBLIC_HEADER_FILES patchPartitioner.h refiner.h topology.h - uniformEvaluator.h + uniformEvaluator.h + vertexSplit.h ) add_library(osdutil diff --git a/opensubdiv/osdutil/vertexSplit.h b/opensubdiv/osdutil/vertexSplit.h new file mode 100644 index 00000000..d5642d3f --- /dev/null +++ b/opensubdiv/osdutil/vertexSplit.h @@ -0,0 +1,190 @@ +// +// Copyright 2014 DigitalFish, Inc. +// +// 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 OSDUTIL_VERTEX_SPLIT_H +#define OSDUTIL_VERTEX_SPLIT_H + +#include "../version.h" +#include "../far/mesh.h" +#include "../far/meshFactory.h" + +#include +#include +#include + +namespace OpenSubdiv { +namespace OPENSUBDIV_VERSION { + +/// \brief Duplicates vertices at the finest subdivision level to produce a +/// \brief minimal vertex-varying data table for the face-varying data +/// +/// Modifies the mesh to add duplicate vertices at the finest subdivision level +/// to produce a minimal vertex-varying data table for the face-varying data. +/// The new vertices are inserted after their original vertex, and the patch +/// control vertices are reindexed accordingly. +/// +template +class OsdUtilVertexSplit { +public: + // A table of vertex-varying data at the finest subdivision level + typedef std::vector VVarDataTable; + + /// \brief Constructor + OsdUtilVertexSplit(FarMesh * mesh); + + /// \brief Returns the table of vertex-varying data for the finest + /// \brief subdivision level + VVarDataTable const &GetVVarDataTable() const + { + return _vvarDataTable; + } + +private: + VVarDataTable _vvarDataTable; // the table of vertex-varying data +}; + +template +OsdUtilVertexSplit::OsdUtilVertexSplit(FarMesh * mesh) +{ + typedef std::multimap VertexToFVarMultimap; + + const FarKernelBatchVector& kernelBatchVector = mesh->GetKernelBatches(); + const FarPatchTables* patchTables = mesh->GetPatchTables(); + const FarSubdivisionTables* subdivisionTables = + mesh->GetSubdivisionTables(); + const FarPatchTables::PTable& patchTable = patchTables->GetPatchTable(); + const FarPatchTables::FVarData& fvarData = patchTables->GetFVarData(); + const std::vector& fvarDataTable = fvarData.GetAllData(); + int fvarWidth = fvarData.GetFVarWidth(); + if (fvarWidth == 0) + return; + + // Determine which vertices to split. + typename FarMeshFactory::SplitTable splitTable(patchTable.size()); + VertexToFVarMultimap vertexToFVarMultimap; + for (int i = 0; i < (int)patchTable.size(); ++i) { + int vertex = patchTable[i]; + std::pair vertexRange = + vertexToFVarMultimap.equal_range(vertex); + + int j; + for (j = 0; vertexRange.first != vertexRange.second; + ++vertexRange.first, ++j) + { + int fvar = vertexRange.first->second; + if (std::equal(&fvarDataTable[i * fvarWidth], + &fvarDataTable[(i + 1) * fvarWidth], + &fvarDataTable[fvar * fvarWidth])) + { + splitTable[i] = j; + goto split_vertex; + } + } + + splitTable[i] = j; + vertexToFVarMultimap.insert(std::make_pair(vertex, i)); + + split_vertex:; + } + + // Duplicate vertices in the kernel batches from the last subdivision level. + for (int i = (int)kernelBatchVector.size() - 1; i >= 0; --i) { + const FarKernelBatch& kernelBatch = kernelBatchVector[i]; + if (kernelBatch.GetLevel() != subdivisionTables->GetMaxLevel() - 1) + break; + + int vertexOffset = kernelBatch.GetVertexOffset(); + int firstVertex = vertexOffset + kernelBatch.GetStart(); + int lastVertex = vertexOffset + kernelBatch.GetEnd(); + + // Select the vertices to duplicate from this kernel batch. + typename FarMeshFactory::VertexList duplicateList; + for (int j = firstVertex; j < lastVertex; ++j) { + std::pair::const_iterator, + std::multimap::const_iterator> vertexRange = + vertexToFVarMultimap.equal_range(j); + for (++vertexRange.first; vertexRange.first != vertexRange.second; + ++vertexRange.first) + { + duplicateList.push_back(j); + } + } + + // Duplicate vertices in this kernel batch. + FarMeshFactory::DuplicateVertices(mesh, duplicateList); + + // Interleave the duplicate vertices after their original vertex. + int duplicateVertex = lastVertex; + int nextVertex = firstVertex; + typename FarMeshFactory::VertexPermutation vertexPermutation; + for (int j = firstVertex; j < lastVertex; ++j) { + vertexPermutation[j] = nextVertex++; + + std::pair::const_iterator, + std::multimap::const_iterator> vertexRange = + vertexToFVarMultimap.equal_range(j); + for (++vertexRange.first; vertexRange.first != vertexRange.second; + ++vertexRange.first) + { + vertexPermutation[duplicateVertex++] = nextVertex++; + } + } + + FarMeshFactory::PermuteVertices(mesh, vertexPermutation); + } + + // Split the vertices in the mesh. + FarMeshFactory::SplitVertices(mesh, splitTable); + + // Map each vertex to its associated face-varying data. + typedef std::map VertexToFVarMap; + VertexToFVarMap vertexToFVarMap; + for (int i = 0; i < (int)patchTable.size(); ++i) { + vertexToFVarMap.insert(std::make_pair(patchTable[i], i)); + } + + // Create the vertex-varying data table. + int lastLevel = subdivisionTables->GetMaxLevel() - 1; + int firstVertex = subdivisionTables->GetFirstVertexOffset(lastLevel); + int numVertices = subdivisionTables->GetNumVertices(lastLevel); + + _vvarDataTable.resize(numVertices * fvarWidth); + for (int i = 0; i < (int)patchTable.size(); ++i) { + int vertex = patchTable[i]; + int fvar = vertexToFVarMap.find(vertex)->second; + for (int j = 0; j < fvarWidth; ++j) { + _vvarDataTable[(vertex - firstVertex) * fvarWidth + j] = + fvarDataTable[fvar * fvarWidth + j]; + } + } +} + +} // namespace OPENSUBDIV_VERSION + +using namespace OPENSUBDIV_VERSION; + +} // namespace OpenSubdiv + +#endif