From 9303b10b5421db8cfd039152e79f2ac2e56827d0 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Mon, 19 Apr 2021 13:53:49 +0200 Subject: [PATCH 1/9] import mesh using common utility function --- src/common/CMakeLists.txt | 58 ++++---- src/common/utilities/load_save.cpp | 222 +++++++++++++++++++++++++++++ src/common/utilities/load_save.h | 45 ++++++ src/meshlab/mainwindow_RunTime.cpp | 58 +++----- 4 files changed, 320 insertions(+), 63 deletions(-) create mode 100644 src/common/utilities/load_save.cpp create mode 100644 src/common/utilities/load_save.h diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index b8251cdb3..b1f377488 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -42,22 +42,6 @@ else() endif() set(HEADERS - globals.h - plugins/containers/generic_container_iterator.h - plugins/containers/decorate_plugin_container.h - plugins/containers/edit_plugin_container.h - plugins/containers/filter_plugin_container.h - plugins/containers/io_plugin_container.h - plugins/containers/render_plugin_container.h - plugins/interfaces/meshlab_plugin.h - plugins/interfaces/meshlab_plugin_logger.h - plugins/interfaces/decorate_plugin.h - plugins/interfaces/edit_plugin.h - plugins/interfaces/filter_plugin.h - plugins/interfaces/io_plugin.h - plugins/interfaces/render_plugin.h - plugins/meshlab_plugin_type.h - plugins/plugin_manager.h ml_document/helpers/mesh_document_state_data.h ml_document/helpers/mesh_model_state_data.h ml_document/base_types.h @@ -73,11 +57,28 @@ set(HEADERS parameters/rich_parameter.h parameters/rich_parameter_list.h parameters/value.h + plugins/containers/generic_container_iterator.h + plugins/containers/decorate_plugin_container.h + plugins/containers/edit_plugin_container.h + plugins/containers/filter_plugin_container.h + plugins/containers/io_plugin_container.h + plugins/containers/render_plugin_container.h + plugins/interfaces/meshlab_plugin.h + plugins/interfaces/meshlab_plugin_logger.h + plugins/interfaces/decorate_plugin.h + plugins/interfaces/edit_plugin.h + plugins/interfaces/filter_plugin.h + plugins/interfaces/io_plugin.h + plugins/interfaces/render_plugin.h + plugins/meshlab_plugin_type.h + plugins/plugin_manager.h python/function.h python/function_parameter.h python/function_set.h python/python_utils.h utilities/file_format.h + utilities/load_save.h + globals.h GLExtensionsManager.h GLLogStream.h filterscript.h @@ -91,18 +92,6 @@ set(HEADERS searcher.h) set(SOURCES - globals.cpp - plugins/containers/decorate_plugin_container.cpp - plugins/containers/edit_plugin_container.cpp - plugins/containers/filter_plugin_container.cpp - plugins/containers/io_plugin_container.cpp - plugins/containers/render_plugin_container.cpp - plugins/interfaces/meshlab_plugin_logger.cpp - plugins/interfaces/decorate_plugin.cpp - plugins/interfaces/filter_plugin.cpp - plugins/interfaces/io_plugin.cpp - plugins/meshlab_plugin_type.cpp - plugins/plugin_manager.cpp ml_document/helpers/mesh_document_state_data.cpp ml_document/cmesh.cpp ml_document/mesh_document.cpp @@ -116,10 +105,23 @@ set(SOURCES parameters/rich_parameter.cpp parameters/rich_parameter_list.cpp parameters/value.cpp + plugins/containers/decorate_plugin_container.cpp + plugins/containers/edit_plugin_container.cpp + plugins/containers/filter_plugin_container.cpp + plugins/containers/io_plugin_container.cpp + plugins/containers/render_plugin_container.cpp + plugins/interfaces/meshlab_plugin_logger.cpp + plugins/interfaces/decorate_plugin.cpp + plugins/interfaces/filter_plugin.cpp + plugins/interfaces/io_plugin.cpp + plugins/meshlab_plugin_type.cpp + plugins/plugin_manager.cpp python/function.cpp python/function_parameter.cpp python/function_set.cpp python/python_utils.cpp + utilities/load_save.cpp + globals.cpp GLExtensionsManager.cpp GLLogStream.cpp filterscript.cpp diff --git a/src/common/utilities/load_save.cpp b/src/common/utilities/load_save.cpp new file mode 100644 index 000000000..f847abea3 --- /dev/null +++ b/src/common/utilities/load_save.cpp @@ -0,0 +1,222 @@ +/**************************************************************************** +* MeshLab o o * +* A versatile mesh processing toolbox o o * +* _ O _ * +* Copyright(C) 2005-2020 \/)\/ * +* 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 "load_save.h" + +#include +#include + +#include "../globals.h" +#include "../plugins/plugin_manager.h" + +namespace meshlab { + +void loadMesh(const QString& fileName, IOPlugin* ioPlugin, const RichParameterList& prePar, const std::list& meshList, std::list& maskList, vcg::CallBackPos *cb) +{ + QFileInfo fi(fileName); + QString extension = fi.suffix(); + + // the original directory path before we switch it + QString origDir = QDir::current().path(); + + // this change of dir is needed for subsequent textures/materials loading + QDir::setCurrent(fi.absoluteDir().absolutePath()); + + // Adjust the file name after changing the directory + QString fileNameSansDir = fi.fileName(); + + try { + ioPlugin->open(extension, fileNameSansDir, meshList ,maskList, prePar, cb); + } + catch(const MLException& e) { + QDir::setCurrent(origDir); // undo the change of directory before leaving + throw e; + } + + QString warningError = ioPlugin->warningMessageString(); + + //saveRecentFileList(fileName); + + auto itmesh = meshList.begin(); + auto itmask = maskList.begin(); + for (unsigned int i = 0; i < meshList.size(); ++i){ + MeshModel* mm = *itmesh; + int mask = *itmask; + + //if (!(mm->cm.textures.empty())) + // updateTexture(mm->id()); + + // In case of polygonal meshes the normal should be updated accordingly + if( mask & vcg::tri::io::Mask::IOM_BITPOLYGONAL) { + mm->updateDataMask(MeshModel::MM_POLYGONAL); // just to be sure. Hopefully it should be done in the plugin... + int degNum = vcg::tri::Clean::RemoveDegenerateFace(mm->cm); + if(degNum) + ioPlugin->log("Warning model contains " + std::to_string(degNum) +" degenerate faces. Removed them."); + mm->updateDataMask(MeshModel::MM_FACEFACETOPO); + vcg::tri::UpdateNormal::PerBitQuadFaceNormalized(mm->cm); + vcg::tri::UpdateNormal::PerVertexFromCurrentFaceNormal(mm->cm); + } // standard case + else { + vcg::tri::UpdateNormal::PerFaceNormalized(mm->cm); + if(!( mask & vcg::tri::io::Mask::IOM_VERTNORMAL) ) + vcg::tri::UpdateNormal::PerVertexAngleWeighted(mm->cm); + } + + vcg::tri::UpdateBounding::Box(mm->cm); + if(mm->cm.fn==0 && mm->cm.en==0) { + if(mask & vcg::tri::io::Mask::IOM_VERTNORMAL) + mm->updateDataMask(MeshModel::MM_VERTNORMAL); + } + + if(mm->cm.fn==0 && mm->cm.en>0) { + if (mask & vcg::tri::io::Mask::IOM_VERTNORMAL) + mm->updateDataMask(MeshModel::MM_VERTNORMAL); + } + + //updateMenus(); + int delVertNum = vcg::tri::Clean::RemoveDegenerateVertex(mm->cm); + int delFaceNum = vcg::tri::Clean::RemoveDegenerateFace(mm->cm); + vcg::tri::Allocator::CompactEveryVector(mm->cm); + if(delVertNum>0 || delFaceNum>0 ) + ioPlugin->reportWarning(warningError + "\n" + QString("Warning mesh contains %1 vertices with NAN coords and %2 degenerated faces.\nCorrected.").arg(delVertNum).arg(delFaceNum)); + //QMessageBox::warning(this, "MeshLab Warning", QString("Warning mesh contains %1 vertices with NAN coords and %2 degenerated faces.\nCorrected.").arg(delVertNum).arg(delFaceNum) ); + + //mm->cm.Tr = mtr; + + //computeRenderingDataOnLoading(mm,isareload, rendOpt); + ++itmesh; + ++itmask; + } + + //updateLayerDialog(); + + + //meshDoc()->setBusy(false); + + QDir::setCurrent(origDir); // undo the change of directory before leaving +} + + +void loadWithStandardParameters(const QString& filename, MeshDocument& md, vcg::CallBackPos *cb) +{ + QFileInfo fi(filename); + QString extension = fi.suffix(); + PluginManager& pm = meshlab::pluginManagerInstance(); + IOPlugin *ioPlugin = pm.inputMeshPlugin(extension); + + if (ioPlugin == nullptr) + throw MLException( + "Mesh " + filename + " cannot be opened. Your MeshLab version " + "has not plugin to read " + extension + " file format"); + + + RichParameterList prePar; + ioPlugin->initPreOpenParameter(extension, prePar); + prePar.join(meshlab::defaultGlobalParameterList()); + + + unsigned int nMeshes = ioPlugin->numberMeshesContainedInFile(extension, filename); + std::list meshList; + for (unsigned int i = 0; i < nMeshes; i++) { + MeshModel *mm = md.addNewMesh(filename, fi.fileName()); + if (nMeshes != 1) { + // if the file contains more than one mesh, this id will be + // != -1 + mm->setIdInFile(i); + } + meshList.push_back(mm); + } + + std::list masks; + + loadMesh(fi.fileName(), ioPlugin, prePar, meshList, masks, cb); + RichParameterList par; + ioPlugin->initOpenParameter(extension, meshList, par); + ioPlugin->applyOpenParameter(extension, meshList, par); +} + + +void reloadMesh( + const QString& filename, + MeshDocument& md, + MeshModel& mm, + vcg::CallBackPos* cb) +{ + // reload can affect only meshes loaded from files containing a single mesh, + // or if the mesh is the first one loaded from that file. + // if a file contains more than one meshes (idInFile != -1), the reload of + // all the contained meshes will be performed by the mesh with idInFile == 0 + if (mm.idInFile() < 1) { + QFileInfo fi(filename); + QString extension = fi.suffix(); + PluginManager& pm = meshlab::pluginManagerInstance(); + IOPlugin *ioPlugin = pm.inputMeshPlugin(extension); + + if (ioPlugin == nullptr) { + throw MLException( + "Mesh " + filename + " cannot be opened. Your MeshLab " + "version has not plugin to read " + extension + + " file format"); + } + + RichParameterList prePar; + ioPlugin->initPreOpenParameter(extension, prePar); + prePar.join(meshlab::defaultGlobalParameterList()); + + unsigned int nMeshes = ioPlugin->numberMeshesContainedInFile(extension, filename); + std::list meshList; + + //simple case: just one meshe expected in the file + if (mm.idInFile() == -1 && nMeshes == 1) { + meshList.push_back(&mm); + } + else if (mm.idInFile() == 0 && nMeshes > 1) { + //looking for all the other meshes that should be reloaded from the file + for (MeshModel* m : md.meshIterator()) { + if (m->fullName() == mm.fullName() && m->idInFile() >= 0) { + meshList.push_back(m); + } + } + if (meshList.size() != nMeshes){ + throw MLException( + "Cannot reload " + filename + ": the number of layers " + "in this meshlab project associated to this file is " + "different from the number of meshes contained in the " + "file."); + } + } + else { + throw MLException( + "Cannot reload " + filename + ": expected number layers is " + "different from the number of meshes contained in th file."); + } + + std::list masks; + loadMesh(fi.fileName(), ioPlugin, prePar, meshList, masks, cb); + RichParameterList par; + ioPlugin->initOpenParameter(extension, meshList, par); + ioPlugin->applyOpenParameter(extension, meshList, par); + } +} + +} diff --git a/src/common/utilities/load_save.h b/src/common/utilities/load_save.h new file mode 100644 index 000000000..8f118c4f7 --- /dev/null +++ b/src/common/utilities/load_save.h @@ -0,0 +1,45 @@ +/**************************************************************************** +* MeshLab o o * +* A versatile mesh processing toolbox o o * +* _ O _ * +* Copyright(C) 2005-2020 \/)\/ * +* 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. * +* * +****************************************************************************/ + +#ifndef MESHLAB_LOAD_SAVE_H +#define MESHLAB_LOAD_SAVE_H + +#include "../ml_shared_data_context/ml_shared_data_context.h" +#include "../plugins/interfaces/io_plugin.h" + +/** + * Utility functions to load/save meshes using plugins loaded in the plugin + * manager. + */ + +namespace meshlab { + +void loadMesh(const QString& fileName, IOPlugin* ioPlugin, const RichParameterList& prePar, const std::list& meshList, std::list& maskList, vcg::CallBackPos *cb); + +void loadWithStandardParameters(const QString& filename, MeshDocument& mm, vcg::CallBackPos *cb); + +void reloadMesh(const QString& filename, MeshDocument& md, MeshModel& mm, vcg::CallBackPos *cb); + +} + +#endif // MESHLAB_LOAD_SAVE_H diff --git a/src/meshlab/mainwindow_RunTime.cpp b/src/meshlab/mainwindow_RunTime.cpp index aa0693b3a..45456a566 100644 --- a/src/meshlab/mainwindow_RunTime.cpp +++ b/src/meshlab/mainwindow_RunTime.cpp @@ -46,6 +46,7 @@ #include #include #include +#include #include "rich_parameter_gui/richparameterlistdialog.h" @@ -2370,28 +2371,22 @@ bool MainWindow::importMeshWithLayerManagement(QString fileName) // Opening files in a transparent form (IO plugins contribution is hidden to user) bool MainWindow::importMesh(QString fileName) { - if (!GLA()) - { + if (!GLA()) { this->newProject(); if(!GLA()) return false; } - - //QStringList suffixList; - // HashTable storing all supported formats together with - // the (1-based) index of first plugin which is able to open it - //QHash allKnownFormats; - //PM.LoadFormats(suffixList, allKnownFormats,PluginManager::IMPORT); QStringList fileNameList; if (fileName.isEmpty()) fileNameList = QFileDialog::getOpenFileNames(this,tr("Import Mesh"), lastUsedDirectory.path(), PM.inputMeshFormatListDialog().join(";;")); else fileNameList.push_back(fileName); - if (fileNameList.isEmpty()) return false; - else - { + if (fileNameList.isEmpty()) { + return false; + } + else { //save path away so we can use it again QString path = fileNameList.first(); path.truncate(path.lastIndexOf("/")); @@ -2400,8 +2395,7 @@ bool MainWindow::importMesh(QString fileName) QElapsedTimer allFileTime; allFileTime.start(); - for(const QString& fileName : fileNameList) - { + for(const QString& fileName : fileNameList) { QFileInfo fi(fileName); if(!fi.exists()) { QString errorMsgFormat = "Unable to open file:\n\"%1\"\n\nError details: file %1 does not exist."; @@ -2417,8 +2411,7 @@ bool MainWindow::importMesh(QString fileName) QString extension = fi.suffix(); IOPlugin *pCurrentIOPlugin = PM.inputMeshPlugin(extension); - if (pCurrentIOPlugin == nullptr) - { + if (pCurrentIOPlugin == nullptr) { QString errorMsgFormat("Unable to open file:\n\"%1\"\n\nError details: file format " + extension + " not supported."); QMessageBox::critical(this, tr("Meshlab Opening Error"), errorMsgFormat.arg(fileName)); return false; @@ -2449,14 +2442,19 @@ bool MainWindow::importMesh(QString fileName) meshList.push_back(mm); } qb->show(); - QElapsedTimer t; - t.start(); - Matrix44m mtr; - mtr.SetIdentity(); std::list masks; - bool open = loadMesh(fileName,pCurrentIOPlugin, meshList, masks,&prePar,mtr,false); - if(open) - { + try { + QElapsedTimer t; + t.start(); + meshlab::loadMesh(fileName, pCurrentIOPlugin, prePar, meshList, masks, QCallBack); + saveRecentFileList(fileName); + updateLayerDialog(); + for (MeshModel* mm : meshList) + computeRenderingDataOnLoading(mm, false, nullptr); + QString warningString = pCurrentIOPlugin->warningMessageString(); + if (!warningString.isEmpty()){ + QMessageBox::warning(this, "Meshlab Opening Warning", warningString); + } GLA()->Logf(0, "Opened mesh %s in %i msec", qUtf8Printable(fileName), t.elapsed()); RichParameterList par; @@ -2470,22 +2468,12 @@ bool MainWindow::importMesh(QString fileName) for (MeshModel* mm : meshList) pCurrentIOPlugin->applyOpenParameter(extension, *mm, par); } - /*MultiViewer_Container* mv = GLA()->mvc(); - if (mv != NULL) - { - for(int glarid = 0;glarid < mv->viewerCounter();++glarid) - { - GLArea* ar = mv->getViewer(glarid); - if (ar != NULL) - MLSceneRenderModeAdapter::setupRequestedAttributesAccordingToRenderMode(mm->id(),*ar); - } - }*/ } - else - { + catch (const MLException& e){ for (MeshModel* mm : meshList) meshDoc()->delMesh(mm); - GLA()->Logf(0, "Warning: Mesh %s has not been opened", qUtf8Printable(fileName)); + GLA()->Logf(0, "Error: File %s has not been loaded", qUtf8Printable(fileName)); + QMessageBox::critical(this, "Meshlab Opening Error", e.what()); } }// end foreach file of the input list GLA()->Logf(0,"All files opened in %i msec",allFileTime.elapsed()); From c77ff46f901a1170be5bcdb88d3b8ba6fa0450cf Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Mon, 19 Apr 2021 17:27:40 +0200 Subject: [PATCH 2/9] fix qmake --- src/common/common.pro | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/common/common.pro b/src/common/common.pro index 5f57e013d..2a8346c3a 100644 --- a/src/common/common.pro +++ b/src/common/common.pro @@ -71,6 +71,7 @@ HEADERS += \ python/function_set.h \ python/python_utils.h \ utilities/file_format.h \ + utilities/load_save.h \ GLExtensionsManager.h \ filterscript.h \ GLLogStream.h \ @@ -111,6 +112,7 @@ SOURCES += \ python/function_parameter.cpp \ python/function_set.cpp \ python/python_utils.cpp \ + utilities/load_save.cpp \ GLExtensionsManager.cpp \ filterscript.cpp \ GLLogStream.cpp \ From 58b65bc9e5d363a53119a2d5eba02822b478de68 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Tue, 20 Apr 2021 10:26:55 +0200 Subject: [PATCH 3/9] import aln uses new load functions --- src/common/utilities/load_save.cpp | 15 ++++++++--- src/meshlab/mainwindow_RunTime.cpp | 41 ++++++++++++++++-------------- src/vcglib | 2 +- 3 files changed, 34 insertions(+), 24 deletions(-) diff --git a/src/common/utilities/load_save.cpp b/src/common/utilities/load_save.cpp index f847abea3..c683ce9db 100644 --- a/src/common/utilities/load_save.cpp +++ b/src/common/utilities/load_save.cpp @@ -149,10 +149,17 @@ void loadWithStandardParameters(const QString& filename, MeshDocument& md, vcg:: std::list masks; - loadMesh(fi.fileName(), ioPlugin, prePar, meshList, masks, cb); - RichParameterList par; - ioPlugin->initOpenParameter(extension, meshList, par); - ioPlugin->applyOpenParameter(extension, meshList, par); + try{ + loadMesh(fi.fileName(), ioPlugin, prePar, meshList, masks, cb); + RichParameterList par; + ioPlugin->initOpenParameter(extension, meshList, par); + ioPlugin->applyOpenParameter(extension, meshList, par); + } + catch(const MLException& e){ + for (MeshModel* mm : meshList) + md.delMesh(mm); + throw e; + } } diff --git a/src/meshlab/mainwindow_RunTime.cpp b/src/meshlab/mainwindow_RunTime.cpp index 45456a566..021adccdf 100644 --- a/src/meshlab/mainwindow_RunTime.cpp +++ b/src/meshlab/mainwindow_RunTime.cpp @@ -1740,21 +1740,21 @@ bool MainWindow::openProject(QString fileName) { vector rmv; int retVal = ALNParser::ParseALN(rmv, qUtf8Printable(fileName)); - if(retVal != ALNParser::NoError) - { + if(retVal != ALNParser::NoError) { QMessageBox::critical(this, tr("Meshlab Opening Error"), "Unable to open ALN file"); return false; } - bool openRes=true; - vector::iterator ir; - for(ir=rmv.begin();ir!=rmv.end() && openRes;++ir) - { - QString relativeToProj = fi.absoluteDir().absolutePath() + "/" + (*ir).filename.c_str(); - meshDoc()->addNewMesh(relativeToProj,relativeToProj); - openRes = loadMeshWithStandardParams(relativeToProj,this->meshDoc()->mm(),ir->transformation); - if(!openRes) - meshDoc()->delMesh(meshDoc()->mm()); + for(const RangeMap& rm : rmv) { + QString relativeToProj = fi.absoluteDir().absolutePath() + "/" + rm.filename.c_str(); + try { + meshlab::loadWithStandardParameters(relativeToProj, *meshDoc(), QCallBack); + meshDoc()->mm()->cm.Tr.Import(rm.transformation); + computeRenderingDataOnLoading(meshDoc()->mm(), false, nullptr); + } + catch (const MLException& e){ + QMessageBox::critical(this, "Meshlab Opening Error", e.what()); + } } } @@ -1883,18 +1883,21 @@ bool MainWindow::appendProject(QString fileName) { vector rmv; int retVal = ALNParser::ParseALN(rmv, qUtf8Printable(fileName)); - if(retVal != ALNParser::NoError) - { + if(retVal != ALNParser::NoError) { QMessageBox::critical(this, tr("Meshlab Opening Error"), "Unable to open ALN file"); return false; } - for(vector::iterator ir=rmv.begin();ir!=rmv.end();++ir) - { - QString relativeToProj = fi.absoluteDir().absolutePath() + "/" + (*ir).filename.c_str(); - meshDoc()->addNewMesh(relativeToProj,relativeToProj); - if(!loadMeshWithStandardParams(relativeToProj,this->meshDoc()->mm(),(*ir).transformation)) - meshDoc()->delMesh(meshDoc()->mm()); + for(const RangeMap& rm : rmv) { + QString relativeToProj = fi.absoluteDir().absolutePath() + "/" + rm.filename.c_str(); + try { + meshlab::loadWithStandardParameters(relativeToProj, *meshDoc(), QCallBack); + meshDoc()->mm()->cm.Tr.Import(rm.transformation); + computeRenderingDataOnLoading(meshDoc()->mm(), false, nullptr); + } + catch (const MLException& e){ + QMessageBox::critical(this, "Meshlab Opening Error", e.what()); + } } } diff --git a/src/vcglib b/src/vcglib index 0a17efe9a..0882d6258 160000 --- a/src/vcglib +++ b/src/vcglib @@ -1 +1 @@ -Subproject commit 0a17efe9a809d2fc82838affe5d0a239e9e546fc +Subproject commit 0882d6258252174ee6635b3d1d337bdedad97ab8 From 6beda8b6e5ea5d12b3aefa4767ae999e5e00b22b Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Tue, 20 Apr 2021 12:01:29 +0200 Subject: [PATCH 4/9] reload supports multiple meshes loaded from a single file --- src/common/utilities/load_save.cpp | 7 +++-- src/meshlab/mainwindow_RunTime.cpp | 47 +++++++++++++++++++++++------- 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/src/common/utilities/load_save.cpp b/src/common/utilities/load_save.cpp index c683ce9db..8f2e5ac8d 100644 --- a/src/common/utilities/load_save.cpp +++ b/src/common/utilities/load_save.cpp @@ -213,13 +213,16 @@ void reloadMesh( } } else { - throw MLException( + //ignore this case: this mesh will be reloaded by the mesh with id = 0 + if (! (mm.idInFile() > 0 && nMeshes > 1)) { + throw MLException( "Cannot reload " + filename + ": expected number layers is " "different from the number of meshes contained in th file."); + } } std::list masks; - loadMesh(fi.fileName(), ioPlugin, prePar, meshList, masks, cb); + loadMesh(filename, ioPlugin, prePar, meshList, masks, cb); RichParameterList par; ioPlugin->initOpenParameter(extension, meshList, par); ioPlugin->applyOpenParameter(extension, meshList, par); diff --git a/src/meshlab/mainwindow_RunTime.cpp b/src/meshlab/mainwindow_RunTime.cpp index 021adccdf..0d808bd02 100644 --- a/src/meshlab/mainwindow_RunTime.cpp +++ b/src/meshlab/mainwindow_RunTime.cpp @@ -2557,13 +2557,22 @@ void MainWindow::reloadAllMesh() // Discards changes and reloads current file // save current file name qb->show(); - foreach(MeshModel *mmm,meshDoc()->meshList) + QElapsedTimer t; + t.start(); + for(MeshModel *mmm : meshDoc()->meshList) { QString fileName = mmm->fullName(); - Matrix44m mat; - mat.SetIdentity(); - loadMeshWithStandardParams(fileName,mmm,mat,true); + if (!fileName.isEmpty()){ + try { + meshlab::reloadMesh(fileName, *meshDoc(), *mmm, QCallBack); + computeRenderingDataOnLoading(mmm,true, nullptr); + } + catch (const MLException& e) { + QMessageBox::critical(this, "Reload Error", e.what()); + } + } } + GLA()->Log(0, ("All meshes reloaded in " + std::to_string(t.elapsed()) + " msec.").c_str()); qb->reset(); if (_currviewcontainer != NULL) @@ -2580,15 +2589,33 @@ void MainWindow::reload() // Discards changes and reloads current file // save current file name qb->show(); - QString fileName = meshDoc()->mm()->fullName(); - if (fileName.isEmpty()) - { + MeshModel* mm = meshDoc()->mm(); + if (meshDoc()->mm()->idInFile() > 0){ + for (MeshModel *mmm : meshDoc()->meshList){ + if (mmm->label() == mm->label() && mmm->idInFile() == 0) + mm = mmm; // root mesh layer of the file + } + if (mm == meshDoc()->mm()){ + QMessageBox::critical(this, "Reload Error", "Impossible the file: cannot find the root mesh layer of the file."); + return; + } + } + + QString fileName = mm->fullName(); + if (fileName.isEmpty()) { QMessageBox::critical(this, "Reload Error", "Impossible to reload an unsaved mesh model!!"); return; } - Matrix44m mat; - mat.SetIdentity(); - loadMeshWithStandardParams(fileName,meshDoc()->mm(),mat,true); + try { + QElapsedTimer t; + t.start(); + meshlab::reloadMesh(fileName, *meshDoc(), *mm, QCallBack); + computeRenderingDataOnLoading(mm, true, nullptr); + GLA()->Log(0, ("Mesh reloaded in " + std::to_string(t.elapsed()) + " msec.").c_str()); + } + catch (const MLException& e) { + QMessageBox::critical(this, "Reload Error", e.what()); + } qb->reset(); if (_currviewcontainer != NULL) { From 8cc03faac882290103eced11eb2f7e61089e493f Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Tue, 20 Apr 2021 12:19:52 +0200 Subject: [PATCH 5/9] loadRaster function --- src/common/utilities/load_save.cpp | 25 ++++++++++++++++++++++- src/common/utilities/load_save.h | 5 +++-- src/meshlab/mainwindow_RunTime.cpp | 32 +++++++++--------------------- 3 files changed, 36 insertions(+), 26 deletions(-) diff --git a/src/common/utilities/load_save.cpp b/src/common/utilities/load_save.cpp index 8f2e5ac8d..eee63ae09 100644 --- a/src/common/utilities/load_save.cpp +++ b/src/common/utilities/load_save.cpp @@ -117,7 +117,7 @@ void loadMesh(const QString& fileName, IOPlugin* ioPlugin, const RichParameterLi } -void loadWithStandardParameters(const QString& filename, MeshDocument& md, vcg::CallBackPos *cb) +void loadMeshWithStandardParameters(const QString& filename, MeshDocument& md, vcg::CallBackPos *cb) { QFileInfo fi(filename); QString extension = fi.suffix(); @@ -229,4 +229,27 @@ void reloadMesh( } } +void loadRaster(const QString& filename, MeshDocument& md, vcg::CallBackPos* cb) +{ + QFileInfo fi(filename); + QString extension = fi.suffix(); + PluginManager& pm = meshlab::pluginManagerInstance(); + IOPlugin *ioPlugin = pm.inputRasterPlugin(extension); + + if (ioPlugin == nullptr) + throw MLException( + "Raster " + filename + " cannot be opened. Your MeshLab version " + "has not plugin to read " + extension + " file format."); + + RasterModel *rm = md.addNewRaster(); + try { + ioPlugin->openRaster(extension, filename, *rm, cb); + } + catch(const MLException& e){ + md.delRaster(rm); + throw e; + } + +} + } diff --git a/src/common/utilities/load_save.h b/src/common/utilities/load_save.h index 8f118c4f7..b8091d414 100644 --- a/src/common/utilities/load_save.h +++ b/src/common/utilities/load_save.h @@ -36,10 +36,11 @@ namespace meshlab { void loadMesh(const QString& fileName, IOPlugin* ioPlugin, const RichParameterList& prePar, const std::list& meshList, std::list& maskList, vcg::CallBackPos *cb); -void loadWithStandardParameters(const QString& filename, MeshDocument& mm, vcg::CallBackPos *cb); +void loadMeshWithStandardParameters(const QString& filename, MeshDocument& md, vcg::CallBackPos *cb); -void reloadMesh(const QString& filename, MeshDocument& md, MeshModel& mm, vcg::CallBackPos *cb); +void reloadMesh(const QString& filename, MeshDocument& md, MeshModel& m, vcg::CallBackPos *cb); +void loadRaster(const QString& filename, MeshDocument& md, vcg::CallBackPos *cb); } #endif // MESHLAB_LOAD_SAVE_H diff --git a/src/meshlab/mainwindow_RunTime.cpp b/src/meshlab/mainwindow_RunTime.cpp index 0d808bd02..b68eaad82 100644 --- a/src/meshlab/mainwindow_RunTime.cpp +++ b/src/meshlab/mainwindow_RunTime.cpp @@ -1748,7 +1748,7 @@ bool MainWindow::openProject(QString fileName) for(const RangeMap& rm : rmv) { QString relativeToProj = fi.absoluteDir().absolutePath() + "/" + rm.filename.c_str(); try { - meshlab::loadWithStandardParameters(relativeToProj, *meshDoc(), QCallBack); + meshlab::loadMeshWithStandardParameters(relativeToProj, *meshDoc(), QCallBack); meshDoc()->mm()->cm.Tr.Import(rm.transformation); computeRenderingDataOnLoading(meshDoc()->mm(), false, nullptr); } @@ -1891,7 +1891,7 @@ bool MainWindow::appendProject(QString fileName) for(const RangeMap& rm : rmv) { QString relativeToProj = fi.absoluteDir().absolutePath() + "/" + rm.filename.c_str(); try { - meshlab::loadWithStandardParameters(relativeToProj, *meshDoc(), QCallBack); + meshlab::loadMeshWithStandardParameters(relativeToProj, *meshDoc(), QCallBack); meshDoc()->mm()->cm.Tr.Import(rm.transformation); computeRenderingDataOnLoading(meshDoc()->mm(), false, nullptr); } @@ -2136,43 +2136,29 @@ bool MainWindow::importRaster(const QString& fileImg) allFileTime.start(); for(const QString& fileName : fileNameList) { - QFileInfo fi(fileName); - QString extension = fi.suffix(); - IOPlugin *pCurrentIOPlugin = PM.inputRasterPlugin(extension); - //pCurrentIOPlugin->setLog(gla->log); - if (pCurrentIOPlugin == NULL) - { - QString errorMsgFormat("Unable to open file:\n\"%1\"\n\nError details: file format " + extension + " not supported."); - QMessageBox::critical(this, tr("Meshlab Opening Error"), errorMsgFormat.arg(fileName)); - return false; - } - QFileInfo info(fileName); - RasterModel *rm = meshDoc()->addNewRaster(); - qb->show(); - QElapsedTimer t; - t.start(); try { - pCurrentIOPlugin->openRaster(extension, fileName, *rm, QCallBack); + QElapsedTimer t; + t.start(); + meshlab::loadRaster(fileName, *meshDoc(), QCallBack); GLA()->Logf(0, "Opened raster %s in %i msec", qUtf8Printable(fileName), t.elapsed()); GLA()->resetTrackBall(); - GLA()->fov = rm->shot.GetFovFromFocal(); - rm->shot = GLA()->shotFromTrackball().first; + GLA()->fov = meshDoc()->rm()->shot.GetFovFromFocal(); + meshDoc()->rm()->shot = GLA()->shotFromTrackball().first; GLA()->resetTrackBall(); // and then we reset the trackball again, to have the standard view if (!layerDialog->isVisible()) layerDialog->setVisible(true); + GLA()->Logf(0,"All files opened in %i msec",allFileTime.elapsed()); } catch(const MLException& e){ QMessageBox::warning( this, tr("Opening Failure"), "While opening: " + fileName + "\n\n" + e.what()); - meshDoc()->delRaster(rm); GLA()->Logf(0, "Warning: Raster %s has not been opened", qUtf8Printable(fileName)); } }// end foreach file of the input list - GLA()->Logf(0,"All files opened in %i msec",allFileTime.elapsed()); - + if (_currviewcontainer != NULL) _currviewcontainer->updateAllDecoratorsForAllViewers(); From 992a2cfe421335a9448fcde7afac2682f581794d Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Tue, 20 Apr 2021 14:58:29 +0200 Subject: [PATCH 6/9] better management of io warnings --- src/common/plugins/interfaces/io_plugin.cpp | 6 ++++-- src/common/utilities/load_save.cpp | 18 +---------------- src/meshlab/mainwindow_RunTime.cpp | 22 +++++++++++++-------- src/meshlabplugins/io_3ds/meshio.cpp | 12 +++++++---- 4 files changed, 27 insertions(+), 31 deletions(-) diff --git a/src/common/plugins/interfaces/io_plugin.cpp b/src/common/plugins/interfaces/io_plugin.cpp index 66584a40d..aaeb04743 100644 --- a/src/common/plugins/interfaces/io_plugin.cpp +++ b/src/common/plugins/interfaces/io_plugin.cpp @@ -39,8 +39,10 @@ void IOPlugin::open( void IOPlugin::reportWarning(const QString& warningMessage) const { - MeshLabPluginLogger::log(GLLogStream::WARNING, warningMessage.toStdString()); - warnString += "\n" + warningMessage; + if (!warningMessage.isEmpty()){ + MeshLabPluginLogger::log(GLLogStream::WARNING, warningMessage.toStdString()); + warnString += "\n" + warningMessage; + } } void IOPlugin::wrongOpenFormat(const QString& format) diff --git a/src/common/utilities/load_save.cpp b/src/common/utilities/load_save.cpp index eee63ae09..bff5b5d59 100644 --- a/src/common/utilities/load_save.cpp +++ b/src/common/utilities/load_save.cpp @@ -53,19 +53,12 @@ void loadMesh(const QString& fileName, IOPlugin* ioPlugin, const RichParameterLi throw e; } - QString warningError = ioPlugin->warningMessageString(); - - //saveRecentFileList(fileName); - auto itmesh = meshList.begin(); auto itmask = maskList.begin(); for (unsigned int i = 0; i < meshList.size(); ++i){ MeshModel* mm = *itmesh; int mask = *itmask; - //if (!(mm->cm.textures.empty())) - // updateTexture(mm->id()); - // In case of polygonal meshes the normal should be updated accordingly if( mask & vcg::tri::io::Mask::IOM_BITPOLYGONAL) { mm->updateDataMask(MeshModel::MM_POLYGONAL); // just to be sure. Hopefully it should be done in the plugin... @@ -98,21 +91,12 @@ void loadMesh(const QString& fileName, IOPlugin* ioPlugin, const RichParameterLi int delFaceNum = vcg::tri::Clean::RemoveDegenerateFace(mm->cm); vcg::tri::Allocator::CompactEveryVector(mm->cm); if(delVertNum>0 || delFaceNum>0 ) - ioPlugin->reportWarning(warningError + "\n" + QString("Warning mesh contains %1 vertices with NAN coords and %2 degenerated faces.\nCorrected.").arg(delVertNum).arg(delFaceNum)); - //QMessageBox::warning(this, "MeshLab Warning", QString("Warning mesh contains %1 vertices with NAN coords and %2 degenerated faces.\nCorrected.").arg(delVertNum).arg(delFaceNum) ); - - //mm->cm.Tr = mtr; + ioPlugin->reportWarning(QString("Warning mesh contains %1 vertices with NAN coords and %2 degenerated faces.\nCorrected.").arg(delVertNum).arg(delFaceNum)); //computeRenderingDataOnLoading(mm,isareload, rendOpt); ++itmesh; ++itmask; } - - //updateLayerDialog(); - - - //meshDoc()->setBusy(false); - QDir::setCurrent(origDir); // undo the change of directory before leaving } diff --git a/src/meshlab/mainwindow_RunTime.cpp b/src/meshlab/mainwindow_RunTime.cpp index b68eaad82..ff85fec93 100644 --- a/src/meshlab/mainwindow_RunTime.cpp +++ b/src/meshlab/mainwindow_RunTime.cpp @@ -1751,6 +1751,8 @@ bool MainWindow::openProject(QString fileName) meshlab::loadMeshWithStandardParameters(relativeToProj, *meshDoc(), QCallBack); meshDoc()->mm()->cm.Tr.Import(rm.transformation); computeRenderingDataOnLoading(meshDoc()->mm(), false, nullptr); + if (!(meshDoc()->mm()->cm.textures.empty())) + updateTexture(meshDoc()->mm()->id()); } catch (const MLException& e){ QMessageBox::critical(this, "Meshlab Opening Error", e.what()); @@ -1894,6 +1896,8 @@ bool MainWindow::appendProject(QString fileName) meshlab::loadMeshWithStandardParameters(relativeToProj, *meshDoc(), QCallBack); meshDoc()->mm()->cm.Tr.Import(rm.transformation); computeRenderingDataOnLoading(meshDoc()->mm(), false, nullptr); + if (!(meshDoc()->mm()->cm.textures.empty())) + updateTexture(meshDoc()->mm()->id()); } catch (const MLException& e){ QMessageBox::critical(this, "Meshlab Opening Error", e.what()); @@ -2438,8 +2442,11 @@ bool MainWindow::importMesh(QString fileName) meshlab::loadMesh(fileName, pCurrentIOPlugin, prePar, meshList, masks, QCallBack); saveRecentFileList(fileName); updateLayerDialog(); - for (MeshModel* mm : meshList) + for (MeshModel* mm : meshList) { computeRenderingDataOnLoading(mm, false, nullptr); + if (! (mm->cm.textures.empty())) + updateTexture(mm->id()); + } QString warningString = pCurrentIOPlugin->warningMessageString(); if (!warningString.isEmpty()){ QMessageBox::warning(this, "Meshlab Opening Warning", warningString); @@ -2942,17 +2949,19 @@ void MainWindow::updateTexture(int meshid) { QImage img; QFileInfo fi(mymesh->cm.textures[i].c_str()); + QFileInfo mfi(mymesh->fullName()); QString filename = fi.absoluteFilePath(); bool res = img.load(filename); - sometextfailed = sometextfailed || !res; if(!res) { - res = img.load(filename); + QString fn2 = mfi.absolutePath() + "/" + fi.fileName(); + res = img.load(fn2); if(!res) { QString errmsg = QString("Failure of loading texture %1").arg(fi.fileName()); meshDoc()->Log.log(GLLogStream::WARNING,qUtf8Printable(errmsg)); - unexistingtext += "" + filename + "
"; + unexistingtext += "" + fi.fileName() + "
"; + sometextfailed = sometextfailed || !res; } } @@ -2966,12 +2975,9 @@ void MainWindow::updateTexture(int meshid) /*PLEASE EXPLAIN ME!*********************************************************************************************************************************************************************************/ if (!res) - res = img.load(":/images/dummy.png"); + img.load(":/images/dummy.png"); GLuint textid = shared->allocateTexturePerMesh(meshid,img,singleMaxTextureSizeMpx); - if (sometextfailed) - QMessageBox::warning(this,"Texture file has not been correctly loaded",unexistingtext); - for(int tt = 0;tt < mvc->viewerCounter();++tt) { GLArea* ar = mvc->getViewer(tt); diff --git a/src/meshlabplugins/io_3ds/meshio.cpp b/src/meshlabplugins/io_3ds/meshio.cpp index 6bbeda306..2cce37448 100644 --- a/src/meshlabplugins/io_3ds/meshio.cpp +++ b/src/meshlabplugins/io_3ds/meshio.cpp @@ -174,12 +174,14 @@ void ExtraMeshIOPlugin::open( for ( unsigned textureIdx = 0; textureIdx < mm.cm.textures.size(); ++textureIdx) { FILE* pFile = fopen (mm.cm.textures[textureIdx].c_str(), "r"); - if (pFile == NULL) { + if (pFile == nullptr) { missingTextureFilesMsg.append("\n"); missingTextureFilesMsg.append(mm.cm.textures[textureIdx].c_str()); someTextureNotFound = true; } - fclose (pFile); + else { + fclose (pFile); + } } if (someTextureNotFound){ reportWarning("Missing texture files: " + missingTextureFilesMsg); @@ -216,13 +218,15 @@ void ExtraMeshIOPlugin::open( for ( unsigned textureIdx = 0; textureIdx < m.cm.textures.size(); ++textureIdx) { FILE* pFile = fopen (m.cm.textures[textureIdx].c_str(), "r"); - if (pFile == NULL) + if (pFile == nullptr) { missingTextureFilesMsg.append("\n"); missingTextureFilesMsg.append(m.cm.textures[textureIdx].c_str()); someTextureNotFound = true; } - fclose (pFile); + else { + fclose (pFile); + } } if (someTextureNotFound){ reportWarning("Missing texture files: " + missingTextureFilesMsg); From 8b3e5a123c4204d530fcd2f5ecc80bd1b267ecd7 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Tue, 20 Apr 2021 16:37:00 +0200 Subject: [PATCH 7/9] io_3ds loads multiple layers correctly --- sample/furniture.3ds | Bin 0 -> 48293 bytes src/common/plugins/interfaces/io_plugin.cpp | 4 +- src/common/plugins/interfaces/io_plugin.h | 4 +- src/meshlab/mainwindow_RunTime.cpp | 1 + src/meshlabplugins/io_3ds/meshio.cpp | 178 ++++++++++---------- src/meshlabplugins/io_3ds/meshio.h | 21 ++- 6 files changed, 108 insertions(+), 100 deletions(-) create mode 100644 sample/furniture.3ds diff --git a/sample/furniture.3ds b/sample/furniture.3ds new file mode 100644 index 0000000000000000000000000000000000000000..ea39757e03f23c540e4a51f24d6cd2a21144f6c1 GIT binary patch literal 48293 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 RichParameterList prePar; pCurrentIOPlugin->initPreOpenParameter(extension,prePar); if(!prePar.isEmpty()) diff --git a/src/meshlabplugins/io_3ds/meshio.cpp b/src/meshlabplugins/io_3ds/meshio.cpp index 2cce37448..9154eb210 100644 --- a/src/meshlabplugins/io_3ds/meshio.cpp +++ b/src/meshlabplugins/io_3ds/meshio.cpp @@ -75,28 +75,75 @@ void ExtraMeshIOPlugin::exportMaskCapability(const QString &format, int &capabil return; } -void ExtraMeshIOPlugin::initPreOpenParameter( +unsigned int ExtraMeshIOPlugin::numberMeshesContainedInFile( const QString& format, - RichParameterList& parameters) + const QString& fileName) const { - if (format.toUpper() == tr("3DS")){ - parameters.addParam(RichBool(paramNames[SEPARATE_LAYERS], true, "Separate layers", "Import each mesh contained in the file as a separate layer")); + if (format.toUpper() == tr("3DS")) { + Lib3dsFile *file = NULL; + file = lib3ds_file_load(fileName.toStdString().c_str()); + if (!file) + throw MLException("Malformed file."); + // No nodes? Fabricate nodes to display all the meshes. + if( !file->nodes && file->meshes) { + Lib3dsMesh *mesh; + Lib3dsNode *node; + + for (mesh = file->meshes; mesh != NULL; mesh = mesh->next) { + node = lib3ds_node_new_object(); + strcpy(node->name, mesh->name); + node->parent_id = LIB3DS_NO_PARENT; + lib3ds_file_insert_node(file, node); + } + } + if( !file->nodes) { + lib3ds_file_free(file); + throw MLException("Malformed file."); + } + lib3ds_file_eval(file, 0); + unsigned int i = 0; + Lib3dsNode *p = file->nodes; + while (p) { + i++; + p = p->next; + } + log("Expected meshes in file: " + std::to_string(i) ); + lib3ds_file_free(file); + return i; } + else { + wrongOpenFormat(format); + return 0; + } +} + +void ExtraMeshIOPlugin::open( + const QString& formatName, + const QString&, + MeshModel&, + int&, + const RichParameterList&, + CallBackPos*) +{ + wrongOpenFormat(formatName); } void ExtraMeshIOPlugin::open( const QString &formatName, const QString &fileName, - MeshModel &m, - int& mask, - const RichParameterList& params, + const std::list& meshList, + std::list& maskList, + const RichParameterList&, CallBackPos *cb) { // initializing mask - mask = 0; + maskList.clear(); + for (unsigned int i = 0; i < meshList.size(); i++) + maskList.push_back(0); // initializing progress bar status - if (cb != NULL) (*cb)(0, "Loading..."); + if (cb != nullptr) + (*cb)(0, "Loading..."); QString errorMsgFormat = "Error encountered while loading file:\n\"%1\"\n\nError details: %2"; //QString error_2MsgFormat = "Error encountered while loading file:\n\"%1\"\n\n File with more than a mesh.\n Load only the first!"; @@ -136,92 +183,43 @@ void ExtraMeshIOPlugin::open( } lib3ds_file_eval(file, 0); - - bool singleLayer = true; - if ( file->nodes->next) { - singleLayer = params.getBool(paramNames[SEPARATE_LAYERS]); - } - - if (!singleLayer) - { - Lib3dsNode *p; - mask = 0; - int i=1; - for (p=file->nodes; p!=0; p=p->next, ++i) - { - bool normalsUpdated = false; - - MeshModel &mm = *m.parent->addNewMesh(qUtf8Printable(fileName), QString(p->name), false); - if (cb != NULL) (*cb)(i, (QString("Loading Mesh ")+QString(p->name)).toStdString().c_str()); - - vcg::tri::io::Importer3DS::LoadMask(file, p, info); - mm.Enable(info.mask); - - int result = vcg::tri::io::Importer3DS::Load(mm.cm, file, p, info); - if (result != vcg::tri::io::Importer3DS::E_NOERROR) { - reportWarning("3DS Opening Error: " + errorMsgFormat.arg(fileName, vcg::tri::io::Importer3DS::ErrorMsg(result))); - continue; - } - - if(info.mask & vcg::tri::io::Mask::IOM_WEDGNORMAL) - normalsUpdated = true; - - mask |= info.mask; - - // verify if texture files are present - QString missingTextureFilesMsg = "The following texture files were not found:\n"; - bool someTextureNotFound = false; - for ( unsigned textureIdx = 0; textureIdx < mm.cm.textures.size(); ++textureIdx) - { - FILE* pFile = fopen (mm.cm.textures[textureIdx].c_str(), "r"); - if (pFile == nullptr) { - missingTextureFilesMsg.append("\n"); - missingTextureFilesMsg.append(mm.cm.textures[textureIdx].c_str()); - someTextureNotFound = true; - } - else { - fclose (pFile); - } - } - if (someTextureNotFound){ - reportWarning("Missing texture files: " + missingTextureFilesMsg); - } - - vcg::tri::UpdateBounding::Box(mm.cm); // updates bounding box - if (!normalsUpdated) - vcg::tri::UpdateNormal::PerVertex(mm.cm); // updates normals - } - } - else + + Lib3dsNode *p; + int i=1; + auto iter = meshList.begin(); + auto miter = maskList.begin(); + for (p=file->nodes; p!=nullptr; p=p->next, ++iter, ++miter) { bool normalsUpdated = false; - - vcg::tri::io::Importer3DS::LoadMask(file, 0, info); - m.Enable(info.mask); - - int result = vcg::tri::io::Importer3DS::Load(m.cm, file, 0, info); - if (result != vcg::tri::io::Importer3DS::E_NOERROR) - { - lib3ds_file_free(file); - throw MLException("3DS Opening Error: " + errorMsgFormat.arg(fileName, vcg::tri::io::Importer3DS::ErrorMsg(result))); + + MeshModel &mm = *(*iter); + mm.setLabel(QString(p->name)); + if (cb != nullptr) + (*cb)(i/meshList.size() * 100, (QString("Loading Mesh ")+QString(p->name)).toStdString().c_str()); + + vcg::tri::io::Importer3DS::LoadMask(file, p, info); + mm.Enable(info.mask); + + int result = vcg::tri::io::Importer3DS::Load(mm.cm, file, p, info); + if (result != vcg::tri::io::Importer3DS::E_NOERROR) { + reportWarning("3DS Opening Error: " + errorMsgFormat.arg(fileName, vcg::tri::io::Importer3DS::ErrorMsg(result))); + continue; } - + if(info.mask & vcg::tri::io::Mask::IOM_WEDGNORMAL) normalsUpdated = true; - - mask = info.mask; - - + + (*miter) |= info.mask; + // verify if texture files are present QString missingTextureFilesMsg = "The following texture files were not found:\n"; bool someTextureNotFound = false; - for ( unsigned textureIdx = 0; textureIdx < m.cm.textures.size(); ++textureIdx) + for ( unsigned textureIdx = 0; textureIdx < mm.cm.textures.size(); ++textureIdx) { - FILE* pFile = fopen (m.cm.textures[textureIdx].c_str(), "r"); - if (pFile == nullptr) - { + FILE* pFile = fopen (mm.cm.textures[textureIdx].c_str(), "r"); + if (pFile == nullptr) { missingTextureFilesMsg.append("\n"); - missingTextureFilesMsg.append(m.cm.textures[textureIdx].c_str()); + missingTextureFilesMsg.append(mm.cm.textures[textureIdx].c_str()); someTextureNotFound = true; } else { @@ -231,12 +229,12 @@ void ExtraMeshIOPlugin::open( if (someTextureNotFound){ reportWarning("Missing texture files: " + missingTextureFilesMsg); } - - vcg::tri::UpdateBounding::Box(m.cm); // updates bounding box - if (!normalsUpdated) - vcg::tri::UpdateNormal::PerVertex(m.cm); // updates normals + + vcg::tri::UpdateBounding::Box(mm.cm); // updates bounding box + if (!normalsUpdated) + vcg::tri::UpdateNormal::PerVertex(mm.cm); // updates normals } - + if (cb != NULL) (*cb)(99, "Done"); // freeing memory diff --git a/src/meshlabplugins/io_3ds/meshio.h b/src/meshlabplugins/io_3ds/meshio.h index 0dd8506d7..23ea813d1 100644 --- a/src/meshlabplugins/io_3ds/meshio.h +++ b/src/meshlabplugins/io_3ds/meshio.h @@ -44,15 +44,24 @@ public: void exportMaskCapability(const QString& format, int &capability, int &defaultBits) const; - void initPreOpenParameter( - const QString& format, - RichParameterList& parameters); + unsigned int numberMeshesContainedInFile( + const QString& format, + const QString& fileName) const; - void open(const QString &formatName, + void open( + const QString &formatName, const QString &fileName, - MeshModel &m, + MeshModel& m, int& mask, - const RichParameterList ¶ms, + const RichParameterList& params, + vcg::CallBackPos *cb=0); + + void open( + const QString &formatName, + const QString &fileName, + const std::list& meshList, + std::list& maskList, + const RichParameterList& params, vcg::CallBackPos *cb=0); void save( From ed6941641ec4198e6ca3b0830669ab1156cda773 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Tue, 20 Apr 2021 16:47:00 +0200 Subject: [PATCH 8/9] small fix - still need to adjust reload --- src/meshlab/mainwindow_RunTime.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/meshlab/mainwindow_RunTime.cpp b/src/meshlab/mainwindow_RunTime.cpp index 5e7a73978..6d415defd 100644 --- a/src/meshlab/mainwindow_RunTime.cpp +++ b/src/meshlab/mainwindow_RunTime.cpp @@ -2586,7 +2586,7 @@ void MainWindow::reload() MeshModel* mm = meshDoc()->mm(); if (meshDoc()->mm()->idInFile() > 0){ for (MeshModel *mmm : meshDoc()->meshList){ - if (mmm->label() == mm->label() && mmm->idInFile() == 0) + if (mmm->shortName() == mm->shortName() && mmm->idInFile() == 0) mm = mmm; // root mesh layer of the file } if (mm == meshDoc()->mm()){ From 3fb5f8aeaeef749d5bfc187a6f9626e6fd54a7a9 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Wed, 21 Apr 2021 14:42:31 +0200 Subject: [PATCH 9/9] reload - reload all working as expected --- src/common/ml_document/mesh_document.cpp | 21 ++++++ src/common/ml_document/mesh_document.h | 2 +- src/common/utilities/load_save.cpp | 88 +++++++++--------------- src/common/utilities/load_save.h | 23 +++++-- src/meshlab/mainwindow_RunTime.cpp | 65 ++++++++++------- 5 files changed, 112 insertions(+), 87 deletions(-) diff --git a/src/common/ml_document/mesh_document.cpp b/src/common/ml_document/mesh_document.cpp index 6714242d2..a923b49b9 100644 --- a/src/common/ml_document/mesh_document.cpp +++ b/src/common/ml_document/mesh_document.cpp @@ -338,6 +338,27 @@ MeshModel * MeshDocument::addOrGetMesh(QString fullPath, const QString& label, b return addNewMesh(std::move(fullPath),label,setAsCurrent); } +/** + * Returns the ordered list of the meshes loaded from the same + * file in which has been loaded the model mm. + */ +std::list MeshDocument::getMeshesLoadedFromSameFile(MeshModel* mm) +{ + std::list ml; + if (mm->idInFile() == -1){ + ml.push_back(mm); + } + else { + if (!mm->fullName().isEmpty()){ + for (MeshModel* m : meshList){ + if (m->fullName() == mm->fullName()) + ml.push_back(m); + } + } + } + return ml; +} + bool MeshDocument::delMesh(MeshModel *mmToDel) { if(!meshList.removeOne(mmToDel)) diff --git a/src/common/ml_document/mesh_document.h b/src/common/ml_document/mesh_document.h index 3775bb758..f27511009 100644 --- a/src/common/ml_document/mesh_document.h +++ b/src/common/ml_document/mesh_document.h @@ -90,7 +90,7 @@ public: MeshModel* addNewMesh(const CMeshO& mesh, QString Label, bool setAsCurrent=true); MeshModel *addNewMesh(QString fullPath, QString Label, bool setAsCurrent=true); MeshModel *addOrGetMesh(QString fullPath, const QString& Label, bool setAsCurrent=true); - + std::list getMeshesLoadedFromSameFile(MeshModel* mm); ///remove the mesh from the list and delete it from memory bool delMesh(MeshModel *mmToDel); diff --git a/src/common/utilities/load_save.cpp b/src/common/utilities/load_save.cpp index bff5b5d59..ef238215b 100644 --- a/src/common/utilities/load_save.cpp +++ b/src/common/utilities/load_save.cpp @@ -149,68 +149,42 @@ void loadMeshWithStandardParameters(const QString& filename, MeshDocument& md, v void reloadMesh( const QString& filename, - MeshDocument& md, - MeshModel& mm, + const std::list& meshList, vcg::CallBackPos* cb) { - // reload can affect only meshes loaded from files containing a single mesh, - // or if the mesh is the first one loaded from that file. - // if a file contains more than one meshes (idInFile != -1), the reload of - // all the contained meshes will be performed by the mesh with idInFile == 0 - if (mm.idInFile() < 1) { - QFileInfo fi(filename); - QString extension = fi.suffix(); - PluginManager& pm = meshlab::pluginManagerInstance(); - IOPlugin *ioPlugin = pm.inputMeshPlugin(extension); - if (ioPlugin == nullptr) { - throw MLException( - "Mesh " + filename + " cannot be opened. Your MeshLab " - "version has not plugin to read " + extension + - " file format"); - } + QFileInfo fi(filename); + QString extension = fi.suffix(); + PluginManager& pm = meshlab::pluginManagerInstance(); + IOPlugin *ioPlugin = pm.inputMeshPlugin(extension); - RichParameterList prePar; - ioPlugin->initPreOpenParameter(extension, prePar); - prePar.join(meshlab::defaultGlobalParameterList()); - - unsigned int nMeshes = ioPlugin->numberMeshesContainedInFile(extension, filename); - std::list meshList; - - //simple case: just one meshe expected in the file - if (mm.idInFile() == -1 && nMeshes == 1) { - meshList.push_back(&mm); - } - else if (mm.idInFile() == 0 && nMeshes > 1) { - //looking for all the other meshes that should be reloaded from the file - for (MeshModel* m : md.meshIterator()) { - if (m->fullName() == mm.fullName() && m->idInFile() >= 0) { - meshList.push_back(m); - } - } - if (meshList.size() != nMeshes){ - throw MLException( - "Cannot reload " + filename + ": the number of layers " - "in this meshlab project associated to this file is " - "different from the number of meshes contained in the " - "file."); - } - } - else { - //ignore this case: this mesh will be reloaded by the mesh with id = 0 - if (! (mm.idInFile() > 0 && nMeshes > 1)) { - throw MLException( - "Cannot reload " + filename + ": expected number layers is " - "different from the number of meshes contained in th file."); - } - } - - std::list masks; - loadMesh(filename, ioPlugin, prePar, meshList, masks, cb); - RichParameterList par; - ioPlugin->initOpenParameter(extension, meshList, par); - ioPlugin->applyOpenParameter(extension, meshList, par); + if (ioPlugin == nullptr) { + throw MLException( + "Mesh " + filename + " cannot be opened. Your MeshLab " + "version has not plugin to read " + extension + + " file format"); } + + RichParameterList prePar; + ioPlugin->initPreOpenParameter(extension, prePar); + prePar.join(meshlab::defaultGlobalParameterList()); + + unsigned int nMeshes = ioPlugin->numberMeshesContainedInFile(extension, filename); + + if (meshList.size() != nMeshes){ + throw MLException( + "Cannot reload " + filename + ": expected number layers is " + "different from the number of meshes contained in th file."); + } + + std::list masks; + for (MeshModel* mm : meshList){ + mm->Clear(); + } + loadMesh(filename, ioPlugin, prePar, meshList, masks, cb); + RichParameterList par; + ioPlugin->initOpenParameter(extension, meshList, par); + ioPlugin->applyOpenParameter(extension, meshList, par); } void loadRaster(const QString& filename, MeshDocument& md, vcg::CallBackPos* cb) diff --git a/src/common/utilities/load_save.h b/src/common/utilities/load_save.h index b8091d414..61e96cebb 100644 --- a/src/common/utilities/load_save.h +++ b/src/common/utilities/load_save.h @@ -34,13 +34,28 @@ namespace meshlab { -void loadMesh(const QString& fileName, IOPlugin* ioPlugin, const RichParameterList& prePar, const std::list& meshList, std::list& maskList, vcg::CallBackPos *cb); +void loadMesh( + const QString& fileName, + IOPlugin* ioPlugin, + const RichParameterList& prePar, + const std::list& meshList, + std::list& maskList, + vcg::CallBackPos *cb); -void loadMeshWithStandardParameters(const QString& filename, MeshDocument& md, vcg::CallBackPos *cb); +void loadMeshWithStandardParameters( + const QString& filename, + MeshDocument& md, + vcg::CallBackPos *cb); -void reloadMesh(const QString& filename, MeshDocument& md, MeshModel& m, vcg::CallBackPos *cb); +void reloadMesh( + const QString& filename, + const std::list& meshList, + vcg::CallBackPos* cb); -void loadRaster(const QString& filename, MeshDocument& md, vcg::CallBackPos *cb); +void loadRaster( + const QString& filename, + MeshDocument& md, + vcg::CallBackPos *cb); } #endif // MESHLAB_LOAD_SAVE_H diff --git a/src/meshlab/mainwindow_RunTime.cpp b/src/meshlab/mainwindow_RunTime.cpp index 6d415defd..5242e8919 100644 --- a/src/meshlab/mainwindow_RunTime.cpp +++ b/src/meshlab/mainwindow_RunTime.cpp @@ -2553,16 +2553,29 @@ void MainWindow::reloadAllMesh() qb->show(); QElapsedTimer t; t.start(); - for(MeshModel *mmm : meshDoc()->meshList) - { - QString fileName = mmm->fullName(); - if (!fileName.isEmpty()){ - try { - meshlab::reloadMesh(fileName, *meshDoc(), *mmm, QCallBack); - computeRenderingDataOnLoading(mmm,true, nullptr); - } - catch (const MLException& e) { - QMessageBox::critical(this, "Reload Error", e.what()); + MeshDocument* md = meshDoc(); + for(MeshModel *mmm : md->meshIterator()) { + if (mmm->idInFile() <= 0){ + QString fileName = mmm->fullName(); + if (!fileName.isEmpty()){ + std::list meshList = meshDoc()->getMeshesLoadedFromSameFile(mmm); + std::vector isReload(meshList.size(), true); + unsigned int i = 0; + for (MeshModel* m : meshList) { + if (m->cm.VN() == 0) + isReload[i] = false; + i++; + } + try { + meshlab::reloadMesh(fileName, meshList, QCallBack); + i = 0; + for (MeshModel* m : meshList){ + computeRenderingDataOnLoading(m, i++, nullptr); + } + } + catch (const MLException& e) { + QMessageBox::critical(this, "Reload Error", e.what()); + } } } } @@ -2583,29 +2596,31 @@ void MainWindow::reload() // Discards changes and reloads current file // save current file name qb->show(); - MeshModel* mm = meshDoc()->mm(); - if (meshDoc()->mm()->idInFile() > 0){ - for (MeshModel *mmm : meshDoc()->meshList){ - if (mmm->shortName() == mm->shortName() && mmm->idInFile() == 0) - mm = mmm; // root mesh layer of the file - } - if (mm == meshDoc()->mm()){ - QMessageBox::critical(this, "Reload Error", "Impossible the file: cannot find the root mesh layer of the file."); - return; - } - } - QString fileName = mm->fullName(); + QString fileName = meshDoc()->mm()->fullName(); if (fileName.isEmpty()) { QMessageBox::critical(this, "Reload Error", "Impossible to reload an unsaved mesh model!!"); return; } + + std::list meshList = meshDoc()->getMeshesLoadedFromSameFile(meshDoc()->mm()); + std::vector isReload(meshList.size(), true); + unsigned int i = 0; + for (MeshModel* m : meshList){ + if (m->cm.VN() == 0) + isReload[i] = false; + i++; + } + try { QElapsedTimer t; t.start(); - meshlab::reloadMesh(fileName, *meshDoc(), *mm, QCallBack); - computeRenderingDataOnLoading(mm, true, nullptr); - GLA()->Log(0, ("Mesh reloaded in " + std::to_string(t.elapsed()) + " msec.").c_str()); + meshlab::reloadMesh(fileName, meshList, QCallBack); + i = 0; + for (MeshModel* m : meshList){ + computeRenderingDataOnLoading(m, i++, nullptr); + } + GLA()->Log(0, ("File reloaded in " + std::to_string(t.elapsed()) + " msec.").c_str()); } catch (const MLException& e) { QMessageBox::critical(this, "Reload Error", e.what());