diff --git a/src/fgt/filter_aging/filter_aging.cpp b/src/fgt/filter_aging/filter_aging.cpp index a0ea4947e..97c09afe0 100644 --- a/src/fgt/filter_aging/filter_aging.cpp +++ b/src/fgt/filter_aging/filter_aging.cpp @@ -24,13 +24,13 @@ #include #include -#include // DEBUG - -//#include // DEBUG -#include +//#include // DEBUG #include "filter_aging.h" +#include +#include + // Constructor GeometryAgingPlugin::GeometryAgingPlugin() @@ -88,7 +88,8 @@ void GeometryAgingPlugin::initParameterSet(QAction *action, MeshModel &m, Filter { switch(ID(action)) { case FP_ERODE: - params.addFloat("AngleThreshold", 60.0, "Angle Threshold", "The minimum angle between two adjacent faces to consider the edge they are sharing."); + params.addAbsPerc("AngleThreshold", 60.0, 10.0, 170.0, "Angle Threshold", "The minimum angle between two adjacent faces to consider the edge they are sharing."); + params.addAbsPerc("EdgeLenThreshold", m.cm.bbox.Diag()*0.01, 0.0, m.cm.bbox.Diag(),"Edge Len Threshold", "The minimum length of an edge. Usefull to avoid the creation of too many small faces."); break; default: assert(0); @@ -99,165 +100,17 @@ void GeometryAgingPlugin::initParameterSet(QAction *action, MeshModel &m, Filter // The Real Core Function doing the actual mesh processing bool GeometryAgingPlugin::applyFilter(QAction *filter, MeshModel &m, FilterParameterSet ¶ms, vcg::CallBackPos *cb) { - CMeshO::FaceIterator fi; - int borderNum = 0; - int chips = 0; - float angleThreshold = params.getFloat("AngleThreshold"); - bool hasSel = hasSelected(m); - typedef std::pair sedge; // selected edge - map* > edge2Eroder; + float angleThreshold; + float edgeLenTreshold; - // first loop to clear V bits - for(fi=m.cm.face.begin(); fi!=m.cm.face.end(); ++fi) - (*fi).ClearV(); - //tri::UpdateSelection::ClearFace(m.cm); // DEBUG - - // I'm looking for edges whose incident faces normals make an angle of at least - // 60 degrees (or other threshold value chosen by the user). - // I'm also interested in border edges. - for(fi=m.cm.face.begin(); fi!=m.cm.face.end(); ++fi) { - // Typical usage of the callback for showing a nice progress bar in the bottom. - // First parameter is a 0..100 number indicating percentage of completion, the second is an info string. - //cb(100*vcount/model.cm.vert.size(), "Exploring the mesh..."); - - if(!(*fi).IsD()) { - if(hasSel && !(*fi).IsS()) continue; // some faces of the mesh are selected but not the current one - for(int j=0; j<3; j++) { - if((*fi).FFp(j)->IsV()) continue; // face already visited - if(hasSel && !(*fi).FFp(j)->IsS()) continue; // some faces of the mesh are selected, the current one too, but not its j-th neighbour - if(/*(*fi).FFp(j) == &(*fi)*/(*fi).IsB(j)) { // found border edge - borderNum++; - edge2Eroder[sedge(&(*fi), j)] = generateEdgeEroder(true, vcg::Distance((*fi).P0(j), (*fi).P1(j))); - // (*fi).SetS(); // DEBUG - // (*fi).FFp(j)->SetS(); // DEBUG - } - else { // this is not a border edge - // the angle between the two face normals in degrees - // TODO: check non 2-manifold cases, it's all ok? or there are problems? - double ffangle = vcg::Angle((*fi).N(), (*fi).FFp(j)->N())*180/M_PI; - - // the 2 points not shared between the 2 faces - CVertexO *f1p = (*fi).V2(j); - CVertexO *f2p = (*fi).FFp(j)->V2((*fi).FFi(j)); - - Point3 y, median; - - y = (*fi).N().Normalize() ^ (*fi).FFp(j)->N().Normalize(); - median = y ^ (Point3(f1p->P() - f2p->P())); - - /* There are always 2 cases wich produce the same angle value: - ___|_ ____ - | | | - -| |- - - In the first case the edge lies in a concave area of the mesh - while in the second case it lies in a convex area. - I need a way to know wich is the current case. - This is done comparing ffangle with the angle between the - normal to the current face and the median vector. - */ - if(ffangle-angleThreshold >= -0.001 && vcg::Angle((*fi).N(), median)*180/M_PI < ffangle) { - chips++; - // (*fi).SetS(); // DEBUG - // (*fi).FFp(j)->SetS(); // DEBUG - } - } - } - (*fi).SetV(); - } + switch(ID(filter)) { + case FP_ERODE: + angleThreshold = params.getAbsPerc("AngleThreshold"); + edgeLenTreshold = params.getAbsPerc("EdgeLenThreshold"); + return refineNeededEdges(m.cm, FractPoint(), EdgePred(angleThreshold, edgeLenTreshold), hasSelected(m), cb); + default: + assert(0); } - - // erode border edges - /*if(borderNum > 0) { - CMeshO::VertexIterator lastv = tri::Allocator::AddVertices(m.cm, borderNum); - CMeshO::FaceIterator lastf = tri::Allocator::AddFaces(m.cm, borderNum); - for(map* >::iterator it = edge2Eroder.begin(); it != edge2Eroder.end(); ++it) { - if((*it).first.first->IsB((*it).first.second)) { // border face - CMeshO::FacePointer f = (*it).first.first; - int z = (*it).first.second; - vector* e = (*it).second; - vector vv; - vector fv; - vector wtt; - - for(unsigned int j=0; j<3; j++) - vv.push_back(f->V((z+j)%3)); - - for(unsigned int i=0; isize(); i++) { - (*lastv).P() = f->V(z)->P() * (1.0f - e->at(i).yrel) + f->V1(z)->P() * e->at(i).yrel; - if(CMeshO::HasPerVertexNormal()) - (*lastv).N()= (f->V(z)->N() * (1.0f - e->at(i).yrel) + f->V1(z)->N() * e->at(i).yrel).Normalize(); - if(CMeshO::HasPerVertexColor()) - (*lastv).C().lerp(f->V(z)->C(), f->V1(z)->C(), e->at(i).yrel); - vv.push_back(&*lastv); - ++lastv; - } - - fv.push_back(f); - for(unsigned int i=0; iWT(i)); - CMeshO::FaceType::TexCoordType t0 = f->WT(z); - CMeshO::FaceType::TexCoordType t1 = f->WT((z+1)%3); - assert(t0.n() == t1.n()); - for(unsigned int i=0; isize(); i++) { - CMeshO::FaceType::TexCoordType tmp; - tmp.n() = t0.n(); - tmp.t() = (t0.t() * (1.0f - e->at(i).yrel) + t1.t() * e->at(i).yrel); - wtt.push_back(tmp); - } - } - - int orgflag = f->UberFlags(); - for(unsigned int i=0; iV(0) = vv.at((i>0?2+i:i)); - fv.at(i)->V(1) = vv.at((iV(2) = vv.at(2); - if(tri::HasPerWedgeTexCoord(m.cm)) { - fv.at(i)->WT(0) = wtt.at((i>0?2+i:i)); - fv.at(i)->WT(1) = wtt.at((iWT(2) = wtt.at(2); - } - } - - for(unsigned int i=0; iSetB(0); - else - fv.at(i)->ClearB(0); - fv.at(i)->ClearB(1); - fv.at(i)->ClearB(2); - } - if(orgflag & (CMeshO::FaceType::BORDER0 << z+1)) - fv.at(fv.size()-1)->SetB(1); - else - fv.at(fv.size()-1)->ClearB(1); - if(orgflag & (CMeshO::FaceType::BORDER0 << z+2)) - fv.at(0)->SetB(2); - else - fv.at(0)->ClearB(2); - } - } - } - vcg::tri::UpdateTopology::FaceFace(m.cm);*/ - //vcg::tri::UpdateFlags::FaceBorderFromFF(m.cm); - - // clear V bits again - for(fi=m.cm.face.begin(); fi!=m.cm.face.end(); ++fi) - (*fi).ClearV(); - - // Log function dump textual info in the lower part of the MeshLab screen - Log(0,"Found %i border edges and %i internal edges to erode.", borderNum, chips); // DEBUG - - for(map* >::iterator it = edge2Eroder.begin(); it != edge2Eroder.end(); ++it) - delete it->second; - - return true; } @@ -273,14 +126,14 @@ bool GeometryAgingPlugin::hasSelected(MeshModel &m) // randomly creates a vector of eroderPoints and returns a pointer to it -vector* GeometryAgingPlugin::generateEdgeEroder(bool border, float len) +vector* GeometryAgingPlugin::generateEdgeEroder(CMeshO::FacePointer f, int z) { vector* e = new vector(); srand(time(NULL)); //std::cout << "len: " << len << std::endl; // DEBUG - if(border) + if(f->IsB(z)) e->push_back(GeometryAgingPlugin::EroderPoint(0.5, 0.0)); /*else e->push_back(GeometryAgingPlugin::EroderPoint(0.5, 0.0, 0.0));*/ @@ -289,4 +142,220 @@ vector* GeometryAgingPlugin::generateEdgeErode } +// refines the edges which will be eroded in the next phase +bool GeometryAgingPlugin::refineNeededEdges(CMeshO &m, FractPoint frpoi, EdgePred ep, bool hasSelected, CallBackPos *cb) +{ + int j, newVertNum = 0, newFaceNum = 0; + typedef std::pair vvpair; + typedef std::pair vvi; + map edge2Vert; + + // First Loop: we analyze the mesh to compute the number of the new faces and new vertices + CMeshO::FaceIterator fi; + int step = 0, percStep = m.fn / 33; + if(percStep == 0) percStep = 1; + for(fi=m.face.begin(),j=0; fi!=m.face.end(); ++fi) + if(!(*fi).IsD()) + { + if(cb && (++step%percStep) == 0) + (*cb)(step/percStep, "Eroding..."); + for(j=0; j<3; j++) { + if(ep(&(*fi), j) && (!hasSelected || ((*fi).IsS() && (*fi).FFp(j)->IsS()))) { + int numf = ep.numFacesToAdd(&(*fi), j); + if(numf <= 0) continue; + newFaceNum += numf; + if(((*fi).V(j) < (*fi).V1(j)) || (*fi).IsB(j)) + newVertNum += numf; + } + } + } // end face loop + + if(newVertNum==0) { // temporary error message + errorMessage = QString("Found 0 edges to erode. Change filter parameters and try again."); + return false; + } + + CMeshO::VertexIterator lastv = tri::Allocator::AddVertices(m, newVertNum); + + // Second Loop: initialization of the edge-vertexes map + // and the position of new vertexes + for(fi=m.face.begin(); fi!=m.face.end(); ++fi) + if(!(*fi).IsD()) + { + if(cb && (++step%percStep) == 0) + (*cb)(step/percStep, "Eroding..."); + for(j=0; j<3; j++) + if(ep(&(*fi), j) && (!hasSelected || ((*fi).IsS() && (*fi).FFp(j)->IsS()))) + if((*fi).V(j) < (*fi).V1(j) || (*fi).IsB(j)) { + int numf = ep.numFacesToAdd(&(*fi), j) + 1; + for(int p=1; p(&*fi,j), (float)p/numf); + edge2Vert[vvi(vvpair((*fi).V(j), (*fi).V1(j)), p)] = &*lastv; + ++lastv; + } + } + } + assert(lastv == m.vert.end()); + + CMeshO::FaceIterator lastf = tri::Allocator::AddFaces(m, newFaceNum); + CMeshO::FaceIterator oldendf = lastf; + +/* + v0 + + + f0 + + mp01 mp02 + + f3 + f1 f2 + + v1 mp12 v2 + +*/ + + vector vv; // total face vertexes + // 0..2 original face vertexes + // 3... newly created vertexes + vector nf; // total faces (the original one and the newly created) + vector wtt; // per wedge texture coordinates + + for(fi=m.face.begin(); fi!=oldendf; ++fi) + if(!(*fi).IsD()) + { + if(cb && (++step%percStep) == 0) + (*cb)(step/percStep, "Eroding..."); + vv.clear(); + nf.clear(); + wtt.clear(); + vv.push_back((*fi).V(0)); + vv.push_back((*fi).V(1)); + vv.push_back((*fi).V(2)); + int n[3]; + for(j=0; j<3; j++) { + if(ep(&(*fi), j) && (!hasSelected || ((*fi).IsS() && (*fi).FFp(j)->IsS()))) { + n[j] = ep.numFacesToAdd(&(*fi), j); + if((*fi).V(j) < (*fi).V1(j) || (*fi).IsB(j)) + for(int p=1; p= &*m.vert.begin() && (*fi).V(0) <= &m.vert.back()); + assert((*fi).V(1) >= &*m.vert.begin() && (*fi).V(1) <= &m.vert.back()); + assert((*fi).V(2) >= &*m.vert.begin() && (*fi).V(2) <= &m.vert.back()); + } + vcg::tri::UpdateTopology::FaceFace(m); + vcg::tri::UpdateNormals::PerVertexNormalizedPerFace(m); + + Log(0,"Created %i new vertexes and %i new faces.", newVertNum, newFaceNum); // DEBUG + + return true; +} + + +// splits the current edge (z) of the current face creating numf[z] new faces. +// actually the face edges are already splitted: the new vertexes (contained in vector vv) are +// already in their final position but the area need to be retriangulated +// (vector f contains the pointers to all the faces needed to retriangulate the old face) +void GeometryAgingPlugin::splitFaceEdge(CMeshO &m, vector &f, vector &vv, vector &wtt, int numf[3], int z, int orgflag) +{ + if(numf[z] <= 0) return; + + int idx = 3; // index of the first vertex of the curr face (the second will be idx+1) + int idx3; // index of the third vertex of the curr face + int oldf = 0; // face to split index + int newf = 1; // first new face index + int vf0 = (z+1)%3; // index of the vertex to move in face 0 (original face to split) + + if(z == 0) + idx3 = 2; + else if(z == 1) { + if(numf[0] > 0) { + idx3 = 2 + numf[0]; + oldf += numf[0]; + newf += numf[0]; + } + else + idx3 = 0; + } + else { + if(numf[0] > 0) { + idx3 = 3; + newf += numf[0] + numf[1]; + } + else if(numf[1] > 0) { + idx3 = 2 + numf[1]; + oldf += numf[1]; + newf += numf[1]; + vf0 = z; + } + else + idx3 = 1; + } + + int i; + for(i=0; iV(vf0) = vv.at(idx); + if(tri::HasPerWedgeTexCoord(m)) + f.at(oldf)->WT((z+1)%3) = wtt.at(idx); + if(idx > 2) + f.at(oldf)->ClearB((z+1)%3); + + for(i=0; iV(0) = vv.at(idx); + f.at(newf)->V(1) = vv.at((i==(numf[z]-1)?(z+1)%3:idx+1)); + f.at(newf)->V(2) = vv.at(idx3); + if(tri::HasPerWedgeTexCoord(m)) { + f.at(newf)->WT(0) = wtt.at(idx); + f.at(newf)->WT(1) = wtt.at((i==(numf[z]-1)?(z+1)%3:idx+1)); + f.at(newf)->WT(2) = wtt.at(idx3); + } + for(int j=0; j<3; j++) assert(f.at(newf)->V(j) != 0); + if(orgflag & (CMeshO::FaceType::BORDER0 << z)) + f.at(newf)->SetB(0); + else + f.at(newf)->ClearB(0); + f.at(newf)->ClearB(1); + f.at(newf)->ClearB(2); + } +} + + Q_EXPORT_PLUGIN(GeometryAgingPlugin) diff --git a/src/fgt/filter_aging/filter_aging.h b/src/fgt/filter_aging/filter_aging.h index 262ad566c..4a15d2721 100644 --- a/src/fgt/filter_aging/filter_aging.h +++ b/src/fgt/filter_aging/filter_aging.h @@ -22,14 +22,93 @@ ****************************************************************************/ - #ifndef GEOMETRYAGINGPLUGIN_H #define GEOMETRYAGINGPLUGIN_H + #include #include #include +//#include + + +struct FractPoint : public std::unary_function, CMeshO::CoordType> +{ + void operator()(CMeshO::VertexType &nv, face::Pos ep, float relpos = 0.5f) { + nv.P() = ep.f->V(ep.z)->P() * (1.0f - relpos) + ep.f->V1(ep.z)->P() * relpos; + if(CMeshO::HasPerVertexNormal()) + nv.N() = (ep.f->V(ep.z)->N() * (1.0f - relpos) + ep.f->V1(ep.z)->N() * relpos).Normalize(); + if(CMeshO::HasPerVertexColor()) + nv.C().lerp(ep.f->V(ep.z)->C(), ep.f->V1(ep.z)->C(), relpos); + } + + Color4 WedgeInterp(Color4 &c0, Color4 &c1, float relpos = 0.5f) + { + Color4 cc; + cc.lerp(c0, c1, relpos); + return cc; + } + + template + TexCoord2 WedgeInterp(TexCoord2 &t0, TexCoord2 &t1, float relpos = 0.5f) + { + TexCoord2 tmp; + assert(t0.n() == t1.n()); + tmp.n() = t0.n(); + tmp.t() = t0.t() * (1.0f - relpos) + t1.t() * relpos; + return tmp; + } +}; + + +class EdgePred +{ + public: + EdgePred(float angleTh, float edgeLenTh) { + this->angleTh = angleTh; + this->edgeLenTh = edgeLenTh; + } + + bool operator()(CMeshO::FacePointer f, int idx) { + if(f->IsB(idx)) return true; + + // the angle between the two face normals in degrees + // TODO: check non 2-manifold cases, it's all ok? or there are problems? + /*double ffangle = vcg::Angle(f->N(), f->FFp(idx)->N()) * 180 / M_PI; + + // the 2 points not shared between the 2 faces + CVertexO *f1p = f->V2(idx); + CVertexO *f2p = f->FFp(idx)->V2(f->FFi(idx)); + + Point3 y, median; + + y = f->N().Normalize() ^ f->FFp(idx)->N().Normalize(); + median = y ^ (Point3(f1p->P() - f2p->P())); + */ + /* There are always 2 cases wich produce the same angle value: + ___|_ ____ + | | | + -| |- + + In the first case the edge lies in a concave area of the mesh + while in the second case it lies in a convex area. + We need a way to know wich is the current case. + This is done comparing ffangle with the angle between the + normal to the current face and the median vector. + */ + //return (ffangle-angleThreshold >= -0.001 && vcg::Angle(f->N(), median) * 180 / M_PI < ffangle); + return false; + } + + int numFacesToAdd(CMeshO::FacePointer f, int idx) { + return (int)(Distance(f->V(idx)->P(), f->V1(idx)->P()) / edgeLenTh) - 1; + } + + protected: + float angleTh; + float edgeLenTh; +}; class GeometryAgingPlugin : public QObject, public MeshFilterInterface @@ -62,9 +141,10 @@ class GeometryAgingPlugin : public QObject, public MeshFilterInterface virtual bool applyFilter(QAction *filter, MeshModel &m, FilterParameterSet ¶ms, vcg::CallBackPos *cb); protected: - virtual bool hasSelected(MeshModel &m); - vector* generateEdgeEroder(bool border, float len); - + bool hasSelected(MeshModel &m); + vector* generateEdgeEroder(CMeshO::FacePointer f, int z); + bool refineNeededEdges(CMeshO &m, FractPoint mid, EdgePred ep, bool hasSelected, CallBackPos *cb); + void splitFaceEdge(CMeshO &m, vector &f, vector &vv, vector &wtt, int numf[3], int z, int orgflag); };