/**************************************************************************** * MeshLab o o * * Visual and Computer Graphics Library o o * * _ O _ * * Copyright(C) 2004-2021 \/)\/ * * 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 #include #include #include "mesh_model.h" #include "../utilities/load_save.h" #include #include #include using namespace vcg; MeshModel::MeshModel(unsigned int id, const QString& fullFileName, const QString& labelName) : idInsideFile(-1), visible(true) { /*glw.m = &(cm);*/ clear(); _id=id; if(!fullFileName.isEmpty()) this->fullPathFileName=fullFileName; if(!labelName.isEmpty()) this->_label=labelName; } void MeshModel::clear() { setMeshModified(false); // These data are always active on the mesh currentDataMask = MM_NONE; currentDataMask |= MM_VERTCOORD | MM_VERTNORMAL | MM_VERTFLAG ; currentDataMask |= MM_FACEVERT | MM_FACENORMAL | MM_FACEFLAG ; visible=true; cm.Tr.SetIdentity(); cm.sfn=0; cm.svn=0; } void MeshModel::updateBoxAndNormals() { tri::UpdateBounding::Box(cm); if(cm.fn>0) { tri::UpdateNormal::PerFaceNormalized(cm); tri::UpdateNormal::PerVertexAngleWeighted(cm); } } QString MeshModel::relativePathName(const QString& path) const { QDir documentDir (path); QString relPath=documentDir.relativeFilePath(this->fullPathFileName); //if(relPath.size()>1 && relPath[0]=='.' && relPath[1]=='.') // qDebug("Error we have a mesh that is not in the same folder of the project: %s ", qUtf8Printable(relPath)); 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 (":/img/dummy.png"). * * Returns the list of non-loaded textures that have been modified with * ":/img/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 (textures.find(textName) == textures.end()){ 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; } void MeshModel::saveTextures( const QString& basePath, int quality, GLLogStream* log, CallBackPos* cb) { for (const std::string& tname : cm.textures){ meshlab::saveImage( basePath + "/" + QString::fromStdString(tname), textures.at(tname), quality, log, cb); } } QImage MeshModel::getTexture(const std::string& tn) const { auto it = textures.find(tn); if (it != textures.end()) return it->second; else return QImage(); } void MeshModel::clearTextures() { textures.clear(); cm.textures.clear(); } void MeshModel::addTexture(std::string name, const QImage& txt) { cm.textures.push_back(name); textures[name]=txt; } void MeshModel::setTexture(std::string name, const QImage& txt) { auto it = textures.find(name); if (it != textures.end()) it->second = txt; } void MeshModel::changeTextureName( const std::string& oldName, std::string newName) { if (oldName != newName) { auto mit = textures.find(oldName); auto tit = std::find(cm.textures.begin(), cm.textures.end(), oldName); if (mit != textures.end() && tit != cm.textures.end()){ *tit = newName; textures[newName] = mit->second; textures.erase(mit); } } } int MeshModel::io2mm(int single_iobit) { switch(single_iobit) { case tri::io::Mask::IOM_NONE : return MM_NONE; case tri::io::Mask::IOM_VERTCOORD : return MM_VERTCOORD; case tri::io::Mask::IOM_VERTCOLOR : return MM_VERTCOLOR; case tri::io::Mask::IOM_VERTFLAGS : return MM_VERTFLAG; case tri::io::Mask::IOM_VERTQUALITY : return MM_VERTQUALITY; case tri::io::Mask::IOM_VERTNORMAL : return MM_VERTNORMAL; case tri::io::Mask::IOM_VERTTEXCOORD : return MM_VERTTEXCOORD; case tri::io::Mask::IOM_VERTRADIUS : return MM_VERTRADIUS; case tri::io::Mask::IOM_FACEINDEX : return MM_FACEVERT ; case tri::io::Mask::IOM_FACEFLAGS : return MM_FACEFLAG ; case tri::io::Mask::IOM_FACECOLOR : return MM_FACECOLOR ; case tri::io::Mask::IOM_FACEQUALITY : return MM_FACEQUALITY; case tri::io::Mask::IOM_FACENORMAL : return MM_FACENORMAL ; case tri::io::Mask::IOM_WEDGTEXCOORD : return MM_WEDGTEXCOORD; case tri::io::Mask::IOM_WEDGCOLOR : return MM_WEDGCOLOR; case tri::io::Mask::IOM_WEDGNORMAL : return MM_WEDGNORMAL ; case tri::io::Mask::IOM_BITPOLYGONAL : return MM_POLYGONAL ; default: assert(0); return MM_NONE; // FIXME: Returning this is not the best solution (!) break; } ; } /**** DATAMASK STUFF ****/ bool MeshModel::hasDataMask(const int maskToBeTested) const { return ((currentDataMask & maskToBeTested)!= 0); } void MeshModel::updateDataMask() { currentDataMask = MM_NONE; currentDataMask |= MM_VERTCOORD | MM_VERTNORMAL | MM_VERTFLAG | MM_VERTQUALITY | MM_VERTCOLOR; currentDataMask |= MM_FACEVERT | MM_FACENORMAL | MM_FACEFLAG ; if (cm.vert.IsVFAdjacencyEnabled()) currentDataMask |= MM_VERTFACETOPO; if (cm.vert.IsMarkEnabled()) currentDataMask |= MM_VERTMARK; if (cm.vert.IsTexCoordEnabled()) currentDataMask |= MM_VERTTEXCOORD; if (cm.vert.IsCurvatureEnabled()) currentDataMask |= MM_VERTCURV; if (cm.vert.IsCurvatureDirEnabled()) currentDataMask |= MM_VERTCURVDIR; if (cm.vert.IsRadiusEnabled()) currentDataMask |= MM_VERTRADIUS; if (cm.face.IsQualityEnabled()) currentDataMask |= MM_FACEQUALITY; if (cm.face.IsMarkEnabled()) currentDataMask |= MM_FACEMARK; if (cm.face.IsColorEnabled()) currentDataMask |= MM_FACECOLOR; if (cm.face.IsFFAdjacencyEnabled()) currentDataMask |= MM_FACEFACETOPO; if (cm.face.IsVFAdjacencyEnabled()) currentDataMask |= MM_VERTFACETOPO; if (cm.face.IsCurvatureDirEnabled()) currentDataMask |= MM_FACECURVDIR; if (cm.face.IsWedgeTexCoordEnabled()) currentDataMask |= MM_WEDGTEXCOORD; } void MeshModel::updateDataMask(const MeshModel *m) { updateDataMask(m->currentDataMask); } void MeshModel::updateDataMask(int neededDataMask) { if((neededDataMask & MM_FACEFACETOPO)!=0) { cm.face.EnableFFAdjacency(); tri::UpdateTopology::FaceFace(cm); } if((neededDataMask & MM_VERTFACETOPO)!=0) { cm.vert.EnableVFAdjacency(); cm.face.EnableVFAdjacency(); tri::UpdateTopology::VertexFace(cm); } if((neededDataMask & MM_WEDGTEXCOORD)!=0) cm.face.EnableWedgeTexCoord(); if((neededDataMask & MM_FACECOLOR)!=0) cm.face.EnableColor(); if((neededDataMask & MM_FACEQUALITY)!=0) cm.face.EnableQuality(); if((neededDataMask & MM_FACECURVDIR)!=0) cm.face.EnableCurvatureDir(); if((neededDataMask & MM_FACEMARK)!=0) cm.face.EnableMark(); if((neededDataMask & MM_VERTMARK)!=0) cm.vert.EnableMark(); if((neededDataMask & MM_VERTCURV)!=0) cm.vert.EnableCurvature(); if((neededDataMask & MM_VERTCURVDIR)!=0) cm.vert.EnableCurvatureDir(); if((neededDataMask & MM_VERTRADIUS)!=0) cm.vert.EnableRadius(); if((neededDataMask & MM_VERTTEXCOORD)!=0) cm.vert.EnableTexCoord(); currentDataMask |= neededDataMask; } void MeshModel::clearDataMask(int unneededDataMask) { if( ( (unneededDataMask & MM_VERTFACETOPO)!=0) && hasDataMask(MM_VERTFACETOPO)) {cm.face.DisableVFAdjacency(); cm.vert.DisableVFAdjacency(); } if( ( (unneededDataMask & MM_FACEFACETOPO)!=0) && hasDataMask(MM_FACEFACETOPO)) cm.face.DisableFFAdjacency(); if( ( (unneededDataMask & MM_WEDGTEXCOORD)!=0) && hasDataMask(MM_WEDGTEXCOORD)) cm.face.DisableWedgeTexCoord(); if( ( (unneededDataMask & MM_FACECOLOR)!=0) && hasDataMask(MM_FACECOLOR)) cm.face.DisableColor(); if( ( (unneededDataMask & MM_FACEQUALITY)!=0) && hasDataMask(MM_FACEQUALITY)) cm.face.DisableQuality(); if( ( (unneededDataMask & MM_FACEMARK)!=0) && hasDataMask(MM_FACEMARK)) cm.face.DisableMark(); if( ( (unneededDataMask & MM_VERTMARK)!=0) && hasDataMask(MM_VERTMARK)) cm.vert.DisableMark(); if( ( (unneededDataMask & MM_VERTCURV)!=0) && hasDataMask(MM_VERTCURV)) cm.vert.DisableCurvature(); if( ( (unneededDataMask & MM_VERTCURVDIR)!=0) && hasDataMask(MM_VERTCURVDIR)) cm.vert.DisableCurvatureDir(); if( ( (unneededDataMask & MM_VERTRADIUS)!=0) && hasDataMask(MM_VERTRADIUS)) cm.vert.DisableRadius(); if( ( (unneededDataMask & MM_VERTTEXCOORD)!=0) && hasDataMask(MM_VERTTEXCOORD)) cm.vert.DisableTexCoord(); currentDataMask = currentDataMask & (~unneededDataMask); } void MeshModel::enable(int openingFileMask) { if( openingFileMask & tri::io::Mask::IOM_VERTTEXCOORD ) updateDataMask(MM_VERTTEXCOORD); if( openingFileMask & tri::io::Mask::IOM_WEDGTEXCOORD ) updateDataMask(MM_WEDGTEXCOORD); if( openingFileMask & tri::io::Mask::IOM_VERTCOLOR ) updateDataMask(MM_VERTCOLOR); if( openingFileMask & tri::io::Mask::IOM_FACECOLOR ) updateDataMask(MM_FACECOLOR); if( openingFileMask & tri::io::Mask::IOM_VERTRADIUS ) updateDataMask(MM_VERTRADIUS); if( openingFileMask & tri::io::Mask::IOM_CAMERA ) updateDataMask(MM_CAMERA); if( openingFileMask & tri::io::Mask::IOM_VERTQUALITY ) updateDataMask(MM_VERTQUALITY); if( openingFileMask & tri::io::Mask::IOM_FACEQUALITY ) updateDataMask(MM_FACEQUALITY); if( openingFileMask & tri::io::Mask::IOM_BITPOLYGONAL ) updateDataMask(MM_POLYGONAL); } bool MeshModel::meshModified() const { return modified; } void MeshModel::setMeshModified(bool b) { modified = b; } int MeshModel::dataMask() const { return currentDataMask; }