/**************************************************************************** * VCGLib o o * * Visual and Computer Graphics Library o o * * _ O _ * * Copyright(C) 2004-2016 \/)\/ * * 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. * * * ****************************************************************************/ #ifndef MIQ_QUADRANGULATOR_H #define MIQ_QUADRANGULATOR_H #include #include #include #include #include #include #include #include #include #include #define precisionQ 0.0000000001 namespace vcg { namespace tri { template class Quadrangulator { public: typedef typename TriMesh::FaceType TriFaceType; typedef typename TriMesh::VertexType TriVertexType; typedef typename TriMesh::CoordType CoordType; typedef typename TriMesh::ScalarType ScalarType; typedef typename PolyMesh::FaceType PolyFaceType; typedef typename PolyMesh::VertexType PolyVertexType; typedef typename PolyMesh::CoordType PolyCoordType; typedef typename PolyMesh::ScalarType PolyScalarType; struct InterpolationInfo { CoordType Pos3D; vcg::Point2 PosUV; ScalarType alpha; bool to_split; InterpolationInfo() { Pos3D=CoordType(0,0,0); PosUV=vcg::Point2(0,0); to_split=false; alpha=-1; } }; //the interpolation map that is saved once to be univoque per edge typedef std::pair KeyEdgeType; std::map InterpMap; //ScalarType UVtolerance; private: bool ToSplit(const vcg::Point2 &uv0, const vcg::Point2 &uv1, int Dir, ScalarType &alpha) { ScalarType val0=uv0.V(Dir); ScalarType val1=uv1.V(Dir); int IntegerLine0=floor(val0); int IntegerLine1=floor(val1); if (IntegerLine0==IntegerLine1) return false;//no integer line pass throught the edge bool swapped=false; if (IntegerLine0>IntegerLine1) { std::swap(IntegerLine0,IntegerLine1); std::swap(val0,val1); assert(val1>=val0); swapped=true; } //then get the first if extist that overcome the threshold int IntegerSplit=IntegerLine0+1; bool found=false; ScalarType dist1,dist0; for (int i=IntegerSplit;i<=IntegerLine1;i++) { dist1=fabs(val1-IntegerSplit); dist0=fabs(val0-IntegerSplit); // if ((dist0>=UVtolerance)&& // (dist1>=UVtolerance)) if ((val0!=IntegerSplit)&& (val1!=IntegerSplit)) { found=true; break; } IntegerSplit++; } if (!found)return false; //have to check distance also in opposite direction ScalarType lenght=val1-val0; assert(lenght>=0); //alpha=1.0-(dist/lenght); alpha=(dist1/lenght); if (swapped)alpha=1-alpha; assert((alpha>0)&&(alpha<1)); return true; } void RoundInitial(TriMesh &to_split) { ScalarType minTolerance=precisionQ; //first add all eddge for (int i=0;i UV=f->WT(j).P(); int int0=floor(UV.X()+0.5); int int1=floor(UV.Y()+0.5); ScalarType diff0=(fabs(UV.X()-(ScalarType)int0)); ScalarType diff1=(fabs(UV.Y()-(ScalarType)int1)); if (diff0WT(j).P()=UV; } } } void RoundSplits(TriMesh &to_split,int dir) { ScalarType minTolerance=precisionQ; //first add all eddge for (size_t i=0;iP0(j); CoordType p1=f->P1(j); KeyEdgeType k(p0,p1); assert(InterpMap.count(k)==1); if (!InterpMap[k].to_split)continue; //then get the intepolated value vcg::Point2 UV=InterpMap[k].PosUV; int int0=floor(UV.X()+0.5); int int1=floor(UV.Y()+0.5); ScalarType diff0=(fabs(UV.X()-(ScalarType)int0)); ScalarType diff1=(fabs(UV.Y()-(ScalarType)int1)); if (diff0P0(j); CoordType p1=f->P1(j); vcg::Point2 Uv0=f->V0(j)->T().P(); vcg::Point2 Uv1=f->V1(j)->T().P(); KeyEdgeType k(p0,p1); // printf("p0 (%5.5f,%5.5f,%5.5f) p1(%5.5f,%5.5f,%5.5f) \n",p0.X(),p0.Y(),p0.Z(),p1.X(),p1.Y(),p1.Z()); // printf("uv0 (%5.5f,%5.5f) uv1(%5.5f,%5.5f) \n",Uv0.X(),Uv0.Y(),Uv1.X(),Uv1.Y()); // fflush(stdout); assert(InterpMap.count(k)==0); InterpMap[k]=InterpolationInfo(); } } //then set the ones to be splitted for (size_t i=0;iP0(j); CoordType p1=f->P1(j); vcg::Point2 uv0=f->V0(j)->T().P(); vcg::Point2 uv1=f->V1(j)->T().P(); ScalarType alpha; if (!ToSplit(uv0,uv1,dir,alpha))continue; KeyEdgeType k(p0,p1); assert(InterpMap.count(k)==1); InterpMap[k].Pos3D=p0*alpha+p1*(1-alpha); InterpMap[k].PosUV=uv0*alpha+uv1*(1-alpha); InterpMap[k].to_split=true; InterpMap[k].alpha=alpha; } } //then make them coherent for (size_t i=0;iP0(j); CoordType p1=f->P1(j); vcg::Point2 uv0=f->V0(j)->T().P(); vcg::Point2 uv1=f->V1(j)->T().P(); // if (p0>p1)continue; //only one verse of coherence KeyEdgeType k0(p0,p1); assert(InterpMap.count(k0)==1);//there should be already in the //table and it should be coherent KeyEdgeType k1(p1,p0); if(InterpMap.count(k1)==0)continue;//REAL border, no need for update bool to_split0=InterpMap[k0].to_split; bool to_split1=InterpMap[k1].to_split; //the find all possible cases if ((!to_split0)&&(!to_split1))continue; if ((to_split0)&&(to_split1)) { CoordType Pos3D=InterpMap[k1].Pos3D; InterpMap[k0].Pos3D=Pos3D; //check if need to make coherent also the UV Position //skip the fake border and do the rest bool IsBorderFF=(f->FFp(j)==f); if (!IsBorderFF) //in this case they should have same UVs InterpMap[k0].PosUV=InterpMap[k1].PosUV; else { ScalarType alpha=InterpMap[k1].alpha; assert((alpha>=0)&&(alpha<=1)); alpha=1-alpha; InterpMap[k0].PosUV=alpha*uv0+(1-alpha)*uv1; InterpMap[k0].alpha=alpha; } } else if ((!to_split0)&&(to_split1)) { CoordType Pos3D=InterpMap[k1].Pos3D; InterpMap[k0].Pos3D=Pos3D; //check if need to make coherent also the UV Position //skip the fake border and do the rest bool IsBorderFF=(f->FFp(j)==f); InterpMap[k0].to_split=true; if (!IsBorderFF) //in this case they should have same UVs InterpMap[k0].PosUV=InterpMap[k1].PosUV; else //recalculate , it pass across a seam { ScalarType alpha=InterpMap[k1].alpha; assert((alpha>=0)&&(alpha<=1)); alpha=1-alpha; InterpMap[k0].PosUV=alpha*uv0+(1-alpha)*uv1; InterpMap[k0].alpha=alpha; } } } } RoundSplits(to_split,dir); } // Basic subdivision class // This class must provide methods for finding the position of the newly created vertices // In this implemenation we simply put the new vertex in the MidPoint position. // Color and TexCoords are interpolated accordingly. template struct SplitMidPoint : public std::unary_function , typename MESH_TYPE::CoordType > { typedef typename MESH_TYPE::VertexType VertexType; typedef typename MESH_TYPE::FaceType FaceType; typedef typename MESH_TYPE::CoordType CoordType; std::map *MapEdge; void operator()(typename MESH_TYPE::VertexType &nv, vcg::face::Pos ep) { VertexType* v0=ep.f->V0(ep.z); VertexType* v1=ep.f->V1(ep.z); assert(v0!=v1); CoordType p0=v0->P(); CoordType p1=v1->P(); assert(p0!=p1); KeyEdgeType k(p0,p1); bool found=(MapEdge->count(k)==1); assert(found); bool to_split=(*MapEdge)[k].to_split; assert(to_split); //get the value on which the edge must be splitted nv.P()= (*MapEdge)[k].Pos3D; //nv.N()= v0->N()*alpha+v1->N()*(1.0-alpha); nv.T().P()=(*MapEdge)[k].PosUV; } vcg::TexCoord2 WedgeInterp(vcg::TexCoord2 &t0, vcg::TexCoord2 &t1) { return (vcg::TexCoord2(0,0)); } SplitMidPoint(std::map *_MapEdge){MapEdge=_MapEdge;} }; template class EdgePredicate { typedef typename MESH_TYPE::VertexType VertexType; typedef typename MESH_TYPE::FaceType FaceType; typedef typename MESH_TYPE::ScalarType ScalarType; std::map *MapEdge; public: bool operator()(vcg::face::Pos ep) const { VertexType* v0=ep.f->V0(ep.z); VertexType* v1=ep.f->V1(ep.z); assert(v0!=v1); CoordType p0=v0->P(); CoordType p1=v1->P(); assert(p0!=p1); KeyEdgeType k(p0,p1); bool found=(MapEdge->count(k)==1); assert(found); bool to_split=(*MapEdge)[k].to_split; return(to_split); } EdgePredicate(std::map *_MapEdge){MapEdge=_MapEdge;} }; void SplitTrisDir(TriMesh &to_split, int dir) { bool done=true; //int step=0; while (done) { printf("Number of Vertices %d \n",to_split.vn); fflush(stdout); InitSplitMap(to_split,dir); SplitMidPoint splMd(&InterpMap); EdgePredicate eP(&InterpMap); done=vcg::tri::RefineE,EdgePredicate >(to_split,splMd,eP); } printf("Number of Vertices %d \n",to_split.vn); fflush(stdout); fflush(stdout); } bool IsOnIntegerLine(vcg::Point2 uv0, vcg::Point2 uv1) { for (int dir=0;dir<2;dir++) { ScalarType val0=uv0.V(dir); ScalarType val1=uv1.V(dir); int integer0=floor(uv0.V(dir)+0.5); int integer1=floor(uv1.V(dir)+0.5); if (integer0!=integer1)continue; // if ((fabs(val0-(ScalarType)integer0))>=UVtolerance)continue; // if ((fabs(val1-(ScalarType)integer1))>=UVtolerance)continue; if (val0!=(ScalarType)floor(val0))continue; if (val1!=(ScalarType)floor(val1))continue; return true; } return false; } bool IsOnIntegerVertex(vcg::Point2 uv, bool IsB) { int onIntegerL=0; for (int dir=0;dir<2;dir++) { ScalarType val0=uv.V(dir); int integer0=floor(val0+0.5); //if ((fabs(val0-(ScalarType)integer0))0))return true; return (onIntegerL==2); } void InitIntegerEdgesVert(TriMesh &Tmesh) { //IntegerEdges.clear(); vcg::tri::UpdateFlags::FaceSetF(Tmesh); vcg::tri::UpdateFlags::FaceClearS(Tmesh); vcg::tri::UpdateFlags::VertexClearS(Tmesh); for (unsigned int i=0;iIsD())continue; for (int j=0;j<3;j++) { bool IsBorder=f->IsB(j); if (IsBorder) f->ClearF(j); else { vcg::Point2 uv0=f->WT(j).P(); vcg::Point2 uv1=f->WT((j+1)%3).P(); if (IsOnIntegerLine(uv0,uv1)) { f->ClearF(j); TriFaceType *f1=f->FFp(j); int z=f->FFi(j); assert(f1!=f); f1->ClearF(z); } } bool BorderV=f->V(j)->IsB(); if (IsOnIntegerVertex(f->WT(j).P(),BorderV)) f->V(j)->SetS(); } } } short int AlignmentEdge(TriFaceType *f, int edge_index) { vcg::Point2 uv0=f->WT(edge_index).P(); vcg::Point2 uv1=f->WT((edge_index+1)%3).P(); if (uv0.X()==uv1.X())return 0; if (uv0.Y()==uv1.Y())return 1; return -1; } void FindPolygon(vcg::face::Pos &currPos, std::vector &poly, std::vector &UVpoly) { currPos.F()->SetV(); currPos.F()->C()=vcg::Color4b(255,0,0,255); poly.clear(); assert(currPos.V()->IsS()); TriVertexType *v_init=currPos.V(); poly.push_back(currPos.V()); //retrieve UV int indexV0=currPos.E(); short int Align=AlignmentEdge(currPos.F(),currPos.E()); std::vector TempUVpoly; TempUVpoly.push_back(Align); do { currPos.NextNotFaux(); currPos.F()->SetV(); currPos.F()->C()=vcg::Color4b(255,0,0,255); if ((currPos.V()->IsS())&&(currPos.V()!=v_init)) { poly.push_back(currPos.V()); short int Align=AlignmentEdge(currPos.F(),currPos.E()); TempUVpoly.push_back(Align); } }while (currPos.V()!=v_init); //then shift the order of UV by one //to be consistent with edge ordering int size=TempUVpoly.size(); for (int i=0;i > &polygons, std::vector > &UV) { vcg::tri::UpdateFlags::FaceClearV(Tmesh); for (unsigned int i=0;iIsV())continue; for (int j=0;j<3;j++) { TriVertexType* v0=f->V0(j); if (!v0->IsS())continue; if (f->IsF(j))continue; vcg::face::Pos startPos(f,j); std::vector poly; std::vector< short int> UVpoly; FindPolygon(startPos,poly,UVpoly); if (poly.size()>2) { assert(poly.size()==UVpoly.size()); std::reverse(poly.begin(),poly.end()); // std::reverse(UVpoly.begin(),UVpoly.end()); polygons.push_back(poly); UV.push_back(UVpoly); } //only one polygon per initial face break; } } } //FUNCTIONS NEEDED BY "UV WEDGE TO VERTEX" FILTER static void ExtractVertex(const TriMesh & srcMesh, const TriFaceType & f, int whichWedge, const TriMesh & dstMesh, TriVertexType & v) { (void)srcMesh; (void)dstMesh; // This is done to preserve every single perVertex property // perVextex Texture Coordinate is instead obtained from perWedge one. v.ImportData(*f.cV(whichWedge)); v.T() = f.cWT(whichWedge); } static bool CompareVertex(const TriMesh & m, TriVertexType & vA, TriVertexType & vB) { (void)m; return (vA.cT() == vB.cT()); } void ConvertWTtoVT(TriMesh &Tmesh) { int vn = Tmesh.vn; vcg::tri::AttributeSeam::SplitVertex(Tmesh, ExtractVertex, CompareVertex); vcg::tri::UpdateTopology::FaceFace(Tmesh); // vcg::tri::UpdateFlags::FaceBorderFromFF(Tmesh); } void ConvertVTtoWT(TriMesh &Tmesh) { vcg::tri::UpdateTexture::WedgeTexFromVertexTex(Tmesh); vcg::tri::Clean::RemoveDuplicateVertex(Tmesh); } void ReupdateMesh(TriMesh &Tmesh) { vcg::tri::UpdateNormal::PerFaceNormalized(Tmesh); // update Normals vcg::tri::UpdateNormal::PerVertexNormalized(Tmesh);// update Normals //compact the mesh vcg::tri::Allocator::CompactVertexVector(Tmesh); vcg::tri::Allocator::CompactFaceVector(Tmesh); vcg::tri::UpdateTopology::FaceFace(Tmesh); // update Topology vcg::tri::UpdateTopology::TestFaceFace(Tmesh); //and test it //set flags vcg::tri::UpdateFlags::VertexClearV(Tmesh); vcg::tri::UpdateFlags::FaceBorderFromFF(Tmesh); vcg::tri::UpdateFlags::VertexBorderFromFaceBorder(Tmesh); } public: void TestIsProper(TriMesh &Tmesh) { //test manifoldness int test=vcg::tri::Clean::CountNonManifoldVertexFF(Tmesh); //assert(test==0); if (test != 0) cerr << "Assertion failed: TestIsProper NonManifoldVertices!" << endl; test=vcg::tri::Clean::CountNonManifoldEdgeFF(Tmesh); //assert(test==0); if (test != 0) cerr << "Assertion failed: TestIsProper NonManifoldEdges" << endl; for (unsigned int i=0;iIsD()); for (int z=0;z<3;z++) { //int indexOpp=f->FFi(z); TriFaceType *Fopp=f->FFp(z); if (Fopp==f) continue; //assert( f->FFp(z)->FFp(f->FFi(z))==f ); if (f->FFp(z)->FFp(f->FFi(z))!=f) cerr << "Assertion failed: TestIsProper f->FFp(z)->FFp(f->FFi(z))!=f " << endl; } } } void Quadrangulate(TriMesh &Tmesh, PolyMesh &Pmesh, std::vector< std::vector< short int> > &UV) { UV.clear(); Pmesh.Clear(); vcg::tri::UpdateTopology::FaceFace(Tmesh); TestIsProper(Tmesh); RoundInitial(Tmesh); //UVtolerance=tolerance; //split to per vert ConvertWTtoVT(Tmesh); vcg::tri::Allocator::CompactVertexVector(Tmesh); vcg::tri::Allocator::CompactFaceVector(Tmesh); vcg::tri::UpdateTopology::FaceFace(Tmesh); (void)Pmesh; //TestIsProper(Tmesh); //then split the tris along X SplitTrisDir(Tmesh,0); SplitTrisDir(Tmesh,1); //merge back the mesh and WT coords ConvertVTtoWT(Tmesh); //CleanMesh(Pmesh); //update properties of the mesh ReupdateMesh(Tmesh); //test manifoldness TestIsProper(Tmesh); InitIntegerEdgesVert(Tmesh); for (int i=0;i > polygons; FindPolygons(Tmesh,polygons,UV); //then add to the polygonal mesh Pmesh.Clear(); int numV=vcg::tri::UpdateSelection::VertexCount(Tmesh); //first create vertices vcg::tri::Allocator::AddVertices(Pmesh,numV); std::map VertMap; int index=0; for(unsigned int i=0;i UV=Tmesh.vert[i].T().P(); Pmesh.vert[index].P()=typename PolyMesh::CoordType(pos.X(),pos.Y(),pos.Z()); Pmesh.vert[index].N()=typename PolyMesh::CoordType(norm.X(),norm.Y(),norm.Z()); Pmesh.vert[index].T().P()=UV; VertMap[pos]=index; index++; } //then add polygonal mesh vcg::tri::Allocator::AddFaces(Pmesh,polygons.size()); for (unsigned int i=0;iP(); assert(VertMap.count(pos)==1); int index=VertMap[pos]; Pmesh.face[i].V(j)=&(Pmesh.vert[index]); } } } }; } } #endif