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; }
bool IORenderman::save(const QString &formatName, const QString &fileName, MeshModel &m, const int mask,const RichParameterSet & par, vcg::CallBackPos *cb, QWidget *parent) { QString errorMsgFormat = "Error encountered while exportering file %1:\n%2"; //***get the filter parameters and create destination directory*** //MeshModel* m = md.mm(); this->cb = cb; QTime tt; tt.start(); //time for debuging qDebug("Starting apply filter"); if(templates.isEmpty()) //there aren't any template { this->errorMessage = "No template scene has been found in \"render_template\" directory"; return false; } QString templateName = templates.at(par.getEnum("scene")); //name of selected template QFileInfo templateFile = QFileInfo(templatesDir.absolutePath() + QDir::separator() + templateName + QDir::separator() + templateName + ".rib"); //directory of current mesh //QString meshDirString = m->pathName(); ////creating of destination directory //bool delRibFiles = !par.getBool("SaveScene"); ////if SaveScene is true create in same mesh directory, otherwise in system temporary directry //QDir destDir = QDir::temp(); //system temporary directry //if(!delRibFiles) { // //destDir = QDir(par.getSaveFileName("SceneName")); // destDir = QDir(meshDirString); //} ////create scene directory if don't exists //if(!destDir.cd("scene")) { // if(!destDir.mkdir("scene") || !destDir.cd("scene")) { // this->errorMessage = "Creating scene directory at " + destDir.absolutePath(); // return false; // } //} //create a new directory with template name QDir destDir = QFileInfo(fileName).dir(); QString destDirString = templateName + "-" + QFileInfo(m.fullName()).completeBaseName(); QString newDir = destDirString; int k = 0; while(destDir.cd(newDir)) { destDir.cdUp(); newDir = destDirString + "-" + QString::number(++k); //dir templateName+k } if(!destDir.mkdir(newDir) || !destDir.cd(newDir)) { this->errorMessage = "Creating scene directory at " + destDir.absolutePath(); return false; } //destination diretory destDirString = destDir.absolutePath(); //***Texture: take the list of texture mesh QStringList textureListPath = QStringList(); for(size_t i=0; i<m.cm.textures.size(); i++) { QString path = QString(m.cm.textures[i].c_str()); textureListPath << path; } //***read the template files and create the new scenes files QStringList shaderDirs, textureDirs, proceduralDirs, imagesRendered; qDebug("Starting reading cycle %i",tt.elapsed()); if(!makeScene(&m, &textureListPath, par, &templateFile, destDirString, &shaderDirs, &textureDirs, &proceduralDirs, &imagesRendered)) return false; //message already set qDebug("Cycle ending at %i",tt.elapsed()); Log(GLLogStream::FILTER,"Successfully created scene"); //check if the final rib file will render any image /*if(imagesRendered.size() == 0) { this->errorMessage = "The template description hasn't a statement to render any image"; return false; }*/ //***copy the rest of template files (shaders, textures, procedural..) UtilitiesHQR::copyFiles(templateFile.dir(), destDir, textureDirs); UtilitiesHQR::copyFiles(templateFile.dir(), destDir, shaderDirs); UtilitiesHQR::copyFiles(templateFile.dir(), destDir, proceduralDirs); qDebug("Copied needed file at %i",tt.elapsed()); //***convert the texture mesh to tiff format if(!textureListPath.empty() && (m.cm.HasPerWedgeTexCoord() || m.cm.HasPerVertexTexCoord())) { QString textureName = QFileInfo(textureListPath.first()).completeBaseName(); //just texture name QFile srcFile(m.pathName() + QDir::separator() + textureListPath.first()); //destination directory it's the first readable/writable between textures directories QString newImageDir = "."; foreach(QString dir, textureDirs) { if(dir!="." && destDir.cd(dir)) { newImageDir = dir; destDir.cdUp(); break; } } qDebug("source texture directory: %s", qPrintable(srcFile.fileName())); QString newTex = destDirString + QDir::separator() + newImageDir + QDir::separator() + textureName; qDebug("destination texture directory: %s", qPrintable(newTex + ".tiff")); if(srcFile.exists()) { //convert image to tiff format (the one readable in aqsis) QImage image; image.load(srcFile.fileName()); image.save(newTex + ".tiff", "Tiff"); } else { this->errorMessage = "Not founded the texture file: " + srcFile.fileName(); return false; //the mesh has a texture not existing } }
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; }
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; }
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; }
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; }
// 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; }
// 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 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 ExtraFilter_SlicePlugin::applyFilter(QAction *filter, MeshDocument &m, RichParameterSet &parlst, vcg::CallBackPos *cb) { vcg::tri::UpdateBounding<CMeshO>::Box(m.mm()->cm); switch(ID(filter)) { case FP_WAFFLE_SLICE : { MeshModel* base = m.mm(); CMeshO &cmBase = base->cm; Box3f &bbox = m.mm()->cm.bbox; if (tri::Clean<CMeshO>::CountNonManifoldEdgeFF(cmBase)>0 || (tri::Clean<CMeshO>::CountNonManifoldVertexFF(cmBase,false) != 0)) { Log("Mesh is not two manifold, cannot apply filter"); return false; } if(parlst.getFloat("spacing") >= 1) { Log("the selected distance between the planes is greater than 1. The filter had no effect"); return false; } Point3f planeAxis(0,0,0); Axis axis = (Axis) parlst.getEnum("planeAxis"); planeAxis[axis] = 1.0f; float length = parlst.getFloat("length"); bool hideBase = parlst.getBool("hideBase"); bool hideEdge = parlst.getBool("hideEdge"); bool hidePlanes = parlst.getBool("hidePlanes"); bool hideExtrudes = parlst.getBool("hideExtrudes"); // set common SVG Properties const float maxdim=m.mm()->cm.bbox.Dim()[m.mm()->cm.bbox.MaxDim()]; Point3f sizeCm=m.mm()->cm.bbox.Dim()*(length/maxdim); // to check for dimensions with custom axis Axis axisOrthog, axisJoint; Point2f sizeCmOrth; switch(axis) { case X: { axisOrthog = (Axis) Succ<X>::value; axisJoint = (Axis) Succ<Succ<X>::value>::value; pr.sizeCm = Point2f(sizeCm[1],sizeCm[2]); sizeCmOrth.X()=sizeCm[0]; sizeCmOrth.Y()=sizeCm[2]; } break; case Y: { axisOrthog = (Axis) Succ<Y>::value; axisJoint = (Axis) Succ<Succ<Y>::value>::value; pr.sizeCm = Point2f(sizeCm[0],sizeCm[2]); sizeCmOrth.X()=sizeCm[0]; sizeCmOrth.Y()=sizeCm[1]; } break; case Z: { axisOrthog = (Axis) Succ<Z>::value; axisJoint = (Axis) Succ<Succ<Z>::value>::value; pr.sizeCm = Point2f(sizeCm[0],sizeCm[1]); sizeCmOrth.X()=sizeCm[1]; sizeCmOrth.Y()=sizeCm[2]; } break; } Log("sizecm %fx%f",pr.sizeCm[0],pr.sizeCm[1]); vector<CMeshO*> ev; const float planeDist = maxdim * parlst.getFloat("spacing"); const int planeNum = (planeDist == 0) ? 1 : ( ((bbox.Dim()*planeAxis)/planeDist)+1 ); //evito la divisione per 0 const float lengthDiag = bbox.Diag()/2.0; Segment2f lati[3]; //the rectangle is made up of three segments, the fourth side is ignored, so I never use it const float eps =planeDist*parlst.getFloat("eps"); float epsTmp; float start; int rectNum; if(parlst.getFloat("eps") < 1) { start = - (planeNum/2) * planeDist; //I have to go back from the center of half the length epsTmp = eps/2.0; generateRectSeg(0, epsTmp, bbox.Diag()*2, lati); rectNum = planeNum; } else { start = 0; epsTmp = bbox.Diag(); generateRectSeg(0, bbox.Diag()*2, bbox.Diag()*2, lati); rectNum = 1; Log("thickness is greater than 1: the extrusions will overlap"); } float planeOffset = parlst.getFloat("planeOffset"); pr.lineWidthPt=200; pr.scale=2/maxdim; pr.numCol=(int)(max((int)sqrt(planeNum*1.0f),2)+1); pr.numRow=(int)(planeNum*1.0f/pr.numCol)+1; pr.projDir = planeAxis; pr.projCenter = m.mm()->cm.bbox.Center(); pr.enumeration = true; MeshModel* cap, *cap2, *extr; QString layername; for(int pl = 0; pl < 2; ++pl) { // Log("################## PLANE %i", pl); Plane3f slicingPlane; Point3f planeCenter = bbox.Center() + planeAxis*planeOffset*lengthDiag; int numAdd = 0; // int i=4; //if I want to apply only the plan number i I decomment this variable and comment the cycle for(int i = 0; i < planeNum; ++i) //cropping the plans { // Log("######## LAYER %i", i); planeCenter[axis] = start + planeDist*i;; slicingPlane.Init(planeCenter,planeAxis); layername.sprintf("EdgeMesh_%d_%d",pl,numAdd); cap= m.addNewMesh("",qPrintable(layername)); vcg::IntersectionPlaneMesh<CMeshO, CMeshO, float>(base->cm, slicingPlane, cap->cm ); tri::Clean<CMeshO>::RemoveDuplicateVertex(cap->cm); if(cap->cm.edge.size()>= 3) { Incastri temp; // int j=1; //if I want to apply only the rectangle number j I decomment this variable and comment the cycle for(int j = 0; j <rectNum ; ++j) //clipping the rectangles { // Log("#### RECTANGLE %i", j); float newDist = start + planeDist*j; modifyOnY(lati, newDist+epsTmp, newDist-epsTmp); temp = subtraction(cap->cm, lati, axis, axisOrthog, axisJoint, planeCenter[axis]); if(temp == NOT_PLANE) {Log("ATTENTION! The IntersectionPlaneMesh did not return a plane silhouette in the current plane!"); m.delMesh(cap); break;} if(temp== NOT_SAGOME) {Log("ATTENTION! The IntersectionPlaneMesh did not return a simple closed silhouette for the plane %i, on axis %i",i, pl); m.delMesh(cap); break;} } if(temp > NOT_SAGOME) { ev.push_back(&(cap->cm)); //add the silhouette to those for export to SVG layername.sprintf("CappedSlice_%d_%d",pl,numAdd); //add the silhouettes converted in mesh cap2= m.addNewMesh("",qPrintable(layername)); tri::CapEdgeMesh(cap->cm, cap2->cm); layername.sprintf("Extruded_%d_%d",pl,numAdd++); //add the mesh extruded extr= m.addNewMesh("",qPrintable(layername)); cap2->updateDataMask(MeshModel::MM_FACEFACETOPO); extr->updateDataMask(MeshModel::MM_FACEFACETOPO); tri::UpdateTopology<CMeshO>::FaceFace(cap2->cm); tri::ExtrudeBoundary<CMeshO>(cap2->cm,extr->cm,eps,planeAxis); if(hideEdge) cap->visible =false; if(hidePlanes) cap2->visible =false; if(hideExtrudes) extr->visible =false; } }else m.delMesh(cap); //if the intersection does not exist I reject the result } QString fname;//= parlst.getSaveFileName("filename"); if(fname=="") fname.sprintf("Slice_%d.svg",pl); if (!fname.endsWith(".svg")) fname+=".svg"; tri::io::ExporterSVG<CMeshO>::Save(ev, fname.toStdString().c_str(), pr); ev.clear(); planeAxis[axis] = 0.0f; planeAxis[axisOrthog] = 1.0f; flipOnX(lati); pr.sizeCm = sizeCmOrth; pr.projDir = planeAxis; Axis aSwap = axis; axis = axisOrthog; axisOrthog = aSwap; } if(hideEdge) cap->visible =false; if(hidePlanes) cap2->visible =false; if(hideExtrudes) extr->visible =false; if(hideBase) base->visible =false; if(hideBase) base->visible =false; } break; } return true; }
//write to an opened file the attribute of object entity QString IORenderman::convertObject(int currentFrame, QString destDir, MeshModel* m,const RichParameterSet &par, QStringList* textureList)//, ObjValues* dummyValues) { QString name = "meshF" + QString::number(currentFrame) + "O" + QString::number(numberOfDummies) + ".rib"; numberOfDummies++; FILE *fout = fopen(qPrintable(destDir + QDir::separator() + name),"wb"); if(fout == NULL) { this->errorMessage = "Impossible to create the file: " + destDir + QDir::separator() + name; return ""; } fprintf(fout,"AttributeBegin\n"); //name fprintf(fout,"Attribute \"identifier\" \"string name\" [ \"meshlabMesh\" ]\n"); //modify the transformation matrix vcg::Matrix44f scaleMatrix = vcg::Matrix44f::Identity(); float dummyX = objectBound[1] - objectBound[0]; float dummyY = objectBound[3] - objectBound[2]; float dummyZ = objectBound[5] - objectBound[4]; //autoscale float scale = 1.0; if(par.getBool("Autoscale")) { float ratioX = dummyX / m->cm.trBB().DimX(); float ratioY = dummyY / m->cm.trBB().DimY(); float ratioZ = dummyZ / m->cm.trBB().DimZ(); scale = std::min<float>(ratioX, std::min<float>(ratioY, ratioZ)); //scale factor is min ratio scaleMatrix.SetScale(scale,scale,scale); } //center mesh vcg::Point3f c = m->cm.trBB().Center(); vcg::Matrix44f translateBBMatrix; translateBBMatrix.SetTranslate(-c[0],-c[1],-c[2]); //align float dx = 0.0, dy = 0.0, dz = 0.0; switch(par.getEnum("AlignX")) { case IORenderman::TOP: dx = (dummyX - m->cm.trBB().DimX() * scale) / 2; break; case IORenderman::BOTTOM: dx = -(dummyX - m->cm.trBB().DimX() * scale) / 2; break; case IORenderman::CENTER: break; //is already center } switch(par.getEnum("AlignY")) { case IORenderman::TOP: dy = (dummyY - m->cm.trBB().DimY() * scale) / 2; break; case IORenderman::BOTTOM: dy = -(dummyY - m->cm.trBB().DimY() * scale) / 2; break; case IORenderman::CENTER: break; //is already center } switch(par.getEnum("AlignZ")) { case IORenderman::TOP: dz = (dummyZ - m->cm.trBB().DimZ() * scale) / 2; break; case IORenderman::BOTTOM: dz = -(dummyZ - m->cm.trBB().DimZ() * scale) / 2; break; case IORenderman::CENTER: break; //is already center } vcg::Matrix44f alignMatrix; alignMatrix = alignMatrix.SetTranslate(dx,dy,dz); vcg::Matrix44f templateMatrix = transfMatrixStack.top(); //by default is identity vcg::Matrix44f result = templateMatrix * alignMatrix * scaleMatrix * translateBBMatrix; //write transformation matrix (after transpose it) writeMatrix(fout, &result); //write bounding box fprintf(fout,"Bound %g %g %g %g %g %g\n", m->cm.trBB().min.X(), m->cm.trBB().max.X(), m->cm.trBB().min.Y(), m->cm.trBB().max.Y(), m->cm.trBB().min.Z(), m->cm.trBB().max.Z()); //force the shading interpolation to smooth fprintf(fout,"ShadingInterpolation \"smooth\"\n"); //shader fprintf(fout,"%s\n",qPrintable(surfaceShaderStack.top())); //texture mapping (are TexCoord needed for texture mapping?) if(!textureList->empty() > 0 && (m->cm.HasPerWedgeTexCoord() || m->cHasPerVertexTexCoord(m))) { //multi-texture don't work!I need ad-hoc shader and to read the texture index for vertex.. //foreach(QString textureName, *textureList) { //read only the first texture QString textureName = QFileInfo(textureList->first()).completeBaseName(); fprintf(fout,"Surface \"paintedplastic\" \"Kd\" 1.0 \"Ks\" 0.0 \"texturename\" [\"%s.tx\"]\n", qPrintable(textureName)); } //geometry QString filename = "geometry.rib"; fprintf(fout,"ReadArchive \"%s\"\n", qPrintable(filename)); if(!convertedGeometry) { //make the conversion only once convertedGeometry = true; QString geometryDest = destDir + QDir::separator() + filename; int res = vcg::tri::io::ExporterRIB<CMeshO>::Save(m->cm, qPrintable(geometryDest), vcg::tri::io::Mask::IOM_ALL, false, cb); if(res != vcg::tri::io::ExporterRIB<CMeshO>::E_NOERROR) { fclose(fout); this->errorMessage = QString(vcg::tri::io::ExporterRIB<CMeshO>::ErrorMsg(res)); return ""; } else Log(GLLogStream::FILTER,"Successfully converted mesh"); } fprintf(fout,"AttributeEnd\n"); fclose(fout); return name; }
bool FilterMutualInfoPlugin::preAlignment(MeshDocument &md, RichParameterSet & par, vcg::CallBackPos *cb) { Solver solver; MutualInfo mutual; if (md.rasterList.size()==0) { Log(0, "You need a Raster Model to apply this filter!"); return false; } else { align.mesh=&md.mm()->cm; solver.optimize_focal=par.getBool("Estimate Focal"); solver.fine_alignment=par.getBool("Fine"); int rendmode= par.getEnum("RenderingMode"); switch(rendmode){ case 0: align.mode=AlignSet::COMBINE; break; case 1: align.mode=AlignSet::NORMALMAP; break; case 2: align.mode=AlignSet::COLOR; break; case 3: align.mode=AlignSet::SPECULAR; break; case 4: align.mode=AlignSet::SILHOUETTE; break; case 5: align.mode=AlignSet::SPECAMB; break; default: align.mode=AlignSet::COMBINE; break; } vcg::Point3f *vertices = new vcg::Point3f[align.mesh->vn]; vcg::Point3f *normals = new vcg::Point3f[align.mesh->vn]; vcg::Color4b *colors = new vcg::Color4b[align.mesh->vn]; unsigned int *indices = new unsigned int[align.mesh->fn*3]; for(int i = 0; i < align.mesh->vn; i++) { vertices[i] = align.mesh->vert[i].P(); normals[i] = align.mesh->vert[i].N(); colors[i] = align.mesh->vert[i].C(); } for(int i = 0; i < align.mesh->fn; i++) for(int k = 0; k < 3; k++) indices[k+i*3] = align.mesh->face[i].V(k) - &*align.mesh->vert.begin(); glBindBufferARB(GL_ARRAY_BUFFER_ARB, align.vbo); glBufferDataARB(GL_ARRAY_BUFFER_ARB, align.mesh->vn*sizeof(vcg::Point3f), vertices, GL_STATIC_DRAW_ARB); glBindBufferARB(GL_ARRAY_BUFFER_ARB, align.nbo); glBufferDataARB(GL_ARRAY_BUFFER_ARB, align.mesh->vn*sizeof(vcg::Point3f), normals, GL_STATIC_DRAW_ARB); glBindBufferARB(GL_ARRAY_BUFFER_ARB, align.cbo); glBufferDataARB(GL_ARRAY_BUFFER_ARB, align.mesh->vn*sizeof(vcg::Color4b), colors, GL_STATIC_DRAW_ARB); glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, align.ibo); glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, align.mesh->fn*3*sizeof(unsigned int), indices, GL_STATIC_DRAW_ARB); glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0); // it is safe to delete after copying data to VBO delete []vertices; delete []normals; delete []colors; delete []indices; for (int r=0; r<md.rasterList.size();r++) { if(md.rasterList[r]->visible) { align.image=&md.rasterList[r]->currentPlane->image; align.shot=md.rasterList[r]->shot; align.resize(800); align.shot.Intrinsics.ViewportPx[0]=int((double)align.shot.Intrinsics.ViewportPx[1]*align.image->width()/align.image->height()); align.shot.Intrinsics.CenterPx[0]=(int)(align.shot.Intrinsics.ViewportPx[0]/2); if (solver.fine_alignment) solver.optimize(&align, &mutual, align.shot); else { solver.iterative(&align, &mutual, align.shot); Log(0, "Vado di rough",r); } md.rasterList[r]->shot=align.shot; float ratio=(float)md.rasterList[r]->currentPlane->image.height()/(float)align.shot.Intrinsics.ViewportPx[1]; md.rasterList[r]->shot.Intrinsics.ViewportPx[0]=md.rasterList[r]->currentPlane->image.width(); md.rasterList[r]->shot.Intrinsics.ViewportPx[1]=md.rasterList[r]->currentPlane->image.height(); md.rasterList[r]->shot.Intrinsics.PixelSizeMm[1]/=ratio; md.rasterList[r]->shot.Intrinsics.PixelSizeMm[0]/=ratio; md.rasterList[r]->shot.Intrinsics.CenterPx[0]=(int)((float)md.rasterList[r]->shot.Intrinsics.ViewportPx[0]/2.0); md.rasterList[r]->shot.Intrinsics.CenterPx[1]=(int)((float)md.rasterList[r]->shot.Intrinsics.ViewportPx[1]/2.0); Log(0, "Image %d completed",r); } else Log(0, "Image %d skipped",r); } } 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); m->updateDataMask(MeshModel::MM_VERTQUALITY); // needed to store patch index 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; } tri::ParamEdgeCollapseParameter pecp; IsoParametrizator::ReturnCode ret=Parametrizator.Parametrize<CMeshO>(mesh,pecp,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); switch(ret) { case IsoParametrizator::MultiComponent: this->errorMessage="non possible parameterization because of multi componet mesh"; return false; case IsoParametrizator::NonSizeCons: this->errorMessage="non possible parameterization because of non size consistent mesh"; return false; case IsoParametrizator::NonManifoldE: this->errorMessage="non possible parameterization because of non manifold edges"; return false; case IsoParametrizator::NonManifoldV: this->errorMessage="non possible parameterization because of non manifold vertices"; return false; case IsoParametrizator::NonWatertigh: this->errorMessage="non possible parameterization because of non watertight mesh"; return false; case IsoParametrizator::FailParam: this->errorMessage="non possible parameterization cause one of the following reasons:\n Topologycal noise \n Too Low resolution mesh \n Too Bad triangulation \n"; return false; default: this->errorMessage="unknown error"; return false; } } // At this point we are sure that everithing went ok so we can allocate surely the abstract AbstractMesh *abs_mesh = new AbstractMesh(); ParamMesh *para_mesh = new ParamMesh(); Parametrizator.ExportMeshes(*para_mesh,*abs_mesh); CMeshO::PerMeshAttributeHandle<IsoParametrization> isoPHandle; isoPHandle=tri::Allocator<CMeshO>::AddPerMeshAttribute<IsoParametrization>(*mesh,"isoparametrization"); bool isOK=isoPHandle().Init(abs_mesh,para_mesh); ///copy back to original mesh isoPHandle().CopyParametrization<CMeshO>(mesh); if (!isOK) { this->errorMessage="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 : { CMeshO::PerMeshAttributeHandle<IsoParametrization> isoPHandle = tri::Allocator<CMeshO>::FindPerMeshAttribute<IsoParametrization>(*mesh,"isoparametrization"); bool b=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"); if (!SamplingRate>2) { this->errorMessage="Sampling rate must be >1"; return false; } MeshModel* mm=md.addNewMesh("","Re-meshed"); CMeshO *rem=&mm->cm; DiamSampler DiamSampl; DiamSampl.Init(&isoPHandle()); bool done=DiamSampl.SamplePos(SamplingRate); assert(done); 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); tri::UpdateNormal<CMeshO>::PerFace(*rem); return true; } case ISOP_DIAMPARAM : { CMeshO::PerMeshAttributeHandle<IsoParametrization> isoPHandle = tri::Allocator<CMeshO>::FindPerMeshAttribute<IsoParametrization>(*mesh,"isoparametrization"); bool b=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); tri::UpdateNormal<CMeshO>::PerFace(*rem); return true; } case ISOP_LOAD : { QString AbsName = par.getString("AbsName"); m->updateDataMask(MeshModel::MM_WEDGTEXCOORD); m->updateDataMask(MeshModel::MM_VERTTEXCOORD); m->updateDataMask(MeshModel::MM_FACECOLOR); m->updateDataMask(MeshModel::MM_VERTQUALITY); m->updateDataMask(MeshModel::MM_FACEMARK); if(!QFile(m->fullName()).exists()) { this->errorMessage="File not exists"; return false; } CMeshO::PerMeshAttributeHandle<IsoParametrization> isoPHandle = tri::Allocator<CMeshO>::FindPerMeshAttribute<IsoParametrization>(*mesh,"isoparametrization"); bool b=tri::Allocator<CMeshO>::IsValidHandle<IsoParametrization>(*mesh,isoPHandle); if (!b) isoPHandle=tri::Allocator<CMeshO>::AddPerMeshAttribute<IsoParametrization>(*mesh,"isoparametrization"); QByteArray ba = AbsName.toLatin1(); char *path=ba.data(); AbstractMesh *abs_mesh = new AbstractMesh(); ParamMesh *para_mesh = new ParamMesh(); bool Done=isoPHandle().LoadBaseDomain<CMeshO>(path,mesh,para_mesh,abs_mesh,true); if (!Done) { this->errorMessage="Abstract domain doesnt fit well with the parametrized mesh"; delete para_mesh; delete abs_mesh; return false; } return true; } case ISOP_SAVE : { m->updateDataMask(MeshModel::MM_VERTQUALITY); CMeshO::PerMeshAttributeHandle<IsoParametrization> isoPHandle = tri::Allocator<CMeshO>::FindPerMeshAttribute<IsoParametrization>(*mesh,"isoparametrization"); bool b=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; } case ISOP_TRANSFER: { MeshModel *mmtrg = par.getMesh("targetMesh"); MeshModel *mmsrc = par.getMesh("targetMesh"); CMeshO *trgMesh=&mmtrg->cm; CMeshO *srcMesh=&mmsrc->cm; CMeshO::PerMeshAttributeHandle<IsoParametrization> isoPHandle = tri::Allocator<CMeshO>::FindPerMeshAttribute<IsoParametrization>(*mesh,"isoparametrization"); bool b=tri::Allocator<CMeshO>::IsValidHandle<IsoParametrization>(*srcMesh,isoPHandle); if (!b) { this->errorMessage="Your source mesh must have the abstract isoparametrization. Use the Isoparametrization command."; return false; } IsoTransfer IsoTr; AbstractMesh *abs_mesh = isoPHandle().AbsMesh(); ParamMesh *para_mesh = isoPHandle().ParaMesh(); mmtrg->updateDataMask(MeshModel::MM_WEDGTEXCOORD); mmtrg->updateDataMask(MeshModel::MM_VERTTEXCOORD); mmtrg->updateDataMask(MeshModel::MM_FACECOLOR); mmtrg->updateDataMask(MeshModel::MM_VERTQUALITY); mmtrg->updateDataMask(MeshModel::MM_FACEMARK); IsoTr.Transfer<CMeshO>(isoPHandle(),*trgMesh); isoPHandle().Clear(); tri::Allocator<CMeshO>::DeletePerMeshAttribute(*srcMesh,isoPHandle); isoPHandle=tri::Allocator<CMeshO>::AddPerMeshAttribute<IsoParametrization>(*trgMesh,"isoparametrization"); isoPHandle().AbsMesh()=abs_mesh; isoPHandle().SetParamMesh<CMeshO>(trgMesh,para_mesh); return true; } } return false; }