diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 0f5863cd9..d03cbc0f7 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -125,6 +125,8 @@ set(SOURCES mlapplication.cpp searcher.cpp) +set(RESOURCES meshlab-common.qrc) + set(TARGET_TYPE SHARED) if(WIN32) set(TARGET_TYPE STATIC) diff --git a/src/common/common.pro b/src/common/common.pro index 1b2c4e8bc..f443f098f 100644 --- a/src/common/common.pro +++ b/src/common/common.pro @@ -122,6 +122,9 @@ SOURCES += \ ml_selection_buffers.cpp \ $$MESHLAB_EXTERNAL_DIRECTORY/easyexif/exif.cpp +RESOURCES += \ + meshlab-common.qrc + macx:QMAKE_POST_LINK = "\ if [ -d $$MESHLAB_DISTRIB_DIRECTORY/meshlab.app/Contents/Frameworks/ ]; \ then \ diff --git a/src/common/img/dummy.png b/src/common/img/dummy.png new file mode 100644 index 000000000..03c4c8d2c Binary files /dev/null and b/src/common/img/dummy.png differ diff --git a/src/common/meshlab-common.qrc b/src/common/meshlab-common.qrc new file mode 100644 index 000000000..e7301593c --- /dev/null +++ b/src/common/meshlab-common.qrc @@ -0,0 +1,5 @@ + + + img/dummy.png + + diff --git a/src/common/ml_document/mesh_model.cpp b/src/common/ml_document/mesh_model.cpp index e41085e54..17bf989f0 100644 --- a/src/common/ml_document/mesh_model.cpp +++ b/src/common/ml_document/mesh_model.cpp @@ -26,6 +26,7 @@ #include #include "mesh_model.h" +#include "../utilities/load_save.h" #include @@ -78,6 +79,66 @@ QString MeshModel::relativePathName(const QString& path) const return relPath; } +/** + * @brief Starting from the (still unloaded) textures contained in the contained + * CMeshO, loads the textures in the map of QImages contained in the MeshModel. + * + * The contained CMeshO will have a list of texture names like ":filename.png", + * and these names will be mapped with the actual loaded image in the map + * "textures". + * + * When a texture is not found, a dummy texture will be used (":dummy.png"). + * + * Returns the list of non-loaded textures that have been modified with + * ":dummy.png" in the contained mesh. + */ +std::list MeshModel::loadTextures( + GLLogStream* log, + vcg::CallBackPos* cb) +{ + std::list unloadedTextures; + for (std::string& textName : cm.textures){ + if (textName.front() != ':'){ + QImage img(":/img/dummy.png"); + QFileInfo finfo(QString::fromStdString(textName)); + try { + img = meshlab::loadImage(finfo.absoluteFilePath(), log, cb); + textName = ":" + finfo.fileName().toStdString(); + } catch (const MLException& e) { + try { //could be relative to the meshmodel + QFileInfo mfi(fullName()); + QString fn2 = mfi.absolutePath() + "/" + finfo.fileName(); + img = meshlab::loadImage(fn2, log, cb); + textName = ":" + finfo.fileName().toStdString(); + } catch (const MLException& e) { + if (log){ + log->log( + GLLogStream::WARNING, "Failed loading " + textName + + "; using a dummy texture"); + } + else { + std::cerr << + "Failed loading " + textName + "; using a dummy texture\n"; + } + unloadedTextures.push_back(textName); + textName = ":dummy.png"; + } + } + textures[textName] = img; + } + } + return unloadedTextures; +} + +QImage MeshModel::getTexture(const std::string& tn) +{ + auto it = textures.find(tn); + if (it != textures.end()) + return it->second; + else + return QImage(); +} + int MeshModel::io2mm(int single_iobit) { switch(single_iobit) diff --git a/src/common/ml_document/mesh_model.h b/src/common/ml_document/mesh_model.h index d885ad0b1..75e5d8227 100644 --- a/src/common/ml_document/mesh_model.h +++ b/src/common/ml_document/mesh_model.h @@ -133,7 +133,6 @@ public: int idInFile() const {return idInsideFile;} void setIdInFile(int id) {idInsideFile = id;} - // Some notes about the files and naming. // Each mesh when shown in the layer dialog has a label. // By default the label is just the name of the file, but the @@ -168,6 +167,10 @@ public: bool visible; // used in rendering; Needed for toggling on and off the meshes bool isVisible() const { return visible; } + std::list loadTextures(GLLogStream* log = nullptr, vcg::CallBackPos* cb = nullptr); + + QImage getTexture(const std::string& tn); + // This function is roughly equivalent to the updateDataMask, // but it takes in input a mask coming from a filetype instead of a filter requirement (like topology etc) void enable(int openingFileMask); diff --git a/src/common/utilities/load_save.cpp b/src/common/utilities/load_save.cpp index b20d2b887..a153e0254 100644 --- a/src/common/utilities/load_save.cpp +++ b/src/common/utilities/load_save.cpp @@ -33,8 +33,15 @@ namespace meshlab { -void loadMesh(const QString& fileName, IOPlugin* ioPlugin, const RichParameterList& prePar, const std::list& meshList, std::list& maskList, vcg::CallBackPos *cb) +std::list loadMesh( + const QString& fileName, + IOPlugin* ioPlugin, + const RichParameterList& prePar, + const std::list& meshList, + std::list& maskList, + vcg::CallBackPos *cb) { + std::list unloadedTextures; QFileInfo fi(fileName); QString extension = fi.suffix(); @@ -61,6 +68,9 @@ void loadMesh(const QString& fileName, IOPlugin* ioPlugin, const RichParameterLi MeshModel* mm = *itmesh; int mask = *itmask; + std::list tmp = mm->loadTextures(nullptr, cb); + unloadedTextures.insert(unloadedTextures.end(), tmp.begin(), tmp.end()); + // 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... @@ -100,6 +110,7 @@ void loadMesh(const QString& fileName, IOPlugin* ioPlugin, const RichParameterLi ++itmask; } QDir::setCurrent(origDir); // undo the change of directory before leaving + return unloadedTextures; } diff --git a/src/common/utilities/load_save.h b/src/common/utilities/load_save.h index 3eab98aa5..33fb3d719 100644 --- a/src/common/utilities/load_save.h +++ b/src/common/utilities/load_save.h @@ -34,7 +34,7 @@ namespace meshlab { -void loadMesh( +std::list loadMesh( const QString& fileName, IOPlugin* ioPlugin, const RichParameterList& prePar, diff --git a/src/meshlab/mainwindow_RunTime.cpp b/src/meshlab/mainwindow_RunTime.cpp index 8c00a58f3..e617e93d2 100644 --- a/src/meshlab/mainwindow_RunTime.cpp +++ b/src/meshlab/mainwindow_RunTime.cpp @@ -2164,19 +2164,19 @@ bool MainWindow::loadMesh(const QString& fileName, IOPlugin *pCurrentIOPlugin, c { if ((GLA() == NULL)) return false; - + 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(); - + // retrieving corresponding IO plugin if (pCurrentIOPlugin == 0) { @@ -2187,7 +2187,7 @@ bool MainWindow::loadMesh(const QString& fileName, IOPlugin *pCurrentIOPlugin, c } meshDoc()->setBusy(true); pCurrentIOPlugin->setLog(&meshDoc()->Log); - + unsigned int nMeshes = pCurrentIOPlugin->numberMeshesContainedInFile(extension, fileNameSansDir, prePar); if (nMeshes != meshList.size()) { QMessageBox::warning( @@ -2200,7 +2200,7 @@ bool MainWindow::loadMesh(const QString& fileName, IOPlugin *pCurrentIOPlugin, c } try { - pCurrentIOPlugin->open(extension, fileNameSansDir, meshList ,maskList, prePar, QCallBack); + meshlab::loadMesh(fileName, pCurrentIOPlugin, prePar, meshList, maskList, QCallBack); } catch(const MLException& e) { QMessageBox::warning( @@ -2211,8 +2211,7 @@ bool MainWindow::loadMesh(const QString& fileName, IOPlugin *pCurrentIOPlugin, c QDir::setCurrent(origDir); // undo the change of directory before leaving return false; } - - + //std::cout << "Opened mesh: in " << tm.elapsed() << " secs\n"; // After opening the mesh lets ask to the io plugin if this format // requires some optional, or userdriven post-opening processing. @@ -2221,15 +2220,15 @@ bool MainWindow::loadMesh(const QString& fileName, IOPlugin *pCurrentIOPlugin, c //RichParameterSet par; //pCurrentIOPlugin->initOpenParameter(extension, *mm, par); //pCurrentIOPlugin->applyOpenParameter(extension, *mm, par); - + QString err = pCurrentIOPlugin->warningMessageString(); if (!err.isEmpty()) { QMessageBox::warning(this, tr("Opening Problems"), QString("While opening: '%1'\n\n").arg(fileName)+ err); } - + saveRecentFileList(fileName); - + auto itmesh = meshList.begin(); auto itmask = maskList.begin(); for (unsigned int i = 0; i < meshList.size(); ++i){ @@ -2284,12 +2283,11 @@ bool MainWindow::loadMesh(const QString& fileName, IOPlugin *pCurrentIOPlugin, c } updateLayerDialog(); - - + meshDoc()->setBusy(false); - + QDir::setCurrent(origDir); // undo the change of directory before leaving - + return true; } @@ -2442,7 +2440,8 @@ bool MainWindow::importMesh(QString fileName) try { QElapsedTimer t; t.start(); - meshlab::loadMesh(fileName, pCurrentIOPlugin, prePar, meshList, masks, QCallBack); + std::list unloadedTextures = + meshlab::loadMesh(fileName, pCurrentIOPlugin, prePar, meshList, masks, QCallBack); saveRecentFileList(fileName); updateLayerDialog(); for (MeshModel* mm : meshList) { @@ -2451,6 +2450,11 @@ bool MainWindow::importMesh(QString fileName) updateTexture(mm->id()); } QString warningString = pCurrentIOPlugin->warningMessageString(); + if (unloadedTextures.size() > 0){ + warningString += "\n\nThe following textures have not been loaded: \n"; + for (const std::string& txt : unloadedTextures) + warningString += QString::fromStdString(txt) + "\n"; + } if (!warningString.isEmpty()){ QMessageBox::warning(this, "Meshlab Opening Warning", warningString); } @@ -2947,39 +2951,15 @@ void MainWindow::updateTexture(int meshid) totalTextureNum+=mp->cm.textures.size(); int singleMaxTextureSizeMpx = int(textmemMB/((totalTextureNum != 0)? totalTextureNum : 1)); - bool sometextfailed = false; - QString unexistingtext = "In mesh file " + mymesh->fullName() + " : Failure loading textures:
"; - for(size_t i =0; i< mymesh->cm.textures.size();++i) + + bool sometextnotfound = false; + for(const std::string& textname : mymesh->cm.textures) { - QImage img; - QFileInfo fi(mymesh->cm.textures[i].c_str()); - QFileInfo mfi(mymesh->fullName()); - QString filename = fi.absoluteFilePath(); - bool res = img.load(filename); - if(!res) - { - 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 += "" + fi.fileName() + "
"; - sometextfailed = sometextfailed || !res; - } - } - - /*PLEASE EXPLAIN ME!*********************************************************************************************************************************************************************************/ - //if(!res && filename.endsWith("dds",Qt::CaseInsensitive)) - //{ - // qDebug("DDS binding!"); - // int newTexId = shared->bindTexture(filename); - // shared->txtcont.push_back(newTexId); - //} - /*PLEASE EXPLAIN ME!*********************************************************************************************************************************************************************************/ - - if (!res) + QImage img = mymesh->getTexture(textname); + + if (img.isNull()){ img.load(":/images/dummy.png"); + } GLuint textid = shared->allocateTexturePerMesh(meshid,img,singleMaxTextureSizeMpx); for(int tt = 0;tt < mvc->viewerCounter();++tt) @@ -2989,8 +2969,8 @@ void MainWindow::updateTexture(int meshid) ar->setupTextureEnv(textid); } } - if (sometextfailed) - QMessageBox::warning(this,"Texture file has not been correctly loaded",unexistingtext); + if (sometextnotfound) + QMessageBox::warning(this,"Texture error", "Some texture has not been found. Using dummy texture."); QDir::setCurrent(cwd); }