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 ¶meterSet = 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