/**************************************************************************** * MeshLab o o * * An extendible mesh processor o o * * _ O _ * * Copyright(C) 2005, 2006 \/)\/ * * 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 "baseio.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; using namespace vcg; class PEdge; class PFace; class PVertex; struct PUsedTypes : public UsedTypes ::AsVertexType, Use ::AsEdgeType, Use ::AsFaceType> {}; class PVertex : public Vertex {}; class PEdge : public Edge< PUsedTypes, edge::VertexRef, edge::BitFlags> {}; class PFace :public vcg::Face< PUsedTypes, face::PolyInfo, // this is necessary if you use component in vcg/simplex/face/component_polygon.h face::PFVAdj, // Pointer to the vertices (just like FVAdj ) face::Color4b, face::BitFlags, // bit flags face::Normal3f, // normal face::WedgeTexCoord2f > {}; class PMesh : public tri::TriMesh< vector, vector, vector > {}; // initialize importing parameters void BaseMeshIOPlugin::initPreOpenParameter(const QString &formatName, const QString &/*filename*/, RichParameterList &parlst) { if (formatName.toUpper() == tr("PTX")) { parlst.addParam(RichInt("meshindex", 0, "Index of Range Map to be Imported", "PTX files may contain more than one range map. 0 is the first range map. If the number if higher than the actual mesh number, the import will fail")); parlst.addParam(RichBool("pointsonly", true, "Keep only points", "Import points a point cloud only, with radius and normals, no triangulation involved, isolated points and points with normals with steep angles are removed.")); parlst.addParam(RichBool("usecolor", true, "import color", "Read color from PTX, if color is not present, uses reflectance instead")); parlst.addParam(RichBool("flipfaces", false, "LEICA: flip normal direction", "LEICA PTX exporter goes counterclockwise, FARO PTX exporter goes clockwise")); parlst.addParam(RichBool("pointcull", true, "delete unsampled points", "Deletes unsampled points in the grid that are normally located in [0,0,0]")); parlst.addParam(RichBool("anglecull", true, "Cull faces by angle", "short")); parlst.addParam(RichFloat("angle", 85.0, "Angle limit for face culling", "short")); } if (formatName.toUpper() == tr("STL")) { parlst.addParam(RichBool(stlUnifyParName(), true, "Unify Duplicated Vertices in STL files", "The STL format is not an vertex-indexed format. Each triangle is composed by independent vertices, so, usually, duplicated vertices should be unified")); } } bool BaseMeshIOPlugin::open(const QString &formatName, const QString &fileName, MeshModel &m, int& mask, const RichParameterList &parlst, CallBackPos *cb, QWidget * /*parent*/) { //bool normalsUpdated = false; QString errorMsgFormat = "Error encountered while loading file:\n\"%1\"\n\nError details: %2"; if(!QFile::exists(fileName)) { errorMessage = errorMsgFormat.arg(fileName, "File does not exist"); return false; } // initializing mask mask = 0; // initializing progress bar status if (cb != NULL) (*cb)(0, "Loading..."); //string filename = fileName.toUtf8().data(); string filename = QFile::encodeName(fileName).constData(); if (formatName.toUpper() == tr("PLY")) { tri::io::ImporterPLY::LoadMask(filename.c_str(), mask); // small patch to allow the loading of per wedge color into faces. if (mask & tri::io::Mask::IOM_WEDGCOLOR) mask |= tri::io::Mask::IOM_FACECOLOR; m.Enable(mask); int result = tri::io::ImporterPLY::Open(m.cm, filename.c_str(), mask, cb); if (result != 0) // all the importers return 0 on success { if (tri::io::ImporterPLY::ErrorCritical(result)) { errorMessage = errorMsgFormat.arg(fileName, tri::io::ImporterPLY::ErrorMsg(result)); return false; } } } else if (formatName.toUpper() == tr("STL")) { if (!tri::io::ImporterSTL::LoadMask(filename.c_str(), mask)) { errorMessage = errorMsgFormat.arg(fileName, tri::io::ImporterSTL::ErrorMsg(tri::io::ImporterSTL::E_MALFORMED)); return false; } m.Enable(mask); int result = tri::io::ImporterSTL::Open(m.cm, filename.c_str(), mask, cb); if (result != 0) // all the importers return 0 on success { errorMessage = errorMsgFormat.arg(fileName, tri::io::ImporterSTL::ErrorMsg(result)); return false; } bool stluinf = parlst.getBool(stlUnifyParName()); if (stluinf) { tri::Clean::RemoveDuplicateVertex(m.cm); tri::Allocator::CompactEveryVector(m.cm); } } else if ((formatName.toUpper() == tr("OBJ")) || (formatName.toUpper() == tr("QOBJ"))) { tri::io::ImporterOBJ::Info oi; oi.cb = cb; if (!tri::io::ImporterOBJ::LoadMask(filename.c_str(), oi)) return false; m.Enable(oi.mask); int result = tri::io::ImporterOBJ::Open(m.cm, filename.c_str(), oi); if (result != tri::io::ImporterOBJ::E_NOERROR) { if (result & tri::io::ImporterOBJ::E_NON_CRITICAL_ERROR) errorMessage = errorMsgFormat.arg(fileName, tri::io::ImporterOBJ::ErrorMsg(result)); else { errorMessage = errorMsgFormat.arg(fileName, tri::io::ImporterOBJ::ErrorMsg(result)); return false; } } // if (oi.mask & tri::io::Mask::IOM_WEDGNORMAL) // normalsUpdated = true; m.Enable(oi.mask); if (m.hasDataMask(MeshModel::MM_POLYGONAL)) qDebug("Mesh is Polygonal!"); mask = oi.mask; } else if (formatName.toUpper() == tr("PTX")) { tri::io::ImporterPTX::Info importparams; importparams.meshnum = parlst.getInt("meshindex"); importparams.anglecull = parlst.getBool("anglecull"); importparams.angle = parlst.getFloat("angle"); importparams.savecolor = parlst.getBool("usecolor"); importparams.pointcull = parlst.getBool("pointcull"); importparams.pointsonly = parlst.getBool("pointsonly"); importparams.flipfaces = parlst.getBool("flipfaces"); // if color, add to mesh if (importparams.savecolor) importparams.mask |= tri::io::Mask::IOM_VERTCOLOR; if (importparams.pointsonly) importparams.mask |= tri::io::Mask::IOM_VERTRADIUS; // reflectance is stored in quality importparams.mask |= tri::io::Mask::IOM_VERTQUALITY; m.Enable(importparams.mask); int result = tri::io::ImporterPTX::Open(m.cm, filename.c_str(), importparams, cb); if (result == 1) { errorMessage = errorMsgFormat.arg(fileName, tri::io::ImporterPTX::ErrorMsg(result)); return false; } // update mask mask = importparams.mask; } else if (formatName.toUpper() == tr("OFF")) { int loadMask; if (!tri::io::ImporterOFF::LoadMask(filename.c_str(), loadMask)) { errorMessage = errorMsgFormat.arg(fileName, tri::io::ImporterOFF::ErrorMsg(tri::io::ImporterOFF::InvalidFile)); return false; } m.Enable(loadMask); int result = tri::io::ImporterOFF::Open(m.cm, filename.c_str(), mask, cb); if (result != 0) // OFFCodes enum is protected { errorMessage = errorMsgFormat.arg(fileName, tri::io::ImporterOFF::ErrorMsg(result)); return false; } } else if (formatName.toUpper() == tr("VMI")) { int loadMask; if (!tri::io::ImporterVMI::LoadMask(filename.c_str(), loadMask)) return false; m.Enable(loadMask); int result = tri::io::ImporterVMI::Open(m.cm, filename.c_str(), mask, cb); if (result != 0) { errorMessage = errorMsgFormat.arg(fileName, tri::io::ImporterOFF::ErrorMsg(result)); return false; } } else if (formatName.toUpper() == tr("GTS")) { int loadMask; if (!tri::io::ImporterGTS::LoadMask(filename.c_str(), loadMask)) return false; m.Enable(loadMask); tri::io::ImporterGTS::Options opt; opt.flipFaces = true; int result = tri::io::ImporterGTS::Open(m.cm, filename.c_str(), mask, opt, cb); if (result != 0) { errorMessage = errorMsgFormat.arg(fileName, vcg::tri::io::ImporterGTS::ErrorMsg(result)); return false; } } else if (formatName.toUpper() == tr("FBX")) { m.Enable(tri::io::Mask::IOM_WEDGTEXCOORD); int result = tri::io::ImporterFBX::Open(m.cm, filename.c_str(),cb); if(m.cm.textures.empty()) m.clearDataMask(tri::io::Mask::IOM_WEDGTEXCOORD); if (result != 0) { errorMessage = errorMsgFormat.arg(fileName, vcg::tri::io::ImporterFBX::ErrorMsg(result)); return false; } }else { assert(0); // Unknown File type return false; } // Add a small pass to convert backslash into forward slash for(auto i = m.cm.textures.begin();i!=m.cm.textures.end();++i) { std::replace(i->begin(), i->end(), '\\', '/'); } // 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) { if (!QFile::exists(m.cm.textures[textureIdx].c_str())) { missingTextureFilesMsg.append("\n"); missingTextureFilesMsg.append(m.cm.textures[textureIdx].c_str()); someTextureNotFound = true; } } if (someTextureNotFound) log("Missing texture files: %s", qUtf8Printable(missingTextureFilesMsg)); if (cb != NULL) (*cb)(99, "Done"); return true; } bool BaseMeshIOPlugin::save(const QString &formatName, const QString &fileName, MeshModel &m, const int mask, const RichParameterList & par, CallBackPos *cb, QWidget * /*parent*/) { QString errorMsgFormat = "Error encountered while exportering file %1:\n%2"; string filename = QFile::encodeName(fileName).constData(); //string filename = fileName.toUtf8().data(); string ex = formatName.toUtf8().data(); bool binaryFlag = false; if (formatName.toUpper() == tr("STL") || formatName.toUpper() == tr("PLY")) binaryFlag = par.getBool("Binary"); if (formatName.toUpper() == tr("PLY")) { tri::io::PlyInfo pi; pi.mask = mask; // custom attributes for (const RichParameter& pr : par) { QString pname = pr.name(); if (pname.startsWith("PVAF")){ // if pname starts with PVAF, it is a PLY per-vertex float custom attribute if (par.getBool(pname)) // if it is true, add to save list pi.AddPerVertexFloatAttribute(qUtf8Printable(pname.mid(4))); } else if (pname.startsWith("PVA3F")){ // if pname starts with PVA3F, it is a PLY per-vertex point3f custom attribute if (par.getBool(pname)) // if it is true, add to save list pi.AddPerVertexPoint3fAttribute(m.cm, qUtf8Printable(pname.mid(5))); } else if (pname.startsWith("PFAF")){ // if pname starts with PFAF, it is a PLY per-face float custom attribute if (par.getBool(pname)) // if it is true, add to save list pi.AddPerFaceFloatAttribute(qUtf8Printable(pname.mid(4))); } else if (pname.startsWith("PFA3F")){ // if pname starts with PFA3F, it is a PLY per-face point3f custom attribute //if (par.findParameter(pname)->value().getBool()) // if it is true, add to save list //pi.add(m, qUtf8Printable(pname.mid(5))); } } int result = tri::io::ExporterPLY::Save(m.cm, filename.c_str(), binaryFlag, pi, cb); if (result != 0) { errorMessage = errorMsgFormat.arg(fileName, tri::io::ExporterPLY::ErrorMsg(result)); return false; } return true; } if (formatName.toUpper() == tr("STL")) { bool magicsFlag = par.getBool("ColorMode"); int result = tri::io::ExporterSTL::Save(m.cm, filename.c_str(), binaryFlag, mask, "STL generated by MeshLab", magicsFlag); if (result != 0) { errorMessage = errorMsgFormat.arg(fileName, tri::io::ExporterSTL::ErrorMsg(result)); return false; } return true; } if (formatName.toUpper() == tr("WRL")) { int result = tri::io::ExporterWRL::Save(m.cm, filename.c_str(), mask, cb); if (result != 0) { errorMessage = errorMsgFormat.arg(fileName, tri::io::ExporterWRL::ErrorMsg(result)); return false; } return true; } if (formatName.toUpper() == tr("OFF")) { if (mask & tri::io::Mask::IOM_BITPOLYGONAL) m.updateDataMask(MeshModel::MM_FACEFACETOPO); int result = tri::io::ExporterOFF::Save(m.cm, filename.c_str(), mask); if (result != 0) { errorMessage = errorMsgFormat.arg(fileName, tri::io::ExporterOFF::ErrorMsg(result)); return false; } return true; } if (formatName.toUpper() == tr("OBJ")) { tri::Allocator::CompactEveryVector(m.cm); int result; if ((mask & tri::io::Mask::IOM_BITPOLYGONAL) && (par.getBool("poligonalize"))) { m.updateDataMask(MeshModel::MM_FACEFACETOPO); PMesh pm; tri::PolygonSupport::ImportFromTriMesh(pm, m.cm); result = tri::io::ExporterOBJ::Save(pm, filename.c_str(), mask, cb); } else { result = tri::io::ExporterOBJ::Save(m.cm, filename.c_str(), mask, cb); } if (result != 0) { errorMessage = errorMsgFormat.arg(fileName, tri::io::ExporterOBJ::ErrorMsg(result)); return false; } return true; } if (formatName.toUpper() == tr("DXF")) { int result = tri::io::ExporterDXF::Save(m.cm, filename.c_str()); if (result != 0) { errorMessage = errorMsgFormat.arg(fileName, tri::io::ExporterDXF::ErrorMsg(result)); return false; } return true; } if (formatName.toUpper() == tr("GTS")) { int result = vcg::tri::io::ExporterGTS::Save(m.cm, filename.c_str(), mask); if (result != 0) { errorMessage = errorMsgFormat.arg(fileName, vcg::tri::io::ExporterGTS::ErrorMsg(result)); return false; } return true; } assert(0); // unknown format return false; } QString BaseMeshIOPlugin::pluginName() const { return "IOBase"; } /* returns the list of the file's type which can be imported */ QList BaseMeshIOPlugin::importFormats() const { QList formatList; formatList << FileFormat("Stanford Polygon File Format", tr("PLY")); formatList << FileFormat("STL File Format", tr("STL")); formatList << FileFormat("Alias Wavefront Object", tr("OBJ")); formatList << FileFormat("Quad Object", tr("QOBJ")); formatList << FileFormat("Object File Format", tr("OFF")); formatList << FileFormat("PTX File Format", tr("PTX")); formatList << FileFormat("VCG Dump File Format", tr("VMI")); formatList << FileFormat("FBX Autodesk Interchange Format", tr("FBX")); return formatList; } /* returns the list of the file's type which can be exported */ QList BaseMeshIOPlugin::exportFormats() const { QList formatList; formatList << FileFormat("Stanford Polygon File Format", tr("PLY")); formatList << FileFormat("STL File Format", tr("STL")); formatList << FileFormat("Alias Wavefront Object", tr("OBJ")); formatList << FileFormat("Object File Format", tr("OFF")); formatList << FileFormat("VRML File Format", tr("WRL")); formatList << FileFormat("DXF File Format", tr("DXF")); return formatList; } /* returns the mask on the basis of the file's type. otherwise it returns 0 if the file format is unknown */ void BaseMeshIOPlugin::GetExportMaskCapability(const QString &format, int &capability, int &defaultBits) const { if (format.toUpper() == tr("PLY")) { capability = tri::io::ExporterPLY::GetExportMaskCapability(); // For the default bits of the ply format disable flags and normals that usually are not useful. defaultBits = capability; defaultBits &= (~tri::io::Mask::IOM_FLAGS); defaultBits &= (~tri::io::Mask::IOM_VERTNORMAL); defaultBits &= (~tri::io::Mask::IOM_FACENORMAL); } if (format.toUpper() == tr("STL")) { capability = tri::io::ExporterSTL::GetExportMaskCapability(); defaultBits = capability; } if (format.toUpper() == tr("OBJ")) { capability = defaultBits = tri::io::ExporterOBJ::GetExportMaskCapability() | tri::io::Mask::IOM_BITPOLYGONAL; } if (format.toUpper() == tr("OFF")) { capability = defaultBits = tri::io::ExporterOFF::GetExportMaskCapability(); } if (format.toUpper() == tr("WRL")) { capability = defaultBits = tri::io::ExporterWRL::GetExportMaskCapability(); } if (format.toUpper() == tr("DXF")) { capability = defaultBits = tri::io::Mask::IOM_VERTCOORD | tri::io::Mask::IOM_FACEINDEX;} } //void BaseMeshIOPlugin::initOpenParameter(const QString &format, MeshModel &/*m*/, RichParameterSet &par) //{ // if(format.toUpper() == tr("STL")) // par.addParam(new RichBool("Unify",true, "Unify Duplicated Vertices", // "The STL format is not an vertex-indexed format. Each triangle is composed by independent vertices, so, usually, duplicated vertices should be unified")); //} void BaseMeshIOPlugin::initSaveParameter(const QString &format, MeshModel &m, RichParameterList &par) { if (format.toUpper() == tr("STL") || format.toUpper() == tr("PLY")) par.addParam(RichBool("Binary", true, "Binary encoding", "Save the mesh using a binary encoding. If false the mesh is saved in a plain, readable ascii format.")); if (format.toUpper() == tr("STL")) par.addParam(RichBool("ColorMode", true, "Materialise Color Encoding", "Save the color using a binary encoding according to the Materialise's Magic style (e.g. RGB coding instead of BGR coding).")); if (format.toUpper() == tr("OBJ") && m.hasDataMask(MeshModel::MM_POLYGONAL)) //only shows up when the poligonalization is possible par.addParam(RichBool("poligonalize", false, "Convert triangles to polygons", "The layer seems to have faux-edges, if true, MeshLab will try to convert triangles to polygons before exporting. WARNING: unstable, may cause crash")); // default is false, because it is a bit buggy, and should be anable only when sure if (format.toUpper() == tr("PLY")){ std::vector AttribNameVector; vcg::tri::Allocator::GetAllPerVertexAttribute< float >(m.cm, AttribNameVector); for (int i = 0; i < (int)AttribNameVector.size(); i++) { QString va_name = AttribNameVector[i].c_str(); par.addParam(RichBool("PVAF" + va_name, false, "V(f): " + va_name, "Save this custom scalar (f) per-vertex attribute.")); } vcg::tri::Allocator::GetAllPerVertexAttribute< vcg::Point3f >(m.cm, AttribNameVector); for (int i = 0; i < (int)AttribNameVector.size(); i++) { QString va_name = AttribNameVector[i].c_str(); par.addParam(RichBool("PVA3F" + va_name, false, "V(3f): " + va_name, "Save this custom vector (3f) per-vertex attribute.")); } vcg::tri::Allocator::GetAllPerFaceAttribute< float >(m.cm, AttribNameVector); for (int i = 0; i < (int)AttribNameVector.size(); i++) { QString va_name = AttribNameVector[i].c_str(); par.addParam(RichBool("PFAF" + va_name, false, "F(f): " + va_name, "Save this custom scalar (f) per-face attribute.")); } vcg::tri::Allocator::GetAllPerFaceAttribute< vcg::Point3f >(m.cm, AttribNameVector); for (int i = 0; i < (int)AttribNameVector.size(); i++) { QString va_name = AttribNameVector[i].c_str(); par.addParam(RichBool("PFA3F" + va_name, false, "F(3f): " + va_name, "Save this custom vector (3f) per-face attribute.")); } } } //void BaseMeshIOPlugin::applyOpenParameter(const QString &format, MeshModel &m, const RichParameterSet &par) //{ // if(format.toUpper() == tr("STL")) // if (par.findParameter(stlUnifyParName())->value().getBool()) // { // tri::Clean::RemoveDuplicateVertex(m.cm); // tri::Allocator::CompactEveryVector(m.cm); // } //} MESHLAB_PLUGIN_NAME_EXPORTER(BaseMeshIOPlugin)