From 137e34a44fc3454ff2e7266f9e2d1ab49e3b73c4 Mon Sep 17 00:00:00 2001 From: nicopietroni Date: Wed, 12 Nov 2014 15:37:21 +0000 Subject: [PATCH] first release version --- vcg/complex/algorithms/quadrangulator.h | 740 ++++++++++++++++++++++++ 1 file changed, 740 insertions(+) create mode 100644 vcg/complex/algorithms/quadrangulator.h diff --git a/vcg/complex/algorithms/quadrangulator.h b/vcg/complex/algorithms/quadrangulator.h new file mode 100644 index 00000000..6e0784e3 --- /dev/null +++ b/vcg/complex/algorithms/quadrangulator.h @@ -0,0 +1,740 @@ +#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(); + + 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