From 89f2abd68df81b33af564a06bb9be83758234585 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Fri, 11 Jun 2021 14:40:00 +0200 Subject: [PATCH] expose mesh conversions in meshlab-common --- src/common/CMakeLists.txt | 2 + src/common/common.pro | 2 + .../utilities/eigen_mesh_conversions.cpp | 341 ++++++++++++++++++ src/common/utilities/eigen_mesh_conversions.h | 58 +++ src/common/utilities/load_save.cpp | 2 +- src/common/utilities/load_save.h | 2 +- .../filter_mesh_booleans.cpp | 80 +--- .../filter_mesh_booleans.h | 9 - 8 files changed, 416 insertions(+), 80 deletions(-) create mode 100644 src/common/utilities/eigen_mesh_conversions.cpp create mode 100644 src/common/utilities/eigen_mesh_conversions.h diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index e0685a4b6..101f0e93e 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -70,6 +70,7 @@ set(HEADERS python/function_parameter.h python/function_set.h python/python_utils.h + utilities/eigen_mesh_conversions.h utilities/file_format.h utilities/load_save.h globals.h @@ -111,6 +112,7 @@ set(SOURCES python/function_parameter.cpp python/function_set.cpp python/python_utils.cpp + utilities/eigen_mesh_conversions.cpp utilities/load_save.cpp globals.cpp GLExtensionsManager.cpp diff --git a/src/common/common.pro b/src/common/common.pro index b80f4f6dd..34743bafc 100644 --- a/src/common/common.pro +++ b/src/common/common.pro @@ -70,6 +70,7 @@ HEADERS += \ python/function_parameter.h \ python/function_set.h \ python/python_utils.h \ + utilities/eigen_mesh_conversions.h \ utilities/file_format.h \ utilities/load_save.h \ GLExtensionsManager.h \ @@ -110,6 +111,7 @@ SOURCES += \ python/function_parameter.cpp \ python/function_set.cpp \ python/python_utils.cpp \ + utilities/eigen_mesh_conversions.cpp \ utilities/load_save.cpp \ GLExtensionsManager.cpp \ filterscript.cpp \ diff --git a/src/common/utilities/eigen_mesh_conversions.cpp b/src/common/utilities/eigen_mesh_conversions.cpp new file mode 100644 index 000000000..7b73117de --- /dev/null +++ b/src/common/utilities/eigen_mesh_conversions.cpp @@ -0,0 +1,341 @@ +/**************************************************************************** +* MeshLab o o * +* A versatile mesh processing toolbox o o * +* _ O _ * +* Copyright(C) 2005-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 "eigen_mesh_conversions.h" +#include "../mlexception.h" + +/** + * @brief Creates a CMeshO mesh from the data contained in the given matrices. + * The only matrix required to be non-empty is the 'vertices' matrix. + * Other matrices may be empty. + * + * If normals and quality matrices are give, their sizes must be coherent with + * the sizes of vertex and face matrices. If this requirement is not satisfied, + * a MLException will be thrown. + * + * @param vertices: #V*3 matrix of scalars (vertex coordinates) + * @param faces: #F*3 matrix of integers (vertex indices composing the faces) + * @param vertexNormals: #V*3 matrix of scalars (vertex normals) + * @param faceNormals: #F*3 matrix of scalars (face normals) + * @param vertexQuality: #V vector of scalars (vertex quality) + * @param faceQuality: #F vector of scalars (face quality) + * @return a CMeshO made of the given components + */ +CMeshO meshlab::meshFromMatrices( + const EigenMatrixX3m& vertices, + const Eigen::MatrixX3i& faces, + const EigenMatrixX3m& vertexNormals, + const EigenMatrixX3m& faceNormals, + const EigenVectorXm& vertexQuality, + const EigenVectorXm& faceQuality) +{ + CMeshO m; + if (vertices.rows() > 0) { + //add vertices and their associated normals and quality if any + std::vector ivp(vertices.rows()); + + bool hasVNormals = vertexNormals.rows() > 0; + bool hasVQuality = vertexQuality.rows() > 0; + if (hasVNormals && (vertices.rows() != vertexNormals.rows())) { + throw MLException( + "Error while creating mesh: the number of vertex normals " + "is different from the number of vertices."); + } + if (hasVQuality && (vertices.rows() != vertexQuality.size())) { + throw MLException( + "Error while creating mesh: the number of vertex quality " + "values is different from the number of vertices."); + } + CMeshO::VertexIterator vi = + vcg::tri::Allocator::AddVertices(m, vertices.rows()); + for (unsigned int i = 0; i < vertices.rows(); ++i, ++vi) { + ivp[i] = &*vi; + vi->P() = CMeshO::CoordType(vertices(i,0), vertices(i,1), vertices(i,2)); + if (hasVNormals) { + vi->N() = CMeshO::CoordType( + vertexNormals(i,0), + vertexNormals(i,1), + vertexNormals(i,2)); + } + if (hasVQuality) { + vi->Q() = vertexQuality(i); + } + } + + //add faces and their associated normals and quality if any + + bool hasFNormals = faceNormals.rows() > 0; + bool hasFQuality = faceQuality.rows() > 0; + if (hasFNormals && (faces.rows() != faceNormals.rows())) { + throw MLException( + "Error while creating mesh: the number of face normals " + "is different from the number of faces."); + } + if (hasFQuality) { + if (faces.rows() != faceQuality.size()) { + throw MLException( + "Error while creating mesh: the number of face quality " + "values is different from the number of faces."); + } + m.face.EnableQuality(); + } + CMeshO::FaceIterator fi = + vcg::tri::Allocator::AddFaces(m, faces.rows()); + for (unsigned int i = 0; i < faces.rows(); ++i, ++fi) { + for (unsigned int j = 0; j < 3; j++){ + if ((unsigned int)faces(i,j) >= ivp.size()) { + throw MLException( + "Error while creating mesh: bad vertex index " + + QString::number(faces(i,j)) + " in face " + + QString::number(i) + "; vertex " + QString::number(j) + "."); + } + } + fi->V(0)=ivp[faces(i,0)]; + fi->V(1)=ivp[faces(i,1)]; + fi->V(2)=ivp[faces(i,2)]; + + if (hasFNormals){ + fi->N() = CMeshO::CoordType( + faceNormals(i,0), + faceNormals(i,1), + faceNormals(i,2)); + } + if (hasFQuality) { + fi->Q() = faceQuality(i); + } + } + if (!hasFNormals){ + vcg::tri::UpdateNormal::PerFace(m); + } + if (!hasVNormals){ + vcg::tri::UpdateNormal::PerVertex(m); + } + } + else { + throw MLException("Error while creating mesh: Vertex matrix is empty."); + } + + return m; +} + +/** + * @brief Get a #V*3 Eigen matrix of scalars containing the coordinates of the + * vertices of a CMeshO. + * The vertices in the mesh must be compact (no deleted vertices). + * If the mesh is not compact, a vcg::MissingCompactnessException will be thrown. + * + * @param mesh: input mesh + * @return #V*3 matrix of scalars (vertex coordinates) + */ +EigenMatrixX3m meshlab::vertexMatrix(const CMeshO& mesh) +{ + vcg::tri::RequireVertexCompactness(mesh); + + // create eigen matrix of vertices + EigenMatrixX3m vert(mesh.VN(), 3); + + // copy vertices + for (int i = 0; i < mesh.VN(); i++){ + for (int j = 0; j < 3; j++){ + vert(i,j) = mesh.vert[i].cP()[j]; + } + } + + return vert; +} + +/** + * @brief Get a #F*3 Eigen matrix of integers containing the vertex indices of + * a CMeshO. + * The faces in the mesh must be compact (no deleted faces). + * If the mesh is not compact, a vcg::MissingCompactnessException will be thrown. + * + * @param mesh: input mesh + * @return #F*3 matrix of integers (vertex indices composing the faces) + */ +Eigen::MatrixX3i meshlab::faceMatrix(const CMeshO& mesh) +{ + vcg::tri::RequireFaceCompactness(mesh); + + // create eigen matrix of faces + Eigen::MatrixXi faces(mesh.FN(), 3); + + // copy faces + for (int i = 0; i < mesh.FN(); i++){ + for (int j = 0; j < 3; j++){ + faces(i,j) = (int)vcg::tri::Index(mesh,mesh.face[i].cV(j)); + } + } + + return faces; +} + +EigenMatrixX3m meshlab::vertexNormalMatrix(const CMeshO& mesh) +{ + vcg::tri::RequireVertexCompactness(mesh); + + // create eigen matrix of vertex normals + EigenMatrixX3m vertexNormals(mesh.VN(), 3); + + // per vertices normals + for (int i = 0; i < mesh.VN(); i++){ + for (int j = 0; j < 3; j++){ + vertexNormals(i,j) = mesh.vert[i].cN()[j]; + } + } + + return vertexNormals; +} + +EigenMatrixX3m meshlab::faceNormalMatrix(const CMeshO& mesh) +{ + vcg::tri::RequireFaceCompactness(mesh); + + // create eigen matrix of face normals + EigenMatrixX3m faceNormals(mesh.FN(), 3); + + // per face normals + for (int i = 0; i < mesh.FN(); i++){ + for (int j = 0; j < 3; j++){ + faceNormals(i,j) = mesh.face[i].cN()[j]; + } + } + + return faceNormals; +} + +EigenMatrixX3m meshlab::vertexColorMatrix(const CMeshO& mesh) +{ + vcg::tri::RequireVertexCompactness(mesh); + EigenMatrixX3m vertexColors(mesh.VN(), 4); + + for (int i = 0; i < mesh.VN(); i++){ + for (int j = 0; j < 4; j++){ + vertexColors(i,j) = mesh.vert[i].C()[j] / 255.0; + } + } + + return vertexColors; +} + +EigenMatrixX3m meshlab::faceColorMatrix(const CMeshO& mesh) +{ + vcg::tri::RequireFaceCompactness(mesh); + vcg::tri::RequirePerFaceColor(mesh); + + EigenMatrixX3m faceColors(mesh.FN(), 4); + + for (int i = 0; i < mesh.FN(); i++){ + for (int j = 0; j < 4; j++){ + faceColors(i,j) = mesh.face[i].C()[j] / 255.0; + } + } + + return faceColors; +} + +Eigen::Matrix meshlab::vertexColorArray( + const CMeshO& mesh) +{ + vcg::tri::RequireVertexCompactness(mesh); + Eigen::Matrix vertexColors(mesh.VN()); + + for (int i = 0; i < mesh.VN(); i++){ + vertexColors(i) = + vcg::Color4::ToUnsignedA8R8G8B8(mesh.vert[i].C()); + } + + return vertexColors; +} + +Eigen::Matrix meshlab::faceColorArray( + const CMeshO& mesh) +{ + vcg::tri::RequireFaceCompactness(mesh); + vcg::tri::RequirePerFaceColor(mesh); + + Eigen::Matrix faceColors(mesh.FN()); + + for (int i = 0; i < mesh.FN(); i++){ + faceColors(i) = + vcg::Color4::ToUnsignedA8R8G8B8(mesh.face[i].C()); + } + + return faceColors; +} + +EigenVectorXm meshlab::vertexQualityArray(const CMeshO& mesh) +{ + vcg::tri::RequireVertexCompactness(mesh); + vcg::tri::RequirePerVertexQuality(mesh); + + EigenVectorXm qv(mesh.VN()); + for (int i = 0; i < mesh.VN(); i++){ + qv(i) = mesh.vert[i].cQ(); + } + return qv; +} + +EigenVectorXm meshlab::faceQualityArray(const CMeshO& mesh) +{ + vcg::tri::RequireFaceCompactness(mesh); + vcg::tri::RequirePerFaceQuality(mesh); + + EigenVectorXm qf(mesh.FN()); + for (int i = 0; i < mesh.FN(); i++){ + qf(i) = mesh.face[i].cQ(); + } + return qf; +} + +EigenMatrixX2m meshlab::vertexTexCoordMatrix(const CMeshO& mesh) +{ + vcg::tri::RequireVertexCompactness(mesh); + vcg::tri::RequirePerVertexTexCoord(mesh); + + EigenMatrixX2m uv (mesh.VN(), 2); + + // per vertices uv + for (int i = 0; i < mesh.VN(); i++) { + uv(i,0) = mesh.vert[i].cT().U(); + uv(i,1) = mesh.vert[i].cT().V(); + } + + return uv; +} + +EigenMatrixX2m meshlab::wedgeTexCoordMatrix(const CMeshO& mesh) +{ + vcg::tri::RequireFaceCompactness(mesh); + vcg::tri::RequirePerVertexTexCoord(mesh); + EigenMatrixX2m m(mesh.FN()*3, 2); + + for (int i = 0; i < mesh.FN(); i++) { + int base = i * 3; + for (int j = 0; j < 3; j++){ + m(base+j, 0) = mesh.face[i].WT(j).u(); + m(base+j, 1) = mesh.face[i].WT(j).v(); + } + } + return m; +} diff --git a/src/common/utilities/eigen_mesh_conversions.h b/src/common/utilities/eigen_mesh_conversions.h new file mode 100644 index 000000000..aeca1911d --- /dev/null +++ b/src/common/utilities/eigen_mesh_conversions.h @@ -0,0 +1,58 @@ +/**************************************************************************** +* MeshLab o o * +* A versatile mesh processing toolbox o o * +* _ O _ * +* Copyright(C) 2005-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. * +* * +****************************************************************************/ + +#ifndef MESHLAB_EIGEN_MESH_CONVERSIONS_H +#define MESHLAB_EIGEN_MESH_CONVERSIONS_H + +#include +#include "../ml_document/cmesh.h" + +typedef Eigen::Matrix EigenMatrixX3m; +typedef Eigen::Matrix EigenMatrixX2m; +typedef Eigen::Matrix EigenVectorXm; + +namespace meshlab { + +CMeshO meshFromMatrices( + const EigenMatrixX3m& vertices, + const Eigen::MatrixX3i& faces = Eigen::MatrixX3i(), + const EigenMatrixX3m& vertexNormals = EigenMatrixX3m(), + const EigenMatrixX3m& faceNormals = EigenMatrixX3m(), + const EigenVectorXm& vertexQuality = EigenVectorXm(), + const EigenVectorXm& faceQuality = EigenVectorXm()); + +EigenMatrixX3m vertexMatrix(const CMeshO& mesh); +Eigen::MatrixX3i faceMatrix(const CMeshO& mesh); +EigenMatrixX3m vertexNormalMatrix(const CMeshO& mesh); +EigenMatrixX3m faceNormalMatrix(const CMeshO& mesh); +EigenMatrixX3m vertexColorMatrix(const CMeshO& mesh); +EigenMatrixX3m faceColorMatrix(const CMeshO& mesh); +Eigen::Matrix vertexColorArray(const CMeshO& mesh); +Eigen::Matrix faceColorArray(const CMeshO& mesh); +EigenVectorXm vertexQualityArray(const CMeshO& mesh); +EigenVectorXm faceQualityArray(const CMeshO& mesh); +EigenMatrixX2m vertexTexCoordMatrix(const CMeshO& mesh); +EigenMatrixX2m wedgeTexCoordMatrix(const CMeshO& mesh); +} + +#endif // MESHLAB_EIGEN_MESH_CONVERSIONS_H diff --git a/src/common/utilities/load_save.cpp b/src/common/utilities/load_save.cpp index 0f50bc364..61dff539c 100644 --- a/src/common/utilities/load_save.cpp +++ b/src/common/utilities/load_save.cpp @@ -2,7 +2,7 @@ * MeshLab o o * * A versatile mesh processing toolbox o o * * _ O _ * -* Copyright(C) 2005-2020 \/)\/ * +* Copyright(C) 2005-2021 \/)\/ * * Visual Computing Lab /\/| * * ISTI - Italian National Research Council | * * \ * diff --git a/src/common/utilities/load_save.h b/src/common/utilities/load_save.h index 2c2bfc066..3a18ba193 100644 --- a/src/common/utilities/load_save.h +++ b/src/common/utilities/load_save.h @@ -2,7 +2,7 @@ * MeshLab o o * * A versatile mesh processing toolbox o o * * _ O _ * -* Copyright(C) 2005-2020 \/)\/ * +* Copyright(C) 2005-2021 \/)\/ * * Visual Computing Lab /\/| * * ISTI - Italian National Research Council | * * \ * diff --git a/src/meshlabplugins/filter_mesh_booleans/filter_mesh_booleans.cpp b/src/meshlabplugins/filter_mesh_booleans/filter_mesh_booleans.cpp index 41f1a2d22..f0fb78503 100644 --- a/src/meshlabplugins/filter_mesh_booleans/filter_mesh_booleans.cpp +++ b/src/meshlabplugins/filter_mesh_booleans/filter_mesh_booleans.cpp @@ -23,6 +23,8 @@ #include "filter_mesh_booleans.h" +#include + #include /** @@ -294,68 +296,6 @@ std::map FilterMeshBooleans::applyFilter( return std::map(); } -/** - * @brief Puts coords and triangle indices of m into V and F matrices - * @param m - * @param V - * @param F - */ -void FilterMeshBooleans::CMeshOToEigen(const CMeshO& m, Eigen::MatrixX3d& V, Eigen::MatrixX3i& F) -{ - vcg::tri::RequireVertexCompactness(m); - vcg::tri::RequireFaceCompactness(m); - V.resize(m.VN(), 3); - for (int i = 0; i < m.VN(); i++){ - for (int j = 0; j < 3; j++){ - V(i,j) = m.vert[i].P()[j]; - } - } - F.resize(m.FN(), 3); - for (int i = 0; i < m.FN(); i++){ - for (int j = 0; j < 3; j++){ - F(i,j) = (int) vcg::tri::Index(m,m.face[i].cV(j)); - } - } -} - -/** - * @brief Returns a CMeshO containing the triangle mesh contained in V and F - * @param V - * @param F - * @return - */ -CMeshO FilterMeshBooleans::EigenToCMeshO(const Eigen::MatrixX3d& V, const Eigen::MatrixX3i& F) -{ - CMeshO m; - CMeshO::VertexIterator vi = - vcg::tri::Allocator::AddVertices(m, V.rows()); - std::vector ivp(V.rows()); - for (unsigned int i = 0; i < V.rows(); ++i, ++vi) { - ivp[i] = &*vi; - vi->P() = CMeshO::CoordType(V(i,0), V(i,1), V(i,2)); - } - - CMeshO::FaceIterator fi = - vcg::tri::Allocator::AddFaces(m, F.rows()); - for (unsigned int i = 0; i < F.rows(); ++i, ++fi) { - for (unsigned int j = 0; j < 3; j++){ - if ((unsigned int)F(i,j) >= ivp.size()) { - throw MLException( - "Error while creating mesh: bad vertex index " + - QString::number(F(i,j)) + " in face " + - QString::number(i) + "; vertex " + QString::number(j) + "."); - } - } - fi->V(0)=ivp[F(i,0)]; - fi->V(1)=ivp[F(i,1)]; - fi->V(2)=ivp[F(i,2)]; - } - vcg::tri::UpdateNormal::PerFace(m); - vcg::tri::UpdateNormal::PerVertex(m); - - return m; -} - /** * @brief Executes the boolean operation between m1 and m2, and puts the result * as a new mesh into md. @@ -394,13 +334,15 @@ void FilterMeshBooleans::booleanOperation( throw MLException("Boolean Operation not found! Please report this issue on https://github.com/cnr-isti-vclab/meshlab/issues"); } - Eigen::MatrixX3d V1, V2, VR; - Eigen::MatrixX3i F1, F2, FR; - Eigen::VectorXi indices; //mapping indices for birth faces - //vcg to eigen meshes - CMeshOToEigen(m1.cm, V1, F1); - CMeshOToEigen(m2.cm, V2, F2); + EigenMatrixX3m V1 = meshlab::vertexMatrix(m1.cm); + Eigen::MatrixX3i F1 = meshlab::faceMatrix(m1.cm); + EigenMatrixX3m V2 = meshlab::vertexMatrix(m2.cm); + Eigen::MatrixX3i F2 = meshlab::faceMatrix(m2.cm); + + EigenMatrixX3m VR; + Eigen::MatrixX3i FR; + Eigen::VectorXi indices; //mapping indices for birth faces bool result = igl::copyleft::cgal::mesh_boolean(V1, F1, V2, F2, (igl::MeshBooleanType)op, VR, FR, indices); @@ -412,7 +354,7 @@ void FilterMeshBooleans::booleanOperation( else { //everything ok, create new mesh into md MeshModel* mesh = md.addNewMesh("", name); - mesh->cm = EigenToCMeshO(VR, FR); + mesh->cm = meshlab::meshFromMatrices(VR, FR); //if transfer option enabled if (transfFaceColor || transfFaceQuality) diff --git a/src/meshlabplugins/filter_mesh_booleans/filter_mesh_booleans.h b/src/meshlabplugins/filter_mesh_booleans/filter_mesh_booleans.h index 403a1ee2f..c2c5794ee 100644 --- a/src/meshlabplugins/filter_mesh_booleans/filter_mesh_booleans.h +++ b/src/meshlabplugins/filter_mesh_booleans/filter_mesh_booleans.h @@ -74,15 +74,6 @@ public: vcg::CallBackPos * cb); private: - //conversion functions - static void CMeshOToEigen( - const CMeshO& m, - Eigen::MatrixX3d& V, - Eigen::MatrixX3i& F); - static CMeshO EigenToCMeshO( - const Eigen::MatrixX3d& V, - const Eigen::MatrixX3i& F); - //generic boolean operation function static void booleanOperation( MeshDocument& md,