From 776fbe45a094c5bb4ea0ab58203e2147dea81094 Mon Sep 17 00:00:00 2001 From: cignoni Date: Mon, 2 Jul 2012 16:41:28 +0000 Subject: [PATCH] First Version of the Voronoi Atlas parametrizator. --- .../parametrization/voronoi_atlas.h | 206 ++++++++++++++++++ vcg/complex/algorithms/voronoi_clustering.h | 107 +++++++-- 2 files changed, 300 insertions(+), 13 deletions(-) create mode 100644 vcg/complex/algorithms/parametrization/voronoi_atlas.h diff --git a/vcg/complex/algorithms/parametrization/voronoi_atlas.h b/vcg/complex/algorithms/parametrization/voronoi_atlas.h new file mode 100644 index 00000000..da65ca33 --- /dev/null +++ b/vcg/complex/algorithms/parametrization/voronoi_atlas.h @@ -0,0 +1,206 @@ +#ifndef VORONOI_ATLAS_H +#define VORONOI_ATLAS_H +#include +#include +#include +#include +#include +#include +#include + +namespace vcg { +namespace tri { + +template +class VoronoiAtlas +{ +//private: +public: + class VoroEdge; + class VoroFace; + class VoroVertex; + struct VoroUsedTypes : public UsedTypes< Use ::template AsVertexType, + Use ::template AsEdgeType, + Use ::template AsFaceType>{}; + + class VoroVertex : public Vertex< VoroUsedTypes, vertex::Coord3f, vertex::Normal3f, vertex::TexCoord2f, vertex::VFAdj , vertex::Qualityf, vertex::Color4b, vertex::BitFlags >{}; + class VoroFace : public Face< VoroUsedTypes, face::VertexRef, face::BitFlags, face::FFAdj ,face::VFAdj , face::WedgeTexCoord2f> {}; + class VoroEdge : public Edge< VoroUsedTypes>{}; + class VoroMesh : public tri::TriMesh< std::vector, std::vector , std::vector > {}; + + + + typedef typename VoroMesh::FaceIterator FaceIterator; + typedef typename VoroMesh::VertexType VertexType; + typedef typename VoroMesh::FaceType FaceType; + + static void CollectUVBorder(VoroMesh *rm, std::vector &uvBorder) + { + tri::UpdateTopology::FaceFace(*rm); + tri::UpdateFlags::FaceClearV(*rm); + for(FaceIterator fi=rm->face.begin();fi!=rm->face.end();++fi) + { + for(int j=0;j<3;++j) + if(face::IsBorder(*fi,j) && !(fi->IsV())) + { + face::Pos pp(&*fi,j,fi->V(j)); + assert(pp.IsBorder()); + face::Pos startPos = pp; + do + { + uvBorder.push_back( pp.F()->WT(pp.VInd()).P() ); + pp.F()->SetV(); + pp.NextB(); + } while(pp != startPos); + } + } + } + + // take a mesh and rescale its uv so that they are in the 0..1 range + static void RegularizeTexArea(VoroMesh &m) + { + float areaTex=0; + float areaGeo=0; + + vcg::Box2f UVBox = tri::UV_Utils::PerWedgeUVBox(m); + for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) + { + areaTex+= fabs((fi->WT(1).P() - fi->WT(0).P()) ^ (fi->WT(2).P() - fi->WT(0).P())) ; + areaGeo+= DoubleArea(*fi); + } + + float ratio = sqrt(areaGeo/areaTex); + + for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) + { + for(int j=0;j<3;++j) + fi->WT(j).P() = (fi->WT(j).P()-UVBox.min) *ratio; + } + } + + +public: + // Main parametrization function: + // it takes a startMesh, copy it and + + + static void Build( MeshType &startMesh, MeshType ¶Mesh, int sampleNum, bool overlap) + { + VoroMesh m; // the mesh used for the processing is a copy of the passed one. + tri::Append::Mesh(m, startMesh); + tri::Clean::RemoveUnreferencedVertex(m); + tri::Allocator::CompactVertexVector(m); + tri::Allocator::CompactFaceVector(m); + + tri::UpdateBounding::Box(m); + std::vector meshRegionVec; + std::vector< std::vector > uvBorders; + do + { + std::vector PoissonSamples; + float diskRadius=0; + tri::PoissonSampling(m,PoissonSamples,sampleNum,diskRadius); + printf("Sampling created a new mesh of %lu points\n",PoissonSamples.size()); + std::vector seedVec; + tri::VoronoiProcessing::SeedToVertexConversion(m,PoissonSamples,seedVec); + tri::UpdateTopology::VertexFace(m); + tri::VoronoiProcessing::ComputePerVertexSources(m,seedVec); + tri::VoronoiProcessing::FaceAssociateRegion(m); + tri::VoronoiProcessing::VoronoiColoring(m,seedVec,true); + tri::io::ExporterPLY::Save(m,"dd.ply",tri::io::Mask::IOM_VERTCOLOR); + + std::vector badRegionVec; + + for(size_t i=0; i::FaceSelectAssociateRegion(m,seedVec[i]); + assert(selCnt>0); + if(overlap){ + tri::UpdateSelection::VertexFromFaceLoose(m); + tri::UpdateSelection::FaceFromVertexLoose(m); + } + tri::Append::Mesh(*rm, m, true); + char buf[100]; sprintf(buf,"reg%02i.ply",i); + tri::io::ExporterPLY::Save(*rm,buf,tri::io::Mask::IOM_VERTCOLOR|tri::io::Mask::IOM_WEDGTEXCOORD ); + + tri::PoissonSolver PS(*rm); + if(PS.IsFeaseable()) + { + PS.Init(); + PS.FixDefaultVertices(); + PS.SolvePoisson(false); + tri::UpdateTexture::WedgeTexFromVertexTex(*rm); + RegularizeTexArea(*rm); + + std::vector uvBorder; + CollectUVBorder(rm,uvBorder); + meshRegionVec.push_back(rm); + uvBorders.push_back(uvBorder); + } else + { + qDebug("ACH - mesh %i is NOT homeomorphic to a disk\n",i); + badRegionVec.push_back(rm); + } + } + + VoroMesh *rm = new VoroMesh(); + tri::VoronoiProcessing::FaceSelectAssociateRegion(m,0); + tri::Append::Mesh(*rm, m, true); + + if(rm->fn>0) + { + qDebug("ACH - unreached faces %i fn\n",rm->fn); + badRegionVec.push_back(rm); + } + m.Clear(); + sampleNum = 10; + if(!badRegionVec.empty()) + { + for(size_t i=0;ifn>10) + tri::Append::Mesh(m, *badRegionVec[i], false); + +// tri::io::ExporterPLY::Save(m,"buf.ply",tri::io::Mask::IOM_VERTCOLOR|tri::io::Mask::IOM_WEDGTEXCOORD ); + + tri::Clean::RemoveDuplicateFace(m); + tri::Clean::RemoveUnreferencedVertex(m); + tri::UpdateNormals::PerVertexPerFace(m); + tri::Allocator::CompactVertexVector(m); + tri::Allocator::CompactFaceVector(m); + qDebug("Still %i faces (from %i regions) to process\n",m.fn,badRegionVec.size()); + } + } while (m.fn>0); +// tri::io::ExporterPLY::Save(m,"vorocolor.ply",tri::io::Mask::IOM_VERTCOLOR); + + std::vector trVec; + Point2f finalSize; + PolyPacker::PackAsObjectOrientedRect(uvBorders,Point2f(1024.0f,1024.0f),trVec,finalSize); + // loop again over all the patches + for(size_t i=0; iface.begin();fi!=rm->face.end();++fi) + { + for(int j=0;j<3;++j) + { + Point2f pp(fi->WT(j).U(),fi->WT(j).V()); + Point2f newpp=trVec[i]*pp; + fi->WT(j).U()=newpp[0]/1024.0f; + fi->WT(j).V()=newpp[1]/1024.0f; + } + } + + char buf[32]; sprintf(buf,"region_aa_%03i.ply",i); +// tri::io::ExporterPLY::Save(*rm,buf,tri::io::Mask::IOM_VERTCOLOR|tri::io::Mask::IOM_WEDGTEXCOORD ); + tri::Append::Mesh(paraMesh, *rm, false); + } +} +}; + + +} // end namespace vcg +} // end namespace tri + + +#endif // VORONOI_ATLAS_H diff --git a/vcg/complex/algorithms/voronoi_clustering.h b/vcg/complex/algorithms/voronoi_clustering.h index 2f2a5ff4..e136a522 100644 --- a/vcg/complex/algorithms/voronoi_clustering.h +++ b/vcg/complex/algorithms/voronoi_clustering.h @@ -97,15 +97,18 @@ static void SeedToVertexConversion(MeshType &m,std::vector &seedPVec, } typedef typename MeshType::template PerVertexAttributeHandle PerVertexPointerHandle; +typedef typename MeshType::template PerFaceAttributeHandle PerFacePointerHandle; static void ComputePerVertexSources(MeshType &m, std::vector &seedVec) { tri::Geo g; VertexPointer farthest; tri::Allocator::DeletePerVertexAttribute(m,"sources"); // delete any conflicting handle regardless of the type... - PerVertexPointerHandle sources = tri::Allocator:: template AddPerVertexAttribute (m,"sources"); - assert(tri::Allocator::IsValidHandle(m,sources)); - g.FarthestVertex(m,seedVec,farthest,std::numeric_limits::max(),&sources); + PerVertexPointerHandle vertexSources = tri::Allocator:: template AddPerVertexAttribute (m,"sources"); + tri::Allocator::DeletePerFaceAttribute(m,"sources"); // delete any conflicting handle regardless of the type... + PerFacePointerHandle faceSources = tri::Allocator:: template AddPerFaceAttribute (m,"sources"); + assert(tri::Allocator::IsValidHandle(m,vertexSources)); + g.FarthestVertex(m,seedVec,farthest,std::numeric_limits::max(),&vertexSources); } static void VoronoiColoring(MeshType &m, std::vector &seedVec, bool frontierFlag=true) @@ -127,24 +130,102 @@ static void VoronoiColoring(MeshType &m, std::vector &seedVec, boo tri::UpdateColor::VertexQualityRamp(m); } -// Given a seed, it selects all the faces such with at least one vertex sourced by the passed VertexPointer. -// Faces are selected more than once. -static void SelectRegion(MeshType &m, VertexPointer vp) + +static void FaceAssociateRegion(MeshType &m) +{ + PerFacePointerHandle faceSources = tri::Allocator:: template GetPerFaceAttribute (m,"sources"); + PerVertexPointerHandle vertexSources = tri::Allocator:: template GetPerVertexAttribute (m,"sources"); + for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) + { + faceSources[fi]=0; + std::vector vp(3); + for(int i=0;i<3;++i) vp[i]=vertexSources[fi->V(i)]; + + for(int i=0;i<3;++i) // First try to assoiciate to the most reached vertex + { + if(vp[0]==vp[1] && vp[0]==vp[2]) faceSources[fi] = vp[0]; + else + { + if(vp[0]==vp[1] && vp[0]->Q()< vp[2]->Q()) faceSources[fi] = vp[0]; + if(vp[0]==vp[2] && vp[0]->Q()< vp[1]->Q()) faceSources[fi] = vp[0]; + if(vp[1]==vp[2] && vp[1]->Q()< vp[0]->Q()) faceSources[fi] = vp[1]; + } + } + } + tri::UpdateTopology::FaceFace(m); + int unassCnt=0; + do + { + unassCnt=0; + for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) + { + if(faceSources[fi]==0) + { + std::vector vp(3); + for(int i=0;i<3;++i) + vp[i]=faceSources[fi->FFp(i)]; + + int cnt[3]={0,0,0}; + if(vp[0]!=0 && (vp[0]==vp[1] || vp[0]==vp[2])) + faceSources[fi] = vp[0]; + else if(vp[1]!=0 && (vp[1]==vp[2])) + faceSources[fi] = vp[1]; + else + faceSources[fi] = std::max(vp[0],std::max(vp[1],vp[2])); + if(faceSources[fi]==0) unassCnt++; + } + } + } + while(unassCnt>0); +} + +static int FaceSelectAssociateRegion(MeshType &m, VertexPointer vp) +{ + PerFacePointerHandle sources = tri::Allocator:: template GetPerFaceAttribute (m,"sources"); + assert(tri::Allocator::IsValidHandle(m,sources)); + tri::UpdateSelection::FaceClear(m); + tri::UpdateSelection::VertexClear(m); + int selCnt=0; + for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) + { + if(sources[fi]==vp) + { + fi->SetS(); + ++selCnt; + } + } + return selCnt; +} + +// Given a seed, it selects all the faces that have at least one vertex sourced by the given VertexPointer. +// vp can be null (it search for unreached faces...) +// returns the number of selected faces; +static int FaceSelectRegion(MeshType &m, VertexPointer vp) { PerVertexPointerHandle sources = tri::Allocator:: template GetPerVertexAttribute (m,"sources"); assert(tri::Allocator::IsValidHandle(m,sources)); tri::UpdateSelection::FaceClear(m); tri::UpdateSelection::VertexClear(m); - + int selCnt=0; for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) { - if( sources[(*fi).V(0)] == vp || - sources[(*fi).V(1)] == vp || - sources[(*fi).V(2)] == vp) - fi->SetS(); - } - tri::UpdateSelection::VertexFromFaceLoose(m); + int minInd = 0; float minVal=std::numeric_limits::max(); + for(int i=0;i<3;++i) + { + if((*fi).V(i)->Q()Q(); + } + } + if( sources[(*fi).V(minInd)] == vp) + { + fi->SetS(); + selCnt++; + } + } + return selCnt; } // find the vertexes of frontier faces