// given a RichParameterSet get back the alignment parameter (dual of the buildParemeterSet) void AlignParameter::buildAlignParameters(RichParameterSet &fps , AlignPair::Param &app) { app.SampleNum=fps.getInt("SampleNum"); app.MinDistAbs=fps.getFloat("MinDistAbs"); app.TrgDistAbs=fps.getFloat("TrgDistAbs"); app.MaxIterNum=fps.getInt("MaxIterNum"); app.SampleMode= fps.getBool("SampleMode")?AlignPair::Param::SMNormalEqualized : AlignPair::Param::SMRandom; app.ReduceFactorPerc=fps.getFloat("ReduceFactorPerc"); app.PassHiFilter=fps.getFloat("PassHiFilter"); app.MatchMode=fps.getBool("MatchMode")? AlignPair::Param::MMRigid : AlignPair::Param::MMClassic; }
bool DynamicMeshSubFilter::configurationHasChanged(MeshDocument& md, RichParameterSet& par){ bool changed = m_seconds != par.getInt("seconds"); changed |= m_fps != par.getInt("fps"); changed |= m_iterations != par.getInt("iterations"); changed |= m_contacts != par.getInt("contacts"); changed |= m_bounciness != par.getFloat("bounciness"); changed |= m_gravity != par.getFloat("gravity"); changed |= m_friction != par.getFloat("friction"); if(unsigned(md.size()) == m_files.size()) for(unsigned i = 0; i < m_files.size(); i++) changed |= m_files.at(i) != md.getMesh(i)->fileName; else changed = true; m_files.clear(); for(int i = 0; i < md.size(); i++) m_files.push_back(md.getMesh(i)->fileName); m_seconds = par.getInt("seconds"); m_fps = par.getInt("fps"); m_iterations = par.getInt("iterations"); m_contacts = par.getInt("contacts"); m_bounciness = par.getFloat("bounciness"); m_gravity = par.getFloat("gravity"); m_friction = par.getFloat("friction"); return changed; }
bool MeshGridPlugin::applyFilter(QAction *algo, MeshDocument &md, RichParameterSet & par, vcg::CallBackPos *cb) { int cols = par.getInt("numVertX"); int rows = par.getInt("numVertY"); float totalw = 1.0; float totalh = 1.0; int w = cols+1; int h = rows+1; float wl = 1.0/cols; float hl = 1.0/rows; qDebug("w %d h %d",w,h); if(w <= 0 || h <= 0) { return false; } md.addNewMesh("",QString("%1_%2").arg(rows).arg(cols)); MeshModel &m=*(md.mm()); // use Grid function to generate Grid std::vector<float> data(w*h,0); tri::Grid<CMeshO>(m.cm, w, h, 1, 1, &data[0]); { // move x and y double halfw = double(w-1)/2; double halfh = double(h-1)/2; double wld = wl/double(w); double hld = hl/float(h); // CMeshO::VertexIterator vi; // for(vi = m.cm.vert.begin(); vi != m.cm.vert.end(); ++vi) // { // // qDebug("pos x: %f y: %f",(*vi).P()[0],(*vi).P()[1]); // //(*vi).P()[0] = (*vi).P()[0] - (wld * halfw); // //(*vi).P()[1] = (*vi).P()[1] - (hld * halfh); // // (*vi).P()[0] = (*vi).P()[0] - totalw/2; // // (*vi).P()[1] = (*vi).P()[1] - totalh/2; // // qDebug("after pos x: %f y: %f",(*vi).P()[0],(*vi).P()[1]); // } } // update bounding box, normals // Matrix44f rot; rot.SetRotateDeg(180,Point3f(0,1,0)); // tri::UpdatePosition<CMeshO>::Matrix(m.cm,rot,false); tri::UpdateNormal<CMeshO>::PerVertexNormalizedPerFace(m.cm); tri::UpdateNormal<CMeshO>::NormalizePerFace(m.cm); tri::UpdateBounding<CMeshO>::Box(m.cm); CMeshO::VertexIterator vi; return true; }
bool FilterCreateIso::applyFilter(QAction *filter, MeshDocument &md, RichParameterSet & par, vcg::CallBackPos * cb) { md.addNewMesh("",this->filterName(ID(filter))); MeshModel &m=*(md.mm()); if(filter->text() == filterName(FP_CREATEISO) ) { SimpleVolume<SimpleVoxel<Scalarm> > volume; typedef vcg::tri::TrivialWalker<CMeshO, SimpleVolume<SimpleVoxel<Scalarm> > > MyWalker; typedef vcg::tri::MarchingCubes<CMeshO, MyWalker> MyMarchingCubes; MyWalker walker; const int gridSize=par.getInt("Resolution"); // Simple initialization of the volume with some cool perlin noise volume.Init(Point3i(gridSize,gridSize,gridSize), Box3m(Point3m(0,0,0),Point3m(1,1,1))); for(int i=0;i<gridSize;i++) for(int j=0;j<gridSize;j++) for(int k=0;k<gridSize;k++) volume.Val(i,j,k)=(j-gridSize/2)*(j-gridSize/2)+(k-gridSize/2)*(k-gridSize/2) + i*gridSize/5*(float)math::Perlin::Noise(i*.2,j*.2,k*.2); printf("[MARCHING CUBES] Building mesh..."); MyMarchingCubes mc(m.cm, walker); walker.BuildMesh<MyMarchingCubes>(m.cm, volume, mc, (gridSize*gridSize)/10,cb); m.UpdateBoxAndNormals(); } return true; }
// // Apply filter // bool FilterTopoPlugin::applyFilter(QAction *filter, MeshModel &m, RichParameterSet & par, vcg::CallBackPos *cb) { // To run the retopology algorithm an istance of RetopoMeshBuilder is needed RetopMeshBuilder rm; // Load topology mesh MeshModel *userMesh = par.getMesh("userMesh"); // Load (input) original mesh MeshModel *inMesh = par.getMesh("inMesh"); // Load iterations value int it = par.getInt("it"); // Load distance value float dist = par.getAbsPerc("dist"); // Destination meshmodel: retopology mesh will replace flat topology mesh MeshModel * outM = par.getMesh("userMesh"); // Prepare mesh inMesh->updateDataMask(MeshModel::MM_FACEMARK); tri::UpdateNormals<CMeshO>::PerFaceNormalized(inMesh->cm); tri::UpdateFlags<CMeshO>::FaceProjection(inMesh->cm); // Init the retopology builder with original input mesh and distance value rm.init(inMesh, dist); // Apply the algorithm return rm.applyTopoMesh(*userMesh, *inMesh, it, dist, *outM); }
void U3DIOPlugin::saveParameters(const RichParameterSet &par) { vcg::Point3f from_target_to_camera = vcg::Point3f(par.getPoint3f(QString("position_val")) - par.getPoint3f(QString("target_val"))); vcg::tri::io::u3dparametersclasses::Movie15Parameters::CameraParameters* sw = _param._campar; //vcg::Point3f p = avoidExponentialNotation(sw->_obj_pos,_param._campar->_obj_bbox_diag); vcg::Point3f p = sw->_obj_pos; _param._campar = new vcg::tri::io::u3dparametersclasses::Movie15Parameters::CameraParameters( par.getFloat(QString("fov_val")),0.0f,from_target_to_camera,from_target_to_camera.Norm(),sw->_obj_bbox_diag,p); _param.positionQuality = par.getInt(QString("compression_val")); delete sw; }
// The Real Core Function doing the actual mesh processing. bool FilterCreate::applyFilter(QAction *filter, MeshDocument &md, RichParameterSet & par, vcg::CallBackPos * /*cb*/) { MeshModel &m=(*md.mm()); switch(ID(filter)) { case CR_TETRAHEDRON : vcg::tri::Tetrahedron<CMeshO>(m.cm); break; case CR_ICOSAHEDRON: vcg::tri::Icosahedron<CMeshO>(m.cm); break; case CR_DODECAHEDRON: vcg::tri::Dodecahedron<CMeshO>(m.cm); m.updateDataMask(MeshModel::MM_POLYGONAL); break; case CR_OCTAHEDRON: vcg::tri::Octahedron<CMeshO>(m.cm); break; case CR_SPHERE: vcg::tri::Sphere<CMeshO>(m.cm); break; case CR_BOX: { float sz=par.getFloat("size"); vcg::Box3f b(vcg::Point3f(1,1,1)*(sz/2),vcg::Point3f(1,1,1)*(-sz/2)); vcg::tri::Box<CMeshO>(m.cm,b); m.updateDataMask(MeshModel::MM_POLYGONAL); break; } case CR_CONE: float r0=par.getFloat("r0"); float r1=par.getFloat("r1"); float h=par.getFloat("h"); int subdiv=par.getInt("subdiv"); vcg::tri::Cone<CMeshO>(m.cm,r0,r1,h,subdiv); break; } vcg::tri::UpdateBounding<CMeshO>::Box(m.cm); vcg::tri::UpdateNormals<CMeshO>::PerVertexNormalizedPerFaceNormalized(m.cm); return true; }
bool FilterCSG::applyFilter(QAction *filter, MeshDocument &md, RichParameterSet & par, vcg::CallBackPos *cb) { switch(ID(filter)) { case FP_CSG: { MeshModel *firstMesh = par.getMesh("FirstMesh"); MeshModel *secondMesh = par.getMesh("SecondMesh"); firstMesh->updateDataMask(MeshModel::MM_FACEFACETOPO | MeshModel::MM_FACENORMAL | MeshModel::MM_FACEQUALITY); secondMesh->updateDataMask(MeshModel::MM_FACEFACETOPO | MeshModel::MM_FACENORMAL | MeshModel::MM_FACEQUALITY); if (!isValid (firstMesh->cm, this->errorMessage) || !isValid (secondMesh->cm, this->errorMessage)) return false; firstMesh->updateDataMask(MeshModel::MM_FACENORMAL | MeshModel::MM_FACEQUALITY); secondMesh->updateDataMask(MeshModel::MM_FACENORMAL | MeshModel::MM_FACEQUALITY); typedef CMeshO::ScalarType scalar; typedef Intercept<mpq_class,scalar> intercept; const scalar d = par.getFloat("Delta"); const Point3f delta(d, d, d); const int subFreq = par.getInt("SubDelta"); Log(0, "Rasterizing first volume..."); InterceptVolume<intercept> v = InterceptSet3<intercept>(firstMesh->cm, delta, subFreq, cb); Log(0, "Rasterizing second volume..."); InterceptVolume<intercept> tmp = InterceptSet3<intercept>(secondMesh->cm, delta, subFreq, cb); MeshModel *mesh; switch(par.getEnum("Operator")){ case CSG_OPERATION_INTERSECTION: Log(0, "Intersection..."); v &= tmp; mesh = md.addNewMesh("","intersection"); break; case CSG_OPERATION_UNION: Log(0, "Union..."); v |= tmp; mesh = md.addNewMesh("","union"); break; case CSG_OPERATION_DIFFERENCE: Log(0, "Difference..."); v -= tmp; mesh = md.addNewMesh("","difference"); break; default: assert(0); return true; } Log(0, "Building mesh..."); typedef vcg::intercept::Walker<CMeshO, intercept> MyWalker; MyWalker walker; if (par.getBool("Extended")) { mesh->updateDataMask(MeshModel::MM_FACEFACETOPO); typedef vcg::tri::ExtendedMarchingCubes<CMeshO, MyWalker> MyExtendedMarchingCubes; MyExtendedMarchingCubes mc(mesh->cm, walker); walker.BuildMesh<MyExtendedMarchingCubes>(mesh->cm, v, mc, cb); } else { typedef vcg::tri::MarchingCubes<CMeshO, MyWalker> MyMarchingCubes; MyWalker walker; MyMarchingCubes mc(mesh->cm, walker); walker.BuildMesh<MyMarchingCubes>(mesh->cm, v, mc, cb); } Log(0, "Done"); vcg::tri::UpdateBounding<CMeshO>::Box(mesh->cm); vcg::tri::UpdateNormal<CMeshO>::PerFaceFromCurrentVertexNormal(mesh->cm); } return true; default: assert (0); } return true; }
bool CleanFilter::applyFilter(QAction *filter, MeshDocument &md, RichParameterSet & par, vcg::CallBackPos * cb) { MeshModel &m=*(md.mm()); switch(ID(filter)) { case FP_BALL_PIVOTING: { float Radius = par.getAbsPerc("BallRadius"); float Clustering = par.getFloat("Clustering") / 100.0f; float CreaseThr = math::ToRad(par.getFloat("CreaseThr")); bool DeleteFaces = par.getBool("DeleteFaces"); if(DeleteFaces) { m.cm.fn=0; m.cm.face.resize(0); } m.updateDataMask(MeshModel::MM_VERTFACETOPO); int startingFn=m.cm.fn; tri::BallPivoting<CMeshO> pivot(m.cm, Radius, Clustering, CreaseThr); // the main processing pivot.BuildMesh(cb); m.clearDataMask(MeshModel::MM_FACEFACETOPO); Log("Reconstructed surface. Added %i faces",m.cm.fn-startingFn); } break; case FP_REMOVE_ISOLATED_DIAMETER: { float minCC= par.getAbsPerc("MinComponentDiag"); std::pair<int,int> delInfo= tri::Clean<CMeshO>::RemoveSmallConnectedComponentsDiameter(m.cm,minCC); Log("Removed %2 connected components out of %1", delInfo.second, delInfo.first); }break; case FP_REMOVE_ISOLATED_COMPLEXITY: { float minCC= par.getInt("MinComponentSize"); std::pair<int,int> delInfo=tri::Clean<CMeshO>::RemoveSmallConnectedComponentsSize(m.cm,minCC); Log("Removed %i connected components out of %i", delInfo.second, delInfo.first); }break; case FP_REMOVE_WRT_Q: { int deletedFN=0; int deletedVN=0; float val=par.getAbsPerc("MaxQualityThr"); CMeshO::VertexIterator vi; for(vi=m.cm.vert.begin();vi!=m.cm.vert.end();++vi) if(!(*vi).IsD() && (*vi).Q()<val) { tri::Allocator<CMeshO>::DeleteVertex(m.cm, *vi); deletedVN++; } CMeshO::FaceIterator fi; for(fi=m.cm.face.begin();fi!=m.cm.face.end();++fi) if(!(*fi).IsD()) if((*fi).V(0)->IsD() ||(*fi).V(1)->IsD() ||(*fi).V(2)->IsD() ) { tri::Allocator<CMeshO>::DeleteFace(m.cm, *fi); deletedFN++; } m.clearDataMask(MeshModel::MM_FACEFACETOPO); Log("Deleted %i vertices and %i faces with a quality lower than %f", deletedVN,deletedFN,val); }break; case FP_REMOVE_TVERTEX_COLLAPSE : { float threshold = par.getFloat("Threshold"); bool repeat = par.getBool("Repeat"); int total = tri::Clean<CMeshO>::RemoveTVertexByCollapse(m.cm, threshold, repeat); Log("Successfully removed %d t-vertices", total); } break; case FP_REMOVE_TVERTEX_FLIP : { float threshold = par.getFloat("Threshold"); bool repeat = par.getBool("Repeat"); int total = tri::Clean<CMeshO>::RemoveTVertexByFlip(m.cm, threshold, repeat); Log("Successfully removed %d t-vertices", total); } break; case FP_MERGE_CLOSE_VERTEX : { float threshold = par.getAbsPerc("Threshold"); int total = tri::Clean<CMeshO>::MergeCloseVertex(m.cm, threshold); Log("Successfully merged %d vertices", total); } break; case FP_REMOVE_DUPLICATE_FACE : { int total = tri::Clean<CMeshO>::RemoveDuplicateFace(m.cm); Log("Successfully deleted %d duplicated faces", total); } break; case FP_REMOVE_FOLD_FACE: { m.updateDataMask(MeshModel::MM_FACECOLOR); int total = tri::Clean<CMeshO>::RemoveFaceFoldByFlip(m.cm); m.UpdateBoxAndNormals(); Log("Successfully flipped %d folded faces", total); } break; case FP_REMOVE_NON_MANIF_EDGE : { int total = tri::Clean<CMeshO>::RemoveNonManifoldFace(m.cm); Log("Successfully removed %d folded faces", total); } break; case FP_REMOVE_NON_MANIF_VERT : { float threshold = par.getFloat("VertDispRatio"); int total = tri::Clean<CMeshO>::SplitNonManifoldVertex(m.cm,threshold); Log("Successfully split %d non manifold vertices faces", total); } break; case FP_SNAP_MISMATCHED_BORDER : { float threshold = par.getFloat("EdgeDistRatio"); int total = SnapVertexBorder(m.cm, threshold,cb); Log("Successfully Splitted %d faces to snap", total); } break; case FP_COMPACT_FACE : { vcg::tri::Allocator<CMeshO>::CompactFaceVector(m.cm); break; } case FP_COMPACT_VERT : { vcg::tri::Allocator<CMeshO>::CompactVertexVector(m.cm); break; } default : assert(0); // unknown filter; } return true; }
bool ExtraMeshColorizePlugin::applyFilter(QAction *filter, MeshDocument &md, RichParameterSet & par, vcg::CallBackPos *cb){ MeshModel &m=*(md.mm()); switch(ID(filter)) { case CP_SATURATE_QUALITY:{ m.updateDataMask(MeshModel::MM_VERTFACETOPO); tri::UpdateQuality<CMeshO>::VertexSaturate(m.cm, par.getFloat("gradientThr")); if(par.getBool("updateColor")) { Histogramf H; tri::Stat<CMeshO>::ComputePerVertexQualityHistogram(m.cm,H); m.updateDataMask(MeshModel::MM_VERTCOLOR); tri::UpdateColor<CMeshO>::VertexQualityRamp(m.cm,H.Percentile(0.1f),H.Percentile(0.9f)); } Log("Saturated "); } break; case CP_MAP_VQUALITY_INTO_COLOR: m.updateDataMask(MeshModel::MM_VERTCOLOR); case CP_CLAMP_QUALITY: { float RangeMin = par.getFloat("minVal"); float RangeMax = par.getFloat("maxVal"); bool usePerc = par.getDynamicFloat("perc")>0; Histogramf H; tri::Stat<CMeshO>::ComputePerVertexQualityHistogram(m.cm,H); float PercLo = H.Percentile(par.getDynamicFloat("perc")/100.f); float PercHi = H.Percentile(1.0-par.getDynamicFloat("perc")/100.f); if(par.getBool("zeroSym")) { RangeMin = min(RangeMin, -math::Abs(RangeMax)); RangeMax = max(math::Abs(RangeMin), RangeMax); PercLo = min(PercLo, -math::Abs(PercHi)); PercHi = max(math::Abs(PercLo), PercHi); } if(usePerc) { if(ID(filter)==CP_CLAMP_QUALITY) tri::UpdateQuality<CMeshO>::VertexClamp(m.cm,PercLo,PercHi); else tri::UpdateColor<CMeshO>::VertexQualityRamp(m.cm,PercLo,PercHi); Log("Quality Range: %f %f; Used (%f %f) percentile (%f %f) ",H.MinV(),H.MaxV(),PercLo,PercHi,par.getDynamicFloat("perc"),100-par.getDynamicFloat("perc")); } else { if(ID(filter)==CP_CLAMP_QUALITY) tri::UpdateQuality<CMeshO>::VertexClamp(m.cm,RangeMin,RangeMax); else tri::UpdateColor<CMeshO>::VertexQualityRamp(m.cm,RangeMin,RangeMax); Log("Quality Range: %f %f; Used (%f %f)",H.MinV(),H.MaxV(),RangeMin,RangeMax); } break; } case CP_MAP_FQUALITY_INTO_COLOR: { m.updateDataMask(MeshModel::MM_FACECOLOR); float RangeMin = par.getFloat("minVal"); float RangeMax = par.getFloat("maxVal"); float perc = par.getDynamicFloat("perc"); Histogramf H; tri::Stat<CMeshO>::ComputePerFaceQualityHistogram(m.cm,H); float PercLo = H.Percentile(perc/100.f); float PercHi = H.Percentile(1.0-perc/100.f); // Make the range and percentile symmetric w.r.t. zero, so that // the value zero is always colored in yellow if(par.getBool("zeroSym")){ RangeMin = min(RangeMin, -math::Abs(RangeMax)); RangeMax = max(math::Abs(RangeMin), RangeMax); PercLo = min(PercLo, -math::Abs(PercHi)); PercHi = max(math::Abs(PercLo), PercHi); } tri::UpdateColor<CMeshO>::FaceQualityRamp(m.cm,PercLo,PercHi); Log("Quality Range: %f %f; Used (%f %f) percentile (%f %f) ", H.MinV(), H.MaxV(), PercLo, PercHi, perc, 100-perc); break; } case CP_DISCRETE_CURVATURE: { m.updateDataMask(MeshModel::MM_FACEFACETOPO | MeshModel::MM_VERTCURV); m.updateDataMask(MeshModel::MM_VERTCOLOR | MeshModel::MM_VERTQUALITY); tri::UpdateFlags<CMeshO>::FaceBorderFromFF(m.cm); if ( tri::Clean<CMeshO>::CountNonManifoldEdgeFF(m.cm) > 0) { errorMessage = "Mesh has some not 2-manifold faces, Curvature computation requires manifoldness"; // text return false; // can't continue, mesh can't be processed } int delvert=tri::Clean<CMeshO>::RemoveUnreferencedVertex(m.cm); if(delvert) Log("Pre-Curvature Cleaning: Removed %d unreferenced vertices",delvert); tri::Allocator<CMeshO>::CompactVertexVector(m.cm); tri::UpdateCurvature<CMeshO>::MeanAndGaussian(m.cm); int curvType = par.getEnum("CurvatureType"); switch(curvType){ case 0: tri::UpdateQuality<CMeshO>::VertexFromMeanCurvature(m.cm); Log( "Computed Mean Curvature"); break; case 1: tri::UpdateQuality<CMeshO>::VertexFromGaussianCurvature(m.cm); Log( "Computed Gaussian Curvature"); break; case 2: tri::UpdateQuality<CMeshO>::VertexFromRMSCurvature(m.cm); Log( "Computed RMS Curvature"); break; case 3: tri::UpdateQuality<CMeshO>::VertexFromAbsoluteCurvature(m.cm); Log( "Computed ABS Curvature"); break; default : assert(0); } Histogramf H; tri::Stat<CMeshO>::ComputePerVertexQualityHistogram(m.cm,H); tri::UpdateColor<CMeshO>::VertexQualityRamp(m.cm,H.Percentile(0.1f),H.Percentile(0.9f)); Log( "Curvature Range: %f %f (Used 90 percentile %f %f) ",H.MinV(),H.MaxV(),H.Percentile(0.1f),H.Percentile(0.9f)); break; } case CP_TRIANGLE_QUALITY: { m.updateDataMask(MeshModel::MM_FACECOLOR | MeshModel::MM_FACEQUALITY); CMeshO::FaceIterator fi; Distribution<float> distrib; float minV = 0; float maxV = 1.0; int metric = par.getEnum("Metric"); if(metric ==4 || metric ==5 ) { if(!m.hasDataMask(MeshModel::MM_VERTTEXCOORD) && !m.hasDataMask(MeshModel::MM_WEDGTEXCOORD)) { this->errorMessage = "This metric need Texture Coordinate"; return false; } } switch(metric){ case 0: { //area / max edge minV = 0; maxV = sqrt(3.0f)/2.0f; for(fi=m.cm.face.begin();fi!=m.cm.face.end();++fi) if(!(*fi).IsD()) (*fi).Q() = vcg::Quality((*fi).P(0), (*fi).P(1),(*fi).P(2)); } break; case 1: { //inradius / circumradius for(fi=m.cm.face.begin();fi!=m.cm.face.end();++fi) if(!(*fi).IsD()) (*fi).Q() = vcg::QualityRadii((*fi).P(0), (*fi).P(1), (*fi).P(2)); } break; case 2: { //mean ratio for(fi=m.cm.face.begin();fi!=m.cm.face.end();++fi) if(!(*fi).IsD()) (*fi).Q() = vcg::QualityMeanRatio((*fi).P(0), (*fi).P(1), (*fi).P(2)); } break; case 3: { // AREA for(fi=m.cm.face.begin();fi!=m.cm.face.end();++fi) if(!(*fi).IsD()) (*fi).Q() = vcg::DoubleArea((*fi))*0.5f; tri::Stat<CMeshO>::ComputePerFaceQualityMinMax(m.cm,minV,maxV); } break; case 4: { //TEXTURE Angle Distortion if(m.hasDataMask(MeshModel::MM_WEDGTEXCOORD)) { for(fi=m.cm.face.begin();fi!=m.cm.face.end();++fi) if(!(*fi).IsD()) (*fi).Q() = Distortion<CMeshO,true>::AngleDistortion(&*fi); } else { for(fi=m.cm.face.begin();fi!=m.cm.face.end();++fi) if(!(*fi).IsD()) (*fi).Q() = Distortion<CMeshO,false>::AngleDistortion(&*fi); } tri::Stat<CMeshO>::ComputePerFaceQualityDistribution(m.cm,distrib); minV = distrib.Percentile(0.05); maxV = distrib.Percentile(0.95); } break; case 5: { //TEXTURE Area Distortion float areaScaleVal, edgeScaleVal; if(m.hasDataMask(MeshModel::MM_WEDGTEXCOORD)) { Distortion<CMeshO,true>::MeshScalingFactor(m.cm, areaScaleVal,edgeScaleVal); for(fi=m.cm.face.begin();fi!=m.cm.face.end();++fi) if(!(*fi).IsD()) (*fi).Q() = Distortion<CMeshO,true>::AreaDistortion(&*fi,areaScaleVal); } else { Distortion<CMeshO,false>::MeshScalingFactor(m.cm, areaScaleVal,edgeScaleVal); for(fi=m.cm.face.begin();fi!=m.cm.face.end();++fi) if(!(*fi).IsD()) (*fi).Q() = Distortion<CMeshO,false>::AreaDistortion(&*fi,areaScaleVal); } tri::Stat<CMeshO>::ComputePerFaceQualityDistribution(m.cm,distrib); minV = distrib.Percentile(0.05); maxV = distrib.Percentile(0.95); } break; default: assert(0); } tri::UpdateColor<CMeshO>::FaceQualityRamp(m.cm,minV,maxV,false); break; } case CP_RANDOM_CONNECTED_COMPONENT: m.updateDataMask(MeshModel::MM_FACEFACETOPO); m.updateDataMask(MeshModel::MM_FACEMARK | MeshModel::MM_FACECOLOR); vcg::tri::UpdateColor<CMeshO>::FaceRandomConnectedComponent(m.cm); break; case CP_RANDOM_FACE: m.updateDataMask(MeshModel::MM_FACEFACETOPO); m.updateDataMask(MeshModel::MM_FACEMARK | MeshModel::MM_FACECOLOR); vcg::tri::UpdateColor<CMeshO>::MultiFaceRandom(m.cm); break; case CP_VERTEX_SMOOTH: { int iteration = par.getInt("iteration"); tri::Smooth<CMeshO>::VertexColorLaplacian(m.cm,iteration,false,cb); } break; case CP_FACE_SMOOTH: { m.updateDataMask(MeshModel::MM_FACEFACETOPO); int iteration = par.getInt("iteration"); tri::Smooth<CMeshO>::FaceColorLaplacian(m.cm,iteration,false,cb); } break; case CP_FACE_TO_VERTEX: m.updateDataMask(MeshModel::MM_VERTCOLOR); tri::UpdateColor<CMeshO>::VertexFromFace(m.cm); break; case CP_VERTEX_TO_FACE: m.updateDataMask(MeshModel::MM_FACECOLOR); tri::UpdateColor<CMeshO>::FaceFromVertex(m.cm); break; case CP_TEXTURE_TO_VERTEX: { m.updateDataMask(MeshModel::MM_VERTCOLOR); if(!HasPerWedgeTexCoord(m.cm)) break; CMeshO::FaceIterator fi; QImage tex(m.cm.textures[0].c_str()); for(fi=m.cm.face.begin();fi!=m.cm.face.end();++fi) if(!(*fi).IsD()) { for (int i=0; i<3; i++) { // note the trick for getting only the fractional part of the uv with the correct wrapping (e.g. 1.5 -> 0.5 and -0.3 -> 0.7) vcg::Point2f newcoord((*fi).WT(i).P().X()-floor((*fi).WT(i).P().X()),(*fi).WT(i).P().Y()-floor((*fi).WT(i).P().Y())); QRgb val = tex.pixel(newcoord[0]*tex.width(),(1-newcoord[1])*tex.height()-1); (*fi).V(i)->C().SetRGB(qRed(val),qGreen(val),qBlue(val)); } } } break; } return true; }
// Core Function doing the actual mesh processing. bool FilterMeasurePlugin::applyFilter(QAction *filter, MeshDocument &md, RichParameterSet & par, vcg::CallBackPos */*cb*/) { CMeshO::FaceIterator fi; switch(ID(filter)) { case FP_MEASURE_TOPO : { CMeshO &m=md.mm()->cm; md.mm()->updateDataMask(MeshModel::MM_FACEFACETOPO); md.mm()->updateDataMask(MeshModel::MM_VERTFACETOPO); tri::Allocator<CMeshO>::CompactFaceVector(m); tri::Allocator<CMeshO>::CompactVertexVector(m); tri::UpdateTopology<CMeshO>::FaceFace(m); tri::UpdateTopology<CMeshO>::VertexFace(m); int edgeManif = tri::Clean<CMeshO>::CountNonManifoldEdgeFF(m,true); int faceEdgeManif = tri::UpdateSelection<CMeshO>::CountFace(m); tri::UpdateSelection<CMeshO>::ClearVertex(m); tri::UpdateSelection<CMeshO>::ClearFace(m); int vertManif = tri::Clean<CMeshO>::CountNonManifoldVertexFF(m,true); tri::UpdateSelection<CMeshO>::FaceFromVertexLoose(m); int faceVertManif = tri::UpdateSelection<CMeshO>::CountFace(m); int edgeNum=0,borderNum=0; tri::Clean<CMeshO>::CountEdges(m, edgeNum, borderNum); int holeNum; Log("V: %6i E: %6i F:%6i",m.vn,edgeNum,m.fn); Log("Boundary Edges %i",borderNum); int connectedComponentsNum = tri::Clean<CMeshO>::CountConnectedComponents(m); Log("Mesh is composed by %i connected component(s)",connectedComponentsNum); if(edgeManif==0 && vertManif==0) Log("Mesh has is two-manifold "); if(edgeManif!=0) Log("Mesh has %i non two manifold edges and %i faces are incident on these edges\n",edgeManif,faceEdgeManif); if(vertManif!=0) Log("Mesh has %i non two manifold vertexes and %i faces are incident on these vertices\n",vertManif,faceVertManif); // For Manifold meshes compute some other stuff if(vertManif && edgeManif) { holeNum = tri::Clean<CMeshO>::CountHoles(m); Log("Mesh has %i holes",holeNum); int genus = tri::Clean<CMeshO>::MeshGenus(m, holeNum, connectedComponentsNum, edgeNum); Log("Genus is %i",genus); } } break; /************************************************************/ case FP_MEASURE_TOPO_QUAD : { CMeshO &m=md.mm()->cm; md.mm()->updateDataMask(MeshModel::MM_FACEFACETOPO); md.mm()->updateDataMask(MeshModel::MM_FACEQUALITY); if (! tri::Clean<CMeshO>::IsFFAdjacencyConsistent(m)){ Log("Error: mesh has a not consistent FF adjacency"); return false; } if (! tri::Clean<CMeshO>::HasConsistentPerFaceFauxFlag(m)) { Log("Warning: mesh has a not consistent FauxEdge tagging"); return false; } if (! tri::Clean<CMeshO>::IsBitTriQuadOnly(m)) { Log("Warning: IsBitTriQuadOnly"); //return false; } // if (! tri::Clean<CMeshO>::HasConsistentEdges(m)) lastErrorDetected |= NOT_EDGES_CONST; int nsinglets= tri::BitQuadOptimization< tri::BitQuad<CMeshO> >::MarkSinglets(m); if ( nsinglets ) { Log("Warning: MarkSinglets"); //return false; } if (! tri::BitQuad<CMeshO>::HasConsistentValencyFlag(m)) { Log("Warning: HasConsistentValencyFlag"); //return false; } int nQuads = tri::Clean<CMeshO>::CountBitQuads(m); int nTris = tri::Clean<CMeshO>::CountBitTris(m); int nPolys = tri::Clean<CMeshO>::CountBitPolygons(m); Log("Mesh has %i tri %i quad and %i polig",nTris,nQuads,nPolys); } break; /************************************************************/ case FP_MEASURE_GEOM : { CMeshO &m=md.mm()->cm; tri::Inertia<CMeshO> I; I.Compute(m); tri::UpdateBounding<CMeshO>::Box(m); float Area = tri::Stat<CMeshO>::ComputeMeshArea(m); float Volume = I.Mass(); Log("Mesh Bounding Box Size %f %f %f", m.bbox.DimX(), m.bbox.DimY(), m.bbox.DimZ()); Log("Mesh Bounding Box Diag %f ", m.bbox.Diag()); Log("Mesh Volume is %f", Volume); Log("Center of Mass is %f %f %f", I.CenterOfMass()[0], I.CenterOfMass()[1], I.CenterOfMass()[2]); Matrix33f IT; I.InertiaTensor(IT); Log("Inertia Tensor is :"); Log(" | %9.6f %9.6f %9.6f |",IT[0][0],IT[0][1],IT[0][2]); Log(" | %9.6f %9.6f %9.6f |",IT[1][0],IT[1][1],IT[1][2]); Log(" | %9.6f %9.6f %9.6f |",IT[2][0],IT[2][1],IT[2][2]); Log("Mesh Surface is %f", Area); Matrix44f PCA; Point4f pcav; I.InertiaTensorEigen(PCA,pcav); Log("Principal axes are :"); Log(" | %9.6f %9.6f %9.6f |",PCA[0][0],PCA[0][1],PCA[0][2]); Log(" | %9.6f %9.6f %9.6f |",PCA[1][0],PCA[1][1],PCA[1][2]); Log(" | %9.6f %9.6f %9.6f |",PCA[2][0],PCA[2][1],PCA[2][2]); // Point3f ax0(PCA[0][0],PCA[0][1],PCA[0][2]); // Point3f ax1(PCA[1][0],PCA[1][1],PCA[1][2]); // Point3f ax2(PCA[2][0],PCA[2][1],PCA[2][2]); // Log("ax0*ax1 %f (len ax0 %f) ",ax0*ax1, Norm(ax0)); // Log("ax1*ax2 %f (len ax1 %f) ",ax1*ax2, Norm(ax1)); // Log("ax0*ax2 %f (len ax2 %f) ",ax0*ax2, Norm(ax2)); Log("axis momenta are :"); Log(" | %9.6f %9.6f %9.6f |",pcav[0],pcav[1],pcav[2]); } break; /************************************************************/ case FP_MEASURE_VERTEX_QUALITY_DISTRIBUTION : case FP_MEASURE_FACE_QUALITY_DISTRIBUTION : { CMeshO &m=md.mm()->cm; Distribution<float> DD; if(ID(filter)==FP_MEASURE_VERTEX_QUALITY_DISTRIBUTION) tri::Stat<CMeshO>::ComputePerVertexQualityDistribution(m, DD, false); else tri::Stat<CMeshO>::ComputePerFaceQualityDistribution(m, DD, false); Log(" Min %f Max %f",DD.Min(),DD.Max()); Log(" Avg %f Med %f",DD.Avg(),DD.Percentile(0.5f)); Log(" StdDev %f",DD.StandardDeviation()); Log(" Variance %f",DD.Variance()); } break; case FP_MEASURE_GAUSSCURV : { CMeshO &m=md.mm()->cm; SimpleTempData<CMeshO::VertContainer, float> TDArea(m.vert,0.0f); SimpleTempData<CMeshO::VertContainer, float> TDAngleSum(m.vert,0); tri::UpdateQuality<CMeshO>::VertexConstant(m,0); float angle[3]; CMeshO::FaceIterator fi; for(fi=m.face.begin(); fi!= m.face.end(); ++fi) { angle[0] = math::Abs(Angle( (*fi).P(1)-(*fi).P(0),(*fi).P(2)-(*fi).P(0) )); angle[1] = math::Abs(Angle( (*fi).P(0)-(*fi).P(1),(*fi).P(2)-(*fi).P(1) )); angle[2] = M_PI-(angle[0]+angle[1]); float area= DoubleArea(*fi)/6.0f; for(int i=0;i<3;++i) { TDArea[(*fi).V(i)]+=area; TDAngleSum[(*fi).V(i)]+=angle[i]; } } CMeshO::VertexIterator vi; float totalSum=0; for(vi=m.vert.begin(); vi!= m.vert.end(); ++vi) { (*vi).Q() = (2.0*M_PI-TDAngleSum[vi]);//*TDArea[vi]; //if((*vi).IsS()) totalSum += (*vi).Q(); } Log("integrated is %f (%f*pi)", totalSum,totalSum/M_PI); } break; case FP_MEASURE_VERTEX_QUALITY_HISTOGRAM: case FP_MEASURE_FACE_QUALITY_HISTOGRAM: { CMeshO &m=md.mm()->cm; float RangeMin = par.getFloat("minVal"); float RangeMax = par.getFloat("maxVal"); int binNum = par.getInt("binNum"); Histogramf H; H.SetRange(RangeMin,RangeMax,binNum); if(ID(filter)==FP_MEASURE_VERTEX_QUALITY_DISTRIBUTION) { for(CMeshO::VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi) if(!(*vi).IsD()) { assert(!math::IsNAN((*vi).Q()) && "You should never try to compute Histogram with Invalid Floating points numbers (NaN)"); H.Add((*vi).Q()); } }else{ for(CMeshO::FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) if(!(*fi).IsD()) { assert(!math::IsNAN((*fi).Q()) && "You should never try to compute Histogram with Invalid Floating points numbers (NaN)"); H.Add((*fi).Q()); } } Log("( -inf..%15.7f) : %i",RangeMin,H.BinCountInd(0)); for(int i=1;i<=binNum;++i) Log("[%15.7f..%15.7f) : %i",H.BinLowerBound(i),H.BinUpperBound(i),H.BinCountInd(i)); Log("[%15.7f.. +inf) : %i",RangeMax,H.BinCountInd(binNum+1)); } break; default: assert(0); } return true; }
//read source files and write to destination file //if it's found the attribute name with value "dummy", the following geometry statement //are replaced with a current mesh conversion bool IORenderman::makeScene(MeshModel* m, QStringList* textureList, const RichParameterSet &par, QFileInfo* templateFile, QString destDirString, QStringList* shaderDirs, QStringList* textureDirs, QStringList* proceduralDirs, QStringList* imagesRendered) { //rib file structure RibFileStack files(templateFile->absolutePath()); //constructor //open file and stream if(!files.pushFile(templateFile->absoluteFilePath())) { this->errorMessage = "Template path is wrong: " + templateFile->absoluteFilePath(); return false; } //output file FILE* fout; fout = fopen(qPrintable(destDirString + QDir::separator() + mainFileName()),"wb"); if(fout==NULL) { this->errorMessage = "Impossible to create file: " + destDirString + QDir::separator() + mainFileName(); return false; } qDebug("Starting to write rib file into %s",qPrintable(destDirString + QDir::separator() + mainFileName())); FILE* fmain = fout; //if change the output file, the main is saved here convertedGeometry = false; //if the mesh is already converted int currentFrame = 0; //frame counter bool stop = false; bool currentDisplayTypeIsFile = false; bool anyOtherDisplayType = false; numberOfDummies = 0; //the dummy object could be than one (e.g. for ambient occlusion passes) QString newFormat = "Format " + QString::number(par.getInt("FormatX")) + " " + QString::number(par.getInt("FormatY")) + " " + QString::number(par.getFloat("PixelAspectRatio")); numOfWorldBegin = 0; //the number of world begin statement found (for progress bar) numOfObject = 0; bool foundDummy = false; bool solidBegin = false; //true only if define a dummy object bool writeLine = true; //true if the line has to be write to final file resetGraphicsState(); //transformation matrix, bound, surface shader QQueue<Procedure> procedures = QQueue<Procedure>(); //every time it's found a procedural call it's stored here //reading cycle while(files.hasNext() && !stop) { if(!solidBegin) writeLine = true; int statementType = 0; //type of statement QString line = files.nextStatement(&statementType); //current line switch(statementType) { //declare other directory for the template case ribParser::OPTION: { QStringList token = ribParser::splitStatement(&line); if(token[2] == "searchpath") { int type = 0; QStringList dirList = readSearchPath(&token,&type); switch(type) { case IORenderman::ARCHIVE: files.addSubDirs(dirList); break; case IORenderman::SHADER: *shaderDirs = dirList; break; case IORenderman::TEXTURE: *textureDirs = dirList; break; case IORenderman::PROCEDURAL: files.addSubDirs(dirList); *proceduralDirs = dirList; break; case IORenderman::ERR: //ignore: maybe an error or another searchpath type (not in RISpec3.2) break; } } break; } //make a map (create the path if needed) case ribParser::MAKE: case ribParser::MAKECUBEFACEENVIRONMENT: { QStringList token = ribParser::splitStatement(&line); QString path = token[2]; //for MakeTexture, MakeShadow, MakeLatLongEnvironment if(statementType == ribParser::MAKECUBEFACEENVIRONMENT) path = token[7]; path = QFileInfo(path).path(); //qDebug("check dir! line: %s\npath: %s",qPrintable(line),qPrintable(path)); checkDir(&destDirString,&path); break; } //begin a new frame case ribParser::FRAMEBEGIN: { QStringList token = ribParser::splitStatement(&line); bool isNum; int i = token[1].toInt(&isNum); if(isNum) currentFrame = i; break; } //set output type (create the path if needed) case ribParser::DISPLAY: { //if output is not a file the format must be the same!! framebuffer is ignored and commented QStringList token = ribParser::splitStatement(&line); //create the path if needed QString path = token[2]; if(path.startsWith('+.')) path = path.mid(2,path.size()); path = QFileInfo(path).path(); //qDebug("check dir! line: %s\npath: %s",qPrintable(line),qPrintable(path)); checkDir(&destDirString,&path); //if there's more "Display" statement with one that's "file" is not considered a final image if(token[5] != "framebuffer") if (!anyOtherDisplayType && token[5] == "file") { currentDisplayTypeIsFile = true; QString img = token[2]; if(img.startsWith('+')) img = img.mid(1,img.size()); *imagesRendered << img; } else anyOtherDisplayType = true; //else //line = "#" + line; //if there's a framebuffer will be open pqsl automatically break; } case ribParser::SCREENWINDOW: { writeLine = false; break; } //a new world description (reset graphics state) case ribParser::WORLDBEGIN: { //make the conversion of texture mesh before the first WorldBegin statement if(numOfWorldBegin == 0 && !textureList->empty() && (m->cm.HasPerWedgeTexCoord() || m->cHasPerVertexTexCoord(m))) { QString textureName = QFileInfo(textureList->first()).completeBaseName(); fprintf(fout,"MakeTexture \"%s.tiff\" \"%s.tx\" \"periodic\" \"periodic\" \"box\" 1 1\n", qPrintable(textureName),qPrintable(textureName)); } numOfWorldBegin++; //if there's another type of display the format is not change if(!anyOtherDisplayType && currentDisplayTypeIsFile) { fprintf(fout,"%s\n", qPrintable(newFormat)); } currentDisplayTypeIsFile = false; anyOtherDisplayType = false; //is right?yes,because before the next WorldBegin will there be a new Display statement resetGraphicsState(); //reset the graphics state break; } //set transform in graphics state case ribParser::TRANSFORM: { transfMatrixStack.pop(); transfMatrixStack.push(getMatrix(&line)); break; } //set surface in graphics state case ribParser::SURFACE: { //the surface shader remain the same of template surfaceShaderStack.pop(); surfaceShaderStack.push(line); break; } //set bound in graphics state case ribParser::BOUND: { //take the transformation bound QStringList token = ribParser::splitStatement(&line); int index = 1; if(token[index] == "[") index++; for(int i=0; i<6; i++) { bool isNumber; float number = token[index+i].toFloat(&isNumber); if(isNumber) objectBound[i] = number; } break; } //looking for a dummy case ribParser::ATTRIBUTE: { QStringList token = ribParser::splitStatement(&line); //RISpec3.2 not specify what and how many attributes are there in renderman //we take care of name only if(token[2] == "identifier") { //Attribute "identifier" "string name" [ "object name" ] if(token[5] == "string name" || token[5] == "name") { int index = 7; if(token[index] == "[") index++; if(token[index] == "\"") index++; if(token[index].trimmed().toLower() == "dummy") {//found a dummy object? foundDummy = true; } } } break; } //the begin of a set of statement to define a solid case ribParser::SOLIDBEGIN: { if(foundDummy) { solidBegin = true; writeLine = false; } } //the end of solid definition case ribParser::SOLIDEND: { if(solidBegin) { //if and only if foundDummy is true QString filename = convertObject(currentFrame, destDirString, m, par, textureList); qDebug("dummy conversion, filename: %s",qPrintable(filename)); if(filename == "") { //error in parseObject fclose(fmain); return false; } fprintf(fout,"ReadArchive \"%s\"\n",qPrintable(filename)); solidBegin = false; foundDummy = false; } //reset bound (and surface?) resetBound(); break; } //there's a geometric statement...if there was a dummy too, replace geometry case ribParser::GEOMETRIC: { if(foundDummy) { QString filename = convertObject(currentFrame, destDirString, m, par, textureList); qDebug("dummy conversion, filename: %s",qPrintable(filename)); if(filename == "") { //error in parseObject fclose(fmain); return false; } fprintf(fout,"ReadArchive \"%s\"\n",qPrintable(filename)); writeLine = false; foundDummy = false; } //reset bound (and surface?) resetBound(); break; } //add a new graphics state "level" case ribParser::ATTRIBUTEBEGIN: { transfMatrixStack.push(transfMatrixStack.top()); surfaceShaderStack.push(surfaceShaderStack.top()); break; } //remove a "level" to graphics state stack case ribParser::ATTRIBUTEEND: { transfMatrixStack.pop(); surfaceShaderStack.pop(); break; } //a procedural statement: managed at the end of cycle case ribParser::PROCEDURAL: { //manage only dealayedreadarchive //0: Procedural //1: " //2: DelayedReadArchive //3: " //4: [ //5: " //6: filename //7: " //8: ] //9: [ //10-15: bound element //11: ] QStringList token = ribParser::splitStatement(&line); if(token[2] == "DelayedReadArchive") { qDebug("found a procedural: %s",qPrintable(token[6])); Procedure p = Procedure(); p.name = token[6]; p.matrix = transfMatrixStack.top(); p.surfaceShader = surfaceShaderStack.top(); //have i to read bound from actual graphics state or from procedural call? for(int i = 0; i < 6; i++) p.bound[i] = token[10 + i].toFloat(); //from procedural call (don't check if it's a number) //p.bound[i] = objectBound[i]; //from actual graphics state procedures.enqueue(p); } break; } //the end of scene is reached case ribParser::NOMORESTATEMENT: { qDebug("Stack empty"); stop = true; writeLine = false; } } //end of switch if(writeLine) { //copy the same line in file fprintf(fout,"%s\n",qPrintable(line)); } if((!files.hasNext() || stop) && !procedures.isEmpty()) { qDebug("There's a procedural to manage"); //continue the cycle over procedure files.. Procedure p = procedures.dequeue(); //add procedure to rib file stack if exist bool noProc = false; while(!files.searchFile(p.name) && !noProc) if(procedures.isEmpty()) noProc = true; else p = procedures.dequeue(); if(!noProc) { //it's true only if all procedures elements don't exist fclose(fout); fout = fopen(qPrintable(destDirString + QDir::separator() + p.name),"wb"); if(fout==NULL) { this->errorMessage = "Impossible to create file: " + destDirString + QDir::separator() + p.name; return false; } qDebug("Starting to write rib file into %s",qPrintable(destDirString + QDir::separator() + p.name)); //restore the graphics state to the procedure call state transfMatrixStack << p.matrix; surfaceShaderStack << p.surfaceShader; for(int i = 0; i < 6; i++) objectBound[i] = p.bound[i]; //continue cycle writeLine = true; stop = false; } } } //end of cycle fclose(fout); return true; }
// The Real Core Function doing the actual mesh processing. bool FilterCreate::applyFilter(QAction *filter, MeshDocument &md, RichParameterSet & par, vcg::CallBackPos * /*cb*/) { MeshModel* m=md.addNewMesh("",this->filterName(ID(filter))); switch(ID(filter)) { case CR_TETRAHEDRON : vcg::tri::Tetrahedron<CMeshO>(m->cm); break; case CR_ICOSAHEDRON: vcg::tri::Icosahedron<CMeshO>(m->cm); break; case CR_DODECAHEDRON: vcg::tri::Dodecahedron<CMeshO>(m->cm); m->updateDataMask(MeshModel::MM_POLYGONAL); break; case CR_OCTAHEDRON: vcg::tri::Octahedron<CMeshO>(m->cm); break; case CR_TORUS: { float hRadius=par.getFloat("hRadius"); float vRadius=par.getFloat("vRadius"); int hSubdiv=par.getInt("hSubdiv"); int vSubdiv=par.getInt("vSubdiv"); vcg::tri::Torus(m->cm,hRadius,vRadius,hSubdiv,vSubdiv); break; } case CR_RANDOM_SPHERE: { CMeshO tt; int pointNum = par.getInt("pointNum"); int oversamplingFactor =100; if(pointNum <= 100) oversamplingFactor = 1000; if(pointNum >= 10000) oversamplingFactor = 50; if(pointNum >= 100000) oversamplingFactor = 20; vcg::math::MarsenneTwisterRNG rng; vcg::tri::Allocator<CMeshO>::AddVertices(tt,pointNum*50); for(CMeshO::VertexIterator vi=tt.vert.begin();vi!=tt.vert.end();++vi) vcg::math::GeneratePointOnUnitSphereUniform(rng,vi->P()); vcg::tri::UpdateBounding<CMeshO>::Box(tt); const float SphereArea = 4*M_PI; float poissonRadius = 2.0*sqrt((SphereArea / float(pointNum*2))/M_PI); std::vector<vcg::Point3f> poissonSamples; vcg::tri::TrivialSampler<CMeshO> pdSampler(poissonSamples); vcg::tri::SurfaceSampling<CMeshO, vcg::tri::TrivialSampler<CMeshO> >::PoissonDiskParam pp; vcg::tri::SurfaceSampling<CMeshO,vcg::tri::TrivialSampler<CMeshO> >::PoissonDiskPruning(pdSampler, tt, poissonRadius, pp); m->cm.Clear(); vcg::tri::Allocator<CMeshO>::AddVertices(m->cm,poissonSamples.size()); for(size_t i=0;i<poissonSamples.size();++i) { m->cm.vert[i].P()=poissonSamples[i]; m->cm.vert[i].N()=m->cm.vert[i].P(); } } break; case CR_SPHERE: { int rec = par.getInt("subdiv"); float radius = par.getFloat("radius"); m->cm.face.EnableFFAdjacency(); m->updateDataMask(MeshModel::MM_FACEFACETOPO); assert(vcg::tri::HasPerVertexTexCoord(m->cm) == false); vcg::tri::Sphere<CMeshO>(m->cm,rec); for(CMeshO::VertexIterator vi = m->cm.vert.begin();vi!= m->cm.vert.end();++vi) vi->P()=vi->P()*radius; break; } case CR_BOX: { float sz=par.getFloat("size"); vcg::Box3f b(vcg::Point3f(1,1,1)*(sz/2),vcg::Point3f(1,1,1)*(-sz/2)); vcg::tri::Box<CMeshO>(m->cm,b); m->updateDataMask(MeshModel::MM_POLYGONAL); break; } case CR_CONE: float r0=par.getFloat("r0"); float r1=par.getFloat("r1"); float h=par.getFloat("h"); int subdiv=par.getInt("subdiv"); vcg::tri::Cone<CMeshO>(m->cm,r0,r1,h,subdiv); break; } vcg::tri::UpdateBounding<CMeshO>::Box(m->cm); vcg::tri::UpdateNormal<CMeshO>::PerVertexNormalizedPerFaceNormalized(m->cm); return true; }
bool RandomFillFilter::parametersAreNotCorrect(MeshDocument& md, RichParameterSet& par){ return md.size() < 2 || par.getMesh("container") == 0 || par.getMesh("container") == par.getMesh("filler") || par.getInt("fps") <= 0 || par.getInt("iterations") <= 0 || par.getInt("contacts") <= 0 || par.getFloat("bounciness") < 0.f || par.getFloat("bounciness") > 1.f || par.getFloat("factor") < 0.f || par.getFloat("factor") > 1.f; }
bool FilterFractal::applyFilter(QAction* filter, MeshDocument &md, RichParameterSet &par, vcg::CallBackPos* cb) { if(this->getClass(filter) == MeshFilterInterface::MeshCreation) md.addNewMesh("",this->filterName(ID(filter))); switch(ID(filter)) { case CR_FRACTAL_TERRAIN: case FP_FRACTAL_MESH: { MeshModel* mm = md.mm(); float maxHeight = .0; int smoothingSteps = 0; if(ID(filter) == CR_FRACTAL_TERRAIN) { int steps = par.getInt("steps"); steps = ((steps<2)? 2: steps); float gridSide = .0; FractalUtils<CMeshO>::GenerateGrid(mm->cm, steps, gridSide); maxHeight = par.getDynamicFloat("maxHeight") * gridSide; } else { maxHeight = par.getAbsPerc("maxHeight"); smoothingSteps = par.getInt("smoothingSteps"); } FractalUtils<CMeshO>::FractalArgs args (mm, par.getEnum("algorithm"),par.getFloat("seed"), par.getFloat("octaves"), par.getFloat("lacunarity"), par.getFloat("fractalIncrement"), par.getFloat("offset"), par.getFloat("gain"), maxHeight, par.getDynamicFloat("scale"), smoothingSteps, par.getBool("saveAsQuality")); if(args.saveAsQuality) mm->updateDataMask(MeshModel::MM_VERTQUALITY); return FractalUtils<CMeshO>::ComputeFractalPerturbation(mm->cm, args, cb); } break; case FP_CRATERS: { if (md.meshList.size() < 2) { errorMessage = "There must be at least two layers to apply the craters generation filter."; return false; } CMeshO* samples = &(par.getMesh("samples_mesh")->cm); if (samples->face.size() > 0) { errorMessage = "The sample layer selected should be a points cloud."; return false; } CMeshO* target = &(par.getMesh("target_mesh")->cm); if (samples == target) { errorMessage = "The sample layer and the target layer must be different."; return false; } float minRadius = par.getDynamicFloat("min_radius"); float maxRadius = par.getDynamicFloat("max_radius"); if (maxRadius <= minRadius) { errorMessage = "Min radius is greater than max radius."; return false; } float minDepth = par.getDynamicFloat("min_depth"); float maxDepth = par.getDynamicFloat("max_depth"); if (maxDepth <= minDepth) { errorMessage = "Min depth is greater than max depth."; return false; } // reads parameters CratersUtils<CMeshO>::CratersArgs args(par.getMesh("target_mesh"), par.getMesh("samples_mesh"), par.getEnum("rbf"), par.getInt("seed"), minRadius, maxRadius, minDepth, maxDepth, par.getInt("smoothingSteps"), par.getBool("save_as_quality"), par.getBool("invert"), par.getBool("ppNoise"), par.getBool("successiveImpacts"), par.getDynamicFloat("elevation"), par.getEnum("blend"), par.getDynamicFloat("blendThreshold")); return CratersUtils<CMeshO>::GenerateCraters(args, cb); } break; } return false; }
// The Real Core Function doing the actual mesh processing. bool FilterCreate::applyFilter(QAction *filter, MeshDocument &md, RichParameterSet & par, CallBackPos * /*cb*/) { MeshModel & currM = *md.mm(); MeshModel* m; switch(ID(filter)) { case CR_TETRAHEDRON : m = md.addNewMesh("", this->filterName(ID(filter))); tri::Tetrahedron<CMeshO>(m->cm); break; case CR_ICOSAHEDRON: m = md.addNewMesh("", this->filterName(ID(filter))); tri::Icosahedron<CMeshO>(m->cm); break; case CR_DODECAHEDRON: m = md.addNewMesh("", this->filterName(ID(filter))); tri::Dodecahedron<CMeshO>(m->cm); m->updateDataMask(MeshModel::MM_POLYGONAL); break; case CR_OCTAHEDRON: m = md.addNewMesh("", this->filterName(ID(filter))); tri::Octahedron<CMeshO>(m->cm); break; case CR_ANNULUS: m = md.addNewMesh("", this->filterName(ID(filter))); tri::Annulus<CMeshO>(m->cm,par.getFloat("internalRadius"), par.getFloat("externalRadius"), par.getInt("sides")); break; case CR_TORUS: { m = md.addNewMesh("", this->filterName(ID(filter))); float hRadius=par.getFloat("hRadius"); float vRadius=par.getFloat("vRadius"); int hSubdiv=par.getInt("hSubdiv"); int vSubdiv=par.getInt("vSubdiv"); tri::Torus(m->cm,hRadius,vRadius,hSubdiv,vSubdiv); } break; case CR_FITPLANE: { Box3m selBox; //boundingbox of the selected vertices std::vector< Point3m > selected_pts; //copy of selected vertices, for plane fitting if (&currM == NULL) { errorMessage = "No mesh layer selected"; return false; } if (currM.cm.svn == 0 && currM.cm.sfn == 0) // if no selection, fail { errorMessage = "No selection"; return false; } m = md.addNewMesh("", "Fitted Plane"); if (currM.cm.svn == 0 || currM.cm.sfn != 0) { tri::UpdateSelection<CMeshO>::VertexClear(currM.cm); tri::UpdateSelection<CMeshO>::VertexFromFaceLoose(currM.cm); } Point3m Naccum = Point3m(0.0, 0.0, 0.0); for (CMeshO::VertexIterator vi = currM.cm.vert.begin(); vi != currM.cm.vert.end(); ++vi) if (!(*vi).IsD() && (*vi).IsS()) { Point3m p = (*vi).P(); selBox.Add(p); selected_pts.push_back(p); Naccum = Naccum + (*vi).N(); } Log("Using %i vertexes to build a fitting plane", int(selected_pts.size())); Plane3m plane; FitPlaneToPointSet(selected_pts, plane); plane.Normalize(); // check if normal of the interpolated plane is coherent with average normal of the used points, otherwise, flip // i do this because plane fitter does not take in account source noramls, and a fliped fit is terrible to see Naccum = (Naccum / (CMeshO::ScalarType)selected_pts.size()).Normalize(); if ((plane.Direction() * Naccum) < 0.0) plane.Set(-plane.Direction(), -plane.Offset()); float errorSum = 0; for (size_t i = 0; i < selected_pts.size(); ++i) errorSum += fabs(SignedDistancePlanePoint(plane, selected_pts[i])); Log("Fitting Plane avg error is %f", errorSum / float(selected_pts.size())); Log("Fitting Plane normal is [%f, %f, %f]", plane.Direction().X(), plane.Direction().Y(), plane.Direction().Z()); Log("Fitting Plane offset is %f", plane.Offset()); // find center of selection on plane Point3m centerP; for (size_t i = 0; i < selected_pts.size(); ++i) { centerP += plane.Projection(selected_pts[i]); } centerP /= selected_pts.size(); Log("center [%f, %f, %f]", centerP.X(), centerP.Y(), centerP.Z()); // find horizontal and vertical axis Point3m dirH, dirV; int orientation = par.getEnum("orientation"); if (orientation == 0) { if ((plane.Direction().X() <= plane.Direction().Y()) && (plane.Direction().X() <= plane.Direction().Z())) dirH = Point3m(1.0, 0.0, 0.0) ^ plane.Direction(); else if ((plane.Direction().Y() <= plane.Direction().X()) && (plane.Direction().Y() <= plane.Direction().Z())) dirH = Point3m(0.0, 1.0, 0.0) ^ plane.Direction(); else dirH = Point3m(0.0, 0.0, 1.0) ^ plane.Direction(); dirH.Normalize(); dirV = dirH ^ plane.Direction(); dirV.Normalize(); } else { Matrix33m cov; vector<Point3m> PtVec; for (size_t i = 0; i < selected_pts.size(); ++i) PtVec.push_back(plane.Projection(selected_pts[i])); cov.Covariance(PtVec, centerP); Matrix33f eigenvecMatrix; Point3f eigenvecVector; Eigen::Matrix3d em; cov.ToEigenMatrix(em); Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> eig(em); Eigen::Vector3d c_val = eig.eigenvalues(); Eigen::Matrix3d c_vec = eig.eigenvectors(); eigenvecMatrix.FromEigenMatrix(c_vec); eigenvecVector.FromEigenVector(c_val); // max eigenvector is best horizontal axis, but is not guarantee is orthogonal to plane normal, so // I use eigenvector ^ plane direction and assign it to vertical plane axis if ((eigenvecVector[0]<=eigenvecVector[1]) && (eigenvecVector[0]<=eigenvecVector[2])) dirV = Point3m(eigenvecMatrix[0][0], eigenvecMatrix[0][1], eigenvecMatrix[0][2]) ^ plane.Direction(); if ((eigenvecVector[1]<=eigenvecVector[0]) && (eigenvecVector[1]<=eigenvecVector[2])) dirV = Point3m(eigenvecMatrix[1][0], eigenvecMatrix[1][1], eigenvecMatrix[1][2]) ^ plane.Direction(); else dirV = Point3m(eigenvecMatrix[2][0], eigenvecMatrix[2][1], eigenvecMatrix[2][2]) ^ plane.Direction(); dirV.Normalize(); dirH = plane.Direction() ^ dirV; dirH.Normalize(); } Log("H [%f, %f, %f]", dirH.X(), dirH.Y(), dirH.Z()); Log("V [%f, %f, %f]", dirV.X(), dirV.Y(), dirV.Z()); // find extent float dimH = -1000000; float dimV = -1000000; for (size_t i = 0; i < selected_pts.size(); ++i) { Point3m pp = plane.Projection(selected_pts[i]); float distH = fabs(((pp - centerP) * dirH)); float distV = fabs(((pp - centerP) * dirV)); if (distH > dimH) dimH = distH; if (distV > dimV) dimV = distV; } float exScale = par.getFloat("extent"); dimV = dimV * exScale; dimH = dimH * exScale; Log("extent on plane [%f, %f]", dimV, dimH); int vertNum = par.getInt("subdiv") + 1; if (vertNum <= 1) vertNum = 2; int numV, numH; numV = numH = vertNum; // UV vector, just in case float *UUs, *VVs; UUs = new float[numH*numV]; VVs = new float[numH*numV]; int vind = 0; for (int ir = 0; ir < numV; ir++) for (int ic = 0; ic < numH; ic++) { Point3m newP = (centerP + (dirV * -dimV) + (dirH * -dimH)); newP = newP + (dirH * ic * (2.0 * dimH / (numH-1))) + (dirV * ir * (2.0 * dimV / (numV-1))); tri::Allocator<CMeshO>::AddVertex(m->cm, newP, plane.Direction()); UUs[vind] = ic * (1.0 / (numH - 1)); VVs[vind] = ir * (1.0 / (numV - 1)); vind++; } FaceGrid(m->cm, numH, numV); bool hasUV = par.getBool("hasuv"); if (hasUV) { m->updateDataMask(MeshModel::MM_WEDGTEXCOORD); CMeshO::FaceIterator fi; for (fi = m->cm.face.begin(); fi != m->cm.face.end(); ++fi) { for (int i = 0; i<3; ++i) { int vind = (*fi).V(i)->Index(); (*fi).WT(i).U() = UUs[vind]; (*fi).WT(i).V() = VVs[vind]; } } } delete[] UUs; // delete temporary UV storage delete[] VVs; } break; case CR_RANDOM_SPHERE: { int pointNum = par.getInt("pointNum"); int sphereGenTech = par.getEnum("sphereGenTech"); math::MarsenneTwisterRNG rng; m = md.addNewMesh("", this->filterName(ID(filter))); m->cm.Clear(); std::vector<Point3m> sampleVec; switch(sphereGenTech) { case 0: // Montecarlo { for(int i=0;i<pointNum;++i) sampleVec.push_back(math::GeneratePointOnUnitSphereUniform<CMeshO::ScalarType>(rng)); } break; case 1: // Poisson Disk { int oversamplingFactor =100; if(pointNum <= 100) oversamplingFactor = 1000; if(pointNum >= 10000) oversamplingFactor = 50; if(pointNum >= 100000) oversamplingFactor = 20; CMeshO tt; tri::Allocator<CMeshO>::AddVertices(tt,pointNum*oversamplingFactor); for(CMeshO::VertexIterator vi=tt.vert.begin();vi!=tt.vert.end();++vi) vi->P()=math::GeneratePointOnUnitSphereUniform<CMeshO::ScalarType>(rng); tri::UpdateBounding<CMeshO>::Box(tt); const float SphereArea = 4*M_PI; float poissonRadius = 2.0*sqrt((SphereArea / float(pointNum*2))/M_PI); std::vector<Point3m> sampleVec; tri::TrivialSampler<CMeshO> pdSampler(sampleVec); tri::SurfaceSampling<CMeshO, tri::TrivialSampler<CMeshO> >::PoissonDiskParam pp; tri::SurfaceSampling<CMeshO,tri::TrivialSampler<CMeshO> >::PoissonDiskPruning(pdSampler, tt, poissonRadius, pp); } break; case 2: // Disco Ball GenNormal<CMeshO::ScalarType>::DiscoBall(pointNum,sampleVec); break; case 3: // Recursive Oct GenNormal<CMeshO::ScalarType>::RecursiveOctahedron(pointNum,sampleVec); break; case 4: // Fibonacci GenNormal<CMeshO::ScalarType>::Fibonacci(pointNum,sampleVec); break; } for(size_t i=0;i<sampleVec.size();++i) tri::Allocator<CMeshO>::AddVertex(m->cm,sampleVec[i],sampleVec[i]); } break; case CR_SPHERE_CAP: { int rec = par.getInt("subdiv"); const float angleDeg = par.getFloat("angle"); m = md.addNewMesh("", this->filterName(ID(filter))); m->updateDataMask(MeshModel::MM_FACEFACETOPO); tri::UpdateTopology<CMeshO>::FaceFace(m->cm); tri::SphericalCap(m->cm,math::ToRad(angleDeg),rec); } break; case CR_SPHERE: { int rec = par.getInt("subdiv"); float radius = par.getFloat("radius"); m = md.addNewMesh("", this->filterName(ID(filter))); m->cm.face.EnableFFAdjacency(); m->updateDataMask(MeshModel::MM_FACEFACETOPO); assert(tri::HasPerVertexTexCoord(m->cm) == false); tri::Sphere<CMeshO>(m->cm,rec); tri::UpdatePosition<CMeshO>::Scale(m->cm,radius); } break; case CR_BOX: { float sz=par.getFloat("size"); Box3m b(Point3m(1,1,1)*(-sz/2),Point3m(1,1,1)*(sz/2)); m = md.addNewMesh("", this->filterName(ID(filter))); tri::Box<CMeshO>(m->cm,b); m->updateDataMask(MeshModel::MM_POLYGONAL); } break; case CR_CONE: { float r0 = par.getFloat("r0"); float r1 = par.getFloat("r1"); float h = par.getFloat("h"); int subdiv = par.getInt("subdiv"); m = md.addNewMesh("", this->filterName(ID(filter))); tri::Cone<CMeshO>(m->cm, r0, r1, h, subdiv); } break; }//CASE FILTER tri::UpdateBounding<CMeshO>::Box(m->cm); tri::UpdateNormal<CMeshO>::PerVertexNormalizedPerFaceNormalized(m->cm); return true; }
bool SdfGpuPlugin::applyFilter(QAction */*filter*/, MeshDocument &md, RichParameterSet & pars, vcg::CallBackPos *cb) { MeshModel* mm = md.mm(); //RETRIEVE PARAMETERS mOnPrimitive = (ONPRIMITIVE) pars.getEnum("onPrimitive"); // assert( mOnPrimitive==ON_VERTICES && "Face mode not supported yet" ); unsigned int numViews = pars.getInt("numberRays"); int peel = pars.getInt("peelingIteration"); mTolerance = pars.getFloat("peelingTolerance"); mPeelingTextureSize = pars.getInt("DepthTextureSize"); mUseVBO = pars.getBool("useVBO"); if(mAction != SDF_DEPTH_COMPLEXITY) mMinCos = vcg::math::Cos(math::ToRad(pars.getFloat("coneAngle")/2.0)); std::vector<Point3f> coneDirVec; if(mAction == SDF_OBSCURANCE) mTau = pars.getFloat("obscuranceExponent"); else if(mAction==SDF_SDF) { mRemoveFalse = pars.getBool("removeFalse"); mRemoveOutliers = pars.getBool("removeOutliers"); } //MESH CLEAN UP setupMesh( md, mOnPrimitive ); //GL INIT if(!initGL(*mm)) return false; // if(mOnPrimitive==ON_VERTICES) vertexDataToTexture(*mm); else faceDataToTexture(*mm); //Uniform sampling of directions over a sphere std::vector<Point3f> unifDirVec; GenNormal<float>::Uniform(numViews,unifDirVec); Log(0, "Number of rays: %i ", unifDirVec.size() ); Log(0, "Number of rays for GPU outliers removal: %i ", coneDirVec.size() ); coneDirVec.clear(); vector<int> mDepthDistrib(peel,0); //Do the actual calculation of sdf or obscurance for each ray unsigned int tracedRays = 0; for(vector<vcg::Point3f>::iterator vi = unifDirVec.begin(); vi != unifDirVec.end(); vi++) { (*vi).Normalize(); TraceRay(peel, (*vi), md.mm()); cb(100*((float)tracedRays/(float)unifDirVec.size()), "Tracing rays..."); this->glContext->makeCurrent(); ++tracedRays; mDepthComplexity = std::max(mDepthComplexity, mTempDepthComplexity); mDepthDistrib[mTempDepthComplexity]++; mTempDepthComplexity = 0; } //read back the result texture and store result in the mesh if(mAction == SDF_OBSCURANCE) { if(mOnPrimitive == ON_VERTICES) applyObscurancePerVertex(*mm,unifDirVec.size()); else applyObscurancePerFace(*mm,unifDirVec.size()); } else if(mAction == SDF_SDF) { if(mOnPrimitive == ON_VERTICES) applySdfPerVertex(*mm); else applySdfPerFace(*mm); } Log(0, "Mesh depth complexity %i (The accuracy of the result depends on the value you provided for the max number of peeling iterations, \n if you get warnings try increasing" " the peeling iteration parameter)\n", mDepthComplexity ); //Depth complexity distribution log. Useful to know which is the probability to find a number of layers looking at the mesh or scene. Log(0, "Depth complexity NumberOfViews\n", mDepthComplexity ); for(int j = 0; j < peel; j++) { Log(0, " %i %i\n", j, mDepthDistrib[j] ); } //Clean & Exit releaseGL(*mm); mDepthComplexity = 0; return true; }
// The Real Core Function doing the actual mesh processing. bool FilterAutoalign::applyFilter(QAction *filter, MeshDocument &md, RichParameterSet & par, vcg::CallBackPos *cb) { switch(ID(filter)) { case FP_ALIGN_4PCS : { MeshModel *fixMesh= par.getMesh("fixMesh"); MeshModel *movMesh= par.getMesh("movMesh"); MeshModel *sampleMesh= 0; bool showSample = par.getBool("showSample"); if(showSample) sampleMesh = md.addOrGetMesh("sample","sample",false, RenderMode(vcg::GLW::DMPoints)); tri::UpdateNormal<CMeshO>::NormalizePerVertex(fixMesh->cm); tri::UpdateNormal<CMeshO>::NormalizePerVertex(movMesh->cm); tri::Clean<CMeshO>::RemoveUnreferencedVertex(fixMesh->cm); tri::Clean<CMeshO>::RemoveUnreferencedVertex(movMesh->cm); tri::Allocator<CMeshO>::CompactEveryVector(fixMesh->cm); tri::Allocator<CMeshO>::CompactEveryVector(movMesh->cm); fixMesh->updateDataMask(MeshModel::MM_VERTMARK); movMesh->updateDataMask(MeshModel::MM_VERTMARK); vcg::tri::FourPCS<CMeshO> fpcs; fpcs.par.Default(); fpcs.par.overlap = par.getFloat("overlap"); fpcs.par.sampleNumP = par.getInt("sampleNum"); fpcs.par.deltaPerc = par.getFloat("tolerance"); fpcs.par.seed = par.getInt("randSeed"); fpcs.Init(movMesh->cm,fixMesh->cm); Matrix44m Tr; bool res = fpcs.Align(Tr,cb); if(res) { Log("4PCS Completed, radius %f",fpcs.par.samplingRadius); Log("Tested %i candidate, best score was %i\n",fpcs.U.size(),fpcs.U[fpcs.iwinner].score); Log("Estimated overlap is now %f \n",fpcs.par.overlap); Log("Init %5.0f Coplanar Search %5.0f",fpcs.stat.init(),fpcs.stat.select()); Log("findCongruent %5.0f testAlignment %5.0f",fpcs.stat.findCongruent(),fpcs.stat.testAlignment()); movMesh->cm.Tr = Tr; if(showSample) { sampleMesh->cm.Clear(); for(size_t i=0;i<fpcs.subsetQ.size();++i) tri::Allocator<CMeshO>::AddVertex(sampleMesh->cm, fpcs.subsetQ[i]->P(), fpcs.subsetQ[i]->N()); tri::UpdateBounding<CMeshO>::Box(sampleMesh->cm); } } else Log("4PCS Failed"); } break; case FP_BEST_ROTATION : { MeshModel *fixMesh= par.getMesh("fixMesh"); MeshModel *movMesh= par.getMesh("movMesh"); int searchRange = par.getInt("searchRange"); int rotNum = par.getInt("RotationNumber"); int gridSize = par.getInt("gridSize"); int sampleSize = par.getInt("sampleSize"); MeshModel *sample=md.addOrGetMesh("sample", "sample",false); MeshModel *occ=md.addOrGetMesh("occ", "occ",false); tri::Guess<Scalarm> GG; std::vector<tri::Guess<Scalarm>::Result> ResultVec; GG.pp.MatrixNum = rotNum; GG.pp.GridSize =gridSize; GG.pp.SampleNum = sampleSize; GG.Init<CMeshO>(fixMesh->cm, movMesh->cm); for(size_t i=0;i<GG.RotMVec.size();++i) { Point3m baseTran = GG.ComputeBaseTranslation(GG.RotMVec[i]); Point3m bestTran; int res = GG.SearchBestTranslation(GG.u[0],GG.RotMVec[i],searchRange,baseTran,bestTran); ResultVec.push_back(tri::Guess<Scalarm>::Result(GG.BuildResult(GG.RotMVec[i],baseTran,bestTran), res, i, bestTran)); } sort(ResultVec.begin(),ResultVec.end()); movMesh->cm.Tr.Import(ResultVec.back().m); tri::Build(sample->cm,GG.movVertBase); sample->cm.Tr.Import(ResultVec.back().m); qDebug("Result %i",ResultVec.back().score); GG.GenerateOccupancyMesh(occ->cm,0,ResultVec.back().m); occ->UpdateBoxAndNormals(); Log("Automatic Rough Alignment Tested %i rotations, best err was %i",GG.RotMVec.size(), ResultVec.back().score); } break; default: assert (0); } return true; }
bool SdfPlugin::applyFilter(MeshDocument& md, RichParameterSet& pars, vcg::CallBackPos* cb){ enum ONPRIMITIVE{ON_VERTICES, ON_FACES} onPrimitive; MeshModel* mm = md.mm(); CMeshO& m = mm->cm; //--- Retrieve parameters float widenessRad = math::ToRad(pars.getFloat("coneWidth")); int raysPerCone = pars.getInt("numberRays"); onPrimitive = (ONPRIMITIVE) pars.getEnum("onPrimitive"); qDebug() << "which primitive?" << onPrimitive; float lo01pec = pars.getFloat("lowQuantile"); float hi01pec = pars.getFloat("hiQuantile"); assert( onPrimitive==ON_VERTICES && "Face mode not supported yet" ); //--- If on vertices, do some cleaning first if( onPrimitive == ON_VERTICES ){ int dup = tri::Clean<CMeshO>::RemoveDuplicateVertex(m); int unref = tri::Clean<CMeshO>::RemoveUnreferencedVertex(m); if (dup > 0 || unref > 0) Log("Removed %i duplicate and %i unreferenced vertices\n",dup,unref); } //--- Updating mesh metadata tri::UpdateBounding<CMeshO>::Box(m); tri::UpdateNormals<CMeshO>::PerFaceNormalized(m); tri::UpdateNormals<CMeshO>::PerVertexAngleWeighted(m); tri::UpdateNormals<CMeshO>::NormalizeVertex(m); tri::UpdateFlags<CMeshO>::FaceProjection(m); //--- Enable & Reset the necessary attributes switch(onPrimitive){ case ON_VERTICES: // qDebug() << "initializing vert quality"; mm->updateDataMask(MeshModel::MM_VERTQUALITY); tri::UpdateQuality<CMeshO>::VertexConstant(m,0); break; case ON_FACES: mm->updateDataMask(MeshModel::MM_FACEQUALITY); tri::UpdateQuality<CMeshO>::FaceConstant(m,0); break; } //--- Add the mesh to an indexing structure (fast ray intersection) Log("Initializing spatial accelleration..."); mm->updateDataMask(MeshModel::MM_FACEMARK); TriMeshGrid static_grid; //TODO: rename spatial index static_grid.Set(m.face.begin(), m.face.end()); Log("Initializing spatial accelleration... DONE!"); // since we are measuring the interior of the shape // A ray should never go beyond this value float maxDist=m.bbox.Diag(); // This is a small number to avoid self-intersection during ray // casting. It's a very common trick float epsilon = maxDist / 10000.0; //--- Ray casting vector<Ray3f> cone; vector<float> coneSdf; Ray3f ray; float t; for(unsigned int i=0; i<m.vert.size(); i++){ CVertexO& v = m.vert[i]; //--- Update progressbar cb( i/m.vert.size(), "Casting rays into volume..."); //--- Generate the set of cones ray.Set( v.P(), -v.N() ); ray.SetOrigin( ray.P(epsilon) ); generateRayCone( ray, widenessRad, raysPerCone, cone, coneSdf, (i==266) ); //--- Trace rays in cone float mind = +numeric_limits<float>::max(); float maxd = -numeric_limits<float>::max(); for(unsigned int j=0; j<cone.size(); j++){ bool hasInt = tri::DoRay<CMeshO,TriMeshGrid>(m,static_grid,cone[j],maxDist,t); coneSdf[j] = (hasInt==true) ? t : numeric_limits<float>::quiet_NaN(); mind = (hasInt && (t<mind)) ? t : mind; maxd = (hasInt && (t>maxd)) ? t : maxd; if( i==266 ){ qDebug() << " sampled: " << coneSdf[j] << " dir: " << toString(cone[j].Direction()) << " hasInt: " << hasInt; } } //--- Compute per-cone statistics Histogram<float> H; H.Clear(); H.SetRange( mind, maxd, 100); for(unsigned int j=0; j<cone.size(); j++) if(!math::IsNAN(coneSdf[j])) H.Add(coneSdf[j]); float loperc = H.Percentile(lo01pec); float hiperc = H.Percentile(hi01pec); if( i == 266){ qDebug() << "percentiles: " << loperc << " " << hiperc; } //--- Compute average of samples, throwing away outliers if( i == 266) qDebug() << "averaging samples"; float totVal = 0, totCnt = 0; for(unsigned int j=0; j<coneSdf.size(); j++) if( !math::IsNAN(coneSdf[j]) ){ // Histogram statistics valid only for dense sets (more 3 members..) if( coneSdf[j]<loperc || coneSdf[j]>hiperc && coneSdf.size()>3) continue; // Weight giving more importance to aligned samples float weight = cone[j].Direction().dot( ray.Direction() ); // Even more importance weight = powf( weight, 10 ); if( i==266 ){ qDebug() << "sampled: " << coneSdf[j] << "weight " << weight << " dir:" << toString(cone[j].Direction()); } totVal += weight*coneSdf[j]; totCnt += weight; } //--- Save in mesh v.Q() = totCnt>0 ? (totVal/totCnt) : 0; } //----------------------------------------------------------------------------// // // STEROIDS STARTS HERE // //----------------------------------------------------------------------------// //--- Create the medial cloud by offsetting the samples of the medial amount MeshModel* medCloud = md.addNewMesh("medial cloud.obj", NULL, false); for(unsigned int i=0; i<m.vert.size(); i++){ Ray3f r; r.Set(m.vert[i].P(), -m.vert[i].N()); tri::Allocator<CMeshO>::AddVertices(medCloud->cm,1); medCloud->cm.vert.back().P() = r.P(m.vert[i].Q() / 2 ); } //--- Data for distance queries vcg::tri::FaceTmark<CMeshO> mf; mf.SetMesh( &m ); vcg::face::PointDistanceBaseFunctor<float> PDistFunct; Log("querying real distances"); // Query the location of closest point on the mesh, then measure the difference float allowedDiff = .02; vector<float> realdistances(m.vn, 0); for(unsigned int i=0; i<m.vert.size(); i++){ Point3f currp = medCloud->cm.vert[i].P(); float minDist = maxDist; Point3f closest; GridClosest(static_grid, PDistFunct, mf, currp, maxDist, minDist, closest); float difference = m.vert[i].Q()/2.0 - minDist; m.vert[i].Q() = exp( -powf(difference/allowedDiff,2) ); } // Correct the viewmodel so that after this is done the original mesh // is shown in wireframe and the medial as a cloud. // mm->glw.cdm = vcg::GLW::DMWire; // show original mesh in wireframe medCloud->glw.cdm = vcg::GLW::DMPoints; // show cloud return true; }
bool AmbientOcclusionPlugin::applyFilter(QAction *filter, MeshDocument &md, RichParameterSet & par, vcg::CallBackPos *cb) { MeshModel &m=*(md.mm()); if(ID(filter)==FP_FACE_AMBIENT_OCCLUSION ) perFace=true; else perFace = false; useGPU = par.getBool("useGPU"); useVBO = par.getBool("useVBO"); depthTexSize = par.getInt("depthTexSize"); depthTexArea = depthTexSize*depthTexSize; numViews = par.getInt("reqViews"); errInit = false; float dirBias = par.getFloat("dirBias"); Point3f coneDir = par.getPoint3f("coneDir"); float coneAngle = par.getFloat("coneAngle"); if(perFace) m.updateDataMask(MeshModel::MM_FACEQUALITY | MeshModel::MM_FACECOLOR); else m.updateDataMask(MeshModel::MM_VERTQUALITY | MeshModel::MM_VERTCOLOR); std::vector<Point3f> unifDirVec; GenNormal<float>::Uniform(numViews,unifDirVec); std::vector<Point3f> coneDirVec; GenNormal<float>::UniformCone(numViews, coneDirVec, math::ToRad(coneAngle), coneDir); std::random_shuffle(unifDirVec.begin(),unifDirVec.end()); std::random_shuffle(coneDirVec.begin(),coneDirVec.end()); int unifNum = floor(unifDirVec.size() * (1.0 - dirBias )); int coneNum = floor(coneDirVec.size() * (dirBias )); viewDirVec.clear(); viewDirVec.insert(viewDirVec.end(),unifDirVec.begin(),unifDirVec.begin()+unifNum); viewDirVec.insert(viewDirVec.end(),coneDirVec.begin(),coneDirVec.begin()+coneNum); numViews = viewDirVec.size(); this->glContext->makeCurrent(); this->initGL(cb,m.cm.vn); unsigned int widgetSize = std::min(maxTexSize, depthTexSize); QSize fbosize(widgetSize,widgetSize); QGLFramebufferObjectFormat frmt; frmt.setInternalTextureFormat(GL_RGBA); frmt.setAttachment(QGLFramebufferObject::Depth); QGLFramebufferObject fbo(fbosize,frmt); qDebug("Start Painting window size %i %i", fbo.width(), fbo.height()); GLenum err = glGetError(); fbo.bind(); processGL(m,viewDirVec); fbo.release(); err = glGetError(); const GLubyte* errname = gluErrorString(err); qDebug("End Painting"); this->glContext->doneCurrent(); return !errInit; }
bool BaseMeshIOPlugin::open(const QString &formatName, const QString &fileName, MeshModel &m, int& mask, const RichParameterSet &parlst, CallBackPos *cb, QWidget * /*parent*/) { bool normalsUpdated = false; // initializing mask mask = 0; // initializing progress bar status if (cb != NULL) (*cb)(0, "Loading..."); QString errorMsgFormat = "Error encountered while loading file:\n\"%1\"\n\nError details: %2"; //string filename = fileName.toUtf8().data(); string filename = QFile::encodeName(fileName).constData (); if (formatName.toUpper() == tr("PLY")) { tri::io::ImporterPLY<CMeshO>::LoadMask(filename.c_str(), mask); // small patch to allow the loading of per wedge color into faces. if(mask & tri::io::Mask::IOM_WEDGCOLOR) mask |= tri::io::Mask::IOM_FACECOLOR; m.Enable(mask); int result = tri::io::ImporterPLY<CMeshO>::Open(m.cm, filename.c_str(), mask, cb); if (result != 0) // all the importers return 0 on success { if(tri::io::ImporterPLY<CMeshO>::ErrorCritical(result) ) { errorMessage = errorMsgFormat.arg(fileName, tri::io::ImporterPLY<CMeshO>::ErrorMsg(result)); return false; } } } else if (formatName.toUpper() == tr("STL")) { int result = tri::io::ImporterSTL<CMeshO>::Open(m.cm, filename.c_str(), cb); if (result != 0) // all the importers return 0 on success { errorMessage = errorMsgFormat.arg(fileName, tri::io::ImporterSTL<CMeshO>::ErrorMsg(result)); return false; } } else if( (formatName.toUpper() == tr("OBJ")) || (formatName.toUpper() == tr("QOBJ")) ) { tri::io::ImporterOBJ<CMeshO>::Info oi; oi.cb = cb; if (!tri::io::ImporterOBJ<CMeshO>::LoadMask(filename.c_str(), oi)) return false; m.Enable(oi.mask); int result = tri::io::ImporterOBJ<CMeshO>::Open(m.cm, filename.c_str(), oi); if (result != tri::io::ImporterOBJ<CMeshO>::E_NOERROR) { if (result & tri::io::ImporterOBJ<CMeshO>::E_NON_CRITICAL_ERROR) errorMessage = errorMsgFormat.arg(fileName, tri::io::ImporterOBJ<CMeshO>::ErrorMsg(result)); else { errorMessage = errorMsgFormat.arg(fileName, tri::io::ImporterOBJ<CMeshO>::ErrorMsg(result)); return false; } } if(oi.mask & tri::io::Mask::IOM_WEDGNORMAL) normalsUpdated = true; m.Enable(oi.mask); if(m.hasDataMask(MeshModel::MM_POLYGONAL)) qDebug("Mesh is Polygonal!"); mask = oi.mask; } else if (formatName.toUpper() == tr("PTX")) { tri::io::ImporterPTX<CMeshO>::Info importparams; importparams.meshnum = parlst.getInt("meshindex"); importparams.anglecull =parlst.getBool("anglecull"); importparams.angle = parlst.getFloat("angle"); importparams.savecolor = parlst.getBool("usecolor"); importparams.pointcull = parlst.getBool("pointcull"); importparams.pointsonly = parlst.getBool("pointsonly"); importparams.switchside = parlst.getBool("switchside"); importparams.flipfaces = parlst.getBool("flipfaces"); // if color, add to mesh if(importparams.savecolor) importparams.mask |= tri::io::Mask::IOM_VERTCOLOR; if(importparams.pointsonly) importparams.mask |= tri::io::Mask::IOM_VERTRADIUS; // reflectance is stored in quality importparams.mask |= tri::io::Mask::IOM_VERTQUALITY; m.Enable(importparams.mask); int result = tri::io::ImporterPTX<CMeshO>::Open(m.cm, filename.c_str(), importparams, cb); if (result == 1) { errorMessage = errorMsgFormat.arg(fileName, tri::io::ImporterPTX<CMeshO>::ErrorMsg(result)); return false; } // update mask mask = importparams.mask; } else if (formatName.toUpper() == tr("OFF")) { int loadMask; if (!tri::io::ImporterOFF<CMeshO>::LoadMask(filename.c_str(),loadMask)) return false; m.Enable(loadMask); int result = tri::io::ImporterOFF<CMeshO>::Open(m.cm, filename.c_str(), mask, cb); if (result != 0) // OFFCodes enum is protected { errorMessage = errorMsgFormat.arg(fileName, tri::io::ImporterOFF<CMeshO>::ErrorMsg(result)); return false; } } else if (formatName.toUpper() == tr("VMI")) { int loadMask; if (!tri::io::ImporterVMI<CMeshO>::LoadMask(filename.c_str(),loadMask)) return false; m.Enable(loadMask); int result = tri::io::ImporterVMI<CMeshO>::Open(m.cm, filename.c_str(), mask, cb); if (result != 0) { errorMessage = errorMsgFormat.arg(fileName, tri::io::ImporterOFF<CMeshO>::ErrorMsg(result)); return false; } } else { assert(0); // Unknown File type return false; } // verify if texture files are present QString missingTextureFilesMsg = "The following texture files were not found:\n"; bool someTextureNotFound = false; for ( unsigned textureIdx = 0; textureIdx < m.cm.textures.size(); ++textureIdx) { if (!QFile::exists(m.cm.textures[textureIdx].c_str())) { missingTextureFilesMsg.append("\n"); missingTextureFilesMsg.append(m.cm.textures[textureIdx].c_str()); someTextureNotFound = true; } } if (someTextureNotFound) Log("Missing texture files: %s", qPrintable(missingTextureFilesMsg)); if (cb != NULL) (*cb)(99, "Done"); return true; }
// The Real Core Function doing the actual mesh processing. // Move Vertex of a random quantity bool PoissonPlugin::applyFilter(QAction *filter, MeshDocument &md, RichParameterSet & par, vcg::CallBackPos *cb) { MeshModel &m=*md.mm(); MeshModel &pm =*md.addNewMesh("","Poisson mesh"); vector<Point3D<float> > Pts(m.cm.vn); vector<Point3D<float> > Nor(m.cm.vn); CoredVectorMeshData mesh; if (m.hasDataMask(MeshModel::MM_WEDGTEXCOORD)){ m.clearDataMask(MeshModel::MM_WEDGTEXCOORD); } if (m.hasDataMask(MeshModel::MM_VERTTEXCOORD)){ m.clearDataMask(MeshModel::MM_VERTTEXCOORD); } //Useless control on the normals. It can just avoid crashes derived from an importer setting up to [0.0f,0.0f,0.0f] the normal vectors of a mesh without per-vertex normal attribute. int zeronrm = 0; for(CMeshO::VertexIterator vi=m.cm.vert.begin(); vi!=m.cm.vert.end(); ++vi) { if(!(*vi).IsD()) { if ((*vi).N() == vcg::Point3f(0.0f,0.0f,0.0f)) ++zeronrm; } } if (zeronrm == m.cm.vn) { Log(GLLogStream::SYSTEM,"All the normal vectors are set to [0.0,0.0,0.0]. Poisson reconstruction filter requires a set of valid per-vertex normal. Filter will be aborted."); return false; } int cnt=0; for(CMeshO::VertexIterator vi=m.cm.vert.begin(); vi!=m.cm.vert.end(); ++vi) if(!(*vi).IsD()){ (*vi).N().Normalize(); for(int ii=0;ii<3;++ii){ Pts[cnt].coords[ii]=(*vi).P()[ii]; Nor[cnt].coords[ii]=(*vi).N()[ii]; } ++cnt; } assert(cnt==m.cm.vn); // Log function dump textual info in the lower part of the MeshLab screen. PoissonParam pp; pp.Depth=par.getInt("OctDepth"); pp.SamplesPerNode = par.getFloat("SamplesPerNode"); pp.SolverDivide=par.getInt("SolverDivide"); pp.Offset = par.getFloat("Offset"); Point3D<float> center; float scale; int ret= Execute2(pp, Pts, Nor, mesh,center,scale,cb); mesh.resetIterator(); int vm = mesh.outOfCorePointCount()+mesh.inCorePoints.size(); int fm = mesh.triangleCount(); Log("Successfully created a mesh of %i vert and %i faces",vm,fm); //m.cm.Clear(); tri::Allocator<CMeshO>::AddVertices(pm.cm,vm); tri::Allocator<CMeshO>::AddFaces(pm.cm,fm); Point3D<float> p; int i; for (i=0; i < int(mesh.inCorePoints.size()); i++){ p=mesh.inCorePoints[i]; pm.cm.vert[i].P()[0] = p.coords[0]*scale+center.coords[0]; pm.cm.vert[i].P()[1] = p.coords[1]*scale+center.coords[1]; pm.cm.vert[i].P()[2] = p.coords[2]*scale+center.coords[2]; } for (int ii=0; ii < mesh.outOfCorePointCount(); ii++){ mesh.nextOutOfCorePoint(p); pm.cm.vert[ii+i].P()[0] = p.coords[0]*scale+center.coords[0]; pm.cm.vert[ii+i].P()[1] = p.coords[1]*scale+center.coords[1]; pm.cm.vert[ii+i].P()[2] = p.coords[2]*scale+center.coords[2]; } TriangleIndex tIndex; int inCoreFlag; int nr_faces=mesh.triangleCount(); for (i=0; i < nr_faces; i++){ // // create and fill a struct that the ply code can handle // mesh.nextTriangle(tIndex,inCoreFlag); if(!(inCoreFlag & CoredMeshData::IN_CORE_FLAG[0])){tIndex.idx[0]+=int(mesh.inCorePoints.size());} if(!(inCoreFlag & CoredMeshData::IN_CORE_FLAG[1])){tIndex.idx[1]+=int(mesh.inCorePoints.size());} if(!(inCoreFlag & CoredMeshData::IN_CORE_FLAG[2])){tIndex.idx[2]+=int(mesh.inCorePoints.size());} for(int j=0; j < 3; j++) { pm.cm.face[i].V(j) = &pm.cm.vert[tIndex.idx[j]]; } //ply_put_element(ply, (void *) &ply_face); //delete[] ply_face.vertices; } // for, write faces // for(int i=0;i<mesh.inCorePoints.size();++i){ // mesh.triangles[i].idx[0]+=mesh.inCorePoints.size(); // mesh.triangles[i].idx[1]+=mesh.inCorePoints.size(); // mesh.triangles[i].idx[2]+=mesh.inCorePoints.size(); // } // Build(m.cm,mesh.inCorePoints,mesh.triangles); Log("Successfully created a mesh of %i faces",pm.cm.vn); pm.UpdateBoxAndNormals(); return true; }
// The Real Core Function doing the actual mesh processing. bool FilterFunctionPlugin::applyFilter(QAction *filter, MeshDocument &md, RichParameterSet & par, vcg::CallBackPos *cb) { if(this->getClass(filter) == MeshFilterInterface::MeshCreation) md.addNewMesh("",this->filterName(ID(filter))); MeshModel &m=*(md.mm()); Q_UNUSED(cb); switch(ID(filter)) { case FF_VERT_SELECTION : { std::string expr = par.getString("condSelect").toStdString(); // muparser initialization and explicitely define parser variables Parser p; setPerVertexVariables(p,m.cm); // set expression inserted by user as string (required by muparser) p.SetExpr(expr); int numvert = 0; time_t start = clock(); // every parser variables is related to vertex coord and attributes. CMeshO::VertexIterator vi; for(vi = m.cm.vert.begin(); vi != m.cm.vert.end(); ++vi)if(!(*vi).IsD()) { setAttributes(vi,m.cm); bool selected = false; // use parser to evaluate boolean function specified above // in case of fail, error dialog contains details of parser's error try { selected = p.Eval(); } catch(Parser::exception_type &e) { errorMessage = e.GetMsg().c_str(); return false; } // set vertex as selected or clear selection if(selected) { (*vi).SetS(); numvert++; } else (*vi).ClearS(); } // strict face selection if(par.getBool("strictSelect")) tri::UpdateSelection<CMeshO>::FaceFromVertexStrict(m.cm); else tri::UpdateSelection<CMeshO>::FaceFromVertexLoose(m.cm); // if succeded log stream contains number of vertices and time elapsed Log( "selected %d vertices in %.2f sec.", numvert, (clock() - start) / (float) CLOCKS_PER_SEC); return true; } break; case FF_FACE_SELECTION : { QString select = par.getString("condSelect"); // muparser initialization and explicitely define parser variables Parser p; setPerFaceVariables(p,m.cm); // set expression inserted by user as string (required by muparser) p.SetExpr(select.toStdString()); int numface = 0; time_t start = clock(); // every parser variables is related to face attributes. CMeshO::FaceIterator fi; for(fi = m.cm.face.begin(); fi != m.cm.face.end(); ++fi)if(!(*fi).IsD()) { setAttributes(fi,m.cm); bool selected = false; // use parser to evaluate boolean function specified above // in case of fail, error dialog contains details of parser's error try { selected = p.Eval(); } catch(Parser::exception_type &e) { errorMessage = e.GetMsg().c_str(); return false; } // set face as selected or clear selection if(selected) { (*fi).SetS(); numface++; } else (*fi).ClearS(); } // if succeded log stream contains number of vertices and time elapsed Log( "selected %d faces in %.2f sec.", numface, (clock() - start) / (float) CLOCKS_PER_SEC); return true; } break; case FF_GEOM_FUNC : case FF_VERT_COLOR: case FF_VERT_NORMAL: { std::string func_x,func_y,func_z,func_a; // FF_VERT_COLOR : x = r, y = g, z = b // FF_VERT_NORMAL : x = r, y = g, z = b func_x = par.getString("x").toStdString(); func_y = par.getString("y").toStdString(); func_z = par.getString("z").toStdString(); if(ID(filter) == FF_VERT_COLOR) func_a = par.getString("a").toStdString(); // muparser initialization and explicitely define parser variables // function for x,y and z must use different parser and variables Parser p1,p2,p3,p4; setPerVertexVariables(p1,m.cm); setPerVertexVariables(p2,m.cm); setPerVertexVariables(p3,m.cm); setPerVertexVariables(p4,m.cm); p1.SetExpr(func_x); p2.SetExpr(func_y); p3.SetExpr(func_z); p4.SetExpr(func_a); double newx=0,newy=0,newz=0,newa=255; errorMessage = ""; time_t start = clock(); // every parser variables is related to vertex coord and attributes. CMeshO::VertexIterator vi; for(vi = m.cm.vert.begin(); vi != m.cm.vert.end(); ++vi)if(!(*vi).IsD()) { setAttributes(vi,m.cm); // every function is evaluated by different parser. // errorMessage dialog contains errors for func x, func y and func z try { newx = p1.Eval(); } catch(Parser::exception_type &e) { showParserError("1st func : ",e); } try { newy = p2.Eval(); } catch(Parser::exception_type &e) { showParserError("2nd func : ",e); } try { newz = p3.Eval(); } catch(Parser::exception_type &e) { showParserError("3rd func : ",e); } if(ID(filter) == FF_VERT_COLOR) { try { newa = p4.Eval(); } catch(Parser::exception_type &e) { showParserError("4th func : ",e); } } if(errorMessage != "") return false; if(ID(filter) == FF_GEOM_FUNC) // set new vertex coord for this iteration (*vi).P() = Point3f(newx,newy,newz); if(ID(filter) == FF_VERT_COLOR) // set new color for this iteration (*vi).C() = Color4b(newx,newy,newz,newa); if(ID(filter) == FF_VERT_NORMAL) // set new color for this iteration (*vi).N() = Point3f(newx,newy,newz); } if(ID(filter) == FF_GEOM_FUNC) { // update bounding box, normalize normals tri::UpdateNormals<CMeshO>::PerVertexNormalizedPerFace(m.cm); tri::UpdateNormals<CMeshO>::NormalizeFace(m.cm); tri::UpdateBounding<CMeshO>::Box(m.cm); } // if succeded log stream contains number of vertices processed and time elapsed Log( "%d vertices processed in %.2f sec.", m.cm.vn, (clock() - start) / (float) CLOCKS_PER_SEC); return true; } break; case FF_VERT_QUALITY: { std::string func_q = par.getString("q").toStdString(); m.updateDataMask(MeshModel::MM_VERTQUALITY); // muparser initialization and define custom variables Parser p; setPerVertexVariables(p,m.cm); // set expression to calc with parser p.SetExpr(func_q); // every parser variables is related to vertex coord and attributes. time_t start = clock(); CMeshO::VertexIterator vi; for(vi = m.cm.vert.begin(); vi != m.cm.vert.end(); ++vi) if(!(*vi).IsD()) { setAttributes(vi,m.cm); // use parser to evaluate function specified above // in case of fail, errorMessage dialog contains details of parser's error try { (*vi).Q() = p.Eval(); } catch(Parser::exception_type &e) { errorMessage = e.GetMsg().c_str(); return false; } } // normalize quality with values in [0..1] if(par.getBool("normalize")) tri::UpdateQuality<CMeshO>::VertexNormalize(m.cm); // map quality into per-vertex color if(par.getBool("map")) tri::UpdateColor<CMeshO>::VertexQualityRamp(m.cm); // if succeded log stream contains number of vertices and time elapsed Log( "%d vertices processed in %.2f sec.", m.cm.vn, (clock() - start) / (float) CLOCKS_PER_SEC); return true; } break; case FF_FACE_COLOR: { std::string func_r = par.getString("r").toStdString(); std::string func_g = par.getString("g").toStdString(); std::string func_b = par.getString("b").toStdString(); std::string func_a = par.getString("a").toStdString(); // muparser initialization and explicitely define parser variables // every function must uses own parser and variables Parser p1,p2,p3,p4; setPerFaceVariables(p1,m.cm); setPerFaceVariables(p2,m.cm); setPerFaceVariables(p3,m.cm); setPerFaceVariables(p4,m.cm); p1.SetExpr(func_r); p2.SetExpr(func_g); p3.SetExpr(func_b); p4.SetExpr(func_a); // RGB is related to every face CMeshO::FaceIterator fi; double newr=0,newg=0,newb=0,newa=255; errorMessage = ""; time_t start = clock(); // every parser variables is related to face attributes. for(fi = m.cm.face.begin(); fi != m.cm.face.end(); ++fi)if(!(*fi).IsD()) { setAttributes(fi,m.cm); // evaluate functions to generate new color // in case of fail, error dialog contains details of parser's error try { newr = p1.Eval(); } catch(Parser::exception_type &e) { showParserError("func r: ",e); } try { newg = p2.Eval(); } catch(Parser::exception_type &e) { showParserError("func g: ",e); } try { newb = p3.Eval(); } catch(Parser::exception_type &e) { showParserError("func b: ",e); } try { newa = p4.Eval(); } catch(Parser::exception_type &e) { showParserError("func a: ",e); } if(errorMessage != "") return false; // set new color for this iteration (*fi).C() = Color4b(newr,newg,newb,newa); } // if succeded log stream contains number of vertices processed and time elapsed Log( "%d faces processed in %.2f sec.", m.cm.fn, (clock() - start) / (float) CLOCKS_PER_SEC); return true; } break; case FF_FACE_QUALITY: { std::string func_q = par.getString("q").toStdString(); m.updateDataMask(MeshModel::MM_FACEQUALITY); // muparser initialization and define custom variables Parser pf; setPerFaceVariables(pf,m.cm); // set expression to calc with parser pf.SetExpr(func_q); time_t start = clock(); errorMessage = ""; // every parser variables is related to face attributes. CMeshO::FaceIterator fi; for(fi = m.cm.face.begin(); fi != m.cm.face.end(); ++fi)if(!(*fi).IsD()) { setAttributes(fi,m.cm); // evaluate functions to generate new quality // in case of fail, error dialog contains details of parser's error try { (*fi).Q() = pf.Eval(); } catch(Parser::exception_type &e) { showParserError("func q: ",e); } if(errorMessage != "") return false; } // normalize quality with values in [0..1] if(par.getBool("normalize")) tri::UpdateQuality<CMeshO>::FaceNormalize(m.cm); // map quality into per-vertex color if(par.getBool("map")) tri::UpdateColor<CMeshO>::FaceQualityRamp(m.cm); // if succeded log stream contains number of faces processed and time elapsed Log( "%d faces processed in %.2f sec.", m.cm.fn, (clock() - start) / (float) CLOCKS_PER_SEC); return true; } break; case FF_DEF_VERT_ATTRIB : { std::string name = par.getString("name").toStdString(); std::string expr = par.getString("expr").toStdString(); // add per-vertex attribute with type float and name specified by user CMeshO::PerVertexAttributeHandle<float> h; if(tri::HasPerVertexAttribute(m.cm,name)) { h = tri::Allocator<CMeshO>::GetPerVertexAttribute<float>(m.cm, name); if(!tri::Allocator<CMeshO>::IsValidHandle<float>(m.cm,h)) { errorMessage = "attribute already exists with a different type"; return false; } } else h = tri::Allocator<CMeshO>::AddPerVertexAttribute<float> (m.cm,name); std::vector<std::string> AllVertexAttribName; tri::Allocator<CMeshO>::GetAllPerVertexAttribute< float >(m.cm,AllVertexAttribName); qDebug("Now mesh has %i vertex float attribute",AllVertexAttribName.size()); Parser p; setPerVertexVariables(p,m.cm); p.SetExpr(expr); time_t start = clock(); // perform calculation of attribute's value with function specified by user CMeshO::VertexIterator vi; for(vi = m.cm.vert.begin(); vi != m.cm.vert.end(); ++vi)if(!(*vi).IsD()) { setAttributes(vi,m.cm); // add new user-defined attribute try { h[vi] = p.Eval(); } catch(Parser::exception_type &e) { errorMessage = e.GetMsg().c_str(); return false; } } // add string, double and handler to vector. // vectors keep tracks of new attributes and let muparser use explicit variables // it's possibile to use custom attributes in other filters v_attrNames.push_back(name); v_attrValue.push_back(0); v_handlers.push_back(h); // if succeded log stream contains number of vertices processed and time elapsed Log( "%d vertices processed in %.2f sec.", m.cm.vn, (clock() - start) / (float) CLOCKS_PER_SEC); return true; } break; case FF_DEF_FACE_ATTRIB : { std::string name = par.getString("name").toStdString(); std::string expr = par.getString("expr").toStdString(); // add per-face attribute with type float and name specified by user // add per-vertex attribute with type float and name specified by user CMeshO::PerFaceAttributeHandle<float> h; if(tri::HasPerFaceAttribute(m.cm,name)) { h = tri::Allocator<CMeshO>::GetPerFaceAttribute<float>(m.cm, name); if(!tri::Allocator<CMeshO>::IsValidHandle<float>(m.cm,h)) { errorMessage = "attribute already exists with a different type"; return false; } } else h = tri::Allocator<CMeshO>::AddPerFaceAttribute<float> (m.cm,name); Parser p; setPerFaceVariables(p,m.cm); p.SetExpr(expr); time_t start = clock(); // every parser variables is related to face attributes. CMeshO::FaceIterator fi; for(fi = m.cm.face.begin(); fi != m.cm.face.end(); ++fi)if(!(*fi).IsD()) { setAttributes(fi,m.cm); // add new user-defined attribute try { h[fi] = p.Eval(); } catch(Parser::exception_type &e) { errorMessage = e.GetMsg().c_str(); return false; } } // // add string, double and handler to vector. // // vectors keep tracks of new attributes and let muparser use explicit variables // // it's possibile to use custom attributes in other filters // f_attrNames.push_back(name); // f_attrValue.push_back(0); // fhandlers.push_back(h); // if succeded log stream contains number of vertices processed and time elapsed Log( "%d faces processed in %.2f sec.", m.cm.fn, (clock() - start) / (float) CLOCKS_PER_SEC); return true; } break; case FF_GRID : { // obtain parameters to generate 2D Grid int w = par.getInt("numVertX"); int h = par.getInt("numVertY"); float wl = par.getFloat("absScaleX"); float hl = par.getFloat("absScaleY"); if(w <= 0 || h <= 0) { errorMessage = "number of vertices must be positive"; return false; } // use Grid function to generate Grid std::vector<float> data(w*h,0); tri::Grid<CMeshO>(m.cm, w, h, wl, hl, &data[0]); // if "centered on origin" is checked than move generated Grid in (0,0,0) if(par.getBool("center")) { // move x and y double halfw = double(w-1)/2; double halfh = double(h-1)/2; double wld = wl/double(w); double hld = hl/float(h); CMeshO::VertexIterator vi; for(vi = m.cm.vert.begin(); vi != m.cm.vert.end(); ++vi) { (*vi).P()[0] = (*vi).P()[0] - (wld * halfw); (*vi).P()[1] = (*vi).P()[1] - (hld * halfh); } } // update bounding box, normals Matrix44f rot; rot.SetRotateDeg(180,Point3f(0,1,0)); tri::UpdatePosition<CMeshO>::Matrix(m.cm,rot,false); tri::UpdateNormals<CMeshO>::PerVertexNormalizedPerFace(m.cm); tri::UpdateNormals<CMeshO>::NormalizeFace(m.cm); tri::UpdateBounding<CMeshO>::Box(m.cm); return true; } break; case FF_ISOSURFACE : { SimpleVolume<SimpleVoxel> volume; typedef vcg::tri::TrivialWalker<CMeshO, SimpleVolume<SimpleVoxel> > MyWalker; typedef vcg::tri::MarchingCubes<CMeshO, MyWalker> MyMarchingCubes; MyWalker walker; Box3d rbb; rbb.min[0]=par.getFloat("minX"); rbb.min[1]=par.getFloat("minY"); rbb.min[2]=par.getFloat("minZ"); rbb.max[0]=par.getFloat("maxX"); rbb.max[1]=par.getFloat("maxY"); rbb.max[2]=par.getFloat("maxZ"); double step=par.getFloat("voxelSize"); Point3i siz= Point3i::Construct((rbb.max-rbb.min)*(1.0/step)); Parser p; double x,y,z; p.DefineVar("x", &x); p.DefineVar("y", &y); p.DefineVar("z", &z); std::string expr = par.getString("expr").toStdString(); p.SetExpr(expr); Log("Filling a Volume of %i %i %i",siz[0],siz[1],siz[2]); volume.Init(siz); for(double i=0;i<siz[0];i++) for(double j=0;j<siz[1];j++) for(double k=0;k<siz[2];k++) { x = rbb.min[0]+step*i; y = rbb.min[1]+step*j; z = rbb.min[2]+step*k; try { volume.Val(i,j,k)=p.Eval(); } catch(Parser::exception_type &e) { errorMessage = e.GetMsg().c_str(); return false; } } // MARCHING CUBES Log("[MARCHING CUBES] Building mesh..."); MyMarchingCubes mc(m.cm, walker); walker.BuildMesh<MyMarchingCubes>(m.cm, volume, mc, 0); Matrix44f tr; tr.SetIdentity(); tr.SetTranslate(rbb.min[0],rbb.min[1],rbb.min[2]); Matrix44f sc; sc.SetIdentity(); sc.SetScale(step,step,step); tr=tr*sc; tri::UpdatePosition<CMeshO>::Matrix(m.cm,tr); tri::UpdateNormals<CMeshO>::PerVertexNormalizedPerFace(m.cm); tri::UpdateBounding<CMeshO>::Box(m.cm); // updates bounding box return true; } break; case FF_REFINE : { std::string condSelect = par.getString("condSelect").toStdString(); std::string expr1 = par.getString("x").toStdString(); std::string expr2 = par.getString("y").toStdString(); std::string expr3 = par.getString("z").toStdString(); bool errorMidPoint = false; bool errorEdgePred = false; std::string msg = ""; // check parsing errors while creating two func obj // display error message MidPointCustom<CMeshO> mid = MidPointCustom<CMeshO>(m.cm,expr1,expr2,expr3,errorMidPoint,msg); CustomEdge<CMeshO> edge = CustomEdge<CMeshO>(condSelect,errorEdgePred,msg); if(errorMidPoint || errorEdgePred) { errorMessage = msg.c_str(); return false; } // Refine current mesh. // Only edge specified with CustomEdge pred are selected // and the new vertex is choosen with MidPointCustom created above RefineE<CMeshO, MidPointCustom<CMeshO>, CustomEdge<CMeshO> > (m.cm, mid, edge, false, cb); m.clearDataMask( MeshModel::MM_VERTMARK); vcg::tri::UpdateNormals<CMeshO>::PerVertexNormalizedPerFace(m.cm); return true; } break; default : assert (0); } return false; }
bool RandomFillFilter::applyFilter(QAction*, MeshDocument &md, RichParameterSet& par, vcg::CallBackPos* cb){ if(parametersAreNotCorrect(md, par)) return false; MeshSubFilter::initialize(md, par, cb); if(cb != 0) (*cb)(0, "Physics renderization of the scene started..."); MeshModel* container = par.getMesh("container"); MeshModel* filler = par.getMesh("filler"); int fillOffset = md.size(); float gravity[3] = {0.0f, par.getBool("useRandomVertices") ? 0.0f : -9.8f, 0.0f}; if(par.getBool("flipNormal")){ vcg::tri::Clean<CMeshO>::FlipMesh(container->cm); tri::UpdateNormals<CMeshO>::PerVertexNormalizedPerFace(container->cm); container->clearDataMask(MeshModel::MM_FACEFACETOPO | MeshModel::MM_FACEFLAGBORDER); } m_engine.clear(); m_engine.setGlobalForce(gravity); m_engine.setIterations(par.getInt("iterations")); m_engine.setMaxContacts(par.getInt("contacts")); m_engine.setBounciness(par.getFloat("bounciness")); m_engine.setFriction(par.getFloat("friction")); m_engine.registerTriMesh(*container, true); srand((unsigned)time(0)); vcg::tri::UpdatePosition<CMeshO>::Matrix(filler->cm, filler->cm.Tr); filler->cm.Tr.SetIdentity(); tri::Inertia<CMeshO> inertiaContainer, inertiaFiller; inertiaContainer.Compute(par.getMesh("container")->cm); inertiaFiller.Compute(par.getMesh("filler")->cm); int objects = abs(inertiaContainer.Mass()/inertiaFiller.Mass())*par.getFloat("factor"); filler->cm.Tr.SetColumn(3, - inertiaFiller.CenterOfMass()); //Restore old generated meshes int restoredMeshes = 0; for(int i = 0; i < md.size(); i++){ if(md.getMesh(i)->fileName.find("randomFillMesh") == 0){ m_engine.registerTriMesh(*md.getMesh(i)); restoredMeshes++; m_engine.integrate(1.0f/par.getInt("fps")); } } int frequency = 1 / par.getFloat("updateFrequency"); // To refactor when the right algorithm has be found if(par.getBool("useRandomVertices")){ for(int i = 0; i < objects; i++){ if(cb != 0) (*cb)(50.f*i/objects, "Computing..."); addRandomObject(md, filler, getRandomOrigin(par), restoredMeshes + i); m_engine.registerTriMesh(*md.getMesh(fillOffset++)); } for(int j = 0; j < par.getFloat("seconds") * par.getInt("fps"); j++){ if(cb != 0) (*cb)(50 + 48.f*j/(par.getFloat("seconds") * par.getInt("fps")), "Computing..."); m_engine.integrate(1.0f/par.getInt("fps")); } }else{ for(int i = 0; i < objects; i++){ if(cb != 0) (*cb)(98.f*i/objects, "Computing..."); addRandomObject(md, filler, inertiaContainer.CenterOfMass(), i); m_engine.registerTriMesh(*md.getMesh(fillOffset++)); if(i % frequency == 0) for(int j = 0; j < par.getFloat("seconds") * par.getInt("fps"); j++) m_engine.integrate(1.0f/par.getInt("fps")); } } m_engine.updateTransform(); filler->cm.Tr.SetIdentity(); m_currentFilterType = m_filterType; if(par.getBool("flipNormal")){ vcg::tri::Clean<CMeshO>::FlipMesh(container->cm); tri::UpdateNormals<CMeshO>::PerVertexNormalizedPerFace(container->cm); container->clearDataMask(MeshModel::MM_FACEFACETOPO | MeshModel::MM_FACEFLAGBORDER); } if(cb != 0) (*cb)(99, "Physics renderization of the scene completed..."); return true; }
/* The Real Core Function doing the actual mesh processing */ bool GeometryAgingPlugin::applyFilter(QAction *filter, MeshDocument &md, RichParameterSet ¶ms, vcg::CallBackPos *cb) { MeshModel &m=*(md.mm()); if( ID(filter) != FP_ERODE) { assert (0); return false; } m.updateDataMask(MeshModel::MM_VERTQUALITY); bool curvature = params.getBool("ComputeCurvature"); if(curvature) computeMeanCurvature(m.cm); // other plugin parameters bool smoothQ = params.getBool("SmoothQuality"); float qualityTh = params.getAbsPerc("QualityThreshold"); float edgeLenTh = params.getAbsPerc("EdgeLenThreshold"); float chipDepth = params.getAbsPerc("ChipDepth"); int octaves = params.getInt("Octaves"); float noiseScale = params.getAbsPerc("NoiseFreqScale"); float noiseClamp = params.getFloat("NoiseClamp"); int dispSteps = (int)params.getFloat("DisplacementSteps"); bool selected = params.getBool("Selected"); bool storeDispl = params.getBool("StoreDisplacement"); // error checking on parameters values if(edgeLenTh == 0.0) edgeLenTh = m.cm.bbox.Diag()*0.02; if(chipDepth == 0.0) chipDepth = m.cm.bbox.Diag()*0.05; noiseClamp = math::Clamp<float>(noiseClamp, 0.0, 1.0); // quality threshold percentage value std::pair<float, float> qRange = tri::Stat<CMeshO>::ComputePerVertexQualityMinMax(m.cm); float qperc = (qualityTh-qRange.first) / (qRange.second-qRange.first); // compute mesh quality, if requested if(curvature) { if(cb) (*cb)(0, "Computing quality values..."); computeMeanCurvature(m.cm); } // eventually, smooth quality values if(smoothQ) tri::Smooth<CMeshO>::VertexQualityLaplacian(m.cm); // if quality values have been recomputed quality threshold may not // be valid, so we recompute its absolute value using the percentage // value chosen by the user if(curvature || smoothQ) { qRange = tri::Stat<CMeshO>::ComputePerVertexQualityMinMax(m.cm); qualityTh = qRange.first + (qRange.second-qRange.first) * qperc; } // edge predicate QualityEdgePred ep = QualityEdgePred(selected, edgeLenTh, qualityTh); // refine needed edges refineMesh(m.cm, ep, selected, cb); // if requested, add erosion attribute to vertexes and initialize it if(storeDispl) { CMeshO::PerVertexAttributeHandle<Point3f> vah = (tri::HasPerVertexAttribute(m.cm, "Erosion") ? tri::Allocator<CMeshO>::GetPerVertexAttribute<Point3f>(m.cm, "Erosion") : tri::Allocator<CMeshO>::AddPerVertexAttribute<Point3f>(m.cm, std::string("Erosion"))); for(CMeshO::VertexIterator vi=m.cm.vert.begin(); vi!=m.cm.vert.end(); vi++) vah[vi] = Point3f(0.0, 0.0, 0.0); } CMeshO::PerVertexAttributeHandle<Point3f> vah = vcg::tri::Allocator<CMeshO>::GetPerVertexAttribute<Point3f>(m.cm, "Erosion"); // vertexes along selection border will not be displaced if(selected) tri::UpdateSelection<CMeshO>::VertexFromFaceStrict(m.cm); // clear vertexes V bit (will be used to mark the vertexes as displaced) tri::UpdateFlags<CMeshO>::VertexClearV(m.cm); // displace vertexes for(int i=0; i<dispSteps; i++) { GridStaticPtr<CFaceO, CMeshO::ScalarType> gM; gM.Set(m.cm.face.begin(), m.cm.face.end()); if(cb) (*cb)( (i+1)*100/dispSteps, "Aging..."); // blend toghether face normals and recompute vertex normal from these normals // to get smoother offest directions tri::Smooth<CMeshO>::FaceNormalLaplacianFF(m.cm, 3); tri::UpdateNormals<CMeshO>::PerVertexFromCurrentFaceNormal(m.cm); tri::UpdateNormals<CMeshO>::NormalizeVertex(m.cm); for(CMeshO::FaceIterator fi=m.cm.face.begin(); fi!=m.cm.face.end(); fi++) { if((*fi).IsD()) continue; for(int j=0; j<3; j++) { if(ep.qVertTest(face::Pos<CMeshO::FaceType>(&*fi,j)) && !(*fi).V(j)->IsV() && (!selected || ((*fi).IsS() && (*fi).FFp(j)->IsS())) ) { double noise; // noise value Point3f dispDir = (*fi).V(j)->N(); // displacement direction Point3f p = (*fi).V(j)->P() / noiseScale; noise = generateNoiseValue(octaves, p); // only values bigger than noiseClamp will be considered noise = (noise<noiseClamp?0.0:(noise-noiseClamp)); // displacement offset Point3f offset = -(dispDir * chipDepth * noise) / dispSteps; (*fi).V(j)->P() += offset; if(faceIntersections(m.cm, face::Pos<CMeshO::FaceType>(&*fi,j), gM)) (*fi).V(j)->P() -= offset; else if(storeDispl) // store displacement vah[(*fi).V(j)] = vah[(*fi).V(j)] + offset; // mark as visited (displaced) (*fi).V(j)->SetV(); } } } // clear vertexes V bit again tri::UpdateFlags<CMeshO>::VertexClearV(m.cm); } // update normals vcg::tri::UpdateNormals<CMeshO>::PerVertexNormalizedPerFace(m.cm); smoothPeaks(m.cm, selected, storeDispl); // readjust selection if(selected) tri::UpdateSelection<CMeshO>::VertexFromFaceLoose(m.cm); return true; }
// The Real Core Function doing the actual mesh processing. // Run mesh optimization bool TriOptimizePlugin::applyFilter(QAction *filter, MeshDocument &md, RichParameterSet & par, vcg::CallBackPos *cb) { MeshModel &m=*(md.mm()); float limit = -std::numeric_limits<float>::epsilon(); if (ID(filter) == FP_CURVATURE_EDGE_FLIP) { int delvert = tri::Clean<CMeshO>::RemoveUnreferencedVertex(m.cm); if (delvert) Log( "Pre-Curvature Cleaning: Removed %d unreferenced vertices", delvert); tri::Allocator<CMeshO>::CompactVertexVector(m.cm); tri::Allocator<CMeshO>::CompactFaceVector(m.cm); m.updateDataMask(MeshModel::MM_FACEFACETOPO); vcg::tri::UpdateFlags<CMeshO>::FaceBorderFromFF(m.cm); if ( tri::Clean<CMeshO>::CountNonManifoldEdgeFF(m.cm) >0) { errorMessage = "Mesh has some not 2-manifold faces, edge flips requires manifoldness"; return false; // can't continue, mesh can't be processed } vcg::tri::PlanarEdgeFlipParameter pp; vcg::LocalOptimization<CMeshO> optimiz(m.cm,&pp); float pthr = par.getFloat("pthreshold"); time_t start = clock(); if (par.getBool("selection")) { // Mark not writable un-selected faces for (CMeshO::FaceIterator fi = m.cm.face.begin(); fi != m.cm.face.end(); ++fi) { if (!(*fi).IsS()) (*fi).ClearW(); else (*fi).SetW(); } // select vertices with at least one incident face selected tri::UpdateSelection<CMeshO>::VertexFromFaceLoose(m.cm); // Mark not writable un-selected vertices for (CMeshO::VertexIterator vi = m.cm.vert.begin(); vi != m.cm.vert.end(); ++vi){ if (!(*vi).IsS()) (*vi).ClearW(); else (*vi).SetW(); } } // VF adjacency needed for edge flips based on vertex curvature vcg::tri::UpdateTopology<CMeshO>::VertexFace(m.cm); vcg::tri::UpdateTopology<CMeshO>::TestVertexFace(m.cm); int metric = par.getEnum("curvtype"); pp.CoplanarAngleThresholdDeg = pthr; switch (metric) { case 0: optimiz.Init<MeanCEFlip>(); break; case 1: optimiz.Init<NSMCEFlip>(); break; case 2: optimiz.Init<AbsCEFlip>(); break; } // stop when flips become harmful optimiz.SetTargetMetric(limit); //optimiz.SetTargetOperations(10); optimiz.DoOptimization(); optimiz.h.clear(); Log( "%d curvature edge flips performed in %.2f sec.", optimiz.nPerfmormedOps, (clock() - start) / (float) CLOCKS_PER_SEC); } if (ID(filter) == FP_PLANAR_EDGE_FLIP) { if ( tri::Clean<CMeshO>::CountNonManifoldEdgeFF(m.cm) >0) { errorMessage = "Mesh has some not 2-manifold faces, edge flips requires manifoldness"; return false; // can't continue, mesh can't be processed } bool selection = par.getBool("selection"); tri::Allocator<CMeshO>::CompactVertexVector(m.cm); tri::Allocator<CMeshO>::CompactFaceVector(m.cm); vcg::tri::UpdateTopology<CMeshO>::FaceFace(m.cm); vcg::tri::UpdateFlags<CMeshO>::FaceBorderFromFF(m.cm); vcg::tri::PlanarEdgeFlipParameter pp; vcg::LocalOptimization<CMeshO> optimiz(m.cm,&pp); float pthr = par.getFloat("pthreshold"); pp.CoplanarAngleThresholdDeg=pthr; time_t start = clock(); int metric = par.getEnum("planartype"); switch (metric) { case 0: optimiz.Init<QEFlip>(); break; case 1: optimiz.Init<QRadiiEFlip>(); break; case 2: optimiz.Init<QMeanRatioEFlip>(); break; case 3: optimiz.Init<MyTriEFlip>(); break; case 4: optimiz.Init<MyTopoEFlip>(); break; } // stop when flips become harmful optimiz.SetTargetMetric(limit); optimiz.DoOptimization(); optimiz.h.clear(); Log( "%d planar edge flips performed in %.2f sec.", optimiz.nPerfmormedOps, (clock() - start) / (float) CLOCKS_PER_SEC); int iternum = par.getInt("iterations"); tri::Smooth<CMeshO>::VertexCoordPlanarLaplacian(m.cm, iternum, 0.0001f, selection,cb); vcg::tri::UpdateNormal<CMeshO>::PerVertexNormalizedPerFace(m.cm); if (par.getBool("selection")) { // Clear Writable flags (faces) CMeshO::FaceIterator fi; for (fi = m.cm.face.begin(); fi != m.cm.face.end(); ++fi) if (!(*fi).IsD()) (*fi).SetW(); // Clear Writable flags (vertices) CMeshO::VertexIterator vi; for (vi = m.cm.vert.begin(); vi != m.cm.vert.end(); ++vi) if (!(*vi).IsD()) (*vi).SetW(); // restore "default" selection for vertices vcg::tri::UpdateSelection<CMeshO>::VertexFromFaceStrict(m.cm); } } if (ID(filter) == FP_NEAR_LAPLACIAN_SMOOTH) { bool selection = par.getBool("selection"); if (selection) vcg::tri::UpdateSelection<CMeshO>::VertexFromFaceStrict(m.cm); int iternum = par.getInt("iterations"); float dthreshold = par.getFloat("AngleDeg"); tri::Smooth<CMeshO>::VertexCoordPlanarLaplacian(m.cm, iternum, math::ToRad(dthreshold), selection,cb); tri::UpdateNormal<CMeshO>::PerVertexNormalizedPerFace(m.cm); } return true; }
bool FilterIsoParametrization::applyFilter(QAction *filter, MeshDocument& md, RichParameterSet & par, vcg::CallBackPos *cb) { MeshModel* m = md.mm(); //get current mesh from document CMeshO *mesh=&m->cm; switch(ID(filter)) { case ISOP_PARAM : { int targetAbstractMinFaceNum = par.getInt("targetAbstractMinFaceNum"); int targetAbstractMaxFaceNum = par.getInt("targetAbstractMaxFaceNum"); int convergenceSpeed = par.getInt("convergenceSpeed"); int stopCriteria=par.getEnum("stopCriteria"); bool doublestep=par.getBool("DoubleStep"); IsoParametrizator Parametrizator; m->updateDataMask(MeshModel::MM_FACEFACETOPO); bool isTXTenabled=m->hasDataMask(MeshModel::MM_VERTTEXCOORD); if (!isTXTenabled) m->updateDataMask(MeshModel::MM_VERTTEXCOORD); bool isVMarkenabled=m->hasDataMask(MeshModel::MM_VERTMARK); if (!isVMarkenabled) m->updateDataMask(MeshModel::MM_VERTMARK); bool isFMarkenabled=m->hasDataMask(MeshModel::MM_FACEMARK); if (!isFMarkenabled) m->updateDataMask(MeshModel::MM_FACEMARK); bool isVColorenabled=m->hasDataMask(MeshModel::MM_VERTCOLOR); if (!isVColorenabled) m->updateDataMask(MeshModel::MM_VERTCOLOR); bool isFColorenabled=m->hasDataMask(MeshModel::MM_FACECOLOR); if (!isFColorenabled) m->updateDataMask(MeshModel::MM_FACECOLOR); int tolerance = targetAbstractMaxFaceNum-targetAbstractMinFaceNum; switch (stopCriteria) { case 0:Parametrizator.SetParameters(cb,targetAbstractMinFaceNum,tolerance,IsoParametrizator::SM_Euristic,convergenceSpeed);break; case 1:Parametrizator.SetParameters(cb,targetAbstractMinFaceNum,tolerance,IsoParametrizator::SM_Corr,convergenceSpeed);break; case 2:Parametrizator.SetParameters(cb,targetAbstractMinFaceNum,tolerance,IsoParametrizator::SM_Reg,convergenceSpeed);break; case 3:Parametrizator.SetParameters(cb,targetAbstractMinFaceNum,tolerance,IsoParametrizator::SM_L2,convergenceSpeed);break; default:Parametrizator.SetParameters(cb,targetAbstractMinFaceNum,tolerance,IsoParametrizator::SM_Euristic,convergenceSpeed);break; } IsoParametrizator::ReturnCode ret=Parametrizator.Parametrize<CMeshO>(mesh,doublestep); if (ret==IsoParametrizator::Done) { Parametrizator.PrintAttributes(); float aggregate,L2; int n_faces; Parametrizator.getValues(aggregate,L2,n_faces); Log("Num Faces of Abstract Domain: %d, One way stretch efficiency: %.4f, Area+Angle Distorsion %.4f ",n_faces,L2,aggregate*100.f); } else { if (!isTXTenabled) m->clearDataMask(MeshModel::MM_VERTTEXCOORD); if (!isFMarkenabled) m->clearDataMask(MeshModel::MM_FACEMARK); if (!isVMarkenabled) m->clearDataMask(MeshModel::MM_VERTMARK); if (!isVColorenabled) m->clearDataMask(MeshModel::MM_VERTCOLOR); if (!isFColorenabled) m->clearDataMask(MeshModel::MM_FACECOLOR); if (ret==IsoParametrizator::NonPrecondition) this->errorMessage="non possible parameterization because of violated preconditions"; else if (ret==IsoParametrizator::FailParam) this->errorMessage="non possible parameterization cause because missing the intepolation for some triangle of original the mesh (maybe due to topologycal noise)"; return false; } Parametrizator.ExportMeshes(para_mesh,abs_mesh); isoPHandle=vcg::tri::Allocator<CMeshO>::AddPerMeshAttribute<IsoParametrization>(*mesh,"isoparametrization"); bool isOK=isoPHandle().Init(&abs_mesh,¶_mesh); ///copy back to original mesh isoPHandle().CopyParametrization<CMeshO>(mesh); if (!isOK) { Log("Problems gathering parameterization \n"); return false; } if (!isVMarkenabled) m->clearDataMask(MeshModel::MM_VERTMARK); if (!isFMarkenabled) m->clearDataMask(MeshModel::MM_FACEMARK); return true; } case ISOP_REMESHING : { bool b=vcg::tri::Allocator<CMeshO>::IsValidHandle<IsoParametrization>(*mesh,isoPHandle); if (!b) { this->errorMessage="You must compute the Base domain before remeshing. Use the Isoparametrization command."; return false; } int SamplingRate=par.getInt("SamplingRate"); MeshModel* mm=md.addNewMesh("Re-meshed"); CMeshO *rem=&mm->cm; DiamSampl.Init(&isoPHandle()); DiamSampl.SamplePos(SamplingRate); DiamSampl.GetMesh<CMeshO>(*rem); int n_diamonds,inFace,inEdge,inStar,n_merged; DiamSampl.getResData(n_diamonds,inFace,inEdge,inStar,n_merged); Log("INTERPOLATION DOMAINS"); Log("In Face: %d \n",inFace); Log("In Diamond: %d \n",inEdge); Log("In Star: %d \n",inStar); Log("Merged %d vertices\n",n_merged); mm->updateDataMask(MeshModel::MM_FACEFACETOPO); mm->updateDataMask(MeshModel::MM_VERTFACETOPO); PrintStats(rem); vcg::tri::UpdateNormals<CMeshO>::PerFace(*rem); return true; } case ISOP_DIAMPARAM : { bool b=vcg::tri::Allocator<CMeshO>::IsValidHandle<IsoParametrization>(*mesh,isoPHandle); if (!b) { this->errorMessage="You must compute the Base domain before remeshing. Use the Isoparametrization command."; return false; } float border_size=par.getDynamicFloat("BorderSize"); MeshModel* mm=md.addNewMesh("Diam-Parameterized"); mm->updateDataMask(MeshModel::MM_WEDGTEXCOORD); mm->updateDataMask(MeshModel::MM_VERTCOLOR); CMeshO *rem=&mm->cm; DiamondParametrizator DiaPara; DiaPara.Init(&isoPHandle()); DiaPara.SetCoordinates<CMeshO>(*rem,border_size); vcg::tri::UpdateNormals<CMeshO>::PerFace(*rem); return true; } case ISOP_LOAD : { QString AbsName = par.getString("AbsName"); bool isTXTenabled=m->hasDataMask(MeshModel::MM_VERTTEXCOORD); if (!isTXTenabled) { this->errorMessage="Per Vertex Text Coordinates are not enabled"; return false; } if(!QFile(m->fullName()).exists()) { this->errorMessage="File not exists"; return false; } bool b=vcg::tri::Allocator<CMeshO>::IsValidHandle<IsoParametrization>(*mesh,isoPHandle); if (!b) isoPHandle=vcg::tri::Allocator<CMeshO>::AddPerMeshAttribute<IsoParametrization>(*mesh,"isoparametrization"); QByteArray ba = AbsName.toLatin1(); char *path=ba.data(); bool Done=isoPHandle().LoadBaseDomain<CMeshO>(path,mesh,¶_mesh,true); if (!Done) { this->errorMessage="Abstract domain doesnt fit well with the parametrized mesh"; return false; } return true; } case ISOP_SAVE : { bool b=vcg::tri::Allocator<CMeshO>::IsValidHandle<IsoParametrization>(*mesh,isoPHandle); if (!b) { this->errorMessage="You must compute the Base domain before remeshing. Use the Isoparametrization command."; return false; } /*QString Qpath=m->fullName();*/ QString AbsName = par.getString("AbsName"); QByteArray ba = AbsName.toLatin1(); char *path=ba.data(); isoPHandle().SaveBaseDomain(path); return true; } } return false; }
// The Real Core Function doing the actual mesh processing. // Move Vertex of a random quantity bool ExtraSampleGPUPlugin::applyFilter(QAction * a, MeshDocument & md , RichParameterSet & par, vcg::CallBackPos * /*cb*/) { switch(ID(a)) { case FP_GPU_EXAMPLE: { CMeshO & mesh = md.mm()->cm; if ((mesh.vn < 3) || (mesh.fn < 1)) return false; const unsigned char * p0 = (const unsigned char *)(&(mesh.vert[0].P())); const unsigned char * p1 = (const unsigned char *)(&(mesh.vert[1].P())); const void * pbase = p0; GLsizei pstride = GLsizei(p1 - p0); const unsigned char * n0 = (const unsigned char *)(&(mesh.vert[0].N())); const unsigned char * n1 = (const unsigned char *)(&(mesh.vert[1].N())); const void * nbase = n0; GLsizei nstride = GLsizei(n1 - n0); glContext->makeCurrent(); glewInit(); glPushAttrib(GL_ALL_ATTRIB_BITS); Context ctx; ctx.acquire(); const GLsizeiptr psize = GLsizeiptr(GLsizei(mesh.vn) * pstride); BufferHandle hPositionBuffer = createBuffer(ctx, psize, pbase); const GLsizeiptr nsize = GLsizeiptr(GLsizei(mesh.vn) * nstride); BufferHandle hNormalBuffer = createBuffer(ctx, nsize, nbase); const GLsizeiptr isize = GLsizeiptr(mesh.fn * 3 * sizeof(GLuint)); BufferHandle hIndexBuffer = createBuffer(ctx, isize); { BoundIndexBufferHandle indexBuffer = ctx.bindIndexBuffer(hIndexBuffer); const CMeshO::VertexType * vbase = &(mesh.vert[0]); GLuint * indices = (GLuint *)indexBuffer->map(GL_WRITE_ONLY); for (size_t i=0; i<mesh.face.size(); ++i) { const CMeshO::FaceType & f = mesh.face[i]; if (f.IsD()) continue; for (int v=0; v<3; ++v) { *indices++ = GLuint(vcg::tri::Index(mesh,f.cV(v))); } } indexBuffer->unmap(); ctx.unbindIndexBuffer(); } const GLsizei width = GLsizei(par.getInt("ImageWidth" )); const GLsizei height = GLsizei(par.getInt("ImageHeight")); RenderbufferHandle hDepth = createRenderbuffer(ctx, GL_DEPTH_COMPONENT24, width, height); Texture2DHandle hColor = createTexture2D(ctx, GL_RGBA8, width, height, GL_RGBA, GL_UNSIGNED_BYTE); FramebufferHandle hFramebuffer = createFramebuffer(ctx, renderbufferTarget(hDepth), texture2DTarget(hColor)); const std::string vertSrc = GLW_STRINGIFY ( varying vec3 vNormalVS; void main(void) { vNormalVS = gl_NormalMatrix * gl_Normal; gl_Position = ftransform(); } ); const std::string fragSrc = GLW_STRINGIFY ( uniform vec3 uLightDirectionVS; varying vec3 vNormalVS; void main(void) { vec3 normal = normalize(vNormalVS); float lambert = max(0.0, dot(normal, -uLightDirectionVS)); gl_FragColor = vec4(vec3(lambert), 1.0); }
bool DynamicMeshSubFilter::parametersAreNotCorrect(MeshDocument&, RichParameterSet& par){ return par.getInt("seconds") < 0 || par.getInt("fps") <= 0 || par.getInt("iterations") <= 0 || par.getInt("contacts") <= 0 || par.getFloat("bounciness") < 0 || par.getFloat("bounciness") > 1; }
// The Real Core Function doing the actual mesh processing. // Move Vertex of a random quantity bool FilterMutualInfoPlugin::applyFilter(QAction *action, MeshDocument &md, RichParameterSet & par, vcg::CallBackPos *cb) { QTime filterTime; filterTime.start(); float thresDiff=par.getFloat("Threshold for refinement convergence"); std::vector<vcg::Point3f> myVec; int leap=(int)((float)md.mm()->cm.vn/1000.0f); CMeshO::VertexIterator vi; for(int i=0;i<=md.mm()->cm.vn;i=i+leap) { myVec.push_back(md.mm()->cm.vert[i].P()); } std::vector<vcg::Shotf> oldShots; for (int r=0; r<md.rasterList.size();r++) { oldShots.push_back(md.rasterList[r]->shot); } Log(0,"Sampled has %i vertices",myVec.size()); std::vector<SubGraph> Graphs; /// Preliminary singular alignment using classic MI switch(ID(action)) { case FP_IMAGE_GLOBALIGN : /// Building of the graph of images if (md.rasterList.size()==0) { Log(0, "You need a Raster Model to apply this filter!"); return false; } this->glContext->makeCurrent(); this->initGL(); if (par.getBool("Pre-alignment")) { preAlignment(md, par, cb); } if (par.getInt("Max number of refinement steps")!=0) { Graphs=buildGraph(md); Log(0, "BuildGraph completed"); for (int i=0; i<par.getInt("Max number of refinement steps"); i++) { AlignGlobal(md, Graphs); float diff=calcShotsDifference(md,oldShots,myVec); Log(0, "AlignGlobal %d of %d completed, average improvement %f pixels",i+1,par.getInt("Max number of refinement steps"),diff); if (diff<thresDiff) break; oldShots.clear(); for (int r=0; r<md.rasterList.size();r++) { oldShots.push_back(md.rasterList[r]->shot); } } } this->glContext->doneCurrent(); Log(0, "Done!"); break; default : assert(0); } Log(0,"Filter completed in %i sec",(int)((float)filterTime.elapsed()/1000.0f)); return true; }