mirror of
https://github.com/lucaspalomodevelop/meshlab.git
synced 2026-03-16 09:34:36 +00:00
529 lines
16 KiB
C++
529 lines
16 KiB
C++
/****************************************************************************
|
|
* 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 <QApplication>
|
|
#include "../meshlab/meshmodel.h"
|
|
#include "../meshlab/interfaces.h"
|
|
#include "../meshlab/filterscript.h"
|
|
#include "../meshlab/plugin_support.h"
|
|
#include <vcg/complex/trimesh/update/bounding.h>
|
|
|
|
class FilterData
|
|
{
|
|
public:
|
|
FilterData();
|
|
QString name;
|
|
QString info;
|
|
int filterClass;
|
|
bool operator <(const FilterData &d) const {return name<d.name;}
|
|
};
|
|
|
|
class FilterOrganizer
|
|
{
|
|
|
|
};
|
|
|
|
QMap<QString, QAction *> filterMap; // a map to retrieve an action from a name. Used for playing filter scripts.
|
|
std::vector<MeshIOInterface*> meshIOPlugins;
|
|
|
|
// Here we need a better way to find the plugins directory.
|
|
// To be implemented:
|
|
// use the QSettings togheter with MeshLab.
|
|
// When meshlab starts if he find the plugins write the absolute path of that directory in a persistent qsetting place.
|
|
// Here we use that QSetting. If it is not set we remember to run meshlab first once.
|
|
// 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).
|
|
|
|
void loadPlugins(FILE *fp=0)
|
|
{
|
|
QDir pluginsDir = QDir(qApp->applicationDirPath());
|
|
#if defined(Q_OS_WIN)
|
|
if (pluginsDir.dirName() == "debug" || pluginsDir.dirName() == "release")
|
|
pluginsDir.cdUp();
|
|
#elif defined(Q_OS_MAC)
|
|
if (pluginsDir.dirName() == "MacOS") {
|
|
pluginsDir.cdUp();
|
|
pluginsDir.cdUp();
|
|
pluginsDir.cdUp();
|
|
}
|
|
int cnt=0;
|
|
while(++cnt <4 && !pluginsDir.exists("../meshlab/plugins") )
|
|
pluginsDir.cdUp();
|
|
#endif
|
|
pluginsDir.cd("../meshlab");
|
|
if(!pluginsDir.exists("plugins"))
|
|
{
|
|
printf( "Error!\n We are not able to find the plugins directory\n"
|
|
"We searched in '%s'\n\n", qPrintable(pluginsDir.path()));
|
|
}
|
|
pluginsDir.cd("plugins");
|
|
|
|
foreach (QString fileName, pluginsDir.entryList(QDir::Files)) {
|
|
QPluginLoader loader(pluginsDir.absoluteFilePath(fileName));
|
|
QObject *plugin = loader.instance();
|
|
|
|
if (plugin) {
|
|
MeshFilterInterface *iFilter = qobject_cast<MeshFilterInterface *>(plugin);
|
|
if (iFilter){
|
|
QAction *filterAction;
|
|
int oldSize = filterMap.size();
|
|
foreach(filterAction, iFilter->actions())
|
|
{
|
|
filterMap[filterAction->text()]=filterAction;
|
|
if(fp) fprintf(fp, "*<b><i>%s</i></b> <br>%s\n",qPrintable(filterAction->text()), qPrintable(iFilter->filterInfo(filterAction)));
|
|
}
|
|
//printf("Loaded %i filtering actions form %s\n", filterMap.size() - oldSize, qPrintable(fileName));
|
|
}
|
|
MeshIOInterface *iIO = qobject_cast<MeshIOInterface *>(plugin);
|
|
if (iIO) meshIOPlugins.push_back(iIO);
|
|
}
|
|
}
|
|
printf("Total %i filtering actions\n", filterMap.size());
|
|
printf("Total %i io plugins\n", meshIOPlugins.size());
|
|
}
|
|
|
|
bool Open(MeshModel &mm, QString fileName)
|
|
{
|
|
// Opening files in a transparent form (IO plugins contribution is hidden to user)
|
|
QStringList filters;
|
|
|
|
// HashTable storing all supported formats togheter with
|
|
// the (1-based) index of first plugin which is able to open it
|
|
QHash<QString, int> allKnownFormats;
|
|
|
|
LoadKnownFilters(meshIOPlugins, filters, allKnownFormats,IMPORT);
|
|
|
|
QFileInfo fi(fileName);
|
|
QDir curdir= QDir::current();
|
|
// this change of dir is needed for subsequent textures/materials loading
|
|
//QDir::setCurrent(fi.absoluteDir().absolutePath());
|
|
|
|
QString extension = fi.suffix();
|
|
qDebug("Opening a file with extention %s",qPrintable(extension));
|
|
// retrieving corresponding IO plugin
|
|
int idx = allKnownFormats[extension.toLower()];
|
|
if (idx == 0)
|
|
{
|
|
printf("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));
|
|
return false;
|
|
}
|
|
MeshIOInterface* pCurrentIOPlugin = meshIOPlugins[idx-1];
|
|
|
|
int mask = 0;
|
|
|
|
FilterParameterSet prePar;
|
|
pCurrentIOPlugin->initPreOpenParameter(extension, fileName,prePar);
|
|
|
|
if (!pCurrentIOPlugin->open(extension, fileName, mm ,mask,prePar))
|
|
{
|
|
printf("MeshLabServer: Failed loading of %s from dir %s\n",qPrintable(fileName),qPrintable(curdir.path()));
|
|
return false;
|
|
}
|
|
vcg::tri::UpdateBounding<CMeshO>::Box(mm.cm);
|
|
QDir::setCurrent(curdir.path());
|
|
return true;
|
|
}
|
|
|
|
bool Save(MeshModel *mm, int mask, QString fileName)
|
|
{
|
|
// Opening files in a transparent form (IO plugins contribution is hidden to user)
|
|
QStringList filters;
|
|
|
|
// HashTable storing all supported formats togheter with
|
|
// the (1-based) index of first plugin which is able to open it
|
|
QHash<QString, int> allKnownFormats;
|
|
|
|
LoadKnownFilters(meshIOPlugins, filters, allKnownFormats,IMPORT);
|
|
|
|
QFileInfo fi(fileName);
|
|
// this change of dir is needed for subsequent textures/materials loading
|
|
// QDir::setCurrent(fi.absoluteDir().absolutePath());
|
|
|
|
QString extension = fi.suffix();
|
|
|
|
// retrieving corresponding IO plugin
|
|
int idx = allKnownFormats[extension.toLower()];
|
|
if (idx == 0)
|
|
{
|
|
printf("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));
|
|
return false;
|
|
}
|
|
MeshIOInterface* pCurrentIOPlugin = meshIOPlugins[idx-1];
|
|
|
|
// optional saving parameters (like ascii/binary encoding)
|
|
FilterParameterSet savePar;
|
|
pCurrentIOPlugin->initSaveParameter(extension, *mm, savePar);
|
|
|
|
if (!pCurrentIOPlugin->save(extension, fileName, *mm ,mask, savePar))
|
|
{
|
|
printf("Failed saving\n");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool Script(MeshDocument &meshDocument, QString scriptfile){
|
|
|
|
MeshModel &mm = *meshDocument.mm();
|
|
|
|
FilterScript scriptPtr;
|
|
|
|
//Open/Load FilterScript
|
|
|
|
if (scriptfile.isEmpty())
|
|
{
|
|
printf("No script specified\n");
|
|
return false;
|
|
}
|
|
scriptPtr.open(scriptfile);
|
|
printf("Starting Script of %i actions",scriptPtr.actionList.size());
|
|
FilterScript::iterator ii;
|
|
for(ii = scriptPtr.actionList.begin();ii!= scriptPtr.actionList.end();++ii){
|
|
FilterParameterSet &par = (*ii).second;
|
|
QString &name = (*ii).first;
|
|
printf("filter: %s\n",qPrintable((*ii).first));
|
|
|
|
QAction *action = filterMap[ (*ii).first];
|
|
MeshFilterInterface *iFilter = qobject_cast<MeshFilterInterface *>(action->parent());
|
|
iFilter->setLog(NULL);
|
|
int req = iFilter->getRequirements(action);
|
|
mm.updateDataMask(req);
|
|
|
|
|
|
|
|
//make sure the PARMESH parameters are initialized
|
|
FilterParameterSet ¶meterSet = (*ii).second;
|
|
for(int i = 0; i < parameterSet.paramList.size(); i++)
|
|
{
|
|
//get a modifieable reference
|
|
FilterParameter ¶meter = parameterSet.paramList[i];
|
|
|
|
//if this is a mesh paramter and the index is valid
|
|
if(parameter.fieldType == FilterParameter::PARMESH)
|
|
{
|
|
if( parameter.fieldVal.toInt() < meshDocument.size() &&
|
|
parameter.fieldVal.toInt() >= 0 )
|
|
{
|
|
parameter.pointerVal = meshDocument.getMesh(parameter.fieldVal.toInt());
|
|
} else
|
|
{
|
|
printf("Meshes loaded: %i, meshes asked for: %i \n", meshDocument.size(), parameter.fieldVal.toInt() );
|
|
printf("One of the filters in the script needs more meshes than you have loaded.\n");
|
|
exit(-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ret = iFilter->applyFilter( action, meshDocument, (*ii).second, NULL);
|
|
//iFilter->applyFilter( action, mm, (*ii).second, QCallBack );
|
|
//GLA()->log.Logf(GLLogStream::WARNING,"Re-Applied filter %s",qPrintable((*ii).first));
|
|
|
|
if(!ret)
|
|
{
|
|
printf("Problem with filter: %s\n",qPrintable((*ii).first));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void Usage()
|
|
{
|
|
printf("\nUsage:\n"
|
|
" meshlabserver arg1 arg2 ... \n"
|
|
"where args can be: \n"
|
|
" -i [filename...] mesh(s) that has to be loaded\n"
|
|
" -o [filename...] mesh(s) where to write the result(s)\n"
|
|
" -s filename script to be applied\n"
|
|
" -d filename dump on a text file a list of all the filtering fucntion\n"
|
|
" -om options data to save in the output files: vc -> vertex colors, vf -> vertex flags, vq -> vertex quality, vn-> vertex normals, vt -> vertex texture coords, "
|
|
" fc -> face colors, ff -> face flags, fq -> face quality, fn-> face normals, "
|
|
" wc -> wedge colors, wn-> wedge normals, wt -> wedge texture coords \n"
|
|
"Example:\n"
|
|
" 'meshlabserver -i input.obj -o output.ply -s meshclean.mlx -om vc fq wn'\n"
|
|
"\nNotes:\n\n"
|
|
"There can be multiple meshes loaded and the order they are listed matters because \n"
|
|
"filters that use meshes as parameters choose the mesh based on the order.\n"
|
|
"The number of output meshes must be either one or equal to the number of input meshes.\n"
|
|
"If the number of output meshes is one then only the first mesh in the input list is saved.\n"
|
|
"The format of the output mesh is guessed by the used extension.\n"
|
|
"Script is optional and must be in the format saved by MeshLab.\n"
|
|
);
|
|
|
|
exit(-1);
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
QApplication app(argc, argv);
|
|
MeshDocument meshDocument;
|
|
QStringList meshNamesIn, meshNamesOut;
|
|
QString scriptName;
|
|
FILE *filterFP=0;
|
|
int mask=0;
|
|
if(argc < 3) Usage();
|
|
int i = 1;
|
|
while(i < argc)
|
|
{
|
|
if(argv[i][0] != '-') Usage();
|
|
switch(argv[i][1])
|
|
{
|
|
case 'i' :
|
|
while( ((i+1) < argc) && argv[i+1][0] != '-')
|
|
{
|
|
meshNamesIn << argv[i+1];
|
|
printf("Input mesh %s\n", qPrintable(meshNamesIn.last() ));
|
|
i++;
|
|
}
|
|
i++;
|
|
break;
|
|
case 'o' :
|
|
{
|
|
if (argv[i][2]==0)
|
|
{
|
|
while( ((i+1) < argc) && argv[i+1][0] != '-')
|
|
{
|
|
meshNamesOut << argv[i+1];
|
|
printf("output mesh %s\n", qPrintable(meshNamesOut.last()));
|
|
i++;
|
|
}
|
|
i++;
|
|
break;
|
|
}
|
|
else if (argv[i][2]=='m')
|
|
{
|
|
printf("Output mask:\n");
|
|
while( ((i+1) < argc) && argv[i+1][0] != '-')
|
|
{
|
|
switch (argv[i+1][0])
|
|
{
|
|
case 'v' :
|
|
{
|
|
switch (argv[i+1][1])
|
|
{
|
|
case 'c' :
|
|
{
|
|
printf("vertex color, ");
|
|
mask |= vcg::tri::io::Mask::IOM_VERTCOLOR;
|
|
i++;
|
|
break;
|
|
}
|
|
case 'f' :
|
|
{
|
|
printf("vertex flags, ");
|
|
mask |= vcg::tri::io::Mask::IOM_VERTFLAGS;
|
|
i++;
|
|
break;
|
|
}
|
|
case 'n' :
|
|
{
|
|
printf("vertex normals, ");
|
|
mask |= vcg::tri::io::Mask::IOM_VERTNORMAL;
|
|
i++;
|
|
break;
|
|
}
|
|
case 'q' :
|
|
{
|
|
printf("vertex quality, ");
|
|
mask |= vcg::tri::io::Mask::IOM_VERTQUALITY;
|
|
i++;
|
|
break;
|
|
}
|
|
case 't' :
|
|
{
|
|
printf("vertex tex coords, ");
|
|
mask |= vcg::tri::io::Mask::IOM_VERTTEXCOORD;
|
|
i++;
|
|
break;
|
|
}
|
|
default :
|
|
{
|
|
i++;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 'f' :
|
|
{
|
|
switch (argv[i+1][1])
|
|
{
|
|
case 'c' :
|
|
{
|
|
printf("face color, ");
|
|
mask |= vcg::tri::io::Mask::IOM_FACECOLOR;
|
|
i++;
|
|
break;
|
|
}
|
|
case 'f' :
|
|
{
|
|
printf("face flags, ");
|
|
mask |= vcg::tri::io::Mask::IOM_FACEFLAGS;
|
|
i++;
|
|
break;
|
|
}
|
|
case 'n' :
|
|
{
|
|
printf("face normals, ");
|
|
mask |= vcg::tri::io::Mask::IOM_FACENORMAL;
|
|
i++;
|
|
break;
|
|
}
|
|
case 'q' :
|
|
{
|
|
printf("face quality, ");
|
|
mask |= vcg::tri::io::Mask::IOM_FACEQUALITY;
|
|
i++;
|
|
break;
|
|
}
|
|
default :
|
|
{
|
|
i++;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 'w' :
|
|
{
|
|
switch (argv[i+1][1])
|
|
{
|
|
case 'c' :
|
|
{
|
|
printf("wedge color, ");
|
|
mask |= vcg::tri::io::Mask::IOM_WEDGCOLOR;
|
|
i++;
|
|
break;
|
|
}
|
|
case 'n' :
|
|
{
|
|
printf("wedge normals, ");
|
|
mask |= vcg::tri::io::Mask::IOM_WEDGNORMAL;
|
|
i++;
|
|
break;
|
|
}
|
|
case 't' :
|
|
{
|
|
printf("wedge tex coords, ");
|
|
mask |= vcg::tri::io::Mask::IOM_WEDGTEXCOORD;
|
|
i++;
|
|
break;
|
|
}
|
|
default :
|
|
{
|
|
i++;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
i++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
i++;
|
|
break;
|
|
}
|
|
|
|
}
|
|
case 's' :
|
|
if( argc <= i+1 ) {
|
|
printf("Missing script name\n");
|
|
exit(-1);
|
|
}
|
|
scriptName = argv[i+1];
|
|
printf("script %s\n", qPrintable(scriptName));
|
|
i += 2;
|
|
break;
|
|
case 'd' :
|
|
if( argc <= i+1 ) {
|
|
printf("Missing dump name\n");
|
|
exit(-1);
|
|
}
|
|
filterFP=fopen(argv[i+1],"w");
|
|
i+=2;
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
printf("Loading Plugins:\n");
|
|
loadPlugins(filterFP);
|
|
|
|
|
|
if(meshNamesIn.isEmpty()) {
|
|
printf("No input mesh\n"); exit(-1);
|
|
} else
|
|
{
|
|
for(int i = 0; i < meshNamesIn.size(); i++)
|
|
{
|
|
MeshModel *mm = new MeshModel( meshNamesIn.at(i).toStdString().c_str() );
|
|
Open(*mm, meshNamesIn.at(i));
|
|
printf("Mesh %s loaded has %i vn %i fn\n", mm->fileName.c_str(), mm->cm.vn, mm->cm.fn);
|
|
|
|
//now add it to the document
|
|
meshDocument.addNewMesh(mm->fileName.c_str(), mm);
|
|
}
|
|
//the first mesh is the one the script is applied to
|
|
meshDocument.setCurrentMesh(0);
|
|
}
|
|
|
|
if(!scriptName.isEmpty())
|
|
{
|
|
printf("Apply FilterScript: '%s'\n",qPrintable(scriptName));
|
|
bool returnValue = Script(meshDocument, scriptName);
|
|
if(!returnValue)
|
|
{
|
|
printf("Failed to apply FilterScript\n");
|
|
exit(-1);
|
|
}
|
|
} else
|
|
printf("No Script to apply.");
|
|
|
|
//if there is not one name or an equal number of in and out names then exit
|
|
if(meshNamesOut.isEmpty() )
|
|
printf("No output mesh names given.");
|
|
else if(meshNamesOut.size() != 1 && meshNamesOut.size() != meshNamesIn.size() ) {
|
|
printf("Wrong number of output mesh names given\n");
|
|
exit(-1);
|
|
} else
|
|
{
|
|
for(int i = 0; i < meshNamesOut.size(); i++)
|
|
{
|
|
Save(meshDocument.getMesh(i), mask, meshNamesOut.at(i));
|
|
printf("Mesh %s saved with: %i vn %i fn\n", qPrintable(meshNamesOut.at(i)), meshDocument.mm()->cm.vn, meshDocument.mm()->cm.fn);
|
|
}
|
|
}
|
|
}
|
|
|