/**************************************************************************** * MeshLab o o * * A versatile mesh processing toolbox o o * * _ O _ * * Copyright(C) 2005 \/)\/ * * Visual Computing Lab /\/| * * ISTI - Italian National Research Council | * * \ * * All rights reserved. * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License (http://www.gnu.org/licenses/gpl.txt) * * for more details. * * * ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class FilterData { public: FilterData(); QString name; QString info; int filterClass; bool operator <(const FilterData &d) const {return nameactions()) fprintf(fp, "*%s
%s
\n", qUtf8Printable(filterAction->text()), qUtf8Printable(iFilter->filterInfo(filterAction))); } QMap generateFilterParameterMap() { QMap FPM; MeshDocument md; MeshModel* mm = md.addNewMesh("", "dummy", true); vcg::tri::Tetrahedron(mm->cm); mm->updateDataMask(MeshModel::MM_ALL); QMap::iterator ai; for(FilterPlugin* fpi : PM.filterPluginIterator()) //for (ai = this->actionFilterMap.begin(); ai != this->actionFilterMap.end(); ++ai) { for (QAction* ai: fpi->actions()) { QString filterName = fpi->filterName(ai);// ->filterName(); //QAction act(filterName,NULL); RichParameterList rp; FPM[filterName] = rp; } } return FPM; } void dumpPluginInfoDoxygen(FILE *fp) { if(!fp) return; int i=0; QMap FPM = generateFilterParameterMap(); fprintf(fp,"/*! \\mainpage MeshLab Filter Documentation\n"); //fprintf(fp,"\\AtBeginDocument{\\setcounter{tocdepth}{1}}"); for(FilterPlugin *iFilter: PM.filterPluginIterator()) { for(QAction *filterAction: iFilter->actions()) { fprintf(fp, "\n\\section f%i %s \n\n" "%s\n" ,i++, qUtf8Printable(filterAction->text()), qUtf8Printable(iFilter->filterInfo(filterAction))); fprintf(fp, "

Parameters

\n"); // fprintf(fp, "\\paragraph fp%i Parameters\n",i); if(! FPM[filterAction->text()].isEmpty()) { fprintf(fp,"\n"); for(RichParameter& pp : FPM[filterAction->text()]) { fprintf(fp,"\n", qUtf8Printable(pp.value().typeName()), qUtf8Printable(pp.fieldDescription()), qUtf8Printable(pp.toolTip())); } fprintf(fp,"
\\c %s %s %s --
\n"); } else fprintf(fp,"No parameters.
"); } } fprintf(fp,"*/"); } bool importMesh(MeshModel &mm, const QString& fileName,FILE* fp = stdout) { // Opening files in a transparent form (IO plugins contribution is hidden to user) QStringList filters; // HashTable storing all supported formats together with // the (1-based) index of first plugin which is able to open it QHash allKnownFormats; //PM.LoadFormats(filters, allKnownFormats,PluginManager::IMPORT); QFileInfo fi(fileName); // this change of dir is needed for subsequent textures/materials loading QDir curDir = QDir::current(); QDir::setCurrent(fi.absolutePath()); QString extension = fi.suffix(); qDebug("Opening a file with extension %s", qUtf8Printable(extension)); // retrieving corresponding IO plugin IOPlugin* pCurrentIOPlugin = PM.inputMeshPlugin(extension); if (pCurrentIOPlugin == 0) { fprintf(fp,"Error encountered while opening file: "); QDir::setCurrent(curDir.absolutePath()); return false; } int mask = 0; RichParameterList prePar; pCurrentIOPlugin->initPreOpenParameter(extension,prePar); prePar.join(meshlab::defaultGlobalParameterList()); try { pCurrentIOPlugin->open(extension, fileName, mm ,mask,prePar); } catch(const MLException& e){ fprintf(fp,"MeshLabServer: Failed loading of %s from dir %s\n", qUtf8Printable(fileName), qUtf8Printable(QDir::currentPath())); QDir::setCurrent(curDir.absolutePath()); return false; } // In case of polygonal meshes the normal should be updated accordingly if( mask & vcg::tri::io::Mask::IOM_BITPOLYGONAL) { mm.updateDataMask(MeshModel::MM_POLYGONAL); // just to be sure. Hopefully it should be done in the plugin... int degNum = vcg::tri::Clean::RemoveDegenerateFace(mm.cm); if(degNum) fprintf(fp,"Warning model contains %i degenerate faces. Removed them.",degNum); mm.updateDataMask(MeshModel::MM_FACEFACETOPO); vcg::tri::UpdateNormal::PerBitQuadFaceNormalized(mm.cm); vcg::tri::UpdateNormal::PerVertexFromCurrentFaceNormal(mm.cm); } // standard case else { if( mask & vcg::tri::io::Mask::IOM_VERTNORMAL) // the mesh already has its per vertex normals (point clouds) { vcg::tri::UpdateNormal::PerFace(mm.cm); vcg::tri::UpdateBounding::Box(mm.cm); // updates bounding box } else mm.updateBoxAndNormals(); // the very standard case } if(mm.cm.fn==0) { if (mask & vcg::tri::io::Mask::IOM_VERTNORMAL) mm.updateDataMask(MeshModel::MM_VERTNORMAL); } else mm.updateDataMask(MeshModel::MM_VERTNORMAL); if (shared != NULL) shared->meshInserted(mm.id()); //vcg::tri::UpdateBounding::Box(mm.cm); QDir::setCurrent(curDir.absolutePath()); return true; } bool exportMesh(MeshModel *mm, const int mask, const QString& fileName,bool writebinary,FILE* fp = stdout) { QFileInfo fi(fileName); // this change of dir is needed for subsequent textures/materials loading QDir curDir = QDir::current(); QDir::setCurrent(fi.absolutePath()); QString extension = fi.suffix(); // retrieving corresponding IO plugin IOPlugin* pCurrentIOPlugin = PM.outputMeshPlugin(extension); if (pCurrentIOPlugin == nullptr) { fprintf(fp,"Error encountered while opening file: "); //QString errorMsgFormat = "Error encountered while opening file:\n\"%1\"\n\nError details: The \"%2\" file extension does not correspond to any supported format."; //QMessageBox::critical(this, tr("Opening Error"), errorMsgFormat.arg(fileName, extension)); QDir::setCurrent(curDir.absolutePath()); return false; } // optional saving parameters (like ascii/binary encoding) RichParameterList savePar; pCurrentIOPlugin->initSaveParameter(extension, *mm, savePar); if(savePar.hasParameter("Binary")){ savePar.setValue("Binary",BoolValue(writebinary)); } int formatmask = 0; int defbits = 0; pCurrentIOPlugin->exportMaskCapability(extension,formatmask,defbits); try { pCurrentIOPlugin->save(extension, fileName, *mm ,mask & formatmask, savePar, nullptr); QDir::setCurrent(curDir.absolutePath()); return true; } catch(const MLException& e) { fprintf(fp,"Failed saving\n"); QDir::setCurrent(curDir.absolutePath()); return false; } } bool loadMesh(const QString& fileName, IOPlugin *pCurrentIOPlugin, MeshModel* mm, int& mask,RichParameterList* prePar, const Matrix44m &mtr, MeshDocument* md, FILE* fp = stdout) { if (mm == NULL) return false; QFileInfo fi(fileName); QString extension = fi.suffix(); if(!fi.exists()) { QString errorMsgFormat = "Unable to open file:\n\"%1\"\n\nError details: file %1 does not exist."; fprintf(fp, "Meshlab Opening Error: %s", errorMsgFormat.arg(fileName).toStdString().c_str()); return false; } if(!fi.isReadable()) { QString errorMsgFormat = "Unable to open file:\n\"%1\"\n\nError details: file %1 is not readable."; fprintf(fp, "Meshlab Opening Error: %s", errorMsgFormat.arg(fileName).toStdString().c_str()); return false; } // the original directory path before we switch it QString origDir = QDir::current().path(); // this change of dir is needed for subsequent textures/materials loading QDir::setCurrent(fi.absoluteDir().absolutePath()); // Adjust the file name after changing the directory QString fileNameSansDir = fi.fileName(); // retrieving corresponding IO plugin if (pCurrentIOPlugin == 0) { QString errorMsgFormat = "Error encountered while opening file:\n\"%1\"\n\nError details: The \"%2\" file extension does not correspond to any supported format."; fprintf(fp, "Opening Error: %s", errorMsgFormat.arg(fileName, extension).toStdString().c_str()); QDir::setCurrent(origDir); // undo the change of directory before leaving return false; } md->setBusy(true); pCurrentIOPlugin->setLog(&md->Log); try { pCurrentIOPlugin->open(extension, fileNameSansDir, *mm ,mask,*prePar); } catch (const MLException& e) { fprintf(fp, "Opening Failure: %s", (QString("While opening: '%1'\n\n").arg(fileName)+e.what()).toStdString().c_str()); // text+ md->setBusy(false); QDir::setCurrent(origDir); // undo the change of directory before leaving return false; } //std::cout << "Opened mesh: in " << tm.elapsed() << " secs\n"; // After opening the mesh lets ask to the io plugin if this format // requires some optional, or userdriven post-opening processing. // and in that case ask for the required parameters and then // ask to the plugin to perform that processing //RichParameterSet par; //pCurrentIOPlugin->initOpenParameter(extension, *mm, par); //pCurrentIOPlugin->applyOpenParameter(extension, *mm, par); QString err = pCurrentIOPlugin->warningMessageString(); if (!err.isEmpty()) { fprintf(fp, "Opening Problems: %s", (QString("While opening: '%1'\n\n").arg(fileName)+err).toStdString().c_str()); } //saveRecentFileList(fileName); //if (!(mm->cm.textures.empty())) // updateTexture(mm->id()); // In case of polygonal meshes the normal should be updated accordingly if( mask & vcg::tri::io::Mask::IOM_BITPOLYGONAL) { mm->updateDataMask(MeshModel::MM_POLYGONAL); // just to be sure. Hopefully it should be done in the plugin... int degNum = vcg::tri::Clean::RemoveDegenerateFace(mm->cm); if(degNum) fprintf(stdout, "Warning model contains %i degenerate faces. Removed them.",degNum); mm->updateDataMask(MeshModel::MM_FACEFACETOPO); vcg::tri::UpdateNormal::PerBitQuadFaceNormalized(mm->cm); vcg::tri::UpdateNormal::PerVertexFromCurrentFaceNormal(mm->cm); } // standard case else { vcg::tri::UpdateNormal::PerFaceNormalized(mm->cm); if(!( mask & vcg::tri::io::Mask::IOM_VERTNORMAL) ) vcg::tri::UpdateNormal::PerVertexAngleWeighted(mm->cm); } vcg::tri::UpdateBounding::Box(mm->cm); // updates bounding box if(mm->cm.fn==0 && mm->cm.en==0) { if(mask & vcg::tri::io::Mask::IOM_VERTNORMAL) mm->updateDataMask(MeshModel::MM_VERTNORMAL); } if(mm->cm.fn==0 && mm->cm.en>0) { if (mask & vcg::tri::io::Mask::IOM_VERTNORMAL) mm->updateDataMask(MeshModel::MM_VERTNORMAL); } //updateMenus(); int delVertNum = vcg::tri::Clean::RemoveDegenerateVertex(mm->cm); int delFaceNum = vcg::tri::Clean::RemoveDegenerateFace(mm->cm); vcg::tri::Allocator::CompactEveryVector(mm->cm); if(delVertNum>0 || delFaceNum>0 ) fprintf(fp, "MeshLab Warning: %s", (QString("Warning mesh contains %1 vertices with NAN coords and %2 degenerated faces.\nCorrected.").arg(delVertNum).arg(delFaceNum)).toStdString().c_str() ); mm->cm.Tr = mtr; //computeRenderingDataOnLoading(mm,isareload, rendOpt); //updateLayerDialog(); md->setBusy(false); QDir::setCurrent(origDir); // undo the change of directory before leaving return true; } bool loadMeshWithStandardParams(QString& fullPath, MeshModel* mm, const Matrix44m &mtr = Matrix44m::Identity(), MeshDocument* md = NULL) { if ((mm == NULL) || (md == NULL)) return false; bool ret = false; if (!mm->isVisible()) { mm->clear(); mm->visible = false; } else mm->clear(); QFileInfo fi(fullPath); QString extension = fi.suffix(); IOPlugin *pCurrentIOPlugin = PM.inputMeshPlugin(extension); if(pCurrentIOPlugin != NULL) { RichParameterList prePar; pCurrentIOPlugin->initPreOpenParameter(extension,prePar); //prePar = prePar.join(currentGlobalParams); int mask = 0; QElapsedTimer t;t.start(); ret = loadMesh(fullPath,pCurrentIOPlugin,mm,mask,&prePar,mtr, md, stdout); } return ret; } bool openProject(MeshDocument& md,const QString& fileName,FILE* fp = stdout) { //bool visiblelayer = layerDialog->isVisible(); //showLayerDlg(false); //globrendtoolbar->setEnabled(false); if (fileName.isEmpty()) return false; QFileInfo fi(fileName); //lastUsedDirectory = fi.absoluteDir(); //TODO: move this to main() if((fi.suffix().toLower()!="aln") && (fi.suffix().toLower()!="mlp") && (fi.suffix().toLower() != "mlb") && (fi.suffix().toLower()!="out") && (fi.suffix().toLower()!="nvm")) { //QMessageBox::critical(this, tr("Meshlab Opening Error"), "Unknown project file extension"); fprintf(fp, "Meshlab Opening Error: Unknown project file extension\n"); return false; } // Common Part: init a Doc if necessary, and //bool activeDoc = (bool) !mdiarea->subWindowList().empty() && mdiarea->currentSubWindow(); //bool activeEmpty = activeDoc && md.meshList.empty(); //if (!activeEmpty) newProject(fileName); md.setFileName(fileName); //mdiarea->currentSubWindow()->setWindowTitle(fileName); md.setDocLabel(fileName); md.setBusy(true); // this change of dir is needed for subsequent textures/materials loading QDir::setCurrent(fi.absoluteDir().absolutePath()); //qb->show(); if (QString(fi.suffix()).toLower() == "aln") { std::vector rmv; int retVal = ALNParser::ParseALN(rmv, qUtf8Printable(fileName)); if(retVal != ALNParser::NoError) { //QMessageBox::critical(this, tr("Meshlab Opening Error"), "Unable to open ALN file"); fprintf(fp,"Meshlab Opening Error: Unable to open ALN file\n"); return false; } bool openRes=true; std::vector::iterator ir; for(ir=rmv.begin();ir!=rmv.end() && openRes;++ir) { QString relativeToProj = fi.absoluteDir().absolutePath() + "/" + (*ir).filename.c_str(); md.addNewMesh(relativeToProj,relativeToProj); openRes = loadMeshWithStandardParams(relativeToProj,md.mm(),ir->transformation, &md); if(!openRes) md.delMesh(md.mm()); } } if (QString(fi.suffix()).toLower() == "mlp" || QString(fi.suffix()).toLower() == "mlb") { std::map rendOpt; if (!MeshDocumentFromXML(md, fileName, (QString(fi.suffix()).toLower() == "mlb"), rendOpt)) { //QMessageBox::critical(this, tr("Meshlab Opening Error"), "Unable to open MeshLab Project file"); fprintf(fp,"Meshlab Opening Error: Unable to open MeshLab Project file\n"); return false; } //GLA()->updateMeshSetVisibilities(); for (int i=0; ifullName(); //md.setBusy(true); Matrix44m trm = md.meshList[i]->cm.Tr; // save the matrix, because loadMeshClear it... if (!loadMeshWithStandardParams(fullPath, md.meshList[i], trm, &md)) md.delMesh(md.meshList[i]); } } ////// BUNDLER if (QString(fi.suffix()).toLower() == "out"){ QString cameras_filename = fileName; QString image_list_filename; QString model_filename; image_list_filename = "list.txt"; if(image_list_filename.isEmpty()) return false; if(!MeshDocumentFromBundler(md,cameras_filename,image_list_filename,model_filename)){ //QMessageBox::critical(this, tr("Meshlab Opening Error"), "Unable to open OUTs file"); fprintf(fp,"Meshlab Opening Error: Unable to open OUTs file\n"); return false; } //WARNING!!!!! i suppose it's not useful anymore but....... /*GLA()->setColorMode(GLW::CMPerVert); GLA()->setDrawMode(GLW::DMPoints);*/ ///////////////////////////////////////////////////////// } //////NVM if (QString(fi.suffix()).toLower() == "nvm"){ QString cameras_filename = fileName; QString model_filename; if(!MeshDocumentFromNvm(md,cameras_filename,model_filename)){ //QMessageBox::critical(this, tr("Meshlab Opening Error"), "Unable to open NVMs file"); fprintf(fp,"Meshlab Opening Error: Unable to open NVMs file\n"); return false; } //WARNING!!!!! i suppose it's not useful anymore but....... /*GLA()->setColorMode(GLW::CMPerVert); GLA()->setDrawMode(GLW::DMPoints);*/ ///////////////////////////////////////////////////////// } md.setBusy(false); //qb->reset(); return true; } bool saveProject(MeshDocument& md,const QString& filename,const QString& outfilemeshmiddlename = QString()) { QFileInfo outprojinfo(filename); QString outdir = outprojinfo.absolutePath(); QDir curDir = QDir::current(); QDir::setCurrent(outprojinfo.absolutePath()); foreach(MeshModel* m,md.meshList) { if (m != NULL) { QString outfilename; QFileInfo fi(m->fullName()); if (!fi.exists()) outfilename = outdir + "/" + m->label().remove(" ") + outfilemeshmiddlename + ".ply"; else outfilename = fi.absolutePath() + "/" + fi.completeBaseName() + outfilemeshmiddlename + "." + fi.completeSuffix(); m->setFileName(outfilename); QFileInfo of(outfilename); m->setLabel(of.fileName()); exportMesh(m,m->dataMask(),outfilename,true); } } QDir::setCurrent(curDir.absolutePath()); return MeshDocumentToXMLFile(md, filename, false, false, outprojinfo.suffix().toLower() == "mlb"); } bool script(MeshDocument &meshDocument,const QString& scriptfile,FILE* fp) { MeshModel* mm = meshDocument.mm(); FilterScript scriptPtr; //Open/Load FilterScript if (scriptfile.isEmpty()) { printf("No script specified\n"); return false; } if (!scriptPtr.open(scriptfile)) { printf("File %s was not found.\n", qUtf8Printable(scriptfile)); return false; } fprintf(fp,"Starting Script of %i actions",scriptPtr.size()); GLLogStream log; for (FilterNameParameterValuesPair& pair : scriptPtr) { bool ret = true; //RichParameterSet &par = (*ii).second; QString fname = pair.filterName(); fprintf(fp,"filter: %s\n", qUtf8Printable(fname)); QAction *action = PM.filterAction(fname); if (action == NULL) { fprintf(fp,"filter %s not found", qUtf8Printable(fname)); return false; } FilterPlugin *iFilter = qobject_cast(action->parent()); iFilter->setLog(&log); int req = iFilter->getRequirements(action); if (mm != NULL) mm->updateDataMask(req); //make sure the PARMESH parameters are initialized //A filter in the script file couldn't have all the required parameter not defined (a script file not generated by MeshLab). //So we have to ask to the filter the default values for all the parameters and integrate them with the parameters' values //defined in the script file. RichParameterList required = iFilter->initParameterList(action,meshDocument); RichParameterList ¶meterSet = pair.second; //The parameters in the script file are more than the required parameters of the filter. The script file is not correct. if (required.size() < parameterSet.size()) { fprintf(fp,"The parameters in the script file are more than the filter %s requires.\n", qUtf8Printable(fname)); return false; } int i = 0; for(RichParameter& rp : required) { if (!parameterSet.hasParameter(rp.name())) { parameterSet.addParam(rp); } assert(parameterSet.size() == required.size()); RichParameter& parameter = parameterSet.at(i); //if this is a mesh parameter and the index is valid if(parameter.value().isMesh()) { RichMesh& md = reinterpret_cast(parameter); if(!(md.value().getMeshId() < (unsigned int) meshDocument.size())) { fprintf(fp,"Meshes loaded: %i, meshes asked for: %i \n", meshDocument.size(), md.value().getMeshId() ); fprintf(fp,"One of the filters in the script needs more meshes than you have loaded.\n"); exit(-1); } } i++; } QGLWidget* wid = NULL; if (shared != NULL) { wid = new QGLWidget(NULL,shared); iFilter->glContext = new MLPluginGLContext(QGLFormat::defaultFormat(), wid->context()->device(),*shared); bool created = iFilter->glContext->create(wid->context()); if ((!created) || (!iFilter->glContext->isValid())) { fprintf(fp, "A valid GLContext is required by the filter to work.\n"); return false; } MLRenderingData dt; MLRenderingData::RendAtts atts; atts[MLRenderingData::ATT_NAMES::ATT_VERTPOSITION] = true; atts[MLRenderingData::ATT_NAMES::ATT_VERTNORMAL] = true; if (iFilter->filterArity(action) == FilterPlugin::SINGLE_MESH) { MLRenderingData::PRIMITIVE_MODALITY pm = MLPoliciesStandAloneFunctions::bestPrimitiveModalityAccordingToMesh(meshDocument.mm()); if ((pm != MLRenderingData::PR_ARITY) && (meshDocument.mm() != NULL)) { dt.set(pm, atts); iFilter->glContext->initPerViewRenderingData(meshDocument.mm()->id(), dt); } if (meshDocument.mm() != NULL) { meshDocument.mm()->cm.svn = int(vcg::tri::UpdateSelection::VertexCount(meshDocument.mm()->cm)); meshDocument.mm()->cm.sfn = int(vcg::tri::UpdateSelection::FaceCount(meshDocument.mm()->cm)); } } else { for (int ii = 0; ii < meshDocument.meshList.size(); ++ii) { MeshModel* mm = meshDocument.meshList[ii]; MLRenderingData::PRIMITIVE_MODALITY pm = MLPoliciesStandAloneFunctions::bestPrimitiveModalityAccordingToMesh(mm); if ((pm != MLRenderingData::PR_ARITY) && (mm != NULL)) { dt.set(pm, atts); iFilter->glContext->initPerViewRenderingData(mm->id(), dt); } if (mm != NULL) { mm->cm.svn = int(vcg::tri::UpdateSelection::VertexCount(mm->cm)); mm->cm.sfn = int(vcg::tri::UpdateSelection::FaceCount(mm->cm)); } } } } meshDocument.setBusy(true); unsigned int postConditionMask = MeshModel::MM_UNKNOWN; try { iFilter->applyFilter( action, pair.second, meshDocument, postConditionMask, filterCallBack); } catch (MLException&) { ret = false; } meshDocument.setBusy(false); if (shared != NULL) delete iFilter->glContext; delete wid; QStringList logOutput; log.print(logOutput); foreach(QString logEntry, logOutput) fprintf(fp,"%s\n",qUtf8Printable(logEntry)); if(!ret) { fprintf(fp,"Problem with filter: %s\n",qUtf8Printable(fname)); return false; } } return true; } private: PluginManager PM; MLSceneGLSharedDataContext* shared; }; namespace commandline { const char inproject('p'); const char outproject('w'); const char overwrite('x'); const char inputmeshes('i'); const char outputmesh('o'); const char layer('l'); const char lastlayer('x'); const char currentlayer('c'); const char mask('m'); const char vertex('v'); const char face('f'); const char wedge('w'); const char mesh('m'); const char color('c'); const char flags('f'); const char normal('n'); const char quality('q'); const char radius('r'); const char polygon('p'); const char texture('t'); const char log('l'); const char dump('d'); const char script('s'); const char saveparam('s'); const char ascii('a'); void usage() { printf("MeshLabServer version: %s\n", qUtf8Printable(MeshLabApplication::appVer())); QFile docum(":/meshlabserver.txt"); if (!docum.open(QIODevice::ReadOnly)) { printf("MeshLabServer was not able to locate meshlabserver.txt file. The program will be closed\n"); exit(-1); } QString help(docum.readAll()); printf("\nUsage:\n%s",qUtf8Printable(help)); docum.close(); } QString optionValueExpression(const char cmdlineopt) { //Validate an option followed by spaces and a filepath return QString ("-" + QString(cmdlineopt) + "\\s+(.+)"); } QString outputmeshExpression() { QString options("(" + QString(vertex) + "|" + QString(face) + "|" + QString(wedge) + "|" + QString(mesh) + "|" +QString(saveparam) + ")(" + QString(color) + "|" + QString(quality) + "|" + QString(flags) + "|" + QString(normal) + "|" + QString(radius) + "|" + QString(texture) + "|" + QString(polygon) + "|" + QString(ascii) + ")"); QString optionslist(options + "(\\s+" + options + ")*"); QString savingmask("-" + QString(mask) + "\\s+" + optionslist); QString layernumber("\\d+"); QString layertosave("-" + QString(layer) + "\\s+(" + layernumber + "|" + currentlayer + "|" + lastlayer + ")"); return optionValueExpression(outputmesh) + "(\\s+(" + savingmask + "|" + layertosave + "\\s+" + savingmask + "|" + layertosave + "))*"; } bool validateCommandLine(const QString& str) { QString logstring("(" + optionValueExpression(log) + "\\s+" + optionValueExpression(dump) + "|" + optionValueExpression(dump) + "\\s+" + optionValueExpression(log) + "|" + optionValueExpression(dump) + "|" + optionValueExpression(log) + ")"); QString arg("(" + optionValueExpression(inproject) + "|" + optionValueExpression(inputmeshes) + "|" + optionValueExpression(outproject) + "(\\s+-" + overwrite + ")?" + "|" + optionValueExpression(script) + "|" + outputmeshExpression() + ")"); QString args("(" + arg + ")(\\s+" + arg + ")*"); QString completecommandline("(" + logstring + "|" + logstring + "\\s+" + args + "|" + args + ")"); QRegExp completecommandlineexp(completecommandline); //completecommandlineexp.setMinimal(true); bool valid = completecommandlineexp.isValid(); if (!valid) return false; completecommandlineexp.indexIn(str); QString rr = completecommandlineexp.cap(); return (completecommandlineexp.matchedLength() == str.size()); } } struct OutFileMesh { OutFileMesh() : writebinary(true) {} QString filename; int mask; bool writebinary; /*WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/ /* We need these two constant values because when we parse the command line we don't know * yet how many layers will have the current document and which will be the current one. * Opening a project and/or importing a file happens after the parsing of the commandline * is completed */ static const int lastlayerconst = -2; static const int currentlayerconst = -1; /******************************************************************************************/ // Possible values can be: // - lastlayerconst #the last layer of a document, DEFAULT value // - currentlayerconst #the current layer of a document, sometimes it's different from the last layer of a document // - a number between [0,inf) #identifying the correspondent layer position // WARNING!!!!! Please note that the layer position is DIFFERENT from the layer id int layerposition; }; struct OutProject { QString filename; bool overwrite; }; int main(int argc, char *argv[]) { GLExtensionsManager::init(); FILE* logfp = stdout; FILE* dumpfp = NULL; MeshLabApplication app(argc, argv); QStringList st = app.arguments(); std::setlocale(LC_ALL, "C"); QLocale::setDefault(QLocale::C); if(argc == 1) { commandline::usage(); //system("pause"); exit(-1); } QStringList scriptfiles; QList outmeshlist; QList outprojectfiles; QString cmdline; for (int ii = 1; ii < argc; ++ii) { QString argum(argv[ii]); argum = argum.trimmed(); if (argum.contains(' ')) argum = "\"" + argum + "\""; cmdline = cmdline + argum + " "; } if (!commandline::validateCommandLine(cmdline.trimmed())) { printf("CommandLine Syntax Error: please refer to the following documentation for a complete list of the MeshLabServer parameters.\n"); commandline::usage(); //system("pause"); exit(-1); } QSettings settings(MeshLabApplication::organization(),MeshLabApplication::appArchitecturalName(MeshLabApplication::HW_64BIT)); QVariant xmlgpupar = settings.value("MeshLab::System::maxGPUMemDedicatedToGeometry"); QDomDocument doc; doc.setContent(xmlgpupar.toString(), false); QDomElement paramelem = doc.firstChild().toElement(); int gpumemmb = paramelem.attribute("value").toInt(); std::ptrdiff_t maxgpumem = (std::ptrdiff_t) gpumemmb * (float)(1024 * 1024); vcg::QtThreadSafeMemoryInfo gpumeminfo(maxgpumem); QGLFormat fmt = QGLFormat::defaultFormat(); fmt.setAlphaBufferSize(8); QGLFormat::setDefaultFormat(fmt); MeshDocument meshDocument; MLSceneGLSharedDataContext shared(meshDocument, gpumeminfo, MeshLabScalarTest::doublePrecision(), 100000,100000); shared.makeCurrent(); if (!GLExtensionsManager::initializeGLextensions_notThrowing()) { printf("GLEW Init: failed!\n"); //system("pause"); exit(-1); } shared.doneCurrent(); printf("Loading Plugins:\n"); MeshLabServer server(&shared); server.loadPlugins(); bool writebinary = true; int i = 1; while(i < argc) { QString tmp = argv[i]; switch(argv[i][1]) { case commandline::inproject : { if (((i+1) < argc) && (argv[i+1][0] != '-')) { QFileInfo finfo(argv[i+1]); QString inputproject = finfo.absoluteFilePath(); bool opened = server.openProject(meshDocument,inputproject, logfp); if (!opened) { fprintf(logfp,"MeshLab Project %s has not been correctly opened. MeshLabServer application will exit.\n",qUtf8Printable(inputproject)); //system("pause"); exit(-1); } else fprintf(logfp,"MeshLab Project %s has been loaded.\n",qUtf8Printable(inputproject)); ++i; } else { fprintf(logfp,"Missing project name. MeshLabServer application will exit.\n"); //system("pause"); exit(-1); } ++i; break; } case commandline::outproject : { if (((i+1) < argc) && (argv[i+1][0] != '-')) { QFileInfo finfo(argv[i+1]); OutProject pr; pr.overwrite = false; pr.filename = finfo.absoluteFilePath(); if (finfo.completeSuffix().toLower() != "mlp") { fprintf(logfp,"Project %s is not a valid \'mlp\' file format. Output file will be renamed as %s.mlp .\n",qUtf8Printable(pr.filename),qUtf8Printable(pr.filename + ".mlp")); pr.filename += ".mlp"; } ++i; QString overtmp('-'); overtmp += commandline::overwrite; if (((i + 1) < argc) && (QString(argv[i+1]) == overtmp)) { pr.overwrite = true; ++i; } outprojectfiles << pr; } ++i; break; } case commandline::inputmeshes : { while( ((i+1) < argc) && argv[i+1][0] != '-') { QFileInfo info(argv[i+1]); //now add it to the document MeshModel* mmod = meshDocument.addNewMesh(info.absoluteFilePath(),""); if (mmod == NULL) { fprintf(logfp,"It was not possible to add new mesh %s to MeshLabServer. The program will exit\n",qUtf8Printable(info.absoluteFilePath())); //system("pause"); exit(-1); } bool opened = server.importMesh(*mmod, info.absoluteFilePath(),logfp); if (!opened) { fprintf(logfp,"It was not possible to import mesh %s into MeshLabServer. The program will exit\n ",qUtf8Printable(info.absoluteFilePath())); //system("pause"); exit(-1); } fprintf(logfp,"Mesh %s loaded has %i vn %i fn\n", qUtf8Printable(info.absoluteFilePath()), mmod->cm.vn, mmod->cm.fn); i++; } i++; break; } case commandline::outputmesh : { QString fileout; int mask = 0; OutFileMesh outfl; if( ((i+1) < argc) && argv[i+1][0] != '-') { QFileInfo info(argv[i+1]); outfl.filename = info.absoluteFilePath(); /*WARNING! in order to maintain backward SYNTAX compatibility (not the SEMANTIC one!) by default the outputmesh saved is the one contained in the current layer*/ outfl.layerposition = OutFileMesh::currentlayerconst; fprintf(logfp,"output mesh %s\n", qUtf8Printable(outfl.filename)); i++; } if (((i + 1) < argc) && (QString(argv[i + 1]) == (QString("-") + commandline::layer))) { i = i + 2; if (argv[i][0] == commandline::lastlayer) outfl.layerposition = OutFileMesh::lastlayerconst; else { if (argv[i][0] == commandline::currentlayer) outfl.layerposition = OutFileMesh::currentlayerconst; else outfl.layerposition = QString(argv[i]).toInt(); } } if (((i + 1) < argc) && (QString(argv[i+1]) == (QString("-") + commandline::mask))) { i = i + 2; do { switch (argv[i][0]) { case commandline::vertex : { switch (argv[i][1]) { case commandline::color : i++; fprintf(logfp,"vertex color, " ); mask |= vcg::tri::io::Mask::IOM_VERTCOLOR; break; case commandline::flags : i++; fprintf(logfp,"vertex flags, " ); mask |= vcg::tri::io::Mask::IOM_VERTFLAGS; break; case commandline::normal : i++; fprintf(logfp,"vertex normals, " ); mask |= vcg::tri::io::Mask::IOM_VERTNORMAL; break; case commandline::quality : i++; fprintf(logfp,"vertex quality, " ); mask |= vcg::tri::io::Mask::IOM_VERTQUALITY; break; case commandline::radius : i++; fprintf(logfp,"vertex radii, " ); mask |= vcg::tri::io::Mask::IOM_VERTRADIUS; break; case commandline::texture : i++; fprintf(logfp,"vertex tex coords, "); mask |= vcg::tri::io::Mask::IOM_VERTTEXCOORD; break; default : i++; fprintf(logfp,"WARNING: unknowns per VERTEX attribute '%s'",argv[i+1]);break; } break; } case commandline::face : { switch (argv[i][1]) { case commandline::color : i++; fprintf(logfp,"face color, " ); mask |= vcg::tri::io::Mask::IOM_FACECOLOR; break; case commandline::flags : i++; fprintf(logfp,"face flags, " ); mask |= vcg::tri::io::Mask::IOM_FACEFLAGS; break; case commandline::normal : i++; fprintf(logfp,"face normals, "); mask |= vcg::tri::io::Mask::IOM_FACENORMAL; break; case commandline::quality : i++; fprintf(logfp,"face quality, "); mask |= vcg::tri::io::Mask::IOM_FACEQUALITY; break; default : i++; fprintf(logfp,"WARNING: unknowns per FACE attribute '%s'",argv[i+1]);break; } break; } case commandline::wedge : { switch (argv[i][1]) { case commandline::color : i++; fprintf(logfp,"wedge color, " ); mask |= vcg::tri::io::Mask::IOM_WEDGCOLOR; break; case commandline::normal : i++; fprintf(logfp,"wedge normals, " ); mask |= vcg::tri::io::Mask::IOM_WEDGNORMAL; break; case commandline::texture : i++; fprintf(logfp,"wedge tex coords, "); mask |= vcg::tri::io::Mask::IOM_WEDGTEXCOORD;break; default : i++; fprintf(logfp,"WARNING: unknowns per WEDGE attribute '%s'",argv[i+1]);break; } break; } case commandline::saveparam : { switch( argv[i][1]) { case commandline::ascii: { writebinary = false; i++; break; } } break; } case commandline::mesh : { switch (argv[i][1]) { case commandline::polygon: i++; fprintf(logfp, "mesh polygon, "); mask |= vcg::tri::io::Mask::IOM_BITPOLYGONAL; break; default: i++; fprintf(logfp, "WARNING: unknowns per MESH attribute '%s'", argv[i + 1]); break; } break; } default : i++; fprintf(logfp,"WARNING: unknowns attribute '%s'",argv[i]);break; } }while (((i) < argc) && (argv[i][0] != '-')); } else ++i; outfl.mask = mask; outfl.writebinary = writebinary; outmeshlist << outfl; break; } case commandline::script : { QFileInfo fi(argv[i+1]); QString scriptName = fi.absoluteFilePath(); scriptfiles << scriptName; i += 2; break; } case commandline::log : { //freopen redirect both std::cout and printf. Now I'm quite sure i will get everything the plugins will print in the standard output (i hope no one used std::cerr...) logfp = fopen(argv[i+1],"a"); if (logfp == NULL) printf("Error occurred opening file %s. It's not possible to redirect the output from the stdout\n",argv[i+1]); else printf("Log is saved in %s\n", argv[i+1]); i += 2; break; } case commandline::dump : { dumpfp = fopen(argv[i+1],"w"); if (dumpfp == NULL) fprintf(logfp,"Error occurred opening file %s. It's not possible to redirect the output from the stdout\n",argv[i+1]); else { server.dumpPluginInfoDoxygen(dumpfp); fclose(dumpfp); fprintf(logfp,"Dump file is saved in %s\n", argv[i+1]); } i+=2; break; } default: { printf("Something bad happened parsing the document. String %s\n",qUtf8Printable(argv[i])); //system("pause"); exit(-1); } } } for(int ii = 0; ii < scriptfiles.size();++ii) { fprintf(logfp,"Apply FilterScript: '%s'\n",qUtf8Printable(scriptfiles[ii])); bool returnValue = server.script(meshDocument, scriptfiles[ii],logfp); if(!returnValue) { fprintf(logfp,"Failed to apply script file %s\n",qUtf8Printable(scriptfiles[ii])); //system("pause"); exit(-1); } } for(int ii = 0;ii < outprojectfiles.size();++ii) { QString outfilemiddlename = ""; if (!outprojectfiles[ii].overwrite) { outfilemiddlename = "_out"; if (ii >= 1) outfilemiddlename += QString::number(ii); } bool saved = server.saveProject(meshDocument,outprojectfiles[ii].filename,outfilemiddlename); if (saved) fprintf(logfp,"Output project has been saved in %s.\n",qUtf8Printable(outprojectfiles[ii].filename)); else { fprintf(logfp,"Project %s has not been correctly saved in. MeshLabServer Application will exit.\n",qUtf8Printable(outprojectfiles[ii].filename)); //system("pause"); exit(-1); } } if (meshDocument.size() < outmeshlist.size()) fprintf(logfp, "Error: trying to save %i meshes, but only %i available in the project\n", outmeshlist.size(), meshDocument.size()); for (int ii = 0; ii < outmeshlist.size(); ++ii) { bool exported = false; if (outmeshlist[ii].layerposition < meshDocument.meshList.size()) { int layertobesaved = outmeshlist[ii].layerposition; if (layertobesaved == OutFileMesh::lastlayerconst) layertobesaved = meshDocument.meshList.size() - 1; else if (layertobesaved == OutFileMesh::currentlayerconst) layertobesaved = meshDocument.meshList.indexOf(meshDocument.mm()); if ((layertobesaved >= 0) && (layertobesaved < meshDocument.meshList.size())) { MeshModel* meshmod = meshDocument.meshList[layertobesaved]; if (meshmod != NULL) exported = server.exportMesh(meshDocument.meshList[layertobesaved], outmeshlist[ii].mask, outmeshlist[ii].filename, outmeshlist[ii].writebinary, logfp); if (exported) fprintf(logfp, "Mesh %s saved as %s (%i vn %i fn)\n", qUtf8Printable(meshmod->fullName()), qUtf8Printable(outmeshlist[ii].filename), meshmod->cm.vn, meshmod->cm.fn); else fprintf(logfp, "Output mesh %s has NOT been saved\n", qUtf8Printable(outmeshlist[ii].filename)); } else fprintf(logfp, "Output mesh %s has NOT been saved. A not existent layer has been requested to be saved\n", qUtf8Printable(outmeshlist[ii].filename)); } else fprintf(logfp, "Invalid layer number %i. Last layer in the current document is the number %i. Output mesh %s will not be saved\n", outmeshlist[ii].layerposition, meshDocument.meshList.size() - 1, qUtf8Printable(outmeshlist[ii].filename)); }//for(int ii if((logfp != NULL) && (logfp != stdout)) { fclose(logfp); } shared.deAllocateGPUSharedData(); //system("pause"); return 0; }//int main()