From 6cc1dc003df9d48cfd7c4e1173e902d954ccd528 Mon Sep 17 00:00:00 2001 From: lvk88 <15655519+lvk88@users.noreply.github.com> Date: Sat, 14 Jan 2023 15:50:06 +0100 Subject: [PATCH 01/22] Issue #1032: Download lib3mf sources and add to build tree --- src/external/CMakeLists.txt | 3 +++ src/external/lib3mf.cmake | 54 +++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 src/external/lib3mf.cmake diff --git a/src/external/CMakeLists.txt b/src/external/CMakeLists.txt index 16f840bc5..30668e3d3 100644 --- a/src/external/CMakeLists.txt +++ b/src/external/CMakeLists.txt @@ -52,6 +52,9 @@ if ((NOT MESHLAB_BUILD_MINI) AND MESHLAB_ALLOW_OPTIONAL_EXTERNAL_LIBRARIES) # lib3ds - optional, for io_3ds include(${CMAKE_CURRENT_SOURCE_DIR}/lib3ds.cmake) + # lib3mf - optional, for io_3mf + include(${CMAKE_CURRENT_SOURCE_DIR}/lib3mf.cmake) + # libigl - optional for filter_mesh_booleans include(${CMAKE_CURRENT_SOURCE_DIR}/libigl.cmake) diff --git a/src/external/lib3mf.cmake b/src/external/lib3mf.cmake new file mode 100644 index 000000000..004bc9cb1 --- /dev/null +++ b/src/external/lib3mf.cmake @@ -0,0 +1,54 @@ +############################################################################# +# MeshLab o o # +# A versatile mesh processing toolbox o o # +# _ O _ # +# Copyright(C) 2005 \/)\/ # +# 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. # +# # +############################################################################# + +option(MESHLAB_ALLOW_DOWNLOAD_SOURCE_LIB3MF "Allow download and use of lib3MF source" ON) + +if(MESHLAB_ALLOW_DOWNLOAD_SOURCE_LIB3MF) + set(LIB3MF_DIR ${MESHLAB_EXTERNAL_DOWNLOAD_DIR}/lib3mf-2.2.0) + set(LIB3MF_CHECK ${LIB3MF_DIR}/CMakeLists.txt) + + if(NOT EXISTS ${LIB3MF_CHECK}) + set(LIB3MF_LINK https://github.com/3MFConsortium/lib3mf/archive/refs/tags/v2.2.0.zip) + set(LIB3MF_MD5 31c6dd3e2599f6f32c0784d8f46480bb) + download_and_unzip( + NAME "Lib3MF" + MD5 ${LIB3MF_MD5} + LINK ${LIB3MF_LINK} + DIR ${MESHLAB_EXTERNAL_DOWNLOAD_DIR}) + if(NOT download_and_unzip_SUCCESS) + message(STATUS "- Lib3MF - download failed") + endif() + endif() + + if(EXISTS ${LIB3MF_CHECK}) + message(STATUS " - Lib3MF - using downloaded source") + set(MESSAGE_QUIET ON) + set(LIB3MF_TESTS OFF) + add_subdirectory(${LIB3MF_DIR} EXCLUDE_FROM_ALL) + unset(MESSAGE_QUIET) + endif() + + add_library(external-lib3mf ALIAS lib3mf) + install(TARGETS lib3mf DESTINATION ${MESHLAB_LIB_INSTALL_DIR}) + +endif() From 8dfb925c1bc2efe2d70122b30b398d1706cc1314 Mon Sep 17 00:00:00 2001 From: lvk88 <15655519+lvk88@users.noreply.github.com> Date: Sat, 14 Jan 2023 16:05:54 +0100 Subject: [PATCH 02/22] Issue #1032: Add empty 3mf io plugin --- src/CMakeLists.txt | 1 + src/meshlabplugins/io_3mf/CMakeLists.txt | 34 ++++++++++++++++++++++++ src/meshlabplugins/io_3mf/io_3mf.cpp | 0 src/meshlabplugins/io_3mf/io_3mf.h | 0 4 files changed, 35 insertions(+) create mode 100644 src/meshlabplugins/io_3mf/CMakeLists.txt create mode 100644 src/meshlabplugins/io_3mf/io_3mf.cpp create mode 100644 src/meshlabplugins/io_3mf/io_3mf.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d27e7ae4e..46ad4962e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -123,6 +123,7 @@ if(NOT DEFINED MESHLAB_PLUGINS) # it may be already defined in parent directory set(MESHLAB_PLUGINS # IO plugins meshlabplugins/io_3ds + meshlabplugins/io_3mf meshlabplugins/io_base meshlabplugins/io_bre meshlabplugins/io_collada diff --git a/src/meshlabplugins/io_3mf/CMakeLists.txt b/src/meshlabplugins/io_3mf/CMakeLists.txt new file mode 100644 index 000000000..ced2df32f --- /dev/null +++ b/src/meshlabplugins/io_3mf/CMakeLists.txt @@ -0,0 +1,34 @@ +############################################################################# +# MeshLab o o # +# A versatile mesh processing toolbox o o # +# _ O _ # +# Copyright(C) 2005 \/)\/ # +# 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. # +# # +############################################################################# + +set(HEADERS + io_3mf.h + ) + +set(SOURCES + io_3mf.cpp + ) + +add_meshlab_plugin(io_3mf ${SOURCES} ${HEADERS}) + +target_link_libraries(io_3mf PUBLIC external-lib3mf) diff --git a/src/meshlabplugins/io_3mf/io_3mf.cpp b/src/meshlabplugins/io_3mf/io_3mf.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/src/meshlabplugins/io_3mf/io_3mf.h b/src/meshlabplugins/io_3mf/io_3mf.h new file mode 100644 index 000000000..e69de29bb From 5fbc09498990b3510a1e7441007def5cf6e5457e Mon Sep 17 00:00:00 2001 From: lvk88 <15655519+lvk88@users.noreply.github.com> Date: Sat, 14 Jan 2023 16:27:03 +0100 Subject: [PATCH 03/22] Issue #1032: Add header and source to io_3mf --- src/meshlabplugins/io_3mf/io_3mf.cpp | 72 ++++++++++++++++++++++++++++ src/meshlabplugins/io_3mf/io_3mf.h | 60 +++++++++++++++++++++++ 2 files changed, 132 insertions(+) diff --git a/src/meshlabplugins/io_3mf/io_3mf.cpp b/src/meshlabplugins/io_3mf/io_3mf.cpp index e69de29bb..36249f8c0 100644 --- a/src/meshlabplugins/io_3mf/io_3mf.cpp +++ b/src/meshlabplugins/io_3mf/io_3mf.cpp @@ -0,0 +1,72 @@ +/**************************************************************************** +* 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 "io_3mf.h" + +Lib3MFPlugin::Lib3MFPlugin() +{ +} + +QString Lib3MFPlugin::pluginName() const +{ + return "3MF importer and exporter"; +} + +std::list Lib3MFPlugin::importFormats() const +{ + return{FileFormat{"3MF", tr("3MF")}}; +} + +std::list Lib3MFPlugin::exportFormats() const +{ + return{FileFormat{"3MF", tr("3MF")}}; +} + +void Lib3MFPlugin::open( + const QString& formatName, + const QString& fileName, + MeshModel& m, + int& mask, + const RichParameterList& par, + vcg::CallBackPos* cb) +{ +} + +void Lib3MFPlugin::save( + const QString &formatName, + const QString &fileName, + MeshModel &m, + const int mask, + const RichParameterList& par, + vcg::CallBackPos* cb) +{ +} + +void Lib3MFPlugin::exportMaskCapability( + const QString &format, + int& capability, + int& defaultBits) const +{ +} + +MESHLAB_PLUGIN_NAME_EXPORTER(Lib3MFPlugin) diff --git a/src/meshlabplugins/io_3mf/io_3mf.h b/src/meshlabplugins/io_3mf/io_3mf.h index e69de29bb..93c086e72 100644 --- a/src/meshlabplugins/io_3mf/io_3mf.h +++ b/src/meshlabplugins/io_3mf/io_3mf.h @@ -0,0 +1,60 @@ +/**************************************************************************** +* 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. * +* * +****************************************************************************/ + +#pragma once + +#include "common/plugins/interfaces/io_plugin.h" + +class Lib3MFPlugin : public QObject, public IOPlugin +{ + Q_OBJECT + MESHLAB_PLUGIN_IID_EXPORTER(IO_PLUGIN_IID) + Q_INTERFACES(IOPlugin) + + public: + Lib3MFPlugin(); + QString pluginName() const override; + std::list importFormats() const override; + std::list exportFormats() const override; + + void open( + const QString& formatName, + const QString& fileName, + MeshModel& m, + int& mask, + const RichParameterList& par, + vcg::CallBackPos* cb) override; + + void save( + const QString &formatName, + const QString &fileName, + MeshModel &m, + const int mask, + const RichParameterList& par, + vcg::CallBackPos* cb) override; + + virtual void exportMaskCapability( + const QString &format, + int& capability, + int& defaultBits) const override; +}; From 806a2d5767786952e18fcbd03ad74f6d0f007e95 Mon Sep 17 00:00:00 2001 From: lvk88 <15655519+lvk88@users.noreply.github.com> Date: Sat, 14 Jan 2023 20:26:37 +0100 Subject: [PATCH 04/22] Issue #1032: Force use of lib3mf bindings from source folder --- src/external/lib3mf.cmake | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/external/lib3mf.cmake b/src/external/lib3mf.cmake index 004bc9cb1..002444ebc 100644 --- a/src/external/lib3mf.cmake +++ b/src/external/lib3mf.cmake @@ -48,7 +48,9 @@ if(MESHLAB_ALLOW_DOWNLOAD_SOURCE_LIB3MF) unset(MESSAGE_QUIET) endif() - add_library(external-lib3mf ALIAS lib3mf) + add_library(external-lib3mf INTERFACE) + target_link_libraries(external-lib3mf INTERFACE lib3mf) + target_include_directories(external-lib3mf INTERFACE ${LIB3MF_DIR}/Autogenerated/Bindings/Cpp) install(TARGETS lib3mf DESTINATION ${MESHLAB_LIB_INSTALL_DIR}) endif() From 4a1eefa66ce5f8616cb067975286b2a71be8983d Mon Sep 17 00:00:00 2001 From: lvk88 <15655519+lvk88@users.noreply.github.com> Date: Sat, 14 Jan 2023 20:26:59 +0100 Subject: [PATCH 05/22] Issue #1032: Add 3mf reader first concept --- src/meshlabplugins/io_3mf/io_3mf.cpp | 82 +++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 2 deletions(-) diff --git a/src/meshlabplugins/io_3mf/io_3mf.cpp b/src/meshlabplugins/io_3mf/io_3mf.cpp index 36249f8c0..ebd765cc6 100644 --- a/src/meshlabplugins/io_3mf/io_3mf.cpp +++ b/src/meshlabplugins/io_3mf/io_3mf.cpp @@ -23,6 +23,11 @@ #include "io_3mf.h" +#include "common/ml_document/mesh_model.h" +#include "wrap/io_trimesh/io_mask.h" + +#include "lib3mf_implicit.hpp" + Lib3MFPlugin::Lib3MFPlugin() { } @@ -34,12 +39,12 @@ QString Lib3MFPlugin::pluginName() const std::list Lib3MFPlugin::importFormats() const { - return{FileFormat{"3MF", tr("3MF")}}; + return{FileFormat{"3MF File Format", tr("3MF")}}; } std::list Lib3MFPlugin::exportFormats() const { - return{FileFormat{"3MF", tr("3MF")}}; + return{FileFormat{"3MF File Format", tr("3MF")}}; } void Lib3MFPlugin::open( @@ -50,6 +55,79 @@ void Lib3MFPlugin::open( const RichParameterList& par, vcg::CallBackPos* cb) { + + using namespace vcg::tri::io; + + const QString errorMsgFormat = "Error encountered while loading file:\n\"%1\"\n\nError details: %2"; + + if( !QFile::exists( fileName ) ) + { + throw MLException(errorMsgFormat.arg(fileName, "File does not exist")); + } + + if( cb != nullptr) + { + (*cb)(0, "Loading..."); + } + + mask = Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX; + + + const auto& wrapper = Lib3MF::CWrapper::loadLibrary(); + if( wrapper == nullptr ) + { + throw MLException(errorMsgFormat.arg(fileName, "Failed to initialize 3MF library")); + } + + const auto& model = wrapper->CreateModel(); + if( model == nullptr ) + { + throw MLException(errorMsgFormat.arg(fileName, "Failed to create 3MF internal model")); + } + + const auto& reader = model->QueryReader("3mf"); + if( model == nullptr ) + { + throw MLException(errorMsgFormat.arg(fileName, "Failed to create 3MF reader object")); + } + + reader->ReadFromFile(fileName.toStdString()); + + const auto& object_iterator = model->GetMeshObjects(); + if( object_iterator == nullptr ) + { + throw MLException(errorMsgFormat.arg(fileName, "Failed to iterate over objects in file")); + } + + while(object_iterator->MoveNext()) + { + const auto& mesh_object = object_iterator->GetCurrentMeshObject(); + const auto n_vertices = mesh_object->GetVertexCount(); + const auto n_triangles = mesh_object->GetTriangleCount(); + auto vertex_iterator = vcg::tri::Allocator::AddVertices(m.cm, n_vertices); + auto face_iterator = vcg::tri::Allocator::AddFaces(m.cm, n_triangles); + m.cm.face.reserve(n_triangles); + for(size_t i = 0; i < n_vertices; ++i) + { + const auto& pos = mesh_object->GetVertex(i).m_Coordinates; + (*vertex_iterator).P()[0] = pos[0]; + (*vertex_iterator).P()[1] = pos[1]; + (*vertex_iterator).P()[2] = pos[2]; + ++vertex_iterator; + } + for(size_t i = 0; i < n_triangles; ++i) + { + const auto& tri = mesh_object->GetTriangle(i).m_Indices; + (*face_iterator).V(0) = &m.cm.vert[tri[0]]; + (*face_iterator).V(1) = &m.cm.vert[tri[1]]; + (*face_iterator).V(2) = &m.cm.vert[tri[2]]; + ++face_iterator; + } + } + + m.enable(mask); + + return; } void Lib3MFPlugin::save( From 54f30daf1927d69a3df4ed0aad30984ec71e223a Mon Sep 17 00:00:00 2001 From: lvk88 <15655519+lvk88@users.noreply.github.com> Date: Sun, 15 Jan 2023 10:30:00 +0100 Subject: [PATCH 06/22] Issue #1032: Use name of 3mf object as label in loaded mesh --- src/meshlabplugins/io_3mf/io_3mf.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/meshlabplugins/io_3mf/io_3mf.cpp b/src/meshlabplugins/io_3mf/io_3mf.cpp index ebd765cc6..c61fd0f0a 100644 --- a/src/meshlabplugins/io_3mf/io_3mf.cpp +++ b/src/meshlabplugins/io_3mf/io_3mf.cpp @@ -102,11 +102,11 @@ void Lib3MFPlugin::open( while(object_iterator->MoveNext()) { const auto& mesh_object = object_iterator->GetCurrentMeshObject(); + m.setLabel(QString::fromStdString(mesh_object->GetName())); const auto n_vertices = mesh_object->GetVertexCount(); const auto n_triangles = mesh_object->GetTriangleCount(); auto vertex_iterator = vcg::tri::Allocator::AddVertices(m.cm, n_vertices); auto face_iterator = vcg::tri::Allocator::AddFaces(m.cm, n_triangles); - m.cm.face.reserve(n_triangles); for(size_t i = 0; i < n_vertices; ++i) { const auto& pos = mesh_object->GetVertex(i).m_Coordinates; From eba9630c6dfb5fda87d7d89966b302433f5e506a Mon Sep 17 00:00:00 2001 From: lvk88 <15655519+lvk88@users.noreply.github.com> Date: Sun, 15 Jan 2023 11:08:37 +0100 Subject: [PATCH 07/22] Issue #1032: Load separate 3mf meshes into separate meshobject --- src/meshlabplugins/io_3mf/io_3mf.cpp | 172 +++++++++++++++++++-------- src/meshlabplugins/io_3mf/io_3mf.h | 13 ++ 2 files changed, 134 insertions(+), 51 deletions(-) diff --git a/src/meshlabplugins/io_3mf/io_3mf.cpp b/src/meshlabplugins/io_3mf/io_3mf.cpp index c61fd0f0a..edcbb7305 100644 --- a/src/meshlabplugins/io_3mf/io_3mf.cpp +++ b/src/meshlabplugins/io_3mf/io_3mf.cpp @@ -28,6 +28,66 @@ #include "lib3mf_implicit.hpp" +namespace +{ + Lib3MF::PLib3MFMeshObjectIterator get_mesh_iterator(const QString& fileName) + { + const QString errorMsgFormat = "Error encountered while loading file:\n\"%1\"\n\nError details: %2"; + + if( !QFile::exists( fileName ) ) + { + throw MLException(errorMsgFormat.arg(fileName, "File does not exist")); + } + + const auto& wrapper = Lib3MF::CWrapper::loadLibrary(); + if( wrapper == nullptr ) + { + throw MLException(errorMsgFormat.arg(fileName, "Failed to initialize 3MF library")); + } + + const auto& model = wrapper->CreateModel(); + if( model == nullptr ) + { + throw MLException(errorMsgFormat.arg(fileName, "Failed to create 3MF internal model")); + } + + const auto& reader = model->QueryReader("3mf"); + if( model == nullptr ) + { + throw MLException(errorMsgFormat.arg(fileName, "Failed to create 3MF reader object")); + } + + reader->ReadFromFile(fileName.toStdString()); + return model->GetMeshObjects(); + } + + void load_mesh_to_meshmodel(const Lib3MF::PMeshObject& lib3mf_mesh_object, MeshModel& mesh_model) + { + mesh_model.setLabel(QString::fromStdString(lib3mf_mesh_object->GetName())); + const auto n_vertices = lib3mf_mesh_object->GetVertexCount(); + const auto n_triangles = lib3mf_mesh_object->GetTriangleCount(); + auto vertex_iterator = vcg::tri::Allocator::AddVertices(mesh_model.cm, n_vertices); + auto face_iterator = vcg::tri::Allocator::AddFaces(mesh_model.cm, n_triangles); + for(size_t i = 0; i < n_vertices; ++i) + { + const auto& pos = lib3mf_mesh_object->GetVertex(i).m_Coordinates; + (*vertex_iterator).P()[0] = pos[0]; + (*vertex_iterator).P()[1] = pos[1]; + (*vertex_iterator).P()[2] = pos[2]; + ++vertex_iterator; + } + for(size_t i = 0; i < n_triangles; ++i) + { + const auto& tri = lib3mf_mesh_object->GetTriangle(i).m_Indices; + (*face_iterator).V(0) = &mesh_model.cm.vert[tri[0]]; + (*face_iterator).V(1) = &mesh_model.cm.vert[tri[1]]; + (*face_iterator).V(2) = &mesh_model.cm.vert[tri[2]]; + ++face_iterator; + } + } + +} + Lib3MFPlugin::Lib3MFPlugin() { } @@ -47,84 +107,94 @@ std::list Lib3MFPlugin::exportFormats() const return{FileFormat{"3MF File Format", tr("3MF")}}; } +unsigned int Lib3MFPlugin::numberMeshesContainedInFile( + const QString& format, + const QString& fileName, + const RichParameterList& preParams) const +{ + const QString errorMsgFormat = "Error encountered while loading file:\n\"%1\"\n\nError details: %2"; + + const auto& object_iterator = get_mesh_iterator(fileName); + if( object_iterator == nullptr ) + { + throw MLException(errorMsgFormat.arg(fileName, "Failed to iterate over objects in file")); + } + return object_iterator->Count(); + +} + void Lib3MFPlugin::open( - const QString& formatName, - const QString& fileName, - MeshModel& m, - int& mask, - const RichParameterList& par, - vcg::CallBackPos* cb) + const QString &format, + const QString &fileName, + const std::list& meshModelList, + std::list& maskList, + const RichParameterList & par, + vcg::CallBackPos *cb) { using namespace vcg::tri::io; const QString errorMsgFormat = "Error encountered while loading file:\n\"%1\"\n\nError details: %2"; - if( !QFile::exists( fileName ) ) + auto mesh_iterator = get_mesh_iterator(fileName); + auto mesh_model_iterator = meshModelList.begin(); + + if(meshModelList.size() != mesh_iterator->Count()) { - throw MLException(errorMsgFormat.arg(fileName, "File does not exist")); + throw MLException(errorMsgFormat.arg(fileName, "Internal error while loading mesh objects: inconsistent number of meshes encontered")); } - if( cb != nullptr) + for(size_t i_mesh = 0; i_mesh < meshModelList.size(); ++i_mesh) { - (*cb)(0, "Loading..."); - } + mesh_iterator->MoveNext(); + const auto& current_mesh_object = mesh_iterator->GetCurrentMeshObject(); + if(current_mesh_object == nullptr) + { + throw MLException(errorMsgFormat.arg(fileName, "Internal error while loading mesh objects: invalid mesh object")); + } - mask = Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX; + auto current_mesh_model = (*mesh_model_iterator); + if(current_mesh_model == nullptr) + { + throw MLException(errorMsgFormat.arg(fileName, "Internal error while loading mesh objects: invalid mesh model")); + } + load_mesh_to_meshmodel(current_mesh_object, *current_mesh_model); + maskList.push_back(Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX); + current_mesh_model->enable( Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX); - const auto& wrapper = Lib3MF::CWrapper::loadLibrary(); - if( wrapper == nullptr ) - { - throw MLException(errorMsgFormat.arg(fileName, "Failed to initialize 3MF library")); + mesh_model_iterator++; } +} - const auto& model = wrapper->CreateModel(); - if( model == nullptr ) - { - throw MLException(errorMsgFormat.arg(fileName, "Failed to create 3MF internal model")); - } +void Lib3MFPlugin::open( + const QString& formatName, + const QString& fileName, + MeshModel& m, + int& mask, + const RichParameterList& par, + vcg::CallBackPos* cb) +{ + using namespace vcg::tri::io; - const auto& reader = model->QueryReader("3mf"); - if( model == nullptr ) - { - throw MLException(errorMsgFormat.arg(fileName, "Failed to create 3MF reader object")); - } + mask = Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX; - reader->ReadFromFile(fileName.toStdString()); + const QString errorMsgFormat = "Error encountered while loading file:\n\"%1\"\n\nError details: %2"; - const auto& object_iterator = model->GetMeshObjects(); + const auto& object_iterator = get_mesh_iterator(fileName); if( object_iterator == nullptr ) { throw MLException(errorMsgFormat.arg(fileName, "Failed to iterate over objects in file")); } - while(object_iterator->MoveNext()) + const auto& mesh_object = object_iterator->GetCurrentMeshObject(); + if(mesh_object == nullptr) { - const auto& mesh_object = object_iterator->GetCurrentMeshObject(); - m.setLabel(QString::fromStdString(mesh_object->GetName())); - const auto n_vertices = mesh_object->GetVertexCount(); - const auto n_triangles = mesh_object->GetTriangleCount(); - auto vertex_iterator = vcg::tri::Allocator::AddVertices(m.cm, n_vertices); - auto face_iterator = vcg::tri::Allocator::AddFaces(m.cm, n_triangles); - for(size_t i = 0; i < n_vertices; ++i) - { - const auto& pos = mesh_object->GetVertex(i).m_Coordinates; - (*vertex_iterator).P()[0] = pos[0]; - (*vertex_iterator).P()[1] = pos[1]; - (*vertex_iterator).P()[2] = pos[2]; - ++vertex_iterator; - } - for(size_t i = 0; i < n_triangles; ++i) - { - const auto& tri = mesh_object->GetTriangle(i).m_Indices; - (*face_iterator).V(0) = &m.cm.vert[tri[0]]; - (*face_iterator).V(1) = &m.cm.vert[tri[1]]; - (*face_iterator).V(2) = &m.cm.vert[tri[2]]; - ++face_iterator; - } + throw MLException(errorMsgFormat.arg(fileName, "Invalid mesh object encountered")); } + load_mesh_to_meshmodel(mesh_object, m); + m.enable(mask); return; diff --git a/src/meshlabplugins/io_3mf/io_3mf.h b/src/meshlabplugins/io_3mf/io_3mf.h index 93c086e72..de5300b72 100644 --- a/src/meshlabplugins/io_3mf/io_3mf.h +++ b/src/meshlabplugins/io_3mf/io_3mf.h @@ -37,6 +37,19 @@ class Lib3MFPlugin : public QObject, public IOPlugin std::list importFormats() const override; std::list exportFormats() const override; + unsigned int numberMeshesContainedInFile( + const QString& format, + const QString& fileName, + const RichParameterList& preParams) const override; + + void open( + const QString &format, + const QString &fileName, + const std::list& meshModelList, + std::list& maskList, + const RichParameterList & par, + vcg::CallBackPos *cb = nullptr) override; + void open( const QString& formatName, const QString& fileName, From cb97da15bab6683064053cc4844da6574d72c719 Mon Sep 17 00:00:00 2001 From: lvk88 <15655519+lvk88@users.noreply.github.com> Date: Sat, 21 Jan 2023 07:45:37 +0100 Subject: [PATCH 08/22] Issue #1032: Use name from mesh only if available --- src/meshlabplugins/io_3mf/io_3mf.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/meshlabplugins/io_3mf/io_3mf.cpp b/src/meshlabplugins/io_3mf/io_3mf.cpp index edcbb7305..0bab785b6 100644 --- a/src/meshlabplugins/io_3mf/io_3mf.cpp +++ b/src/meshlabplugins/io_3mf/io_3mf.cpp @@ -61,9 +61,14 @@ namespace return model->GetMeshObjects(); } - void load_mesh_to_meshmodel(const Lib3MF::PMeshObject& lib3mf_mesh_object, MeshModel& mesh_model) + void load_mesh_to_meshmodel(const Lib3MF::PMeshObject& lib3mf_mesh_object, MeshModel& mesh_model, const std::string& name_postfix) { - mesh_model.setLabel(QString::fromStdString(lib3mf_mesh_object->GetName())); + std::string mesh_name = lib3mf_mesh_object->GetName(); + if(mesh_name.empty()) + { + mesh_name = "Mesh" + name_postfix; + } + mesh_model.setLabel(QString::fromStdString(mesh_name)); const auto n_vertices = lib3mf_mesh_object->GetVertexCount(); const auto n_triangles = lib3mf_mesh_object->GetTriangleCount(); auto vertex_iterator = vcg::tri::Allocator::AddVertices(mesh_model.cm, n_vertices); @@ -159,7 +164,7 @@ void Lib3MFPlugin::open( throw MLException(errorMsgFormat.arg(fileName, "Internal error while loading mesh objects: invalid mesh model")); } - load_mesh_to_meshmodel(current_mesh_object, *current_mesh_model); + load_mesh_to_meshmodel(current_mesh_object, *current_mesh_model, "_" + std::to_string(i_mesh)); maskList.push_back(Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX); current_mesh_model->enable( Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX); @@ -193,7 +198,7 @@ void Lib3MFPlugin::open( throw MLException(errorMsgFormat.arg(fileName, "Invalid mesh object encountered")); } - load_mesh_to_meshmodel(mesh_object, m); + load_mesh_to_meshmodel(mesh_object, m, "_0"); m.enable(mask); From cb28ac98351ef28cea203f37c3ee6a29502de1f7 Mon Sep 17 00:00:00 2001 From: lvk88 <15655519+lvk88@users.noreply.github.com> Date: Sun, 22 Jan 2023 13:07:28 +0100 Subject: [PATCH 09/22] Issue #1032: Read build items and apply transformation to them --- src/meshlabplugins/io_3mf/io_3mf.cpp | 69 ++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 10 deletions(-) diff --git a/src/meshlabplugins/io_3mf/io_3mf.cpp b/src/meshlabplugins/io_3mf/io_3mf.cpp index 0bab785b6..dc1801dd1 100644 --- a/src/meshlabplugins/io_3mf/io_3mf.cpp +++ b/src/meshlabplugins/io_3mf/io_3mf.cpp @@ -24,13 +24,16 @@ #include "io_3mf.h" #include "common/ml_document/mesh_model.h" +#include "vcg/complex/algorithms/update/position.h" #include "wrap/io_trimesh/io_mask.h" #include "lib3mf_implicit.hpp" +#include namespace { - Lib3MF::PLib3MFMeshObjectIterator get_mesh_iterator(const QString& fileName) + + Lib3MF::PModel get_model_from_file(const QString& fileName) { const QString errorMsgFormat = "Error encountered while loading file:\n\"%1\"\n\nError details: %2"; @@ -58,9 +61,23 @@ namespace } reader->ReadFromFile(fileName.toStdString()); + return model; + } + + Lib3MF::PLib3MFMeshObjectIterator get_mesh_iterator(const QString& fileName) + { + const QString errorMsgFormat = "Error encountered while loading file:\n\"%1\"\n\nError details: %2"; + + const auto& model = get_model_from_file(fileName); + return model->GetMeshObjects(); } + Lib3MF::PLib3MFBuildItemIterator get_build_item_iterator(const Lib3MF::PModel& model) + { + return model->GetBuildItems(); + } + void load_mesh_to_meshmodel(const Lib3MF::PMeshObject& lib3mf_mesh_object, MeshModel& mesh_model, const std::string& name_postfix) { std::string mesh_name = lib3mf_mesh_object->GetName(); @@ -119,13 +136,14 @@ unsigned int Lib3MFPlugin::numberMeshesContainedInFile( { const QString errorMsgFormat = "Error encountered while loading file:\n\"%1\"\n\nError details: %2"; - const auto& object_iterator = get_mesh_iterator(fileName); - if( object_iterator == nullptr ) + const auto& model = get_model_from_file(fileName); + const auto& build_item_iterator = get_build_item_iterator(model); + + if( build_item_iterator == nullptr ) { - throw MLException(errorMsgFormat.arg(fileName, "Failed to iterate over objects in file")); + throw MLException(errorMsgFormat.arg(fileName, "Failed to iterate over build items in file")); } - return object_iterator->Count(); - + return build_item_iterator->Count(); } void Lib3MFPlugin::open( @@ -141,18 +159,27 @@ void Lib3MFPlugin::open( const QString errorMsgFormat = "Error encountered while loading file:\n\"%1\"\n\nError details: %2"; - auto mesh_iterator = get_mesh_iterator(fileName); + auto lib3mf_model = get_model_from_file(fileName); + auto build_item_iterator = get_build_item_iterator(lib3mf_model); auto mesh_model_iterator = meshModelList.begin(); - if(meshModelList.size() != mesh_iterator->Count()) + if(meshModelList.size() != build_item_iterator->Count()) { throw MLException(errorMsgFormat.arg(fileName, "Internal error while loading mesh objects: inconsistent number of meshes encontered")); } for(size_t i_mesh = 0; i_mesh < meshModelList.size(); ++i_mesh) { - mesh_iterator->MoveNext(); - const auto& current_mesh_object = mesh_iterator->GetCurrentMeshObject(); + build_item_iterator->MoveNext(); + auto current_build_item = build_item_iterator->GetCurrent(); + const auto& object = current_build_item->GetObjectResource(); + if(!object->IsMeshObject()) + { + throw MLException(errorMsgFormat.arg(fileName, "Error while loading mesh object: build item is not a mesh")); + } + + const auto& current_mesh_object = lib3mf_model->GetMeshObjectByID(object->GetResourceID()); + if(current_mesh_object == nullptr) { throw MLException(errorMsgFormat.arg(fileName, "Internal error while loading mesh objects: invalid mesh object")); @@ -165,6 +192,28 @@ void Lib3MFPlugin::open( } load_mesh_to_meshmodel(current_mesh_object, *current_mesh_model, "_" + std::to_string(i_mesh)); + + if(current_build_item->HasObjectTransform()) + { + auto transform = current_build_item->GetObjectTransform(); + Matrix44m tr; + tr.SetZero(); + tr.V()[0] = transform.m_Fields[0][0]; + tr.V()[1] = transform.m_Fields[0][1]; + tr.V()[2] = transform.m_Fields[0][2]; + tr.V()[4] = transform.m_Fields[1][0]; + tr.V()[5] = transform.m_Fields[1][1]; + tr.V()[6] = transform.m_Fields[1][2]; + tr.V()[8] = transform.m_Fields[2][0]; + tr.V()[9] = transform.m_Fields[2][1]; + tr.V()[10] = transform.m_Fields[2][2]; + tr.V()[12] = transform.m_Fields[3][0]; + tr.V()[13] = transform.m_Fields[3][1]; + tr.V()[14] = transform.m_Fields[3][2]; + tr.V()[15] = 1.0; + vcg::tri::UpdatePositioncm)>::Matrix(current_mesh_model->cm, tr.transpose(), true); + } + maskList.push_back(Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX); current_mesh_model->enable( Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX); From 87b1949df6bbcc85c4e425a2710973e3696fd37e Mon Sep 17 00:00:00 2001 From: lvk88 <15655519+lvk88@users.noreply.github.com> Date: Sun, 22 Jan 2023 13:46:46 +0100 Subject: [PATCH 10/22] Issue #1032: Update single mesh variant of open function --- src/meshlabplugins/io_3mf/io_3mf.cpp | 46 +++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/src/meshlabplugins/io_3mf/io_3mf.cpp b/src/meshlabplugins/io_3mf/io_3mf.cpp index dc1801dd1..e6e083b4d 100644 --- a/src/meshlabplugins/io_3mf/io_3mf.cpp +++ b/src/meshlabplugins/io_3mf/io_3mf.cpp @@ -61,6 +61,7 @@ namespace } reader->ReadFromFile(fileName.toStdString()); + return model; } @@ -171,7 +172,12 @@ void Lib3MFPlugin::open( for(size_t i_mesh = 0; i_mesh < meshModelList.size(); ++i_mesh) { build_item_iterator->MoveNext(); - auto current_build_item = build_item_iterator->GetCurrent(); + const auto& current_build_item = build_item_iterator->GetCurrent(); + if(current_build_item == nullptr) + { + throw MLException(errorMsgFormat.arg(fileName, "Failed to access build item")); + } + const auto& object = current_build_item->GetObjectResource(); if(!object->IsMeshObject()) { @@ -235,19 +241,49 @@ void Lib3MFPlugin::open( const QString errorMsgFormat = "Error encountered while loading file:\n\"%1\"\n\nError details: %2"; - const auto& object_iterator = get_mesh_iterator(fileName); - if( object_iterator == nullptr ) + const auto& lib3mf_model = get_model_from_file(fileName); + + const auto& build_item_iterator = get_build_item_iterator(lib3mf_model); + build_item_iterator->MoveNext(); + const auto& current_build_item = build_item_iterator->GetCurrent(); + if(current_build_item == nullptr) { - throw MLException(errorMsgFormat.arg(fileName, "Failed to iterate over objects in file")); + throw MLException(errorMsgFormat.arg(fileName, "Failed to access build item")); } - const auto& mesh_object = object_iterator->GetCurrentMeshObject(); + const auto& object = current_build_item->GetObjectResource(); + if( !object->IsMeshObject() ) + { + throw MLException(errorMsgFormat.arg(fileName, "Error while loading mesh object: build item is not a mesh")); + } + + const auto& mesh_object = lib3mf_model->GetMeshObjectByID(object->GetResourceID()); if(mesh_object == nullptr) { throw MLException(errorMsgFormat.arg(fileName, "Invalid mesh object encountered")); } load_mesh_to_meshmodel(mesh_object, m, "_0"); + if(current_build_item->HasObjectTransform()) + { + auto transform = current_build_item->GetObjectTransform(); + Matrix44m tr; + tr.SetZero(); + tr.V()[0] = transform.m_Fields[0][0]; + tr.V()[1] = transform.m_Fields[0][1]; + tr.V()[2] = transform.m_Fields[0][2]; + tr.V()[4] = transform.m_Fields[1][0]; + tr.V()[5] = transform.m_Fields[1][1]; + tr.V()[6] = transform.m_Fields[1][2]; + tr.V()[8] = transform.m_Fields[2][0]; + tr.V()[9] = transform.m_Fields[2][1]; + tr.V()[10] = transform.m_Fields[2][2]; + tr.V()[12] = transform.m_Fields[3][0]; + tr.V()[13] = transform.m_Fields[3][1]; + tr.V()[14] = transform.m_Fields[3][2]; + tr.V()[15] = 1.0; + vcg::tri::UpdatePosition::Matrix(m.cm, tr.transpose(), true); + } m.enable(mask); From db1d6e0e544c67c1d38af37b9e18582a8ed6c42b Mon Sep 17 00:00:00 2001 From: lvk88 <15655519+lvk88@users.noreply.github.com> Date: Sun, 22 Jan 2023 14:34:00 +0100 Subject: [PATCH 11/22] Issue #1032: Add 3mf exporter --- src/meshlabplugins/io_3mf/io_3mf.cpp | 61 ++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/src/meshlabplugins/io_3mf/io_3mf.cpp b/src/meshlabplugins/io_3mf/io_3mf.cpp index e6e083b4d..4dd43a64a 100644 --- a/src/meshlabplugins/io_3mf/io_3mf.cpp +++ b/src/meshlabplugins/io_3mf/io_3mf.cpp @@ -24,7 +24,9 @@ #include "io_3mf.h" #include "common/ml_document/mesh_model.h" +#include "lib3mf_types.hpp" #include "vcg/complex/algorithms/update/position.h" +#include "vcg/complex/allocate.h" #include "wrap/io_trimesh/io_mask.h" #include "lib3mf_implicit.hpp" @@ -298,6 +300,65 @@ void Lib3MFPlugin::save( const RichParameterList& par, vcg::CallBackPos* cb) { + const QString errorMsgFormat = "Error encountered while saving file:\n\"%1\"\n\nError details: %2"; + + const auto& wrapper = Lib3MF::CWrapper::loadLibrary(); + if(wrapper == nullptr) + { + throw MLException(errorMsgFormat.arg(fileName, "Could not init 3mf library")); + } + + const auto& model = wrapper->CreateModel(); + if(model == nullptr) + { + throw MLException(errorMsgFormat.arg(fileName, "Could not create model for writing")); + } + + const auto& metadata_group = model->GetMetaDataGroup(); + metadata_group->AddMetaData("","Application", "Meshlab", "string", false); + + const auto& mesh = model->AddMeshObject(); + mesh->SetName(m.label().toStdString()); + + std::vector vertex_buffer; + vertex_buffer.reserve(m.cm.vert.size()); + + std::vector vertex_ids(m.cm.vert.size()); + + std::vector triangle_buffer; + triangle_buffer.reserve(m.cm.face.size()); + + int number_of_vertices = 0; + for(auto vertex_it = m.cm.vert.begin(); vertex_it != m.cm.vert.end(); ++vertex_it) + { + if(vertex_it->IsD()) continue; + vertex_ids[vertex_it - m.cm.vert.begin()] = number_of_vertices; + Lib3MF::sPosition pos; + pos.m_Coordinates[0] = vertex_it->P()[0]; + pos.m_Coordinates[1] = vertex_it->P()[1]; + pos.m_Coordinates[2] = vertex_it->P()[2]; + vertex_buffer.push_back(pos); + number_of_vertices++; + } + + for(auto face_it = m.cm.face.begin(); face_it != m.cm.face.end(); ++face_it) + { + if(face_it->IsD()) continue; + if(face_it->VN() != 3) + { + throw MLException(errorMsgFormat.arg(fileName, "Only triangular meshes can be written to 3mf files")); + } + Lib3MF::sTriangle triangle; + triangle.m_Indices[0] = vertex_ids[vcg::tri::Index(m.cm, face_it->V(0))]; + triangle.m_Indices[1] = vertex_ids[vcg::tri::Index(m.cm, face_it->V(1))]; + triangle.m_Indices[2] = vertex_ids[vcg::tri::Index(m.cm, face_it->V(2))]; + triangle_buffer.push_back(triangle); + } + + mesh->SetGeometry(vertex_buffer, triangle_buffer); + model->AddBuildItem(mesh.get(), wrapper->GetIdentityTransform()); + const auto& writer = model->QueryWriter("3mf"); + writer->WriteToFile(fileName.toStdString()); } void Lib3MFPlugin::exportMaskCapability( From a6bcb3c0ec40bac8ce3b24eb62b55c24d3fd60f4 Mon Sep 17 00:00:00 2001 From: lvk88 <15655519+lvk88@users.noreply.github.com> Date: Thu, 15 Aug 2024 19:18:28 +0200 Subject: [PATCH 12/22] #1032 : Read material properties from 3mf file if they are available --- src/meshlabplugins/io_3mf/io_3mf.cpp | 134 ++++++++++++++++++++++++--- 1 file changed, 120 insertions(+), 14 deletions(-) diff --git a/src/meshlabplugins/io_3mf/io_3mf.cpp b/src/meshlabplugins/io_3mf/io_3mf.cpp index 4dd43a64a..515000efa 100644 --- a/src/meshlabplugins/io_3mf/io_3mf.cpp +++ b/src/meshlabplugins/io_3mf/io_3mf.cpp @@ -23,18 +23,22 @@ #include "io_3mf.h" +#include "common/ml_document/cmesh.h" #include "common/ml_document/mesh_model.h" +#include "lib3mf_implicit.hpp" #include "lib3mf_types.hpp" -#include "vcg/complex/algorithms/update/position.h" +#include "vcg/complex/algorithms/update/color.h" #include "vcg/complex/allocate.h" +#include "vcg/space/color4.h" #include "wrap/io_trimesh/io_mask.h" -#include "lib3mf_implicit.hpp" +#include +#include #include +#include namespace { - Lib3MF::PModel get_model_from_file(const QString& fileName) { const QString errorMsgFormat = "Error encountered while loading file:\n\"%1\"\n\nError details: %2"; @@ -81,8 +85,49 @@ namespace return model->GetBuildItems(); } - void load_mesh_to_meshmodel(const Lib3MF::PMeshObject& lib3mf_mesh_object, MeshModel& mesh_model, const std::string& name_postfix) + void load_mesh_to_meshmodel(const Lib3MF::PModel& model, const Lib3MF::PMeshObject& lib3mf_mesh_object, MeshModel& mesh_model, const std::string& name_postfix) { + + auto load_or_get_texture_id = [&mesh_model, &model](const int id) + { + const std::string string_id = std::to_string(id); + const auto& texture = mesh_model.getTexture(string_id); + if(texture.isNull()) + { + std::vector buffer; + model->GetTexture2DByID(id)->GetAttachment()->WriteToBuffer(buffer); + QImage image; + image.loadFromData(buffer.data(), buffer.size()); + mesh_model.addTexture(string_id, image); + } + + auto texture_id = std::distance(mesh_model.cm.textures.begin(), std::find(mesh_model.cm.textures.begin(), mesh_model.cm.textures.end(), string_id)); + return texture_id; + }; + + std::vector available_property_ids; + + auto base_material_groups = model->GetBaseMaterialGroups(); + while(base_material_groups->MoveNext()) + { + auto current = base_material_groups->GetCurrentBaseMaterialGroup(); + available_property_ids.push_back(current->GetUniqueResourceID()); + } + + auto color_groups = model->GetColorGroups(); + while(color_groups->MoveNext()) + { + auto current = color_groups->GetCurrentColorGroup(); + available_property_ids.push_back(current->GetUniqueResourceID()); + } + + auto texture_groups = model->GetTexture2DGroups(); + while(texture_groups->MoveNext()) + { + auto current = texture_groups->GetCurrentTexture2DGroup(); + available_property_ids.push_back(current->GetUniqueResourceID()); + } + std::string mesh_name = lib3mf_mesh_object->GetName(); if(mesh_name.empty()) { @@ -101,6 +146,8 @@ namespace (*vertex_iterator).P()[2] = pos[2]; ++vertex_iterator; } + + // Load the triangles only, but no colors yet for(size_t i = 0; i < n_triangles; ++i) { const auto& tri = lib3mf_mesh_object->GetTriangle(i).m_Indices; @@ -109,6 +156,60 @@ namespace (*face_iterator).V(2) = &mesh_model.cm.vert[tri[2]]; ++face_iterator; } + + // Load colors or textures, if necessary + for(size_t i = 0; i < n_triangles; ++i) + { + Lib3MF::sTriangleProperties props; + lib3mf_mesh_object->GetTriangleProperties(i, props); + + if(std::find_if(available_property_ids.begin(), available_property_ids.end(), [props](auto resourceId){return resourceId == props.m_ResourceID;}) != available_property_ids.end()){ + const auto& tri = lib3mf_mesh_object->GetTriangle(i).m_Indices; + switch(model->GetPropertyTypeByID(props.m_ResourceID)) + { + case Lib3MF::ePropertyType::BaseMaterial: + { + mesh_model.enable(vcg::tri::io::Mask::IOM_FACECOLOR); + auto baseMaterial = model->GetBaseMaterialGroupByID(props.m_ResourceID); + auto color = baseMaterial->GetDisplayColor(props.m_PropertyIDs[0]); + mesh_model.cm.face[i].C() = vcg::Color4b{color.m_Red, color.m_Green, color.m_Blue, color.m_Alpha}; + break; + } + case Lib3MF::ePropertyType::TexCoord: + { + mesh_model.enable(vcg::tri::io::Mask::IOM_WEDGTEXCOORD); + auto group = model->GetTexture2DGroupByID(props.m_ResourceID); + auto texture_id = load_or_get_texture_id(group->GetTexture2D()->GetUniqueResourceID()); + auto coord0 = group->GetTex2Coord(props.m_PropertyIDs[0]); + auto coord1 = group->GetTex2Coord(props.m_PropertyIDs[1]); + auto coord2 = group->GetTex2Coord(props.m_PropertyIDs[2]); + + mesh_model.cm.face[i].WT(0).U() = coord0.m_U; + mesh_model.cm.face[i].WT(0).V() = coord0.m_V; + mesh_model.cm.face[i].WT(0).N() = texture_id; + + mesh_model.cm.face[i].WT(1).U() = coord1.m_U; + mesh_model.cm.face[i].WT(1).V() = coord1.m_V; + mesh_model.cm.face[i].WT(1).N() = texture_id; + + mesh_model.cm.face[i].WT(2).U() = coord2.m_U; + mesh_model.cm.face[i].WT(2).V() = coord2.m_V; + mesh_model.cm.face[i].WT(2).N() = texture_id; + break; + } + case Lib3MF::ePropertyType::Colors: + { + mesh_model.enable(vcg::tri::io::Mask::IOM_FACECOLOR); + auto colorGroup = model->GetColorGroupByID(props.m_ResourceID); + auto color0 = colorGroup->GetColor(props.m_PropertyIDs[0]); + mesh_model.cm.face[i].C() = vcg::Color4b{color0.m_Red, color0.m_Green, color0.m_Blue, color0.m_Alpha}; + break; + } + default: + break; + }; + } + } } } @@ -157,7 +258,6 @@ void Lib3MFPlugin::open( const RichParameterList & par, vcg::CallBackPos *cb) { - using namespace vcg::tri::io; const QString errorMsgFormat = "Error encountered while loading file:\n\"%1\"\n\nError details: %2"; @@ -165,7 +265,6 @@ void Lib3MFPlugin::open( auto lib3mf_model = get_model_from_file(fileName); auto build_item_iterator = get_build_item_iterator(lib3mf_model); auto mesh_model_iterator = meshModelList.begin(); - if(meshModelList.size() != build_item_iterator->Count()) { throw MLException(errorMsgFormat.arg(fileName, "Internal error while loading mesh objects: inconsistent number of meshes encontered")); @@ -180,6 +279,7 @@ void Lib3MFPlugin::open( throw MLException(errorMsgFormat.arg(fileName, "Failed to access build item")); } + const auto& object = current_build_item->GetObjectResource(); if(!object->IsMeshObject()) { @@ -199,7 +299,14 @@ void Lib3MFPlugin::open( throw MLException(errorMsgFormat.arg(fileName, "Internal error while loading mesh objects: invalid mesh model")); } - load_mesh_to_meshmodel(current_mesh_object, *current_mesh_model, "_" + std::to_string(i_mesh)); + // TODO (lvk88): even if enable WEDGCOLOR, meshlab will crash when trying to add a per vertex wedge color later on + //maskList.push_back(Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX | Mask::IOM_WEDGCOLOR); + //current_mesh_model->enable( Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX | Mask::Mask::IOM_WEDGCOLOR); + + maskList.push_back(Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX); + current_mesh_model->enable( Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX); + + load_mesh_to_meshmodel(lib3mf_model, current_mesh_object, *current_mesh_model, "_" + std::to_string(i_mesh)); if(current_build_item->HasObjectTransform()) { @@ -219,12 +326,9 @@ void Lib3MFPlugin::open( tr.V()[13] = transform.m_Fields[3][1]; tr.V()[14] = transform.m_Fields[3][2]; tr.V()[15] = 1.0; - vcg::tri::UpdatePositioncm)>::Matrix(current_mesh_model->cm, tr.transpose(), true); + current_mesh_model->cm.Tr = tr.transpose(); } - maskList.push_back(Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX); - current_mesh_model->enable( Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX); - mesh_model_iterator++; } } @@ -239,7 +343,8 @@ void Lib3MFPlugin::open( { using namespace vcg::tri::io; - mask = Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX; + mask = Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX | Mask::IOM_VERTCOLOR; + m.enable(mask); const QString errorMsgFormat = "Error encountered while loading file:\n\"%1\"\n\nError details: %2"; @@ -265,7 +370,8 @@ void Lib3MFPlugin::open( throw MLException(errorMsgFormat.arg(fileName, "Invalid mesh object encountered")); } - load_mesh_to_meshmodel(mesh_object, m, "_0"); + load_mesh_to_meshmodel(lib3mf_model, mesh_object, m, "_0"); + current_build_item->GetObjectResource(); if(current_build_item->HasObjectTransform()) { auto transform = current_build_item->GetObjectTransform(); @@ -284,7 +390,7 @@ void Lib3MFPlugin::open( tr.V()[13] = transform.m_Fields[3][1]; tr.V()[14] = transform.m_Fields[3][2]; tr.V()[15] = 1.0; - vcg::tri::UpdatePosition::Matrix(m.cm, tr.transpose(), true); + m.cm.Tr = tr.transpose(); } m.enable(mask); From 562a2c5b6bae1f08b734778c7a9ec38d2e311d76 Mon Sep 17 00:00:00 2001 From: lvk88 <15655519+lvk88@users.noreply.github.com> Date: Thu, 15 Aug 2024 20:11:34 +0200 Subject: [PATCH 13/22] Apply clang-format --- src/meshlabplugins/io_3mf/io_3mf.cpp | 807 +++++++++++++-------------- src/meshlabplugins/io_3mf/io_3mf.h | 117 ++-- 2 files changed, 449 insertions(+), 475 deletions(-) diff --git a/src/meshlabplugins/io_3mf/io_3mf.cpp b/src/meshlabplugins/io_3mf/io_3mf.cpp index 515000efa..f085b80d4 100644 --- a/src/meshlabplugins/io_3mf/io_3mf.cpp +++ b/src/meshlabplugins/io_3mf/io_3mf.cpp @@ -1,30 +1,32 @@ /**************************************************************************** -* 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. * -* * -****************************************************************************/ + * 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 "io_3mf.h" #include "common/ml_document/cmesh.h" #include "common/ml_document/mesh_model.h" +#include "common/parameters/rich_parameter/rich_bool.h" +#include "common/parameters/rich_parameter_list.h" #include "lib3mf_implicit.hpp" #include "lib3mf_types.hpp" #include "vcg/complex/algorithms/update/color.h" @@ -37,440 +39,411 @@ #include #include -namespace +namespace { +Lib3MF::PModel get_model_from_file(const QString& fileName) { - Lib3MF::PModel get_model_from_file(const QString& fileName) - { - const QString errorMsgFormat = "Error encountered while loading file:\n\"%1\"\n\nError details: %2"; - - if( !QFile::exists( fileName ) ) - { - throw MLException(errorMsgFormat.arg(fileName, "File does not exist")); - } - - const auto& wrapper = Lib3MF::CWrapper::loadLibrary(); - if( wrapper == nullptr ) - { - throw MLException(errorMsgFormat.arg(fileName, "Failed to initialize 3MF library")); - } - - const auto& model = wrapper->CreateModel(); - if( model == nullptr ) - { - throw MLException(errorMsgFormat.arg(fileName, "Failed to create 3MF internal model")); - } - - const auto& reader = model->QueryReader("3mf"); - if( model == nullptr ) - { - throw MLException(errorMsgFormat.arg(fileName, "Failed to create 3MF reader object")); - } - - reader->ReadFromFile(fileName.toStdString()); - - return model; - } - - Lib3MF::PLib3MFMeshObjectIterator get_mesh_iterator(const QString& fileName) - { - const QString errorMsgFormat = "Error encountered while loading file:\n\"%1\"\n\nError details: %2"; - - const auto& model = get_model_from_file(fileName); - - return model->GetMeshObjects(); - } - - Lib3MF::PLib3MFBuildItemIterator get_build_item_iterator(const Lib3MF::PModel& model) - { - return model->GetBuildItems(); - } - - void load_mesh_to_meshmodel(const Lib3MF::PModel& model, const Lib3MF::PMeshObject& lib3mf_mesh_object, MeshModel& mesh_model, const std::string& name_postfix) - { - - auto load_or_get_texture_id = [&mesh_model, &model](const int id) - { - const std::string string_id = std::to_string(id); - const auto& texture = mesh_model.getTexture(string_id); - if(texture.isNull()) - { - std::vector buffer; - model->GetTexture2DByID(id)->GetAttachment()->WriteToBuffer(buffer); - QImage image; - image.loadFromData(buffer.data(), buffer.size()); - mesh_model.addTexture(string_id, image); - } - - auto texture_id = std::distance(mesh_model.cm.textures.begin(), std::find(mesh_model.cm.textures.begin(), mesh_model.cm.textures.end(), string_id)); - return texture_id; - }; - - std::vector available_property_ids; - - auto base_material_groups = model->GetBaseMaterialGroups(); - while(base_material_groups->MoveNext()) - { - auto current = base_material_groups->GetCurrentBaseMaterialGroup(); - available_property_ids.push_back(current->GetUniqueResourceID()); - } - - auto color_groups = model->GetColorGroups(); - while(color_groups->MoveNext()) - { - auto current = color_groups->GetCurrentColorGroup(); - available_property_ids.push_back(current->GetUniqueResourceID()); - } - - auto texture_groups = model->GetTexture2DGroups(); - while(texture_groups->MoveNext()) - { - auto current = texture_groups->GetCurrentTexture2DGroup(); - available_property_ids.push_back(current->GetUniqueResourceID()); - } - - std::string mesh_name = lib3mf_mesh_object->GetName(); - if(mesh_name.empty()) - { - mesh_name = "Mesh" + name_postfix; - } - mesh_model.setLabel(QString::fromStdString(mesh_name)); - const auto n_vertices = lib3mf_mesh_object->GetVertexCount(); - const auto n_triangles = lib3mf_mesh_object->GetTriangleCount(); - auto vertex_iterator = vcg::tri::Allocator::AddVertices(mesh_model.cm, n_vertices); - auto face_iterator = vcg::tri::Allocator::AddFaces(mesh_model.cm, n_triangles); - for(size_t i = 0; i < n_vertices; ++i) - { - const auto& pos = lib3mf_mesh_object->GetVertex(i).m_Coordinates; - (*vertex_iterator).P()[0] = pos[0]; - (*vertex_iterator).P()[1] = pos[1]; - (*vertex_iterator).P()[2] = pos[2]; - ++vertex_iterator; - } - - // Load the triangles only, but no colors yet - for(size_t i = 0; i < n_triangles; ++i) - { - const auto& tri = lib3mf_mesh_object->GetTriangle(i).m_Indices; - (*face_iterator).V(0) = &mesh_model.cm.vert[tri[0]]; - (*face_iterator).V(1) = &mesh_model.cm.vert[tri[1]]; - (*face_iterator).V(2) = &mesh_model.cm.vert[tri[2]]; - ++face_iterator; - } - - // Load colors or textures, if necessary - for(size_t i = 0; i < n_triangles; ++i) - { - Lib3MF::sTriangleProperties props; - lib3mf_mesh_object->GetTriangleProperties(i, props); - - if(std::find_if(available_property_ids.begin(), available_property_ids.end(), [props](auto resourceId){return resourceId == props.m_ResourceID;}) != available_property_ids.end()){ - const auto& tri = lib3mf_mesh_object->GetTriangle(i).m_Indices; - switch(model->GetPropertyTypeByID(props.m_ResourceID)) - { - case Lib3MF::ePropertyType::BaseMaterial: - { - mesh_model.enable(vcg::tri::io::Mask::IOM_FACECOLOR); - auto baseMaterial = model->GetBaseMaterialGroupByID(props.m_ResourceID); - auto color = baseMaterial->GetDisplayColor(props.m_PropertyIDs[0]); - mesh_model.cm.face[i].C() = vcg::Color4b{color.m_Red, color.m_Green, color.m_Blue, color.m_Alpha}; - break; - } - case Lib3MF::ePropertyType::TexCoord: - { - mesh_model.enable(vcg::tri::io::Mask::IOM_WEDGTEXCOORD); - auto group = model->GetTexture2DGroupByID(props.m_ResourceID); - auto texture_id = load_or_get_texture_id(group->GetTexture2D()->GetUniqueResourceID()); - auto coord0 = group->GetTex2Coord(props.m_PropertyIDs[0]); - auto coord1 = group->GetTex2Coord(props.m_PropertyIDs[1]); - auto coord2 = group->GetTex2Coord(props.m_PropertyIDs[2]); - - mesh_model.cm.face[i].WT(0).U() = coord0.m_U; - mesh_model.cm.face[i].WT(0).V() = coord0.m_V; - mesh_model.cm.face[i].WT(0).N() = texture_id; - - mesh_model.cm.face[i].WT(1).U() = coord1.m_U; - mesh_model.cm.face[i].WT(1).V() = coord1.m_V; - mesh_model.cm.face[i].WT(1).N() = texture_id; - - mesh_model.cm.face[i].WT(2).U() = coord2.m_U; - mesh_model.cm.face[i].WT(2).V() = coord2.m_V; - mesh_model.cm.face[i].WT(2).N() = texture_id; - break; - } - case Lib3MF::ePropertyType::Colors: - { - mesh_model.enable(vcg::tri::io::Mask::IOM_FACECOLOR); - auto colorGroup = model->GetColorGroupByID(props.m_ResourceID); - auto color0 = colorGroup->GetColor(props.m_PropertyIDs[0]); - mesh_model.cm.face[i].C() = vcg::Color4b{color0.m_Red, color0.m_Green, color0.m_Blue, color0.m_Alpha}; - break; - } - default: - break; - }; - } - } - } + const QString errorMsgFormat = + "Error encountered while loading file:\n\"%1\"\n\nError details: %2"; + if (!QFile::exists(fileName)) { + throw MLException(errorMsgFormat.arg(fileName, "File does not exist")); + } + + const auto& wrapper = Lib3MF::CWrapper::loadLibrary(); + if (wrapper == nullptr) { + throw MLException(errorMsgFormat.arg(fileName, "Failed to initialize 3MF library")); + } + + const auto& model = wrapper->CreateModel(); + if (model == nullptr) { + throw MLException(errorMsgFormat.arg(fileName, "Failed to create 3MF internal model")); + } + + const auto& reader = model->QueryReader("3mf"); + if (model == nullptr) { + throw MLException(errorMsgFormat.arg(fileName, "Failed to create 3MF reader object")); + } + + reader->ReadFromFile(fileName.toStdString()); + + return model; +} + +Lib3MF::PLib3MFMeshObjectIterator get_mesh_iterator(const QString& fileName) +{ + const QString errorMsgFormat = + "Error encountered while loading file:\n\"%1\"\n\nError details: %2"; + + const auto& model = get_model_from_file(fileName); + + return model->GetMeshObjects(); } +Lib3MF::PLib3MFBuildItemIterator get_build_item_iterator(const Lib3MF::PModel& model) +{ + return model->GetBuildItems(); +} + +void load_mesh_to_meshmodel( + const Lib3MF::PModel& model, + const Lib3MF::PMeshObject& lib3mf_mesh_object, + MeshModel& mesh_model, + const std::string& name_postfix, + bool load_material_data) +{ + auto load_or_get_texture_id = [&mesh_model, &model](const int id) { + const std::string string_id = std::to_string(id); + const auto& texture = mesh_model.getTexture(string_id); + if (texture.isNull()) { + std::vector buffer; + model->GetTexture2DByID(id)->GetAttachment()->WriteToBuffer(buffer); + QImage image; + image.loadFromData(buffer.data(), buffer.size()); + mesh_model.addTexture(string_id, image); + } + + auto texture_id = std::distance( + mesh_model.cm.textures.begin(), + std::find(mesh_model.cm.textures.begin(), mesh_model.cm.textures.end(), string_id)); + return texture_id; + }; + + std::vector available_property_ids; + + auto base_material_groups = model->GetBaseMaterialGroups(); + while (base_material_groups->MoveNext()) { + auto current = base_material_groups->GetCurrentBaseMaterialGroup(); + available_property_ids.push_back(current->GetUniqueResourceID()); + } + + auto color_groups = model->GetColorGroups(); + while (color_groups->MoveNext()) { + auto current = color_groups->GetCurrentColorGroup(); + available_property_ids.push_back(current->GetUniqueResourceID()); + } + + auto texture_groups = model->GetTexture2DGroups(); + while (texture_groups->MoveNext()) { + auto current = texture_groups->GetCurrentTexture2DGroup(); + available_property_ids.push_back(current->GetUniqueResourceID()); + } + + std::string mesh_name = lib3mf_mesh_object->GetName(); + if (mesh_name.empty()) { + mesh_name = "Mesh" + name_postfix; + } + mesh_model.setLabel(QString::fromStdString(mesh_name)); + const auto n_vertices = lib3mf_mesh_object->GetVertexCount(); + const auto n_triangles = lib3mf_mesh_object->GetTriangleCount(); + auto vertex_iterator = + vcg::tri::Allocator::AddVertices(mesh_model.cm, n_vertices); + auto face_iterator = + vcg::tri::Allocator::AddFaces(mesh_model.cm, n_triangles); + for (size_t i = 0; i < n_vertices; ++i) { + const auto& pos = lib3mf_mesh_object->GetVertex(i).m_Coordinates; + (*vertex_iterator).P()[0] = pos[0]; + (*vertex_iterator).P()[1] = pos[1]; + (*vertex_iterator).P()[2] = pos[2]; + ++vertex_iterator; + } + + // Load the triangles only, but no colors yet + for (size_t i = 0; i < n_triangles; ++i) { + const auto& tri = lib3mf_mesh_object->GetTriangle(i).m_Indices; + (*face_iterator).V(0) = &mesh_model.cm.vert[tri[0]]; + (*face_iterator).V(1) = &mesh_model.cm.vert[tri[1]]; + (*face_iterator).V(2) = &mesh_model.cm.vert[tri[2]]; + ++face_iterator; + } + + // Load colors or textures, if necessary + + if (load_material_data) { + for (size_t i = 0; i < n_triangles; ++i) { + Lib3MF::sTriangleProperties props; + lib3mf_mesh_object->GetTriangleProperties(i, props); + + if (std::find_if( + available_property_ids.begin(), + available_property_ids.end(), + [props](auto resourceId) { return resourceId == props.m_ResourceID; }) != + available_property_ids.end()) { + const auto& tri = lib3mf_mesh_object->GetTriangle(i).m_Indices; + switch (model->GetPropertyTypeByID(props.m_ResourceID)) { + case Lib3MF::ePropertyType::BaseMaterial: { + mesh_model.enable(vcg::tri::io::Mask::IOM_FACECOLOR); + auto baseMaterial = model->GetBaseMaterialGroupByID(props.m_ResourceID); + auto color = baseMaterial->GetDisplayColor(props.m_PropertyIDs[0]); + mesh_model.cm.face[i].C() = + vcg::Color4b {color.m_Red, color.m_Green, color.m_Blue, color.m_Alpha}; + break; + } + case Lib3MF::ePropertyType::TexCoord: { + mesh_model.enable(vcg::tri::io::Mask::IOM_WEDGTEXCOORD); + auto group = model->GetTexture2DGroupByID(props.m_ResourceID); + auto texture_id = + load_or_get_texture_id(group->GetTexture2D()->GetUniqueResourceID()); + auto coord0 = group->GetTex2Coord(props.m_PropertyIDs[0]); + auto coord1 = group->GetTex2Coord(props.m_PropertyIDs[1]); + auto coord2 = group->GetTex2Coord(props.m_PropertyIDs[2]); + + mesh_model.cm.face[i].WT(0).U() = coord0.m_U; + mesh_model.cm.face[i].WT(0).V() = coord0.m_V; + mesh_model.cm.face[i].WT(0).N() = texture_id; + + mesh_model.cm.face[i].WT(1).U() = coord1.m_U; + mesh_model.cm.face[i].WT(1).V() = coord1.m_V; + mesh_model.cm.face[i].WT(1).N() = texture_id; + + mesh_model.cm.face[i].WT(2).U() = coord2.m_U; + mesh_model.cm.face[i].WT(2).V() = coord2.m_V; + mesh_model.cm.face[i].WT(2).N() = texture_id; + break; + } + case Lib3MF::ePropertyType::Colors: { + mesh_model.enable(vcg::tri::io::Mask::IOM_FACECOLOR); + auto colorGroup = model->GetColorGroupByID(props.m_ResourceID); + auto color0 = colorGroup->GetColor(props.m_PropertyIDs[0]); + mesh_model.cm.face[i].C() = + vcg::Color4b {color0.m_Red, color0.m_Green, color0.m_Blue, color0.m_Alpha}; + break; + } + default: break; + }; + } + } + } +} + +} // namespace + Lib3MFPlugin::Lib3MFPlugin() { } QString Lib3MFPlugin::pluginName() const { - return "3MF importer and exporter"; + return "3MF importer and exporter"; } std::list Lib3MFPlugin::importFormats() const { - return{FileFormat{"3MF File Format", tr("3MF")}}; + return {FileFormat {"3MF File Format", tr("3MF")}}; } std::list Lib3MFPlugin::exportFormats() const { - return{FileFormat{"3MF File Format", tr("3MF")}}; + return {FileFormat {"3MF File Format", tr("3MF")}}; +} + +RichParameterList Lib3MFPlugin::initPreOpenParameter(const QString& /*format*/) const +{ + RichParameterList result; + result.addParam(RichBool( + "usecolors", + false, + "Load colors and textures", + "When turned on, loads color and texture information from the file. Turn off if you " + "experience slow rendering performance.")); + return result; } unsigned int Lib3MFPlugin::numberMeshesContainedInFile( - const QString& format, - const QString& fileName, - const RichParameterList& preParams) const + const QString& format, + const QString& fileName, + const RichParameterList& preParams) const { - const QString errorMsgFormat = "Error encountered while loading file:\n\"%1\"\n\nError details: %2"; + const QString errorMsgFormat = + "Error encountered while loading file:\n\"%1\"\n\nError details: %2"; - const auto& model = get_model_from_file(fileName); - const auto& build_item_iterator = get_build_item_iterator(model); + const auto& model = get_model_from_file(fileName); + const auto& build_item_iterator = get_build_item_iterator(model); - if( build_item_iterator == nullptr ) - { - throw MLException(errorMsgFormat.arg(fileName, "Failed to iterate over build items in file")); - } - return build_item_iterator->Count(); + if (build_item_iterator == nullptr) { + throw MLException( + errorMsgFormat.arg(fileName, "Failed to iterate over build items in file")); + } + return build_item_iterator->Count(); } void Lib3MFPlugin::open( - const QString &format, - const QString &fileName, - const std::list& meshModelList, - std::list& maskList, - const RichParameterList & par, - vcg::CallBackPos *cb) + const QString& format, + const QString& fileName, + const std::list& meshModelList, + std::list& maskList, + const RichParameterList& par, + vcg::CallBackPos* cb) { - using namespace vcg::tri::io; - - const QString errorMsgFormat = "Error encountered while loading file:\n\"%1\"\n\nError details: %2"; - - auto lib3mf_model = get_model_from_file(fileName); - auto build_item_iterator = get_build_item_iterator(lib3mf_model); - auto mesh_model_iterator = meshModelList.begin(); - if(meshModelList.size() != build_item_iterator->Count()) - { - throw MLException(errorMsgFormat.arg(fileName, "Internal error while loading mesh objects: inconsistent number of meshes encontered")); - } - - for(size_t i_mesh = 0; i_mesh < meshModelList.size(); ++i_mesh) - { - build_item_iterator->MoveNext(); - const auto& current_build_item = build_item_iterator->GetCurrent(); - if(current_build_item == nullptr) - { - throw MLException(errorMsgFormat.arg(fileName, "Failed to access build item")); - } - - - const auto& object = current_build_item->GetObjectResource(); - if(!object->IsMeshObject()) - { - throw MLException(errorMsgFormat.arg(fileName, "Error while loading mesh object: build item is not a mesh")); - } - - const auto& current_mesh_object = lib3mf_model->GetMeshObjectByID(object->GetResourceID()); - - if(current_mesh_object == nullptr) - { - throw MLException(errorMsgFormat.arg(fileName, "Internal error while loading mesh objects: invalid mesh object")); - } - - auto current_mesh_model = (*mesh_model_iterator); - if(current_mesh_model == nullptr) - { - throw MLException(errorMsgFormat.arg(fileName, "Internal error while loading mesh objects: invalid mesh model")); - } - - // TODO (lvk88): even if enable WEDGCOLOR, meshlab will crash when trying to add a per vertex wedge color later on - //maskList.push_back(Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX | Mask::IOM_WEDGCOLOR); - //current_mesh_model->enable( Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX | Mask::Mask::IOM_WEDGCOLOR); - - maskList.push_back(Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX); - current_mesh_model->enable( Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX); - - load_mesh_to_meshmodel(lib3mf_model, current_mesh_object, *current_mesh_model, "_" + std::to_string(i_mesh)); - - if(current_build_item->HasObjectTransform()) - { - auto transform = current_build_item->GetObjectTransform(); - Matrix44m tr; - tr.SetZero(); - tr.V()[0] = transform.m_Fields[0][0]; - tr.V()[1] = transform.m_Fields[0][1]; - tr.V()[2] = transform.m_Fields[0][2]; - tr.V()[4] = transform.m_Fields[1][0]; - tr.V()[5] = transform.m_Fields[1][1]; - tr.V()[6] = transform.m_Fields[1][2]; - tr.V()[8] = transform.m_Fields[2][0]; - tr.V()[9] = transform.m_Fields[2][1]; - tr.V()[10] = transform.m_Fields[2][2]; - tr.V()[12] = transform.m_Fields[3][0]; - tr.V()[13] = transform.m_Fields[3][1]; - tr.V()[14] = transform.m_Fields[3][2]; - tr.V()[15] = 1.0; - current_mesh_model->cm.Tr = tr.transpose(); - } - - mesh_model_iterator++; - } + using namespace vcg::tri::io; + + cb(0, std::string("Loading " + fileName.toStdString()).c_str()); + + const QString errorMsgFormat = + "Error encountered while loading file:\n\"%1\"\n\nError details: %2"; + + auto lib3mf_model = get_model_from_file(fileName); + auto build_item_iterator = get_build_item_iterator(lib3mf_model); + auto mesh_model_iterator = meshModelList.begin(); + if (meshModelList.size() != build_item_iterator->Count()) { + throw MLException(errorMsgFormat.arg( + fileName, + "Internal error while loading mesh objects: inconsistent number of meshes encontered")); + } + + auto delta_percent = 100 / meshModelList.size(); + + for (size_t i_mesh = 0; i_mesh < meshModelList.size(); ++i_mesh) { + build_item_iterator->MoveNext(); + const auto& current_build_item = build_item_iterator->GetCurrent(); + if (current_build_item == nullptr) { + throw MLException(errorMsgFormat.arg(fileName, "Failed to access build item")); + } + + const auto& object = current_build_item->GetObjectResource(); + if (!object->IsMeshObject()) { + throw MLException(errorMsgFormat.arg( + fileName, "Error while loading mesh object: build item is not a mesh")); + } + + const auto& current_mesh_object = lib3mf_model->GetMeshObjectByID(object->GetResourceID()); + + if (current_mesh_object == nullptr) { + throw MLException(errorMsgFormat.arg( + fileName, "Internal error while loading mesh objects: invalid mesh object")); + } + + auto current_mesh_model = (*mesh_model_iterator); + if (current_mesh_model == nullptr) { + throw MLException(errorMsgFormat.arg( + fileName, "Internal error while loading mesh objects: invalid mesh model")); + } + + // TODO (lvk88): even if enable WEDGCOLOR, meshlab will crash when trying to add a per + // vertex wedge color later on + // maskList.push_back(Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX | Mask::IOM_WEDGCOLOR); + // current_mesh_model->enable( Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX | + // Mask::Mask::IOM_WEDGCOLOR); + + maskList.push_back(Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX); + current_mesh_model->enable(Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX); + + load_mesh_to_meshmodel( + lib3mf_model, + current_mesh_object, + *current_mesh_model, + "_" + std::to_string(i_mesh), + par.getBool("usecolors")); + + if (current_build_item->HasObjectTransform()) { + auto transform = current_build_item->GetObjectTransform(); + Matrix44m tr; + tr.SetZero(); + tr.V()[0] = transform.m_Fields[0][0]; + tr.V()[1] = transform.m_Fields[0][1]; + tr.V()[2] = transform.m_Fields[0][2]; + tr.V()[4] = transform.m_Fields[1][0]; + tr.V()[5] = transform.m_Fields[1][1]; + tr.V()[6] = transform.m_Fields[1][2]; + tr.V()[8] = transform.m_Fields[2][0]; + tr.V()[9] = transform.m_Fields[2][1]; + tr.V()[10] = transform.m_Fields[2][2]; + tr.V()[12] = transform.m_Fields[3][0]; + tr.V()[13] = transform.m_Fields[3][1]; + tr.V()[14] = transform.m_Fields[3][2]; + tr.V()[15] = 1.0; + current_mesh_model->cm.Tr = tr.transpose(); + } + + mesh_model_iterator++; + cb(i_mesh * delta_percent, + std::string( + "Finished reading mesh " + std::to_string(i_mesh + 1) + " of " + + std::to_string(meshModelList.size())) + .c_str()); + } } void Lib3MFPlugin::open( - const QString& formatName, - const QString& fileName, - MeshModel& m, - int& mask, - const RichParameterList& par, - vcg::CallBackPos* cb) + const QString& formatName, + const QString& fileName, + MeshModel& m, + int& mask, + const RichParameterList& par, + vcg::CallBackPos* cb) { - using namespace vcg::tri::io; - - mask = Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX | Mask::IOM_VERTCOLOR; - m.enable(mask); - - const QString errorMsgFormat = "Error encountered while loading file:\n\"%1\"\n\nError details: %2"; - - const auto& lib3mf_model = get_model_from_file(fileName); - - const auto& build_item_iterator = get_build_item_iterator(lib3mf_model); - build_item_iterator->MoveNext(); - const auto& current_build_item = build_item_iterator->GetCurrent(); - if(current_build_item == nullptr) - { - throw MLException(errorMsgFormat.arg(fileName, "Failed to access build item")); - } - - const auto& object = current_build_item->GetObjectResource(); - if( !object->IsMeshObject() ) - { - throw MLException(errorMsgFormat.arg(fileName, "Error while loading mesh object: build item is not a mesh")); - } - - const auto& mesh_object = lib3mf_model->GetMeshObjectByID(object->GetResourceID()); - if(mesh_object == nullptr) - { - throw MLException(errorMsgFormat.arg(fileName, "Invalid mesh object encountered")); - } - - load_mesh_to_meshmodel(lib3mf_model, mesh_object, m, "_0"); - current_build_item->GetObjectResource(); - if(current_build_item->HasObjectTransform()) - { - auto transform = current_build_item->GetObjectTransform(); - Matrix44m tr; - tr.SetZero(); - tr.V()[0] = transform.m_Fields[0][0]; - tr.V()[1] = transform.m_Fields[0][1]; - tr.V()[2] = transform.m_Fields[0][2]; - tr.V()[4] = transform.m_Fields[1][0]; - tr.V()[5] = transform.m_Fields[1][1]; - tr.V()[6] = transform.m_Fields[1][2]; - tr.V()[8] = transform.m_Fields[2][0]; - tr.V()[9] = transform.m_Fields[2][1]; - tr.V()[10] = transform.m_Fields[2][2]; - tr.V()[12] = transform.m_Fields[3][0]; - tr.V()[13] = transform.m_Fields[3][1]; - tr.V()[14] = transform.m_Fields[3][2]; - tr.V()[15] = 1.0; - m.cm.Tr = tr.transpose(); - } - - m.enable(mask); - - return; + wrongOpenFormat("This should have not happened!"); } void Lib3MFPlugin::save( - const QString &formatName, - const QString &fileName, - MeshModel &m, - const int mask, - const RichParameterList& par, - vcg::CallBackPos* cb) + const QString& formatName, + const QString& fileName, + MeshModel& m, + const int mask, + const RichParameterList& par, + vcg::CallBackPos* cb) { - const QString errorMsgFormat = "Error encountered while saving file:\n\"%1\"\n\nError details: %2"; - - const auto& wrapper = Lib3MF::CWrapper::loadLibrary(); - if(wrapper == nullptr) - { - throw MLException(errorMsgFormat.arg(fileName, "Could not init 3mf library")); - } - - const auto& model = wrapper->CreateModel(); - if(model == nullptr) - { - throw MLException(errorMsgFormat.arg(fileName, "Could not create model for writing")); - } - - const auto& metadata_group = model->GetMetaDataGroup(); - metadata_group->AddMetaData("","Application", "Meshlab", "string", false); - - const auto& mesh = model->AddMeshObject(); - mesh->SetName(m.label().toStdString()); - - std::vector vertex_buffer; - vertex_buffer.reserve(m.cm.vert.size()); - - std::vector vertex_ids(m.cm.vert.size()); - - std::vector triangle_buffer; - triangle_buffer.reserve(m.cm.face.size()); - - int number_of_vertices = 0; - for(auto vertex_it = m.cm.vert.begin(); vertex_it != m.cm.vert.end(); ++vertex_it) - { - if(vertex_it->IsD()) continue; - vertex_ids[vertex_it - m.cm.vert.begin()] = number_of_vertices; - Lib3MF::sPosition pos; - pos.m_Coordinates[0] = vertex_it->P()[0]; - pos.m_Coordinates[1] = vertex_it->P()[1]; - pos.m_Coordinates[2] = vertex_it->P()[2]; - vertex_buffer.push_back(pos); - number_of_vertices++; - } - - for(auto face_it = m.cm.face.begin(); face_it != m.cm.face.end(); ++face_it) - { - if(face_it->IsD()) continue; - if(face_it->VN() != 3) - { - throw MLException(errorMsgFormat.arg(fileName, "Only triangular meshes can be written to 3mf files")); - } - Lib3MF::sTriangle triangle; - triangle.m_Indices[0] = vertex_ids[vcg::tri::Index(m.cm, face_it->V(0))]; - triangle.m_Indices[1] = vertex_ids[vcg::tri::Index(m.cm, face_it->V(1))]; - triangle.m_Indices[2] = vertex_ids[vcg::tri::Index(m.cm, face_it->V(2))]; - triangle_buffer.push_back(triangle); - } - - mesh->SetGeometry(vertex_buffer, triangle_buffer); - model->AddBuildItem(mesh.get(), wrapper->GetIdentityTransform()); - const auto& writer = model->QueryWriter("3mf"); - writer->WriteToFile(fileName.toStdString()); + const QString errorMsgFormat = + "Error encountered while saving file:\n\"%1\"\n\nError details: %2"; + + const auto& wrapper = Lib3MF::CWrapper::loadLibrary(); + if (wrapper == nullptr) { + throw MLException(errorMsgFormat.arg(fileName, "Could not init 3mf library")); + } + + const auto& model = wrapper->CreateModel(); + if (model == nullptr) { + throw MLException(errorMsgFormat.arg(fileName, "Could not create model for writing")); + } + + const auto& metadata_group = model->GetMetaDataGroup(); + metadata_group->AddMetaData("", "Application", "Meshlab", "string", false); + + const auto& mesh = model->AddMeshObject(); + mesh->SetName(m.label().toStdString()); + + std::vector vertex_buffer; + vertex_buffer.reserve(m.cm.vert.size()); + + std::vector vertex_ids(m.cm.vert.size()); + + std::vector triangle_buffer; + triangle_buffer.reserve(m.cm.face.size()); + + int number_of_vertices = 0; + for (auto vertex_it = m.cm.vert.begin(); vertex_it != m.cm.vert.end(); ++vertex_it) { + if (vertex_it->IsD()) + continue; + vertex_ids[vertex_it - m.cm.vert.begin()] = number_of_vertices; + Lib3MF::sPosition pos; + pos.m_Coordinates[0] = vertex_it->P()[0]; + pos.m_Coordinates[1] = vertex_it->P()[1]; + pos.m_Coordinates[2] = vertex_it->P()[2]; + vertex_buffer.push_back(pos); + number_of_vertices++; + } + + for (auto face_it = m.cm.face.begin(); face_it != m.cm.face.end(); ++face_it) { + if (face_it->IsD()) + continue; + if (face_it->VN() != 3) { + throw MLException( + errorMsgFormat.arg(fileName, "Only triangular meshes can be written to 3mf files")); + } + Lib3MF::sTriangle triangle; + triangle.m_Indices[0] = vertex_ids[vcg::tri::Index(m.cm, face_it->V(0))]; + triangle.m_Indices[1] = vertex_ids[vcg::tri::Index(m.cm, face_it->V(1))]; + triangle.m_Indices[2] = vertex_ids[vcg::tri::Index(m.cm, face_it->V(2))]; + triangle_buffer.push_back(triangle); + } + + mesh->SetGeometry(vertex_buffer, triangle_buffer); + model->AddBuildItem(mesh.get(), wrapper->GetIdentityTransform()); + const auto& writer = model->QueryWriter("3mf"); + writer->WriteToFile(fileName.toStdString()); } -void Lib3MFPlugin::exportMaskCapability( - const QString &format, - int& capability, - int& defaultBits) const +void Lib3MFPlugin::exportMaskCapability(const QString& format, int& capability, int& defaultBits) + const { } diff --git a/src/meshlabplugins/io_3mf/io_3mf.h b/src/meshlabplugins/io_3mf/io_3mf.h index de5300b72..870c7e4f5 100644 --- a/src/meshlabplugins/io_3mf/io_3mf.h +++ b/src/meshlabplugins/io_3mf/io_3mf.h @@ -1,25 +1,25 @@ /**************************************************************************** -* 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. * -* * -****************************************************************************/ + * 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. * + * * + ****************************************************************************/ #pragma once @@ -27,47 +27,48 @@ class Lib3MFPlugin : public QObject, public IOPlugin { - Q_OBJECT - MESHLAB_PLUGIN_IID_EXPORTER(IO_PLUGIN_IID) - Q_INTERFACES(IOPlugin) + Q_OBJECT + MESHLAB_PLUGIN_IID_EXPORTER(IO_PLUGIN_IID) + Q_INTERFACES(IOPlugin) - public: - Lib3MFPlugin(); - QString pluginName() const override; - std::list importFormats() const override; - std::list exportFormats() const override; +public: + Lib3MFPlugin(); - unsigned int numberMeshesContainedInFile( - const QString& format, - const QString& fileName, - const RichParameterList& preParams) const override; + QString pluginName() const override; + std::list importFormats() const override; + std::list exportFormats() const override; - void open( - const QString &format, - const QString &fileName, - const std::list& meshModelList, - std::list& maskList, - const RichParameterList & par, - vcg::CallBackPos *cb = nullptr) override; + RichParameterList initPreOpenParameter(const QString& /*format*/) const override; - void open( - const QString& formatName, - const QString& fileName, - MeshModel& m, - int& mask, - const RichParameterList& par, - vcg::CallBackPos* cb) override; + unsigned int numberMeshesContainedInFile( + const QString& format, + const QString& fileName, + const RichParameterList& preParams) const override; - void save( - const QString &formatName, - const QString &fileName, - MeshModel &m, - const int mask, - const RichParameterList& par, - vcg::CallBackPos* cb) override; + void open( + const QString& format, + const QString& fileName, + const std::list& meshModelList, + std::list& maskList, + const RichParameterList& par, + vcg::CallBackPos* cb = nullptr) override; - virtual void exportMaskCapability( - const QString &format, - int& capability, - int& defaultBits) const override; + void open( + const QString& formatName, + const QString& fileName, + MeshModel& m, + int& mask, + const RichParameterList& par, + vcg::CallBackPos* cb) override; + + void save( + const QString& formatName, + const QString& fileName, + MeshModel& m, + const int mask, + const RichParameterList& par, + vcg::CallBackPos* cb) override; + + virtual void + exportMaskCapability(const QString& format, int& capability, int& defaultBits) const override; }; From 7c8805fa3cb952cded08aacf1eb39da2286548dd Mon Sep 17 00:00:00 2001 From: lvk88 <15655519+lvk88@users.noreply.github.com> Date: Thu, 15 Aug 2024 21:20:32 +0200 Subject: [PATCH 14/22] Add switch that enables transforming vertices on import --- src/meshlabplugins/io_3mf/io_3mf.cpp | 65 +++++++++++++++++++--------- 1 file changed, 45 insertions(+), 20 deletions(-) diff --git a/src/meshlabplugins/io_3mf/io_3mf.cpp b/src/meshlabplugins/io_3mf/io_3mf.cpp index f085b80d4..315e85543 100644 --- a/src/meshlabplugins/io_3mf/io_3mf.cpp +++ b/src/meshlabplugins/io_3mf/io_3mf.cpp @@ -27,6 +27,7 @@ #include "common/ml_document/mesh_model.h" #include "common/parameters/rich_parameter/rich_bool.h" #include "common/parameters/rich_parameter_list.h" +#include "external/downloads/lib3mf-2.2.0/Autogenerated/Source/lib3mf_abi.hpp" #include "lib3mf_implicit.hpp" #include "lib3mf_types.hpp" #include "vcg/complex/algorithms/update/color.h" @@ -245,6 +246,13 @@ RichParameterList Lib3MFPlugin::initPreOpenParameter(const QString& /*format*/) "Load colors and textures", "When turned on, loads color and texture information from the file. Turn off if you " "experience slow rendering performance.")); + result.addParam(RichBool( + "forcetransform", + false, + "Transform vertices instead of using transformation matrix", + "When turned on, transform the vertices directly, instead of creating " + "a transformation " + "matrix")); return result; } @@ -263,6 +271,11 @@ unsigned int Lib3MFPlugin::numberMeshesContainedInFile( throw MLException( errorMsgFormat.arg(fileName, "Failed to iterate over build items in file")); } + + if (build_item_iterator->Count() == 0) { + throw MLException(errorMsgFormat.arg(fileName, "The file does not contain any models!")); + } + return build_item_iterator->Count(); } @@ -276,7 +289,9 @@ void Lib3MFPlugin::open( { using namespace vcg::tri::io; - cb(0, std::string("Loading " + fileName.toStdString()).c_str()); + if (cb != nullptr) { + (*cb)(0, std::string("Loading " + fileName.toStdString()).c_str()); + } const QString errorMsgFormat = "Error encountered while loading file:\n\"%1\"\n\nError details: %2"; @@ -338,28 +353,36 @@ void Lib3MFPlugin::open( auto transform = current_build_item->GetObjectTransform(); Matrix44m tr; tr.SetZero(); - tr.V()[0] = transform.m_Fields[0][0]; - tr.V()[1] = transform.m_Fields[0][1]; - tr.V()[2] = transform.m_Fields[0][2]; - tr.V()[4] = transform.m_Fields[1][0]; - tr.V()[5] = transform.m_Fields[1][1]; - tr.V()[6] = transform.m_Fields[1][2]; - tr.V()[8] = transform.m_Fields[2][0]; - tr.V()[9] = transform.m_Fields[2][1]; - tr.V()[10] = transform.m_Fields[2][2]; - tr.V()[12] = transform.m_Fields[3][0]; - tr.V()[13] = transform.m_Fields[3][1]; - tr.V()[14] = transform.m_Fields[3][2]; - tr.V()[15] = 1.0; - current_mesh_model->cm.Tr = tr.transpose(); + tr.V()[0] = transform.m_Fields[0][0]; + tr.V()[1] = transform.m_Fields[0][1]; + tr.V()[2] = transform.m_Fields[0][2]; + tr.V()[4] = transform.m_Fields[1][0]; + tr.V()[5] = transform.m_Fields[1][1]; + tr.V()[6] = transform.m_Fields[1][2]; + tr.V()[8] = transform.m_Fields[2][0]; + tr.V()[9] = transform.m_Fields[2][1]; + tr.V()[10] = transform.m_Fields[2][2]; + tr.V()[12] = transform.m_Fields[3][0]; + tr.V()[13] = transform.m_Fields[3][1]; + tr.V()[14] = transform.m_Fields[3][2]; + tr.V()[15] = 1.0; + if (par.getBool("forcetransform")) { + vcg::tri::UpdatePositioncm)>::Matrix( + current_mesh_model->cm, tr.transpose(), true); + } + else { + current_mesh_model->cm.Tr = tr.transpose(); + } } mesh_model_iterator++; - cb(i_mesh * delta_percent, - std::string( - "Finished reading mesh " + std::to_string(i_mesh + 1) + " of " + - std::to_string(meshModelList.size())) - .c_str()); + if (cb != nullptr) { + cb(i_mesh * delta_percent, + std::string( + "Finished reading mesh " + std::to_string(i_mesh + 1) + " of " + + std::to_string(meshModelList.size())) + .c_str()); + } } } @@ -445,6 +468,8 @@ void Lib3MFPlugin::save( void Lib3MFPlugin::exportMaskCapability(const QString& format, int& capability, int& defaultBits) const { + capability = 0; + defaultBits = 0; } MESHLAB_PLUGIN_NAME_EXPORTER(Lib3MFPlugin) From 022719a64e801226701c6b8c331ae91d9dbff6a0 Mon Sep 17 00:00:00 2001 From: lvk88 <15655519+lvk88@users.noreply.github.com> Date: Thu, 15 Aug 2024 23:10:03 +0200 Subject: [PATCH 15/22] #1832: Fix lib3mf build for ninja under Windows --- src/external/lib3mf.cmake | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/external/lib3mf.cmake b/src/external/lib3mf.cmake index 002444ebc..03db5c6d6 100644 --- a/src/external/lib3mf.cmake +++ b/src/external/lib3mf.cmake @@ -41,11 +41,28 @@ if(MESHLAB_ALLOW_DOWNLOAD_SOURCE_LIB3MF) endif() if(EXISTS ${LIB3MF_CHECK}) - message(STATUS " - Lib3MF - using downloaded source") + message(STATUS "- Lib3MF - Using downloaded Lib3MF sources") set(MESSAGE_QUIET ON) set(LIB3MF_TESTS OFF) add_subdirectory(${LIB3MF_DIR} EXCLUDE_FROM_ALL) + + # Well, this is extremely ugly + # But due to some bug in lib3mf CMake function `generate_product_version`, + # it is not possible to build lib3mf with ninja, because the following + # error message will appear when processing VersionResource.rc + # + # fatal error RC1106: invalid option: -3 + # + # I don't know what causes the bug. A workaround is to just simply exclude VersionResource.rc from the list + # of sources associated to the lib3mf target. + if( WIN32 AND CMAKE_GENERATOR STREQUAL "Ninja" ) + get_target_property(LIB3MF_SRCS lib3mf SOURCES) + LIST(FILTER LIB3MF_SRCS EXCLUDE REGEX "VersionResource.rc") + SET_TARGET_PROPERTIES(lib3mf PROPERTIES SOURCES "${LIB3MF_SRCS}") + endif() unset(MESSAGE_QUIET) + else() + message(FATAL " - Lib3MF - Could not add lib3mf to source tree ") endif() add_library(external-lib3mf INTERFACE) From cd2937ba2990b13bca8935898f38da643e92b048 Mon Sep 17 00:00:00 2001 From: lvk88 <15655519+lvk88@users.noreply.github.com> Date: Fri, 16 Aug 2024 07:49:20 +0200 Subject: [PATCH 16/22] Update copyright years and comments in 3mf plugin --- src/external/lib3mf.cmake | 4 ++-- src/meshlabplugins/io_3mf/CMakeLists.txt | 2 +- src/meshlabplugins/io_3mf/io_3mf.cpp | 2 +- src/meshlabplugins/io_3mf/io_3mf.h | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/external/lib3mf.cmake b/src/external/lib3mf.cmake index 03db5c6d6..0dafc1418 100644 --- a/src/external/lib3mf.cmake +++ b/src/external/lib3mf.cmake @@ -2,7 +2,7 @@ # MeshLab o o # # A versatile mesh processing toolbox o o # # _ O _ # -# Copyright(C) 2005 \/)\/ # +# Copyright(C) 2023 - 2024 \/)\/ # # Visual Computing Lab /\/| # # ISTI - Italian National Research Council | # # \ # @@ -48,7 +48,7 @@ if(MESHLAB_ALLOW_DOWNLOAD_SOURCE_LIB3MF) # Well, this is extremely ugly # But due to some bug in lib3mf CMake function `generate_product_version`, - # it is not possible to build lib3mf with ninja, because the following + # it is not possible to build lib3mf with ninja on Windows, because the following # error message will appear when processing VersionResource.rc # # fatal error RC1106: invalid option: -3 diff --git a/src/meshlabplugins/io_3mf/CMakeLists.txt b/src/meshlabplugins/io_3mf/CMakeLists.txt index ced2df32f..9b76f7323 100644 --- a/src/meshlabplugins/io_3mf/CMakeLists.txt +++ b/src/meshlabplugins/io_3mf/CMakeLists.txt @@ -2,7 +2,7 @@ # MeshLab o o # # A versatile mesh processing toolbox o o # # _ O _ # -# Copyright(C) 2005 \/)\/ # +# Copyright(C) 2023 - 2024 \/)\/ # # Visual Computing Lab /\/| # # ISTI - Italian National Research Council | # # \ # diff --git a/src/meshlabplugins/io_3mf/io_3mf.cpp b/src/meshlabplugins/io_3mf/io_3mf.cpp index 315e85543..ebcae04dc 100644 --- a/src/meshlabplugins/io_3mf/io_3mf.cpp +++ b/src/meshlabplugins/io_3mf/io_3mf.cpp @@ -2,7 +2,7 @@ * MeshLab o o * * A versatile mesh processing toolbox o o * * _ O _ * - * Copyright(C) 2005-2020 \/)\/ * + * Copyright(C) 2023 - 2024 \/)\/ * * Visual Computing Lab /\/| * * ISTI - Italian National Research Council | * * \ * diff --git a/src/meshlabplugins/io_3mf/io_3mf.h b/src/meshlabplugins/io_3mf/io_3mf.h index 870c7e4f5..d31a0e1f0 100644 --- a/src/meshlabplugins/io_3mf/io_3mf.h +++ b/src/meshlabplugins/io_3mf/io_3mf.h @@ -2,7 +2,7 @@ * MeshLab o o * * A versatile mesh processing toolbox o o * * _ O _ * - * Copyright(C) 2005-2020 \/)\/ * + * Copyright(C) 2023 - 2024 \/)\/ * * Visual Computing Lab /\/| * * ISTI - Italian National Research Council | * * \ * From 871b3caa851ff9d6a2a26ac7407de8f7a46696f8 Mon Sep 17 00:00:00 2001 From: lvk88 <15655519+lvk88@users.noreply.github.com> Date: Sat, 17 Aug 2024 10:33:06 +0200 Subject: [PATCH 17/22] Handle error when lib3mf throws exception --- src/meshlabplugins/io_3mf/io_3mf.cpp | 202 +++++++++++++++------------ 1 file changed, 110 insertions(+), 92 deletions(-) diff --git a/src/meshlabplugins/io_3mf/io_3mf.cpp b/src/meshlabplugins/io_3mf/io_3mf.cpp index ebcae04dc..128d420de 100644 --- a/src/meshlabplugins/io_3mf/io_3mf.cpp +++ b/src/meshlabplugins/io_3mf/io_3mf.cpp @@ -27,10 +27,8 @@ #include "common/ml_document/mesh_model.h" #include "common/parameters/rich_parameter/rich_bool.h" #include "common/parameters/rich_parameter_list.h" -#include "external/downloads/lib3mf-2.2.0/Autogenerated/Source/lib3mf_abi.hpp" #include "lib3mf_implicit.hpp" #include "lib3mf_types.hpp" -#include "vcg/complex/algorithms/update/color.h" #include "vcg/complex/allocate.h" #include "vcg/space/color4.h" #include "wrap/io_trimesh/io_mask.h" @@ -264,19 +262,29 @@ unsigned int Lib3MFPlugin::numberMeshesContainedInFile( const QString errorMsgFormat = "Error encountered while loading file:\n\"%1\"\n\nError details: %2"; - const auto& model = get_model_from_file(fileName); - const auto& build_item_iterator = get_build_item_iterator(model); + try { + const auto& model = get_model_from_file(fileName); + const auto& build_item_iterator = get_build_item_iterator(model); - if (build_item_iterator == nullptr) { - throw MLException( - errorMsgFormat.arg(fileName, "Failed to iterate over build items in file")); - } + if (build_item_iterator == nullptr) { + throw MLException( + errorMsgFormat.arg(fileName, "Failed to iterate over build items in file")); + } - if (build_item_iterator->Count() == 0) { - throw MLException(errorMsgFormat.arg(fileName, "The file does not contain any models!")); - } + if (build_item_iterator->Count() == 0) { + throw MLException( + errorMsgFormat.arg(fileName, "The file does not contain any models!")); + } - return build_item_iterator->Count(); + return build_item_iterator->Count(); + } + catch (const Lib3MF::ELib3MFException& e) { + std::stringstream message_stream; + message_stream << "An exception occurred while opening the 3MF file.\n" << e.what(); + log(message_stream.str()); + throw MLException( + errorMsgFormat.arg(fileName, QString::fromStdString((message_stream.str())))); + } } void Lib3MFPlugin::open( @@ -287,103 +295,113 @@ void Lib3MFPlugin::open( const RichParameterList& par, vcg::CallBackPos* cb) { - using namespace vcg::tri::io; - - if (cb != nullptr) { - (*cb)(0, std::string("Loading " + fileName.toStdString()).c_str()); - } - const QString errorMsgFormat = "Error encountered while loading file:\n\"%1\"\n\nError details: %2"; - auto lib3mf_model = get_model_from_file(fileName); - auto build_item_iterator = get_build_item_iterator(lib3mf_model); - auto mesh_model_iterator = meshModelList.begin(); - if (meshModelList.size() != build_item_iterator->Count()) { - throw MLException(errorMsgFormat.arg( - fileName, - "Internal error while loading mesh objects: inconsistent number of meshes encontered")); - } - - auto delta_percent = 100 / meshModelList.size(); + try { + using namespace vcg::tri::io; - for (size_t i_mesh = 0; i_mesh < meshModelList.size(); ++i_mesh) { - build_item_iterator->MoveNext(); - const auto& current_build_item = build_item_iterator->GetCurrent(); - if (current_build_item == nullptr) { - throw MLException(errorMsgFormat.arg(fileName, "Failed to access build item")); + if (cb != nullptr) { + (*cb)(0, std::string("Loading " + fileName.toStdString()).c_str()); } - const auto& object = current_build_item->GetObjectResource(); - if (!object->IsMeshObject()) { + auto lib3mf_model = get_model_from_file(fileName); + auto build_item_iterator = get_build_item_iterator(lib3mf_model); + auto mesh_model_iterator = meshModelList.begin(); + if (meshModelList.size() != build_item_iterator->Count()) { throw MLException(errorMsgFormat.arg( - fileName, "Error while loading mesh object: build item is not a mesh")); + fileName, + "Internal error while loading mesh objects: inconsistent number of meshes " + "encontered")); } - const auto& current_mesh_object = lib3mf_model->GetMeshObjectByID(object->GetResourceID()); + auto delta_percent = 100 / meshModelList.size(); - if (current_mesh_object == nullptr) { - throw MLException(errorMsgFormat.arg( - fileName, "Internal error while loading mesh objects: invalid mesh object")); - } + for (size_t i_mesh = 0; i_mesh < meshModelList.size(); ++i_mesh) { + build_item_iterator->MoveNext(); + const auto& current_build_item = build_item_iterator->GetCurrent(); + if (current_build_item == nullptr) { + throw MLException(errorMsgFormat.arg(fileName, "Failed to access build item")); + } - auto current_mesh_model = (*mesh_model_iterator); - if (current_mesh_model == nullptr) { - throw MLException(errorMsgFormat.arg( - fileName, "Internal error while loading mesh objects: invalid mesh model")); - } + const auto& object = current_build_item->GetObjectResource(); + if (!object->IsMeshObject()) { + throw MLException(errorMsgFormat.arg( + fileName, "Error while loading mesh object: build item is not a mesh")); + } + + const auto& current_mesh_object = + lib3mf_model->GetMeshObjectByID(object->GetResourceID()); - // TODO (lvk88): even if enable WEDGCOLOR, meshlab will crash when trying to add a per - // vertex wedge color later on - // maskList.push_back(Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX | Mask::IOM_WEDGCOLOR); - // current_mesh_model->enable( Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX | - // Mask::Mask::IOM_WEDGCOLOR); - - maskList.push_back(Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX); - current_mesh_model->enable(Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX); - - load_mesh_to_meshmodel( - lib3mf_model, - current_mesh_object, - *current_mesh_model, - "_" + std::to_string(i_mesh), - par.getBool("usecolors")); - - if (current_build_item->HasObjectTransform()) { - auto transform = current_build_item->GetObjectTransform(); - Matrix44m tr; - tr.SetZero(); - tr.V()[0] = transform.m_Fields[0][0]; - tr.V()[1] = transform.m_Fields[0][1]; - tr.V()[2] = transform.m_Fields[0][2]; - tr.V()[4] = transform.m_Fields[1][0]; - tr.V()[5] = transform.m_Fields[1][1]; - tr.V()[6] = transform.m_Fields[1][2]; - tr.V()[8] = transform.m_Fields[2][0]; - tr.V()[9] = transform.m_Fields[2][1]; - tr.V()[10] = transform.m_Fields[2][2]; - tr.V()[12] = transform.m_Fields[3][0]; - tr.V()[13] = transform.m_Fields[3][1]; - tr.V()[14] = transform.m_Fields[3][2]; - tr.V()[15] = 1.0; - if (par.getBool("forcetransform")) { - vcg::tri::UpdatePositioncm)>::Matrix( - current_mesh_model->cm, tr.transpose(), true); + if (current_mesh_object == nullptr) { + throw MLException(errorMsgFormat.arg( + fileName, "Internal error while loading mesh objects: invalid mesh object")); } - else { - current_mesh_model->cm.Tr = tr.transpose(); + + auto current_mesh_model = (*mesh_model_iterator); + if (current_mesh_model == nullptr) { + throw MLException(errorMsgFormat.arg( + fileName, "Internal error while loading mesh objects: invalid mesh model")); } - } - mesh_model_iterator++; - if (cb != nullptr) { - cb(i_mesh * delta_percent, - std::string( - "Finished reading mesh " + std::to_string(i_mesh + 1) + " of " + - std::to_string(meshModelList.size())) - .c_str()); + // TODO (lvk88): even if enable WEDGCOLOR, meshlab will crash when trying to add a + // per vertex wedge color later on maskList.push_back(Mask::IOM_VERTCOORD | + // Mask::IOM_FACEINDEX | Mask::IOM_WEDGCOLOR); current_mesh_model->enable( + // Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX | Mask::Mask::IOM_WEDGCOLOR); + + maskList.push_back(Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX); + current_mesh_model->enable(Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX); + + load_mesh_to_meshmodel( + lib3mf_model, + current_mesh_object, + *current_mesh_model, + "_" + std::to_string(i_mesh), + par.getBool("usecolors")); + + if (current_build_item->HasObjectTransform()) { + auto transform = current_build_item->GetObjectTransform(); + Matrix44m tr; + tr.SetZero(); + tr.V()[0] = transform.m_Fields[0][0]; + tr.V()[1] = transform.m_Fields[0][1]; + tr.V()[2] = transform.m_Fields[0][2]; + tr.V()[4] = transform.m_Fields[1][0]; + tr.V()[5] = transform.m_Fields[1][1]; + tr.V()[6] = transform.m_Fields[1][2]; + tr.V()[8] = transform.m_Fields[2][0]; + tr.V()[9] = transform.m_Fields[2][1]; + tr.V()[10] = transform.m_Fields[2][2]; + tr.V()[12] = transform.m_Fields[3][0]; + tr.V()[13] = transform.m_Fields[3][1]; + tr.V()[14] = transform.m_Fields[3][2]; + tr.V()[15] = 1.0; + if (par.getBool("forcetransform")) { + vcg::tri::UpdatePositioncm)>::Matrix( + current_mesh_model->cm, tr.transpose(), true); + } + else { + current_mesh_model->cm.Tr = tr.transpose(); + } + } + + mesh_model_iterator++; + if (cb != nullptr) { + cb(i_mesh * delta_percent, + std::string( + "Finished reading mesh " + std::to_string(i_mesh + 1) + " of " + + std::to_string(meshModelList.size())) + .c_str()); + } } } + catch (const Lib3MF::ELib3MFException& e) { + std::stringstream message_stream; + message_stream << "An exception occurred while opening the 3MF file.\n" << e.what(); + log(message_stream.str()); + throw MLException( + errorMsgFormat.arg(fileName, QString::fromStdString((message_stream.str())))); + } } void Lib3MFPlugin::open( From d5ab0ffa108ab2d8585fd8cbed39e0464e0fdd4f Mon Sep 17 00:00:00 2001 From: lvk88 <15655519+lvk88@users.noreply.github.com> Date: Sat, 17 Aug 2024 11:31:07 +0200 Subject: [PATCH 18/22] Add more exception handling if reading 3mf fails --- src/meshlabplugins/io_3mf/io_3mf.cpp | 29 ++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/meshlabplugins/io_3mf/io_3mf.cpp b/src/meshlabplugins/io_3mf/io_3mf.cpp index 128d420de..e6967ad14 100644 --- a/src/meshlabplugins/io_3mf/io_3mf.cpp +++ b/src/meshlabplugins/io_3mf/io_3mf.cpp @@ -35,6 +35,7 @@ #include #include +#include #include #include @@ -285,6 +286,20 @@ unsigned int Lib3MFPlugin::numberMeshesContainedInFile( throw MLException( errorMsgFormat.arg(fileName, QString::fromStdString((message_stream.str())))); } + catch (const std::exception& e) { + std::stringstream message_stream; + message_stream << "An exception occurred while opening the 3MF file.\n" << e.what(); + log(message_stream.str()); + throw MLException( + errorMsgFormat.arg(fileName, QString::fromStdString((message_stream.str())))); + } + catch (...) { + std::stringstream message_stream; + message_stream << "An unkown error occurred while opening the 3MF file.\n"; + log(message_stream.str()); + throw MLException( + errorMsgFormat.arg(fileName, QString::fromStdString((message_stream.str())))); + } } void Lib3MFPlugin::open( @@ -402,6 +417,20 @@ void Lib3MFPlugin::open( throw MLException( errorMsgFormat.arg(fileName, QString::fromStdString((message_stream.str())))); } + catch (const std::exception& e) { + std::stringstream message_stream; + message_stream << "An exception occurred while opening the 3MF file.\n" << e.what(); + log(message_stream.str()); + throw MLException( + errorMsgFormat.arg(fileName, QString::fromStdString((message_stream.str())))); + } + catch (...) { + std::stringstream message_stream; + message_stream << "An unkown error occurred while opening the 3MF file.\n"; + log(message_stream.str()); + throw MLException( + errorMsgFormat.arg(fileName, QString::fromStdString((message_stream.str())))); + } } void Lib3MFPlugin::open( From 3eb2147fa3902df811bb8127728d2b0869c719a1 Mon Sep 17 00:00:00 2001 From: lvk88 <15655519+lvk88@users.noreply.github.com> Date: Mon, 19 Aug 2024 19:51:09 +0200 Subject: [PATCH 19/22] Enable 3mf reader to read component objects --- src/meshlabplugins/io_3mf/io_3mf.cpp | 383 +++++++++++++++++++++------ 1 file changed, 299 insertions(+), 84 deletions(-) diff --git a/src/meshlabplugins/io_3mf/io_3mf.cpp b/src/meshlabplugins/io_3mf/io_3mf.cpp index e6967ad14..cb7f0afda 100644 --- a/src/meshlabplugins/io_3mf/io_3mf.cpp +++ b/src/meshlabplugins/io_3mf/io_3mf.cpp @@ -84,22 +84,101 @@ Lib3MF::PLib3MFBuildItemIterator get_build_item_iterator(const Lib3MF::PModel& m return model->GetBuildItems(); } +// Loads all the textures from the model into QImage-s and returns them in a map +// where the key is the unique resource ID of the texture +std::map load_textures(const Lib3MF::PModel& model) +{ + std::map result; + auto textures = model->GetTexture2Ds(); + if (textures == nullptr) { + throw std::runtime_error("Could not get iterator to textures"); + } + + while (textures->MoveNext()) { + auto current_texture = textures->GetCurrentTexture2D(); + auto id = current_texture->GetUniqueResourceID(); + auto attachment = current_texture->GetAttachment(); + if (attachment == nullptr) { + throw std::runtime_error("Attachment to texture returned a nullptr"); + } + std::vector buffer; + attachment->WriteToBuffer(buffer); + QImage image; + image.loadFromData(buffer.data(), buffer.size()); + result.insert({std::to_string(id), image}); + } + + return result; +} + +std::map load_meshes(const Lib3MF::PModel& model) +{ + std::map result; + if (model == nullptr) { + throw std::runtime_error("Got a null model!"); + } + + auto meshes = model->GetMeshObjects(); + if (meshes == nullptr) { + throw std::runtime_error("Could not access mesh iterators!"); + } + + while (meshes->MoveNext()) { + auto current_mesh = meshes->GetCurrentMeshObject(); + if (current_mesh == nullptr) { + throw std::runtime_error("Error accessing mesh from iterator"); + } + + auto id = current_mesh->GetUniqueResourceID(); + CMeshO cmesh; + + auto n_vertices = current_mesh->GetVertexCount(); + auto n_triangles = current_mesh->GetTriangleCount(); + + auto vertex_iterator = vcg::tri::Allocator::AddVertices(cmesh, n_vertices); + auto face_iterator = vcg::tri::Allocator::AddFaces(cmesh, n_triangles); + + for (size_t i = 0; i < n_vertices; ++i) { + const auto& pos = current_mesh->GetVertex(i).m_Coordinates; + (*vertex_iterator).P()[0] = pos[0]; + (*vertex_iterator).P()[1] = pos[1]; + (*vertex_iterator).P()[2] = pos[2]; + ++vertex_iterator; + } + + for (size_t i = 0; i < n_triangles; ++i) { + const auto& tri = current_mesh->GetTriangle(i).m_Indices; + (*face_iterator).V(0) = &cmesh.vert[tri[0]]; + (*face_iterator).V(1) = &cmesh.vert[tri[1]]; + (*face_iterator).V(2) = &cmesh.vert[tri[2]]; + ++face_iterator; + } + + result.insert({id, cmesh}); + } + + return result; +} + void load_mesh_to_meshmodel( - const Lib3MF::PModel& model, - const Lib3MF::PMeshObject& lib3mf_mesh_object, - MeshModel& mesh_model, - const std::string& name_postfix, - bool load_material_data) + const Lib3MF::PModel& model, + const Lib3MF::PMeshObject& lib3mf_mesh_object, + MeshModel& mesh_model, + const std::string& name_postfix, + const std::map& textures, + bool load_material_data) { - auto load_or_get_texture_id = [&mesh_model, &model](const int id) { + auto load_or_get_texture_id = [&mesh_model, &model, &textures](const int id) { const std::string string_id = std::to_string(id); - const auto& texture = mesh_model.getTexture(string_id); - if (texture.isNull()) { - std::vector buffer; - model->GetTexture2DByID(id)->GetAttachment()->WriteToBuffer(buffer); - QImage image; - image.loadFromData(buffer.data(), buffer.size()); - mesh_model.addTexture(string_id, image); + + // If the mesh model doesn't have this texture yet, load it + if (mesh_model.getTexture(string_id).isNull()) { + if (textures.find(string_id) == textures.end()) { + throw std::runtime_error( + "One of the meshes uses a texture that does not exist in the model!"); + } + auto texture = textures.at(string_id); + mesh_model.addTexture(string_id, texture); } auto texture_id = std::distance( @@ -302,6 +381,155 @@ unsigned int Lib3MFPlugin::numberMeshesContainedInFile( } } +void to_cmesh(const Lib3MF::PMeshObject& mesh_object, CMeshO& target) +{ + auto n_vertices = mesh_object->GetVertexCount(); + auto n_triangles = mesh_object->GetTriangleCount(); + + auto vertex_iterator = + vcg::tri::Allocator>::AddVertices( + target, n_vertices); + auto face_iterator = vcg::tri::Allocator>::AddFaces( + target, n_triangles); + + for (int i = 0; i < n_vertices; ++i) { + const auto& pos = mesh_object->GetVertex(i).m_Coordinates; + (*vertex_iterator).P()[0] = pos[0]; + (*vertex_iterator).P()[1] = pos[1]; + (*vertex_iterator).P()[2] = pos[2]; + ++vertex_iterator; + } + + for (size_t i = 0; i < n_triangles; ++i) { + const auto& tri = mesh_object->GetTriangle(i).m_Indices; + (*face_iterator).V(0) = &target.vert[tri[0]]; + (*face_iterator).V(1) = &target.vert[tri[1]]; + (*face_iterator).V(2) = &target.vert[tri[2]]; + ++face_iterator; + } +} + +bool append_props(const Lib3MF::PModel model, const Lib3MF::PMeshObject mesh_object, CMeshO& cmesh) +{ + auto n_triangles = mesh_object->GetTriangleCount(); + + bool result = false; + + for (int i = 0; i < n_triangles; ++i) { + Lib3MF::sTriangleProperties props; + mesh_object->GetTriangleProperties(i, props); + if (props.m_ResourceID == 0) { + continue; + } + + switch (model->GetPropertyTypeByID(props.m_ResourceID)) { + case Lib3MF::ePropertyType::BaseMaterial: { + result = true; + auto baseMaterial = model->GetBaseMaterialGroupByID(props.m_ResourceID); + auto color = baseMaterial->GetDisplayColor(props.m_PropertyIDs[0]); + cmesh.face[i].C() = + vcg::Color4b {color.m_Red, color.m_Green, color.m_Blue, color.m_Alpha}; + break; + } + case Lib3MF::ePropertyType::TexCoord: { + auto group = model->GetTexture2DGroupByID(props.m_ResourceID); + auto texture_id = std::distance( + cmesh.textures.begin(), + std::find( + cmesh.textures.begin(), + cmesh.textures.end(), + std::to_string(group->GetTexture2D()->GetUniqueResourceID()))); + auto coord0 = group->GetTex2Coord(props.m_PropertyIDs[0]); + auto coord1 = group->GetTex2Coord(props.m_PropertyIDs[1]); + auto coord2 = group->GetTex2Coord(props.m_PropertyIDs[2]); + + cmesh.face[i].WT(0).U() = coord0.m_U; + cmesh.face[i].WT(0).V() = coord0.m_V; + cmesh.face[i].WT(0).N() = texture_id; + + cmesh.face[i].WT(1).U() = coord1.m_U; + cmesh.face[i].WT(1).V() = coord1.m_V; + cmesh.face[i].WT(1).N() = texture_id; + + cmesh.face[i].WT(2).U() = coord2.m_U; + cmesh.face[i].WT(2).V() = coord2.m_V; + cmesh.face[i].WT(2).N() = texture_id; + break; + } + case Lib3MF::ePropertyType::Colors: { + // mesh_model.enable(vcg::tri::io::Mask::IOM_FACECOLOR); + result = true; + auto colorGroup = model->GetColorGroupByID(props.m_ResourceID); + auto color0 = colorGroup->GetColor(props.m_PropertyIDs[0]); + cmesh.face[i].C() = + vcg::Color4b {color0.m_Red, color0.m_Green, color0.m_Blue, color0.m_Alpha}; + break; + } + default: break; + }; + } + + return result; +} + +void read_components( + int level, + const Lib3MF::PModel& model, + const Lib3MF::PComponentsObject& componentsObject, + MeshModel& meshModel, + Matrix44m T) +{ + for (int iComponent = 0; iComponent < componentsObject->GetComponentCount(); ++iComponent) { + auto component = componentsObject->GetComponent(iComponent); + auto objectResource = component->GetObjectResource(); + auto currentTransform = T; + if (component->HasTransform()) { + auto transform = component->GetTransform(); + Matrix44m componentT; + componentT.ElementAt(0, 0) = transform.m_Fields[0][0]; + componentT.ElementAt(0, 1) = transform.m_Fields[0][1]; + componentT.ElementAt(0, 2) = transform.m_Fields[0][2]; + componentT.ElementAt(1, 0) = transform.m_Fields[1][0]; + componentT.ElementAt(1, 1) = transform.m_Fields[1][1]; + componentT.ElementAt(1, 2) = transform.m_Fields[1][2]; + componentT.ElementAt(2, 0) = transform.m_Fields[2][0]; + componentT.ElementAt(2, 1) = transform.m_Fields[2][1]; + componentT.ElementAt(2, 2) = transform.m_Fields[2][2]; + componentT.ElementAt(3, 0) = transform.m_Fields[3][0]; + componentT.ElementAt(3, 1) = transform.m_Fields[3][1]; + componentT.ElementAt(3, 2) = transform.m_Fields[3][2]; + componentT.ElementAt(3, 3) = 1.0; + currentTransform = currentTransform * componentT.transpose(); + } + if (objectResource->IsMeshObject()) { + auto meshObject = model->GetMeshObjectByID(objectResource->GetUniqueResourceID()); + auto n_vertices = meshObject->GetVertexCount(); + auto n_triangles = meshObject->GetTriangleCount(); + CMeshO new_cmesh; + for (const auto& texture : meshModel.getTextures()) { + new_cmesh.textures.push_back(texture.first); + } + to_cmesh(meshObject, new_cmesh); + new_cmesh.face.EnableColor(); + new_cmesh.face.EnableWedgeTexCoord(); + meshModel.enable( + vcg::tri::io::Mask::IOM_FACECOLOR | vcg::tri::io::Mask::IOM_WEDGTEXCOORD); + append_props(model, meshObject, new_cmesh); + vcg::tri::UpdatePosition::Matrix(new_cmesh, currentTransform); + vcg::tri::Append::Mesh(meshModel.cm, new_cmesh); + } + else if (objectResource->IsComponentsObject()) { + std::cout << "Component " << objectResource->GetUniqueResourceID() << std::endl; + read_components( + level + 1, + model, + model->GetComponentsObjectByID(objectResource->GetUniqueResourceID()), + meshModel, + currentTransform); + } + } +} + void Lib3MFPlugin::open( const QString& format, const QString& fileName, @@ -313,6 +541,15 @@ void Lib3MFPlugin::open( const QString errorMsgFormat = "Error encountered while loading file:\n\"%1\"\n\nError details: %2"; + // Lib3MF doesn't seem to account for the fact that an object may contain a + // sequence of components and meshes, we can only access either a single + // mesh or a single component! + // Go over every build item + // Get the object that the build item refers to + // If it's a mesh, load it into a cmesh and return it + // If it's a components object, visit the children components recursively, keep track of the + // transformations until we find a mesh and then load it. + try { using namespace vcg::tri::io; @@ -323,91 +560,69 @@ void Lib3MFPlugin::open( auto lib3mf_model = get_model_from_file(fileName); auto build_item_iterator = get_build_item_iterator(lib3mf_model); auto mesh_model_iterator = meshModelList.begin(); - if (meshModelList.size() != build_item_iterator->Count()) { - throw MLException(errorMsgFormat.arg( - fileName, - "Internal error while loading mesh objects: inconsistent number of meshes " - "encontered")); - } + auto textures = load_textures(lib3mf_model); + auto meshes = load_meshes(lib3mf_model); - auto delta_percent = 100 / meshModelList.size(); + auto build_item_count = 0; - for (size_t i_mesh = 0; i_mesh < meshModelList.size(); ++i_mesh) { - build_item_iterator->MoveNext(); - const auto& current_build_item = build_item_iterator->GetCurrent(); - if (current_build_item == nullptr) { - throw MLException(errorMsgFormat.arg(fileName, "Failed to access build item")); - } + auto delta_progress = 100 / build_item_iterator->Count(); - const auto& object = current_build_item->GetObjectResource(); - if (!object->IsMeshObject()) { - throw MLException(errorMsgFormat.arg( - fileName, "Error while loading mesh object: build item is not a mesh")); + while (build_item_iterator->MoveNext()) { + if (cb != nullptr) { + (*cb)( + delta_progress * build_item_count, + std::string("Loading mesh " + std::to_string(build_item_count)).c_str()); } - - const auto& current_mesh_object = - lib3mf_model->GetMeshObjectByID(object->GetResourceID()); - - if (current_mesh_object == nullptr) { - throw MLException(errorMsgFormat.arg( - fileName, "Internal error while loading mesh objects: invalid mesh object")); + auto& mesh_model = *(mesh_model_iterator++); + auto current_build_item = build_item_iterator->GetCurrent(); + auto object_resource = current_build_item->GetObjectResource(); + for (const auto& texture : textures) { + const auto& id = texture.first; + const auto& image = texture.second; + mesh_model->addTexture(id, image); } - - auto current_mesh_model = (*mesh_model_iterator); - if (current_mesh_model == nullptr) { - throw MLException(errorMsgFormat.arg( - fileName, "Internal error while loading mesh objects: invalid mesh model")); + if (object_resource->IsMeshObject()) { + auto mesh_object = + lib3mf_model->GetMeshObjectByID(object_resource->GetUniqueResourceID()); + to_cmesh(mesh_object, mesh_model->cm); + mesh_model->enable( + vcg::tri::io::Mask::IOM_FACECOLOR | vcg::tri::io::Mask::IOM_WEDGTEXCOORD); + append_props(lib3mf_model, mesh_object, mesh_model->cm); + } + else if (object_resource->IsComponentsObject()) { + read_components( + 1, + lib3mf_model, + lib3mf_model->GetComponentsObjectByID(object_resource->GetUniqueResourceID()), + *mesh_model, + Matrix44m::Identity()); } - - // TODO (lvk88): even if enable WEDGCOLOR, meshlab will crash when trying to add a - // per vertex wedge color later on maskList.push_back(Mask::IOM_VERTCOORD | - // Mask::IOM_FACEINDEX | Mask::IOM_WEDGCOLOR); current_mesh_model->enable( - // Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX | Mask::Mask::IOM_WEDGCOLOR); - - maskList.push_back(Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX); - current_mesh_model->enable(Mask::IOM_VERTCOORD | Mask::IOM_FACEINDEX); - - load_mesh_to_meshmodel( - lib3mf_model, - current_mesh_object, - *current_mesh_model, - "_" + std::to_string(i_mesh), - par.getBool("usecolors")); if (current_build_item->HasObjectTransform()) { auto transform = current_build_item->GetObjectTransform(); - Matrix44m tr; - tr.SetZero(); - tr.V()[0] = transform.m_Fields[0][0]; - tr.V()[1] = transform.m_Fields[0][1]; - tr.V()[2] = transform.m_Fields[0][2]; - tr.V()[4] = transform.m_Fields[1][0]; - tr.V()[5] = transform.m_Fields[1][1]; - tr.V()[6] = transform.m_Fields[1][2]; - tr.V()[8] = transform.m_Fields[2][0]; - tr.V()[9] = transform.m_Fields[2][1]; - tr.V()[10] = transform.m_Fields[2][2]; - tr.V()[12] = transform.m_Fields[3][0]; - tr.V()[13] = transform.m_Fields[3][1]; - tr.V()[14] = transform.m_Fields[3][2]; - tr.V()[15] = 1.0; + Matrix44m T; + T.ElementAt(0, 0) = transform.m_Fields[0][0]; + T.ElementAt(0, 1) = transform.m_Fields[0][1]; + T.ElementAt(0, 2) = transform.m_Fields[0][2]; + T.ElementAt(1, 0) = transform.m_Fields[1][0]; + T.ElementAt(1, 1) = transform.m_Fields[1][1]; + T.ElementAt(1, 2) = transform.m_Fields[1][2]; + T.ElementAt(2, 0) = transform.m_Fields[2][0]; + T.ElementAt(2, 1) = transform.m_Fields[2][1]; + T.ElementAt(2, 2) = transform.m_Fields[2][2]; + T.ElementAt(3, 0) = transform.m_Fields[3][0]; + T.ElementAt(3, 1) = transform.m_Fields[3][1]; + T.ElementAt(3, 2) = transform.m_Fields[3][2]; + T.ElementAt(3, 3) = 1.0; if (par.getBool("forcetransform")) { - vcg::tri::UpdatePositioncm)>::Matrix( - current_mesh_model->cm, tr.transpose(), true); + vcg::tri::UpdatePositioncm)>::Matrix( + mesh_model->cm, T.transpose(), true); } else { - current_mesh_model->cm.Tr = tr.transpose(); + mesh_model->cm.Tr = T.transpose(); } } - - mesh_model_iterator++; - if (cb != nullptr) { - cb(i_mesh * delta_percent, - std::string( - "Finished reading mesh " + std::to_string(i_mesh + 1) + " of " + - std::to_string(meshModelList.size())) - .c_str()); - } + build_item_count++; } } catch (const Lib3MF::ELib3MFException& e) { From 3650f7f1c60a31efe2dca6129bb580969d1aefeb Mon Sep 17 00:00:00 2001 From: lvk88 <15655519+lvk88@users.noreply.github.com> Date: Mon, 19 Aug 2024 20:16:43 +0200 Subject: [PATCH 20/22] Remove unused functions in 3mf reader after refactoring --- src/meshlabplugins/io_3mf/io_3mf.cpp | 184 --------------------------- 1 file changed, 184 deletions(-) diff --git a/src/meshlabplugins/io_3mf/io_3mf.cpp b/src/meshlabplugins/io_3mf/io_3mf.cpp index cb7f0afda..a43c0cb5b 100644 --- a/src/meshlabplugins/io_3mf/io_3mf.cpp +++ b/src/meshlabplugins/io_3mf/io_3mf.cpp @@ -111,189 +111,6 @@ std::map load_textures(const Lib3MF::PModel& model) return result; } -std::map load_meshes(const Lib3MF::PModel& model) -{ - std::map result; - if (model == nullptr) { - throw std::runtime_error("Got a null model!"); - } - - auto meshes = model->GetMeshObjects(); - if (meshes == nullptr) { - throw std::runtime_error("Could not access mesh iterators!"); - } - - while (meshes->MoveNext()) { - auto current_mesh = meshes->GetCurrentMeshObject(); - if (current_mesh == nullptr) { - throw std::runtime_error("Error accessing mesh from iterator"); - } - - auto id = current_mesh->GetUniqueResourceID(); - CMeshO cmesh; - - auto n_vertices = current_mesh->GetVertexCount(); - auto n_triangles = current_mesh->GetTriangleCount(); - - auto vertex_iterator = vcg::tri::Allocator::AddVertices(cmesh, n_vertices); - auto face_iterator = vcg::tri::Allocator::AddFaces(cmesh, n_triangles); - - for (size_t i = 0; i < n_vertices; ++i) { - const auto& pos = current_mesh->GetVertex(i).m_Coordinates; - (*vertex_iterator).P()[0] = pos[0]; - (*vertex_iterator).P()[1] = pos[1]; - (*vertex_iterator).P()[2] = pos[2]; - ++vertex_iterator; - } - - for (size_t i = 0; i < n_triangles; ++i) { - const auto& tri = current_mesh->GetTriangle(i).m_Indices; - (*face_iterator).V(0) = &cmesh.vert[tri[0]]; - (*face_iterator).V(1) = &cmesh.vert[tri[1]]; - (*face_iterator).V(2) = &cmesh.vert[tri[2]]; - ++face_iterator; - } - - result.insert({id, cmesh}); - } - - return result; -} - -void load_mesh_to_meshmodel( - const Lib3MF::PModel& model, - const Lib3MF::PMeshObject& lib3mf_mesh_object, - MeshModel& mesh_model, - const std::string& name_postfix, - const std::map& textures, - bool load_material_data) -{ - auto load_or_get_texture_id = [&mesh_model, &model, &textures](const int id) { - const std::string string_id = std::to_string(id); - - // If the mesh model doesn't have this texture yet, load it - if (mesh_model.getTexture(string_id).isNull()) { - if (textures.find(string_id) == textures.end()) { - throw std::runtime_error( - "One of the meshes uses a texture that does not exist in the model!"); - } - auto texture = textures.at(string_id); - mesh_model.addTexture(string_id, texture); - } - - auto texture_id = std::distance( - mesh_model.cm.textures.begin(), - std::find(mesh_model.cm.textures.begin(), mesh_model.cm.textures.end(), string_id)); - return texture_id; - }; - - std::vector available_property_ids; - - auto base_material_groups = model->GetBaseMaterialGroups(); - while (base_material_groups->MoveNext()) { - auto current = base_material_groups->GetCurrentBaseMaterialGroup(); - available_property_ids.push_back(current->GetUniqueResourceID()); - } - - auto color_groups = model->GetColorGroups(); - while (color_groups->MoveNext()) { - auto current = color_groups->GetCurrentColorGroup(); - available_property_ids.push_back(current->GetUniqueResourceID()); - } - - auto texture_groups = model->GetTexture2DGroups(); - while (texture_groups->MoveNext()) { - auto current = texture_groups->GetCurrentTexture2DGroup(); - available_property_ids.push_back(current->GetUniqueResourceID()); - } - - std::string mesh_name = lib3mf_mesh_object->GetName(); - if (mesh_name.empty()) { - mesh_name = "Mesh" + name_postfix; - } - mesh_model.setLabel(QString::fromStdString(mesh_name)); - const auto n_vertices = lib3mf_mesh_object->GetVertexCount(); - const auto n_triangles = lib3mf_mesh_object->GetTriangleCount(); - auto vertex_iterator = - vcg::tri::Allocator::AddVertices(mesh_model.cm, n_vertices); - auto face_iterator = - vcg::tri::Allocator::AddFaces(mesh_model.cm, n_triangles); - for (size_t i = 0; i < n_vertices; ++i) { - const auto& pos = lib3mf_mesh_object->GetVertex(i).m_Coordinates; - (*vertex_iterator).P()[0] = pos[0]; - (*vertex_iterator).P()[1] = pos[1]; - (*vertex_iterator).P()[2] = pos[2]; - ++vertex_iterator; - } - - // Load the triangles only, but no colors yet - for (size_t i = 0; i < n_triangles; ++i) { - const auto& tri = lib3mf_mesh_object->GetTriangle(i).m_Indices; - (*face_iterator).V(0) = &mesh_model.cm.vert[tri[0]]; - (*face_iterator).V(1) = &mesh_model.cm.vert[tri[1]]; - (*face_iterator).V(2) = &mesh_model.cm.vert[tri[2]]; - ++face_iterator; - } - - // Load colors or textures, if necessary - - if (load_material_data) { - for (size_t i = 0; i < n_triangles; ++i) { - Lib3MF::sTriangleProperties props; - lib3mf_mesh_object->GetTriangleProperties(i, props); - - if (std::find_if( - available_property_ids.begin(), - available_property_ids.end(), - [props](auto resourceId) { return resourceId == props.m_ResourceID; }) != - available_property_ids.end()) { - const auto& tri = lib3mf_mesh_object->GetTriangle(i).m_Indices; - switch (model->GetPropertyTypeByID(props.m_ResourceID)) { - case Lib3MF::ePropertyType::BaseMaterial: { - mesh_model.enable(vcg::tri::io::Mask::IOM_FACECOLOR); - auto baseMaterial = model->GetBaseMaterialGroupByID(props.m_ResourceID); - auto color = baseMaterial->GetDisplayColor(props.m_PropertyIDs[0]); - mesh_model.cm.face[i].C() = - vcg::Color4b {color.m_Red, color.m_Green, color.m_Blue, color.m_Alpha}; - break; - } - case Lib3MF::ePropertyType::TexCoord: { - mesh_model.enable(vcg::tri::io::Mask::IOM_WEDGTEXCOORD); - auto group = model->GetTexture2DGroupByID(props.m_ResourceID); - auto texture_id = - load_or_get_texture_id(group->GetTexture2D()->GetUniqueResourceID()); - auto coord0 = group->GetTex2Coord(props.m_PropertyIDs[0]); - auto coord1 = group->GetTex2Coord(props.m_PropertyIDs[1]); - auto coord2 = group->GetTex2Coord(props.m_PropertyIDs[2]); - - mesh_model.cm.face[i].WT(0).U() = coord0.m_U; - mesh_model.cm.face[i].WT(0).V() = coord0.m_V; - mesh_model.cm.face[i].WT(0).N() = texture_id; - - mesh_model.cm.face[i].WT(1).U() = coord1.m_U; - mesh_model.cm.face[i].WT(1).V() = coord1.m_V; - mesh_model.cm.face[i].WT(1).N() = texture_id; - - mesh_model.cm.face[i].WT(2).U() = coord2.m_U; - mesh_model.cm.face[i].WT(2).V() = coord2.m_V; - mesh_model.cm.face[i].WT(2).N() = texture_id; - break; - } - case Lib3MF::ePropertyType::Colors: { - mesh_model.enable(vcg::tri::io::Mask::IOM_FACECOLOR); - auto colorGroup = model->GetColorGroupByID(props.m_ResourceID); - auto color0 = colorGroup->GetColor(props.m_PropertyIDs[0]); - mesh_model.cm.face[i].C() = - vcg::Color4b {color0.m_Red, color0.m_Green, color0.m_Blue, color0.m_Alpha}; - break; - } - default: break; - }; - } - } - } -} - } // namespace Lib3MFPlugin::Lib3MFPlugin() @@ -561,7 +378,6 @@ void Lib3MFPlugin::open( auto build_item_iterator = get_build_item_iterator(lib3mf_model); auto mesh_model_iterator = meshModelList.begin(); auto textures = load_textures(lib3mf_model); - auto meshes = load_meshes(lib3mf_model); auto build_item_count = 0; From 56951e0f2de5e754987e09e92a2ed460accd62d4 Mon Sep 17 00:00:00 2001 From: lvk88 <15655519+lvk88@users.noreply.github.com> Date: Fri, 6 Sep 2024 17:19:25 +0200 Subject: [PATCH 21/22] Update lib3mf version to 2.3.2 --- src/external/lib3mf.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/external/lib3mf.cmake b/src/external/lib3mf.cmake index 0dafc1418..2cd296ee0 100644 --- a/src/external/lib3mf.cmake +++ b/src/external/lib3mf.cmake @@ -24,12 +24,12 @@ option(MESHLAB_ALLOW_DOWNLOAD_SOURCE_LIB3MF "Allow download and use of lib3MF source" ON) if(MESHLAB_ALLOW_DOWNLOAD_SOURCE_LIB3MF) - set(LIB3MF_DIR ${MESHLAB_EXTERNAL_DOWNLOAD_DIR}/lib3mf-2.2.0) + set(LIB3MF_DIR ${MESHLAB_EXTERNAL_DOWNLOAD_DIR}/lib3mf-2.3.2) set(LIB3MF_CHECK ${LIB3MF_DIR}/CMakeLists.txt) if(NOT EXISTS ${LIB3MF_CHECK}) - set(LIB3MF_LINK https://github.com/3MFConsortium/lib3mf/archive/refs/tags/v2.2.0.zip) - set(LIB3MF_MD5 31c6dd3e2599f6f32c0784d8f46480bb) + set(LIB3MF_LINK https://github.com/3MFConsortium/lib3mf/releases/download/v2.3.2/lib3mf-2.3.2-source-with-submodules.zip) + set(LIB3MF_MD5 bb6a4f480c50669ff193d5e418d7500f) download_and_unzip( NAME "Lib3MF" MD5 ${LIB3MF_MD5} From a032ec41a198f2490e58791f4895653f6739ef07 Mon Sep 17 00:00:00 2001 From: lvk88 <15655519+lvk88@users.noreply.github.com> Date: Fri, 6 Sep 2024 22:33:57 +0200 Subject: [PATCH 22/22] Update lib3mf md5sum --- src/external/lib3mf.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/external/lib3mf.cmake b/src/external/lib3mf.cmake index 2cd296ee0..f4cf06c4d 100644 --- a/src/external/lib3mf.cmake +++ b/src/external/lib3mf.cmake @@ -29,7 +29,7 @@ if(MESHLAB_ALLOW_DOWNLOAD_SOURCE_LIB3MF) if(NOT EXISTS ${LIB3MF_CHECK}) set(LIB3MF_LINK https://github.com/3MFConsortium/lib3mf/releases/download/v2.3.2/lib3mf-2.3.2-source-with-submodules.zip) - set(LIB3MF_MD5 bb6a4f480c50669ff193d5e418d7500f) + set(LIB3MF_MD5 e9f3f40de2bd58c3f9109d657c86f3a8) download_and_unzip( NAME "Lib3MF" MD5 ${LIB3MF_MD5}