1 /****************************************************************************
2 * MeshLab                                                           o o     *
3 * A versatile mesh processing toolbox                             o     o   *
4 *                                                                _   O  _   *
5 * Copyright(C) 2005                                                \/)\/    *
6 * Visual Computing Lab                                            /\/|      *
7 * ISTI - Italian National Research Council                           |      *
8 *                                                                    \      *
9 * All rights reserved.                                                      *
10 *                                                                           *
11 * This program is free software; you can redistribute it and/or modify      *
12 * it under the terms of the GNU General Public License as published by      *
13 * the Free Software Foundation; either version 2 of the License, or         *
14 * (at your option) any later version.                                       *
15 *                                                                           *
16 * This program is distributed in the hope that it will be useful,           *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of            *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
19 * GNU General Public License (http://www.gnu.org/licenses/gpl.txt)          *
20 * for more details.                                                         *
21 *                                                                           *
22 ****************************************************************************/
23 
24 #include <common/GLExtensionsManager.h>
25 #include <common/mlapplication.h>
26 #include <common/mlexception.h>
27 #include <common/interfaces.h>
28 #include <common/pluginmanager.h>
29 #include <common/filterscript.h>
30 #include <common/meshlabdocumentxml.h>
31 #include <common/mlexception.h>
32 #include <common/filterparameter.h>
33 #include <wrap/qt/qt_thread_safe_memory_info.h>
34 #include <clocale>
35 
36 #include <QGLFormat>
37 #include <QFileInfo>
38 
39 
40 class FilterData
41 {
42 public:
43     FilterData();
44     QString name;
45     QString info;
46     int filterClass;
operator <(const FilterData & d) const47     bool operator <(const FilterData &d) const {return name<d.name;}
48 };
49 
50 class MeshLabServer
51 {
52 public:
MeshLabServer(MLSceneGLSharedDataContext * shar)53     MeshLabServer(MLSceneGLSharedDataContext* shar)
54 		:shared(shar)
55 	{
56 	}
57 
~MeshLabServer()58     ~MeshLabServer()
59 	{
60 	}
61 
filterCallBack(const int pos,const char * str)62     static bool filterCallBack(const int pos, const char * str)
63     {
64         int static lastPos=-1;
65         if(pos==lastPos) return true;
66         lastPos=pos;
67         printf("%s",str);
68         return true;
69     }
70 
71     // Here we need a better way to find the plugins directory.
72     // To be implemented:
73     // use the QSettings togeter with MeshLab.
74     // When meshlab starts if he find the plugins write the absolute path of that directory in a persistent qsetting place.
75     // Here we use that QSetting. If it is not set we remember to run meshlab first once.
76     // in this way it works safely on mac too and allows the user to put the small meshlabserver binary wherever they desire (/usr/local/bin).
77 
loadPlugins()78     void loadPlugins()
79     {
80         PM.loadPlugins(defaultGlobal);
81 
82         printf("Total %i filtering actions\n", PM.actionFilterMap.size());
83         printf("Total %i io plugins\n", PM.meshIOPlugins().size());
84     }
85 
dumpPluginInfoWiki(FILE * fp)86     void dumpPluginInfoWiki(FILE *fp)
87     {
88         if(!fp) return;
89         foreach(MeshFilterInterface *iFilter, PM.meshFilterPlugins())
90             foreach(QAction *filterAction, iFilter->actions())
91             fprintf(fp, "*<b><i>%s</i></b> <br>%s<br>\n", qUtf8Printable(filterAction->text()), qUtf8Printable(iFilter->filterInfo(filterAction)));
92     }
93 
dumpPluginInfoDoxygen(FILE * fp)94     void dumpPluginInfoDoxygen(FILE *fp)
95     {
96         if(!fp) return;
97         int i=0;
98         QMap<QString, RichParameterSet> FPM = PM.generateFilterParameterMap();
99         fprintf(fp,"/*! \\mainpage MeshLab Filter Documentation\n");
100         //fprintf(fp,"\\AtBeginDocument{\\setcounter{tocdepth}{1}}");
101 
102         foreach(MeshFilterInterface *iFilter, PM.meshFilterPlugins())
103         {
104             foreach(QAction *filterAction, iFilter->actions())
105             {
106                 fprintf(fp,
107                     "\n\\section f%i %s \n\n"
108                     "%s\n"
109                     ,i++, qUtf8Printable(filterAction->text()), qUtf8Printable(iFilter->filterInfo(filterAction)));
110 
111                 fprintf(fp,  "<H2> Parameters </h2>\n");
112                 //            fprintf(fp,  "\\paragraph fp%i Parameters\n",i);
113 
114                 if(! FPM[filterAction->text()].paramList.empty())
115                 {
116                     fprintf(fp,"<TABLE>\n");
117                     foreach(RichParameter* pp, FPM[filterAction->text()].paramList)
118                     {
119                         fprintf(fp,"<TR><TD> \\c %s  </TD> <TD> %s </TD> <TD><i> %s -- </i></TD> </TR>\n",
120 							qUtf8Printable(pp->val->typeName()), qUtf8Printable(pp->pd->fieldDesc), qUtf8Printable(pp->pd->tooltip));
121                     }
122                     fprintf(fp,"</TABLE>\n");
123                 }
124                 else fprintf(fp,"No parameters.<br>");
125 
126             }
127         }
128         fprintf(fp,"*/");
129     }
130 
importMesh(MeshModel & mm,const QString & fileName,FILE * fp=stdout)131     bool importMesh(MeshModel &mm, const QString& fileName,FILE* fp = stdout)
132     {
133         // Opening files in a transparent form (IO plugins contribution is hidden to user)
134         QStringList filters;
135 
136         // HashTable storing all supported formats together with
137         // the (1-based) index  of first plugin which is able to open it
138         QHash<QString, MeshIOInterface*> allKnownFormats;
139 
140         //PM.LoadFormats(filters, allKnownFormats,PluginManager::IMPORT);
141 
142         QFileInfo fi(fileName);
143         // this change of dir is needed for subsequent textures/materials loading
144         QDir curDir = QDir::current();
145         QDir::setCurrent(fi.absolutePath());
146 
147         QString extension = fi.suffix();
148         qDebug("Opening a file with extension %s", qUtf8Printable(extension));
149         // retrieving corresponding IO plugin
150         MeshIOInterface* pCurrentIOPlugin = PM.allKnowInputFormats[extension.toLower()];
151         if (pCurrentIOPlugin == 0)
152         {
153             fprintf(fp,"Error encountered while opening file: ");
154             QDir::setCurrent(curDir.absolutePath());
155             return false;
156         }
157         int mask = 0;
158 
159         RichParameterSet prePar;
160         pCurrentIOPlugin->initPreOpenParameter(extension, fileName,prePar);
161         prePar.join(defaultGlobal);
162 
163         if (!pCurrentIOPlugin->open(extension, fileName, mm ,mask,prePar))
164         {
165             fprintf(fp,"MeshLabServer: Failed loading of %s from dir %s\n", qUtf8Printable(fileName), qUtf8Printable(QDir::currentPath()));
166             QDir::setCurrent(curDir.absolutePath());
167             return false;
168         }
169 
170         // In case of polygonal meshes the normal should be updated accordingly
171         if( mask & vcg::tri::io::Mask::IOM_BITPOLYGONAL)
172         {
173             mm.updateDataMask(MeshModel::MM_POLYGONAL); // just to be sure. Hopefully it should be done in the plugin...
174             int degNum = vcg::tri::Clean<CMeshO>::RemoveDegenerateFace(mm.cm);
175             if(degNum)
176                 fprintf(fp,"Warning model contains %i degenerate faces. Removed them.",degNum);
177             mm.updateDataMask(MeshModel::MM_FACEFACETOPO);
178             vcg::tri::UpdateNormal<CMeshO>::PerBitQuadFaceNormalized(mm.cm);
179             vcg::tri::UpdateNormal<CMeshO>::PerVertexFromCurrentFaceNormal(mm.cm);
180         } // standard case
181         else {
182             if( mask & vcg::tri::io::Mask::IOM_VERTNORMAL) // the mesh already has its per vertex normals (point clouds)
183             {
184                 vcg::tri::UpdateNormal<CMeshO>::PerFace(mm.cm);
185                 vcg::tri::UpdateBounding<CMeshO>::Box(mm.cm);					// updates bounding box
186             }
187             else mm.UpdateBoxAndNormals(); // the very standard case
188         }
189 
190         if(mm.cm.fn==0)
191         {
192             if (mask & vcg::tri::io::Mask::IOM_VERTNORMAL)
193                 mm.updateDataMask(MeshModel::MM_VERTNORMAL);
194         }
195         else
196             mm.updateDataMask(MeshModel::MM_VERTNORMAL);
197 
198 		if (shared != NULL)
199 			shared->meshInserted(mm.id());
200         //vcg::tri::UpdateBounding<CMeshO>::Box(mm.cm);
201         QDir::setCurrent(curDir.absolutePath());
202         return true;
203     }
204 
exportMesh(MeshModel * mm,const int mask,const QString & fileName,bool writebinary,FILE * fp=stdout)205     bool exportMesh(MeshModel *mm, const int mask, const QString& fileName,bool writebinary,FILE* fp = stdout)
206     {
207         QFileInfo fi(fileName);
208         // this change of dir is needed for subsequent textures/materials loading
209         QDir curDir = QDir::current();
210         QDir::setCurrent(fi.absolutePath());
211 
212         QString extension = fi.suffix();
213 
214         // retrieving corresponding IO plugin
215         MeshIOInterface* pCurrentIOPlugin = PM.allKnowOutputFormats[extension.toLower()];
216         if (pCurrentIOPlugin == 0)
217         {
218             fprintf(fp,"Error encountered while opening file: ");
219             //QString errorMsgFormat = "Error encountered while opening file:\n\"%1\"\n\nError details: The \"%2\" file extension does not correspond to any supported format.";
220             //QMessageBox::critical(this, tr("Opening Error"), errorMsgFormat.arg(fileName, extension));
221             QDir::setCurrent(curDir.absolutePath());
222             return false;
223         }
224 
225         // optional saving parameters (like ascii/binary encoding)
226         RichParameterSet savePar;
227         pCurrentIOPlugin->initSaveParameter(extension, *mm, savePar);
228         if(savePar.hasParameter("Binary")){
229             savePar.setValue("Binary",BoolValue(writebinary));
230         }
231 
232         int formatmask = 0;
233         int defbits = 0;
234         pCurrentIOPlugin->GetExportMaskCapability(extension,formatmask,defbits);
235         if (!pCurrentIOPlugin->save(extension, fileName, *mm ,mask & formatmask, savePar))
236         {
237             fprintf(fp,"Failed saving\n");
238             QDir::setCurrent(curDir.absolutePath());
239             return false;
240         }
241         QDir::setCurrent(curDir.absolutePath());
242         return true;
243     }
244 
openProject(MeshDocument & md,const QString & filename)245     bool openProject(MeshDocument& md,const QString& filename)
246     {
247         QDir curDir = QDir::current();
248         QFileInfo fi(filename);
249         std::map<int, MLRenderingData> tmp;
250         bool opened = MeshDocumentFromXML(md,fi.absoluteFilePath(), fi.suffix().toLower() == "mlb",tmp);
251         if (!opened)
252             return false;
253         QDir::setCurrent(fi.absolutePath());
254         //WARNING! I'm not putting inside MeshDocumentFromXML function because I'm too scared of what can happen inside MeshLab code....
255         md.setFileName(fi.absoluteFilePath());
256         ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
257 
258         for (int i=0; i<md.meshList.size(); i++)
259         {
260             if (md.meshList[i] != NULL)
261             {
262                 QString fullPath = md.meshList[i]->fullName();
263                 md.setBusy(true);
264                 Matrix44m trm = md.meshList[i]->cm.Tr; // save the matrix, because loadMeshClear it...
265                 if (!importMesh(*md.meshList[i],fullPath))
266                 {
267                     md.delMesh(md.meshList[i]);
268                     md.setBusy(false);
269                     QDir::setCurrent(curDir.absolutePath());
270                     return false;
271                 }
272                 else
273                     md.meshList[i]->cm.Tr=trm;
274                 md.setCurrentMesh(md.meshList[i]->id());
275                 md.setBusy(false);
276             }
277         }
278         QDir::setCurrent(curDir.absolutePath());
279         return true;
280     }
281 
saveProject(MeshDocument & md,const QString & filename,const QString & outfilemeshmiddlename=QString ())282     bool saveProject(MeshDocument& md,const QString& filename,const QString& outfilemeshmiddlename = QString())
283     {
284         QFileInfo outprojinfo(filename);
285         QString outdir = outprojinfo.absolutePath();
286 
287         QDir curDir = QDir::current();
288         QDir::setCurrent(outprojinfo.absolutePath());
289         foreach(MeshModel* m,md.meshList)
290         {
291             if (m != NULL)
292             {
293                 QString outfilename;
294                 QFileInfo fi(m->fullName());
295                 if (!fi.exists())
296                     outfilename = outdir + "/" + m->label().remove(" ") + outfilemeshmiddlename + ".ply";
297                 else
298                     outfilename =  fi.absolutePath() + "/" + fi.completeBaseName() + outfilemeshmiddlename + "." + fi.completeSuffix();
299                 m->setFileName(outfilename);
300                 QFileInfo of(outfilename);
301                 m->setLabel(of.fileName());
302                 exportMesh(m,m->dataMask(),outfilename,true);
303             }
304         }
305 
306         QDir::setCurrent(curDir.absolutePath());
307         return MeshDocumentToXMLFile(md, filename, false, false, outprojinfo.suffix().toLower() == "mlb");
308     }
309 
script(MeshDocument & meshDocument,const QString & scriptfile,FILE * fp)310     bool script(MeshDocument &meshDocument,const QString& scriptfile,FILE* fp)
311     {
312         MeshModel* mm = meshDocument.mm();
313 
314         FilterScript scriptPtr;
315 
316         //Open/Load FilterScript
317 
318         if (scriptfile.isEmpty())
319         {
320             printf("No script specified\n");
321             return false;
322         }
323         if (!scriptPtr.open(scriptfile))
324         {
325             printf("File %s was not found.\n", qUtf8Printable(scriptfile));
326             return false;
327         }
328         fprintf(fp,"Starting Script of %i actions",scriptPtr.filtparlist.size());
329         GLLogStream log;
330         for(FilterScript::iterator ii = scriptPtr.filtparlist.begin();ii!= scriptPtr.filtparlist.end();++ii)
331         {
332             bool ret = false;
333             //RichParameterSet &par = (*ii).second;
334             QString fname = (*ii)->filterName();
335             fprintf(fp,"filter: %s\n", qUtf8Printable(fname));
336             if (!(*ii)->isXMLFilter())
337             {
338                 QAction *action = PM.actionFilterMap[ fname];
339                 if (action == NULL)
340                 {
341                     fprintf(fp,"filter %s not found", qUtf8Printable(fname));
342                     return false;
343                 }
344 
345                 MeshFilterInterface *iFilter = qobject_cast<MeshFilterInterface *>(action->parent());
346                 iFilter->setLog(&log);
347                 int req = iFilter->getRequirements(action);
348 				if (mm != NULL)
349 					mm->updateDataMask(req);
350                 //make sure the PARMESH parameters are initialized
351 
352                 //A filter in the script file couldn't have all the required parameter not defined (a script file not generated by MeshLab).
353                 //So we have to ask to the filter the default values for all the parameters and integrate them with the parameters' values
354                 //defined in the script file.
355                 RichParameterSet required;
356                 iFilter->initParameterSet(action,meshDocument,required);
357                 OldFilterNameParameterValuesPair* pairold = reinterpret_cast<OldFilterNameParameterValuesPair*>(*ii);
358                 RichParameterSet &parameterSet = pairold->pair.second;
359 
360                 //The parameters in the script file are more than the required parameters of the filter. The script file is not correct.
361                 if (required.paramList.size() < parameterSet.paramList.size())
362                 {
363                     fprintf(fp,"The parameters in the script file are more than the filter %s requires.\n", qUtf8Printable(fname));
364                     return false;
365                 }
366 
367                 for(int i = 0; i < required.paramList.size(); i++)
368                 {
369                     RichParameterCopyConstructor v;
370                     if (!parameterSet.hasParameter(required.paramList[i]->name))
371                     {
372                         required.paramList[i]->accept(v);
373                         parameterSet.addParam(v.lastCreated);
374                     }
375                     assert(parameterSet.paramList.size() == required.paramList.size());
376                     RichParameter* parameter = parameterSet.paramList[i];
377                     //if this is a mesh parameter and the index is valid
378                     if(parameter->val->isMesh())
379                     {
380                         RichMesh* md = reinterpret_cast<RichMesh*>(parameter);
381                         if(	md->meshindex < meshDocument.size() &&
382                             md->meshindex >= 0  )
383                         {
384                             RichMesh* rmesh = new RichMesh(parameter->name,meshDocument.getMesh(md->meshindex),&meshDocument);
385                             parameterSet.paramList.replace(i,rmesh);
386                         } else
387                         {
388                             fprintf(fp,"Meshes loaded: %i, meshes asked for: %i \n", meshDocument.size(), md->meshindex );
389                             fprintf(fp,"One of the filters in the script needs more meshes than you have loaded.\n");
390                             exit(-1);
391                         }
392                         delete parameter;
393                     }
394                 }
395 
396 				QGLWidget* wid = NULL;
397 				if (shared != NULL)
398 				{
399 					wid = new QGLWidget(NULL,shared);
400 					iFilter->glContext = new MLPluginGLContext(QGLFormat::defaultFormat(), wid->context()->device(),*shared);
401 					bool created = iFilter->glContext->create(wid->context());
402 					if ((!created) || (!iFilter->glContext->isValid()))
403 					{
404 						fprintf(fp, "A valid GLContext is required by the filter to work.\n");
405 						return false;
406 					}
407 					MLRenderingData dt;
408 					MLRenderingData::RendAtts atts;
409 					atts[MLRenderingData::ATT_NAMES::ATT_VERTPOSITION] = true;
410 					atts[MLRenderingData::ATT_NAMES::ATT_VERTNORMAL] = true;
411 
412 					if (iFilter->filterArity(action) == MeshFilterInterface::SINGLE_MESH)
413 					{
414 						MLRenderingData::PRIMITIVE_MODALITY pm = MLPoliciesStandAloneFunctions::bestPrimitiveModalityAccordingToMesh(meshDocument.mm());
415 						if ((pm != MLRenderingData::PR_ARITY) && (meshDocument.mm() != NULL))
416 						{
417 							dt.set(pm, atts);
418 							iFilter->glContext->initPerViewRenderingData(meshDocument.mm()->id(), dt);
419 						}
420 
421 						if (meshDocument.mm() != NULL)
422 						{
423 							meshDocument.mm()->cm.svn = int(vcg::tri::UpdateSelection<CMeshO>::VertexCount(meshDocument.mm()->cm));
424 							meshDocument.mm()->cm.sfn = int(vcg::tri::UpdateSelection<CMeshO>::FaceCount(meshDocument.mm()->cm));
425 						}
426 
427 					}
428 					else
429 					{
430 						for (int ii = 0; ii < meshDocument.meshList.size(); ++ii)
431 						{
432 							MeshModel* mm = meshDocument.meshList[ii];
433 							MLRenderingData::PRIMITIVE_MODALITY pm = MLPoliciesStandAloneFunctions::bestPrimitiveModalityAccordingToMesh(mm);
434 							if ((pm != MLRenderingData::PR_ARITY) && (mm != NULL))
435 							{
436 								dt.set(pm, atts);
437 								iFilter->glContext->initPerViewRenderingData(mm->id(), dt);
438 							}
439 
440 							if (mm != NULL)
441 							{
442 								mm->cm.svn = int(vcg::tri::UpdateSelection<CMeshO>::VertexCount(mm->cm));
443 								mm->cm.sfn = int(vcg::tri::UpdateSelection<CMeshO>::FaceCount(mm->cm));
444 							}
445 						}
446 					}
447 				}
448                 meshDocument.setBusy(true);
449                 ret = iFilter->applyFilter( action, meshDocument, pairold->pair.second, filterCallBack);
450                 meshDocument.setBusy(false);
451 				if (shared != NULL)
452 					delete iFilter->glContext;
453 				delete wid;
454 
455             }
456             else
457             {
458 
459                 MeshLabXMLFilterContainer cont = PM.stringXMLFilterMap[ fname];
460                 MLXMLPluginInfo* info = cont.xmlInfo;
461                 MeshLabFilterInterface* cppfilt = cont.filterInterface;
462                 try
463                 {
464                     if (cppfilt != NULL)
465                     {
466                         cppfilt->setLog(&log);
467 
468                         Env env;
469                         env.loadMLScriptEnv(meshDocument,PM);
470                         XMLFilterNameParameterValuesPair* xmlfilt = reinterpret_cast<XMLFilterNameParameterValuesPair*>(*ii);
471                         QMap<QString,QString>& parmap = xmlfilt->pair.second;
472                         for(QMap<QString,QString>::const_iterator it = parmap.constBegin();it != parmap.constEnd();++it)
473                             env.insertExpressionBinding(it.key(),it.value());
474 
475                         EnvWrap envwrap(env);
476                         MLXMLPluginInfo::XMLMapList params = info->filterParameters(fname);
477                         for(int i = 0; i < params.size(); ++i)
478                         {
479                             MLXMLPluginInfo::XMLMap& parinfo = params[i];
480 
481                             //if this is a mesh parameter and the index is valid
482                             if(parinfo[MLXMLElNames::paramType]  == MLXMLElNames::meshType)
483                             {
484                                 QString& parnm = parinfo[MLXMLElNames::paramName];
485                                 MeshModel* meshmdl = envwrap.evalMesh(parnm);
486                                 if( meshmdl == NULL)
487                                 {
488                                     //parnm is associated with ,
489                                     printf("Meshes loaded: %i, meshes asked for: %i \n", meshDocument.size(), envwrap.evalInt(parnm) );
490                                     printf("One of the filters in the script needs more meshes than you have loaded.\n");
491                                     return false;
492                                 }
493                             }
494                         }
495 						QGLWidget* wid = NULL;
496 						if (shared != NULL)
497 						{
498 							wid = new QGLWidget(NULL, shared);
499 							cppfilt->glContext = new MLPluginGLContext(QGLFormat::defaultFormat(), wid->context()->device(), *shared);
500 							bool created = cppfilt->glContext->create(wid->context());
501 							if ((!created) || (!cppfilt->glContext->isValid()))
502 							{
503 								fprintf(fp, "A valid GLContext is required by the filter to work.\n");
504 								return false;
505 							}
506 
507 							MLRenderingData dt;
508 							MLRenderingData::RendAtts atts;
509 							atts[MLRenderingData::ATT_NAMES::ATT_VERTPOSITION] = true;
510 							atts[MLRenderingData::ATT_NAMES::ATT_VERTNORMAL] = true;
511 
512 							if (info->filterAttribute(fname, MLXMLElNames::filterArity) == MLXMLElNames::singleMeshArity)
513 							{
514 								MLRenderingData::PRIMITIVE_MODALITY pm = MLPoliciesStandAloneFunctions::bestPrimitiveModalityAccordingToMesh(meshDocument.mm());
515 								if ((pm != MLRenderingData::PR_ARITY) && (meshDocument.mm() != NULL))
516 								{
517 									dt.set(pm, atts);
518 									cppfilt->glContext->initPerViewRenderingData(meshDocument.mm()->id(), dt);
519 								}
520 
521 								if (meshDocument.mm() != NULL)
522 								{
523 									meshDocument.mm()->cm.svn = int(vcg::tri::UpdateSelection<CMeshO>::VertexCount(meshDocument.mm()->cm));
524 									meshDocument.mm()->cm.sfn = int(vcg::tri::UpdateSelection<CMeshO>::FaceCount(meshDocument.mm()->cm));
525 								}
526 							}
527 							else
528 							{
529 								for (int ii = 0; ii < meshDocument.meshList.size(); ++ii)
530 								{
531 									MeshModel* mm = meshDocument.meshList[ii];
532 									MLRenderingData::PRIMITIVE_MODALITY pm = MLPoliciesStandAloneFunctions::bestPrimitiveModalityAccordingToMesh(mm);
533 									if ((pm != MLRenderingData::PR_ARITY) && (mm != NULL))
534 									{
535 										dt.set(pm, atts);
536 										cppfilt->glContext->initPerViewRenderingData(mm->id(), dt);
537 									}
538 
539 									if (mm != NULL)
540 									{
541 										mm->cm.svn = int(vcg::tri::UpdateSelection<CMeshO>::VertexCount(mm->cm));
542 										mm->cm.sfn = int(vcg::tri::UpdateSelection<CMeshO>::FaceCount(mm->cm));
543 									}
544 
545 								}
546 							}
547 						}
548 
549                         //WARNING!!!!!!!!!!!!
550                         /* IT SHOULD INVOKE executeFilter function. Unfortunately this function create a different thread for each invoked filter, and the MeshLab synchronization mechanisms are quite naive. Better to invoke the filters list in the same thread*/
551                         meshDocument.setBusy(true);
552                         ret = cppfilt->applyFilter( fname, meshDocument, envwrap, filterCallBack );
553                         meshDocument.setBusy(false);
554                         ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
555                         if (shared != NULL)
556 							delete cppfilt->glContext;
557 						delete wid;
558                     }
559                     else
560                         throw MLException("WARNING! The MeshLab Script System is able to manage just the C++ XML filters.");
561                 }
562                 catch (MLException& e)
563                 {
564                     meshDocument.Log.Log(GLLogStream::WARNING,e.what());
565                 }
566             }
567             QStringList logOutput;
568             log.print(logOutput);
569             foreach(QString logEntry, logOutput)
570                 fprintf(fp,"%s\n",qUtf8Printable(logEntry));
571             if(!ret)
572             {
573                 fprintf(fp,"Problem with filter: %s\n",qUtf8Printable(fname));
574                 return false;
575             }
576         }
577         return true;
578     }
579 
580 private:
581     PluginManager PM;
582     RichParameterSet defaultGlobal;
583     MLSceneGLSharedDataContext* shared;
584 };
585 
586 namespace commandline
587 {
588     const char inproject('p');
589     const char outproject('w');
590     const char overwrite('x');
591     const char inputmeshes('i');
592     const char outputmesh('o');
593     const char layer('l');
594     const char lastlayer('x');
595     const char currentlayer('c');
596     const char mask('m');
597     const char vertex('v');
598     const char face('f');
599     const char wedge('w');
600     const char mesh('m');
601     const char color('c');
602     const char flags('f');
603     const char normal('n');
604     const char quality('q');
605     const char radius('r');
606     const char polygon('p');
607     const char texture('t');
608     const char log('l');
609     const char dump('d');
610     const char script('s');
611     const char saveparam('s');
612     const char ascii('a');
613 
usage()614     void usage()
615     {
616         printf("MeshLabServer version: %s\n", qUtf8Printable(MeshLabApplication::appVer()));
617         QFile docum(":/meshlabserver.txt");
618         if (!docum.open(QIODevice::ReadOnly))
619         {
620             printf("MeshLabServer was not able to locate meshlabserver.txt file. The program will be closed\n");
621             exit(-1);
622         }
623         QString help(docum.readAll());
624         printf("\nUsage:\n%s",qUtf8Printable(help));
625         docum.close();
626     }
627 
optionValueExpression(const char cmdlineopt)628     QString optionValueExpression(const char cmdlineopt)
629     {
630         //Validate an option followed by spaces and a filepath
631         return QString ("-" + QString(cmdlineopt) + "\\s+(.+)");
632     }
633 
outputmeshExpression()634     QString outputmeshExpression()
635     {
636 		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) + ")");
637 		QString optionslist(options + "(\\s+" + options + ")*");
638 		QString savingmask("-" + QString(mask) + "\\s+" + optionslist);
639 		QString layernumber("\\d+");
640 		QString layertosave("-" + QString(layer) + "\\s+(" + layernumber + "|" + currentlayer + "|" + lastlayer + ")");
641 		return optionValueExpression(outputmesh) + "(\\s+(" + savingmask + "|" + layertosave + "\\s+" + savingmask + "|" + layertosave + "))*";
642     }
643 
validateCommandLine(const QString & str)644     bool validateCommandLine(const QString& str)
645     {
646         QString logstring("(" + optionValueExpression(log) + "\\s+" +  optionValueExpression(dump) + "|" + optionValueExpression(dump) + "\\s+" +  optionValueExpression(log) + "|" +  optionValueExpression(dump) + "|" + optionValueExpression(log) + ")");
647         QString arg("(" + optionValueExpression(inproject) + "|" + optionValueExpression(inputmeshes) + "|" + optionValueExpression(outproject) + "(\\s+-" + overwrite + ")?" + "|" + optionValueExpression(script) + "|" + outputmeshExpression() + ")");
648         QString args("(" + arg + ")(\\s+" + arg + ")*");
649         QString completecommandline("(" + logstring + "|" + logstring + "\\s+" + args + "|" + args + ")");
650         QRegExp completecommandlineexp(completecommandline);
651         //completecommandlineexp.setMinimal(true);
652 
653         bool valid = completecommandlineexp.isValid();
654         if (!valid)
655             return false;
656         completecommandlineexp.indexIn(str);
657         QString rr = completecommandlineexp.cap();
658         return (completecommandlineexp.matchedLength() == str.size());
659     }
660 
661 }
662 
663 struct OutFileMesh
664 {
OutFileMeshOutFileMesh665     OutFileMesh() : writebinary(true) {}
666     QString filename;
667     int mask;
668     bool writebinary;
669 	/*WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
670 	/* We need these two constant values because when we parse the command line we don't know
671    * yet how many layers will have the current document and which will be the current one.
672    * Opening a project and/or importing a file happens after the parsing of the commandline
673    * is completed */
674 	static const int lastlayerconst = -2;
675 	static const int currentlayerconst = -1;
676 	/******************************************************************************************/
677 
678 	// Possible values can be:
679 	//	- lastlayerconst #the last layer of a document, DEFAULT value
680 	//	- currentlayerconst #the current layer of a document, sometimes it's different from the last layer of a document
681 	//	- a number between [0,inf) #identifying the correspondent layer position
682   // WARNING!!!!! Please note that the layer position is DIFFERENT from the layer id
683 	int layerposition;
684 };
685 
686 struct OutProject
687 {
688     QString filename;
689     bool overwrite;
690 };
691 
main(int argc,char * argv[])692 int main(int argc, char *argv[])
693 {
694     GLExtensionsManager::init();
695     FILE* logfp = stdout;
696     FILE* dumpfp = NULL;
697     MeshLabApplication app(argc, argv);
698     QStringList st = app.arguments();
699     std::setlocale(LC_ALL, "C");
700     QLocale::setDefault(QLocale::C);
701     if(argc == 1)
702     {
703         commandline::usage();
704         //system("pause");
705         exit(-1);
706     }
707     QStringList scriptfiles;
708     QList<OutFileMesh> outmeshlist;
709     QList<OutProject> outprojectfiles;
710 
711     QString cmdline;
712     for (int ii = 1; ii < argc; ++ii)
713     {
714         QString argum(argv[ii]);
715         argum = argum.trimmed();
716         if (argum.contains(' '))
717             argum = "\"" + argum + "\"";
718         cmdline = cmdline + argum + " ";
719     }
720     if (!commandline::validateCommandLine(cmdline.trimmed()))
721     {
722         printf("CommandLine Syntax Error: please refer to the following documentation for a complete list of the MeshLabServer parameters.\n");
723         commandline::usage();
724         //system("pause");
725         exit(-1);
726     }
727 
728 	QSettings settings(MeshLabApplication::organization(),MeshLabApplication::appArchitecturalName(MeshLabApplication::HW_64BIT));
729 
730 	QVariant xmlgpupar = settings.value("MeshLab::System::maxGPUMemDedicatedToGeometry");
731 
732 	QDomDocument doc;
733 	doc.setContent(xmlgpupar.toString(), false);
734 
735 	QDomElement paramelem = doc.firstChild().toElement();
736 	int gpumemmb = paramelem.attribute("value").toInt();
737 
738 	std::ptrdiff_t maxgpumem = (std::ptrdiff_t) gpumemmb * (float)(1024 * 1024);
739 	vcg::QtThreadSafeMemoryInfo gpumeminfo(maxgpumem);
740 
741     QGLFormat fmt = QGLFormat::defaultFormat();
742     fmt.setAlphaBufferSize(8);
743     QGLFormat::setDefaultFormat(fmt);
744 
745 	MeshDocument meshDocument;
746 
747 	MLSceneGLSharedDataContext shared(meshDocument, gpumeminfo, MeshLabScalarTest<MESHLAB_SCALAR>::doublePrecision(), 100000,100000);
748 	shared.makeCurrent();
749 	if (!GLExtensionsManager::initializeGLextensions_notThrowing())
750 	{
751 		printf("GLEW Init: failed!\n");
752 		//system("pause");
753 		exit(-1);
754 	}
755 	shared.doneCurrent();
756     printf("Loading Plugins:\n");
757 	MeshLabServer server(&shared);
758     server.loadPlugins();
759 
760     bool writebinary = true;
761     int i = 1;
762     while(i < argc)
763     {
764         QString tmp = argv[i];
765         switch(argv[i][1])
766         {
767         case commandline::inproject :
768             {
769                 if (((i+1) < argc) && (argv[i+1][0] != '-'))
770                 {
771                     QFileInfo finfo(argv[i+1]);
772                     QString inputproject = finfo.absoluteFilePath();
773                     if (finfo.completeSuffix().toLower() != "mlp")
774                     {
775                         fprintf(logfp,"Project %s is not a valid \'mlp\' file format. MeshLabServer application will exit.\n",qUtf8Printable(inputproject));
776 						//system("pause");
777                         exit(-1);
778                     }
779                     bool opened = server.openProject(meshDocument,inputproject);
780                     if (!opened)
781                     {
782                         fprintf(logfp,"MeshLab Project %s has not been correctly opened. MeshLabServer application will exit.\n",qUtf8Printable(inputproject));
783 						//system("pause");
784                         exit(-1);
785                     }
786                     else
787                         fprintf(logfp,"MeshLab Project %s has been loaded.\n",qUtf8Printable(inputproject));
788                     ++i;
789                 }
790                 else
791                 {
792                     fprintf(logfp,"Missing project name. MeshLabServer application will exit.\n");
793 					//system("pause");
794                     exit(-1);
795                 }
796                 ++i;
797                 break;
798             }
799         case commandline::outproject :
800             {
801                 if (((i+1) < argc) && (argv[i+1][0] != '-'))
802                 {
803                     QFileInfo finfo(argv[i+1]);
804                     OutProject pr;
805                     pr.overwrite = false;
806                     pr.filename = finfo.absoluteFilePath();
807                     if (finfo.completeSuffix().toLower() != "mlp")
808                     {
809                         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"));
810                         pr.filename += ".mlp";
811                     }
812                     ++i;
813 					QString overtmp('-');
814 					overtmp += commandline::overwrite;
815                     if (((i + 1) < argc) && (QString(argv[i+1]) == overtmp))
816                     {
817                         pr.overwrite = true;
818                         ++i;
819                     }
820                     outprojectfiles << pr;
821                 }
822                 ++i;
823                 break;
824             }
825         case commandline::inputmeshes :
826             {
827                 while( ((i+1) < argc) && argv[i+1][0] != '-')
828                 {
829                     QFileInfo info(argv[i+1]);
830                     //now add it to the document
831                     MeshModel* mmod = meshDocument.addNewMesh(info.absoluteFilePath(),"");
832                     if (mmod == NULL)
833                     {
834                         fprintf(logfp,"It was not possible to add new mesh %s to MeshLabServer. The program will exit\n",qUtf8Printable(info.absoluteFilePath()));
835 						//system("pause");
836                         exit(-1);
837                     }
838                     bool opened = server.importMesh(*mmod, info.absoluteFilePath(),logfp);
839                     if (!opened)
840                     {
841                         fprintf(logfp,"It was not possible to import mesh %s into MeshLabServer. The program will exit\n ",qUtf8Printable(info.absoluteFilePath()));
842 						//system("pause");
843                         exit(-1);
844                     }
845                     fprintf(logfp,"Mesh %s loaded has %i vn %i fn\n", qUtf8Printable(info.absoluteFilePath()), mmod->cm.vn, mmod->cm.fn);
846                     i++;
847                 }
848                 i++;
849                 break;
850             }
851         case commandline::outputmesh :
852             {
853                 QString fileout;
854                 int mask = 0;
855                 OutFileMesh outfl;
856                 if( ((i+1) < argc) && argv[i+1][0] != '-')
857                 {
858                     QFileInfo info(argv[i+1]);
859                     outfl.filename = info.absoluteFilePath();
860 
861 					/*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*/
862 					outfl.layerposition = OutFileMesh::currentlayerconst;
863 
864                     fprintf(logfp,"output mesh  %s\n", qUtf8Printable(outfl.filename));
865                     i++;
866                 }
867 
868 				if (((i + 1) < argc) && (QString(argv[i + 1]) == (QString("-") + commandline::layer)))
869 				{
870 					i = i + 2;
871 					if (argv[i][0] == commandline::lastlayer)
872 						outfl.layerposition = OutFileMesh::lastlayerconst;
873 					else
874 					{
875 						if (argv[i][0] == commandline::currentlayer)
876 							outfl.layerposition = OutFileMesh::currentlayerconst;
877 						else
878 							outfl.layerposition = QString(argv[i]).toInt();
879 					}
880 				}
881 
882                 if (((i + 1) < argc) && (QString(argv[i+1]) == (QString("-") + commandline::mask)))
883                 {
884                     i = i + 2;
885                     do
886                     {
887                         switch (argv[i][0])
888                         {
889                         case commandline::vertex :
890                             {
891                                 switch (argv[i][1])
892                                 {
893                                 case commandline::color : i++; fprintf(logfp,"vertex color, "     ); mask |= vcg::tri::io::Mask::IOM_VERTCOLOR;    break;
894                                 case commandline::flags : i++; fprintf(logfp,"vertex flags, "     ); mask |= vcg::tri::io::Mask::IOM_VERTFLAGS;    break;
895                                 case commandline::normal : i++; fprintf(logfp,"vertex normals, "   ); mask |= vcg::tri::io::Mask::IOM_VERTNORMAL;   break;
896                                 case commandline::quality : i++; fprintf(logfp,"vertex quality, "   ); mask |= vcg::tri::io::Mask::IOM_VERTQUALITY;  break;
897                                 case commandline::radius : i++; fprintf(logfp,"vertex radii, "   ); mask |= vcg::tri::io::Mask::IOM_VERTRADIUS;  break;
898                                 case commandline::texture : i++; fprintf(logfp,"vertex tex coords, "); mask |= vcg::tri::io::Mask::IOM_VERTTEXCOORD; break;
899                                 default :  i++; fprintf(logfp,"WARNING: unknowns per VERTEX attribute '%s'",argv[i+1]);break;
900                                 }
901                                 break;
902                             }
903                         case commandline::face :
904                             {
905                                 switch (argv[i][1])
906                                 {
907                                 case commandline::color : i++; fprintf(logfp,"face color, "  ); mask |= vcg::tri::io::Mask::IOM_FACECOLOR;   break;
908                                 case commandline::flags : i++; fprintf(logfp,"face flags, "  ); mask |= vcg::tri::io::Mask::IOM_FACEFLAGS;   break;
909                                 case commandline::normal : i++; fprintf(logfp,"face normals, "); mask |= vcg::tri::io::Mask::IOM_FACENORMAL;  break;
910                                 case commandline::quality : i++; fprintf(logfp,"face quality, "); mask |= vcg::tri::io::Mask::IOM_FACEQUALITY; break;
911                                 default :  i++; fprintf(logfp,"WARNING: unknowns per FACE attribute '%s'",argv[i+1]);break;
912                                 }
913                                 break;
914                             }
915                         case commandline::wedge :
916                             {
917                                 switch (argv[i][1])
918                                 {
919                                 case commandline::color : i++; fprintf(logfp,"wedge color, "     ); mask |= vcg::tri::io::Mask::IOM_WEDGCOLOR;   break;
920                                 case commandline::normal : i++; fprintf(logfp,"wedge normals, "   ); mask |= vcg::tri::io::Mask::IOM_WEDGNORMAL;  break;
921                                 case commandline::texture : i++; fprintf(logfp,"wedge tex coords, "); mask |= vcg::tri::io::Mask::IOM_WEDGTEXCOORD;break;
922                                 default :  i++; fprintf(logfp,"WARNING: unknowns per WEDGE attribute '%s'",argv[i+1]);break;
923                                 }
924                                 break;
925                             }
926 
927                         case commandline::saveparam :
928                              {
929                                 switch( argv[i][1])
930                                 {
931                                     case commandline::ascii:
932                                         {
933                                             writebinary = false;
934                                             i++;
935                                             break;
936                                          }
937                                 }
938                                 break;
939                              }
940 						case commandline::mesh :
941 							{
942 								switch (argv[i][1])
943 								{
944 								case commandline::polygon: i++; fprintf(logfp, "mesh polygon, "); mask |= vcg::tri::io::Mask::IOM_BITPOLYGONAL;   break;
945 								default:  i++; fprintf(logfp, "WARNING: unknowns per MESH attribute '%s'", argv[i + 1]); break;
946 								}
947 								break;
948 							}
949                         default :  i++; fprintf(logfp,"WARNING: unknowns attribute '%s'",argv[i]);break;
950                         }
951                     }while (((i) < argc) && (argv[i][0] != '-'));
952                 }
953                 else
954                     ++i;
955                 outfl.mask = mask;
956                 outfl.writebinary = writebinary;
957                 outmeshlist << outfl;
958                 break;
959             }
960         case commandline::script :
961             {
962                 QFileInfo fi(argv[i+1]);
963                 QString scriptName = fi.absoluteFilePath();
964                 scriptfiles << scriptName;
965                 i += 2;
966                 break;
967             }
968         case commandline::log :
969             {
970                 //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...)
971                 logfp = fopen(argv[i+1],"a");
972                 if (logfp == NULL)
973                     printf("Error occurred opening file %s. It's not possible to redirect the output from the stdout\n",argv[i+1]);
974                 else
975                     printf("Log is saved in %s\n", argv[i+1]);
976                 i += 2;
977                 break;
978             }
979         case commandline::dump :
980             {
981                 dumpfp = fopen(argv[i+1],"w");
982                 if (dumpfp == NULL)
983                     fprintf(logfp,"Error occurred opening file %s. It's not possible to redirect the output from the stdout\n",argv[i+1]);
984                 else
985                 {
986                     server.dumpPluginInfoDoxygen(dumpfp);
987                     fclose(dumpfp);
988                     fprintf(logfp,"Dump file is saved in %s\n", argv[i+1]);
989                 }
990                 i+=2;
991                 break;
992             }
993         default:
994             {
995                 printf("Something bad happened parsing the document. String %s\n",qUtf8Printable(argv[i]));
996 				//system("pause");
997                 exit(-1);
998             }
999         }
1000     }
1001 
1002     for(int ii = 0; ii < scriptfiles.size();++ii)
1003     {
1004         fprintf(logfp,"Apply FilterScript: '%s'\n",qUtf8Printable(scriptfiles[ii]));
1005         bool returnValue = server.script(meshDocument, scriptfiles[ii],logfp);
1006         if(!returnValue)
1007         {
1008             fprintf(logfp,"Failed to apply script file %s\n",qUtf8Printable(scriptfiles[ii]));
1009 			//system("pause");
1010             exit(-1);
1011         }
1012     }
1013 
1014     for(int ii = 0;ii < outprojectfiles.size();++ii)
1015     {
1016         QString outfilemiddlename = "";
1017         if (!outprojectfiles[ii].overwrite)
1018         {
1019             outfilemiddlename = "_out";
1020             if (ii >= 1)
1021                 outfilemiddlename += QString::number(ii);
1022         }
1023         bool saved = server.saveProject(meshDocument,outprojectfiles[ii].filename,outfilemiddlename);
1024         if (saved)
1025             fprintf(logfp,"Output project has been saved in %s.\n",qUtf8Printable(outprojectfiles[ii].filename));
1026         else
1027         {
1028             fprintf(logfp,"Project %s has not been correctly saved in. MeshLabServer Application will exit.\n",qUtf8Printable(outprojectfiles[ii].filename));
1029 			//system("pause");
1030             exit(-1);
1031         }
1032     }
1033 
1034 	if (meshDocument.size() < outmeshlist.size())
1035 		fprintf(logfp, "Error: trying to save %i meshes, but only %i available in the project\n", outmeshlist.size(), meshDocument.size());
1036 
1037 	for (int ii = 0; ii < outmeshlist.size(); ++ii)
1038 	{
1039 		bool exported = false;
1040 		if (outmeshlist[ii].layerposition < meshDocument.meshList.size())
1041 		{
1042 			int layertobesaved = outmeshlist[ii].layerposition;
1043 
1044 			if (layertobesaved == OutFileMesh::lastlayerconst)
1045 				layertobesaved = meshDocument.meshList.size() - 1;
1046 			else
1047 				if (layertobesaved == OutFileMesh::currentlayerconst)
1048 					layertobesaved = meshDocument.meshList.indexOf(meshDocument.mm());
1049 
1050 			if ((layertobesaved >= 0) && (layertobesaved < meshDocument.meshList.size()))
1051 			{
1052 				MeshModel* meshmod = meshDocument.meshList[layertobesaved];
1053 				if (meshmod != NULL)
1054 					exported = server.exportMesh(meshDocument.meshList[layertobesaved], outmeshlist[ii].mask, outmeshlist[ii].filename, outmeshlist[ii].writebinary, logfp);
1055 				if (exported)
1056 					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);
1057 				else
1058 					fprintf(logfp, "Output mesh %s has NOT been saved\n", qUtf8Printable(outmeshlist[ii].filename));
1059 			}
1060 			else
1061 				fprintf(logfp, "Output mesh %s has NOT been saved. A not existent layer has been requested to be saved\n", qUtf8Printable(outmeshlist[ii].filename));
1062 		}
1063 		else
1064 			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));
1065 	}//for(int ii
1066 
1067 
1068 	if((logfp != NULL) && (logfp != stdout))
1069 	{
1070 		fclose(logfp);
1071 	}
1072 
1073 	shared.deAllocateGPUSharedData();
1074 	//system("pause");
1075 	return 0;
1076 }//int main()
1077 
1078