#ifndef VORONOI_ATLAS_H #define VORONOI_ATLAS_H #include #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: struct VoronoiAtlasParam { VoronoiAtlasParam() { sampleNum=10; overlap=false; } struct Stat { void clear() { iterNum=totalTime=unwrapTime=voronoiTime=samplingTime=0;} int totalTime; int unwrapTime; int voronoiTime; int samplingTime; int regionNum; int iterNum; }; int sampleNum; bool overlap; Stat vas; }; // Main parametrization function: // it takes a startMesh, copy it and static void Build( MeshType &startMesh, MeshType ¶Mesh, VoronoiAtlasParam &pp) { pp.vas.clear(); int t0=clock(); 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; // Main processing loop do { int st0=clock(); std::vector PoissonSamples; float diskRadius=0; tri::PoissonSampling(m,PoissonSamples,pp.sampleNum,diskRadius); int st1=clock(); pp.vas.samplingTime+= st1-st0; 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); std::vector badRegionVec; int st2=clock(); pp.vas.voronoiTime+=st2-st1; for(size_t i=0; i::FaceSelectAssociateRegion(m,seedVec[i]); assert(selCnt>0); if(pp.overlap){ tri::UpdateSelection::VertexFromFaceLoose(m); tri::UpdateSelection::FaceFromVertexLoose(m); } tri::Append::Mesh(*rm, m, true); int tp0=clock(); 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); } int tp1=clock(); pp.vas.unwrapTime +=tp1-tp0; ++pp.vas.iterNum; } 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(); pp.sampleNum = 10; if(!badRegionVec.empty()) { for(size_t i=0;ifn>10) tri::Append::Mesh(m, *badRegionVec[i], false); tri::Clean::RemoveDuplicateFace(m); tri::Clean::RemoveUnreferencedVertex(m); tri::Allocator::CompactVertexVector(m); tri::Allocator::CompactFaceVector(m); } } while (m.fn>0); std::vector trVec; Point2f finalSize; PolyPacker::PackAsObjectOrientedRect(uvBorders,Point2f(1024.0f,1024.0f),trVec,finalSize); // loop again over all the patches pp.vas.regionNum=meshRegionVec.size(); 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; } } tri::Append::Mesh(paraMesh, *rm, false); } int t2=clock(); pp.vas.totalTime=t2-t0; } }; //end } // end namespace vcg } // end namespace tri #endif // VORONOI_ATLAS_H