diff --git a/vcg/complex/algorithms/refine_catmullclark.h b/vcg/complex/algorithms/refine_catmullclark.h new file mode 100644 index 000000000..d2a04c828 --- /dev/null +++ b/vcg/complex/algorithms/refine_catmullclark.h @@ -0,0 +1,205 @@ +/**************************************************************************** +* VCGLib o o * +* Visual and Computer Graphics Library o o * +* _ O _ * +* Copyright(C) 2004-2024 \/)\/ * +* Visual Computing Lab /\/| * +* ISTI - Italian National Research Council | * +* \ * +* All rights reserved. * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) * +* for more details. * +* * +****************************************************************************/ + +#pragma once + +namespace vcg { +namespace tri { +/// \ingroup trimesh + +/// \headerfile refine_doosabin.h vcg/complex/algorithms/refine_doosabin.h + +/// \brief This class is used convert between polygonal meshes and triangular meshes + +/** + This class contains two members that allow to build a triangular mesh from a polygonal mesh + and viceversa. In a trimesh, the generic polygons with n sides are codified represented by + tagging the internal edge of the face as 'faux' with the SetF. + */ + +template +class CatmullClark { + typedef typename PolyMeshType::FaceType FaceType; + typedef typename PolyMeshType::FacePointer FacePointer; + typedef typename PolyMeshType::FaceIterator FaceIterator; + typedef typename PolyMeshType::VertexIterator VertexIterator; + +public: +static void Refine(PolyMeshType &baseIn, PolyMeshType &refinedOut, int iterationNum=1) +{ + PolyMeshType baseTmp,outTmp; + + tri::Append::MeshCopy(baseTmp,baseIn); + + for(int i=0;i::MeshCopy(baseTmp,outTmp); + } + tri::Append::MeshCopy(refinedOut,outTmp); +} + + +static void RefineSingleStep(PolyMeshType &baseIn, PolyMeshType &refinedOut) +{ + tri::RequirePolygonalMesh(baseIn); + tri::RequirePolygonalMesh(refinedOut); + refinedOut.Clear(); + //tri::RequireFFAdjacency(baseIn); + // for the output mesh we keep a counter for each vertex as an additional attribute to computing averages easily + auto v_cnH = tri::Allocator::template AddPerVertexAttribute(refinedOut,"counter"); + + // for the input mesh we keep an attribute with the valence of the vertices + auto v_valH = tri::Allocator:: template AddPerVertexAttribute(baseIn,"valence"); + + // step -1 init the vertex valence counter to zero + for(auto &v : baseIn.vert) + v_valH[&v]=0; + + // Compute Valence for each vertex by iterating on all the faces + for(auto &f : baseIn.face) + for(int i=0;i::AddVertex(refinedOut,Point3f(0,0,0)); + printf( "Mesh has %i vert and %i faces\n", refinedOut.VN(), refinedOut.FN() ); + + // We keep two maps to store the new vertices created for the edges and the faces + // and retrieve them when actually building the connectivity of the new mesh + std::map, int> edgeMap; + std::map faceMap; + + // First Step Create the mid face vertices + for(auto &f : baseIn.face) + { + auto vp = tri::Allocator::AddVertex(refinedOut,PolyBarycenter(f)); + faceMap[tri::Index(baseIn,f)] = tri::Index(refinedOut,*vp); + } + + // Second step. Create a vertex for each edge. + // looping over all faces + for(auto &f : baseIn.face) + { + Point3f center = refinedOut.vert[faceMap[tri::Index(baseIn,f)]].P(); + // loop over all edges + for(int i=0;iv1) std::swap(v0,v1); + + // check if the edge is already in the map + auto it = edgeMap.find(std::make_pair(v0,v1)); + + int edgeVertIndex; + if(it == edgeMap.end()) + { + // if not, create a new vertex in the middle of the edge + auto vp = tri::Allocator::AddVertex(refinedOut,Point3f(0,0,0)); + edgeMap[std::make_pair(v0,v1)] = tri::Index(refinedOut,*vp); + edgeVertIndex = tri::Index(refinedOut,*vp); + } + else + { + edgeVertIndex = it->second; + } + + refinedOut.vert[edgeVertIndex].P() += center*0.5; + refinedOut.vert[edgeVertIndex].P() += f.V0(i)->P()*0.25; + refinedOut.vert[edgeVertIndex].P() += f.V1(i)->P()*0.25; + + v_cnH[edgeVertIndex]+=1; // increment the counter of the vertex for the face + } + } + // the only normalization step we do is for edge vertices to handle the fact + // that on boundary faces we do not have two contributions so the weight should be doubled. + for(auto &v : refinedOut.vert) + if(v_cnH[v]>0) v.P() /= v_cnH[&v]; + + // Third step compute new the position of the original vertices + for(auto &f : baseIn.face) + { + Point3f center = refinedOut.vert[faceMap[tri::Index(baseIn,f)]].P(); + + // for each vertex of the face we add to its position + // the coords of the baricenter and of the two vertices adjacent to it + for(int i=0;iv1) std::swap(v0,v1); + Point3f edgep = refinedOut.vert[edgeMap[std::make_pair(v0,v1)]].P(); + + refinedOut.vert[tri::Index(baseIn,f.V(i))].P() += f.V(i)->P() * val1; + refinedOut.vert[tri::Index(baseIn,f.V(i))].P() += center * val2; + refinedOut.vert[tri::Index(baseIn,f.V(i))].P() += edgep * val2; + } + } + + printf( "Mesh has %i vert and %i faces\n", refinedOut.VN(), refinedOut.FN() ); + + // Final step. Create Connectivity: a new face for each wedge of each face of the original mesh + for(auto &f : baseIn.face) + { + // loop over all wedge of the face + for(int i=0;ie01) std::swap(e00,e01); + if(e10>e11) std::swap(e10,e11); + + // retrieve the edge vertices + auto e1it = edgeMap.find(std::make_pair(e00,e01)); + auto e2it = edgeMap.find(std::make_pair(e10,e11)); + + // retrieve the face vertex + auto fit = faceMap.find(tri::Index(baseIn,f)); + + auto v0p = &refinedOut.vert[tri::Index(baseIn,f.V(i))]; + auto v1p = &refinedOut.vert[e1it->second]; + auto v2p = &refinedOut.vert[fit->second]; + auto v3p = &refinedOut.vert[e2it->second]; + + tri::Allocator::AddQuadFace(refinedOut, v0p, v1p, v2p, v3p); + } + }// fprintf(stdout,"Refining starting \n");fflush(stdout); + tri::Allocator::template DeletePerVertexAttribute(refinedOut,v_cnH); + + // for the input mesh we keep an attribute with the valence of the vertices + tri::Allocator:: template DeletePerVertexAttribute(baseIn,v_valH); + +} // end refine Function + +}; // end CatmullClark class +} // end namespace tri +} // end namespace vcg +