From dc23efbfc20d5516cfaf9e30ce8ff8569ff014d2 Mon Sep 17 00:00:00 2001 From: Hugues Delorme Date: Fri, 29 Nov 2024 12:19:10 +0100 Subject: [PATCH] WIP4 --- src/io_assimp/io_assimp_i18n.h | 15 ++++ src/io_assimp/io_assimp_reader.cpp | 48 ++++-------- src/io_assimp/io_assimp_task_progress.cpp | 29 +++++++ src/io_assimp/io_assimp_task_progress.h | 25 ++++++ src/io_assimp/io_assimp_writer.cpp | 95 ++++++++++++++++++++++- 5 files changed, 175 insertions(+), 37 deletions(-) create mode 100644 src/io_assimp/io_assimp_i18n.h create mode 100644 src/io_assimp/io_assimp_task_progress.cpp create mode 100644 src/io_assimp/io_assimp_task_progress.h diff --git a/src/io_assimp/io_assimp_i18n.h b/src/io_assimp/io_assimp_i18n.h new file mode 100644 index 00000000..463d3380 --- /dev/null +++ b/src/io_assimp/io_assimp_i18n.h @@ -0,0 +1,15 @@ +/**************************************************************************** +** Copyright (c) 2024, Fougue Ltd. +** All rights reserved. +** See license at https://github.com/fougue/mayo/blob/master/LICENSE.txt +****************************************************************************/ + +#include "../base/text_id.h" + +namespace Mayo::IO { + +struct AssimpI18N { + MAYO_DECLARE_TEXT_ID_FUNCTIONS(Mayo::IO::AssimpI18N) +}; + +} // namespace Mayo::IO diff --git a/src/io_assimp/io_assimp_reader.cpp b/src/io_assimp/io_assimp_reader.cpp index 5f522bdc..6d5b5962 100644 --- a/src/io_assimp/io_assimp_reader.cpp +++ b/src/io_assimp/io_assimp_reader.cpp @@ -5,6 +5,10 @@ ****************************************************************************/ #include "io_assimp_reader.h" + +#include "io_assimp_i18n.h" +#include "io_assimp_task_progress.h" + #include "../base/brep_utils.h" #include "../base/caf_utils.h" #include "../base/cpp_utils.h" @@ -25,11 +29,6 @@ #include #include -#include - -#include -#include - #include #include #include @@ -40,16 +39,17 @@ #include #include +#include + +#include +#include + //#define MAYO_ASSIMP_READER_HANDLE_SCALING 1 namespace Mayo::IO { namespace { -struct AssimpReaderI18N { - MAYO_DECLARE_TEXT_ID_FUNCTIONS(Mayo::IO::AssimpReaderI18N) -}; - // Retrieve the scaling component in assimp matrix 'trsf' aiVector3D aiMatrixScaling(const aiMatrix4x4& trsf) { @@ -271,28 +271,6 @@ OccHandle createOccTriangulation(const aiMesh* mesh) return triangulation; } -// Provides assimp progress handler for TaskProgress -class AssimpProgressHandler : public Assimp::ProgressHandler { -public: - AssimpProgressHandler(TaskProgress* progress) - : m_progress(progress) - {} - - bool Update(float percent = -1.f) override - { - if (TaskProgress::isAbortRequested(m_progress)) - return false; - - if (percent > 0) - m_progress->setValue(percent * 100); - - return true; - } - -private: - TaskProgress* m_progress = nullptr; -}; - } // namespace // -- @@ -336,7 +314,7 @@ bool AssimpReader::readFile(const FilePath& filepath, TaskProgress* progress) //const unsigned flags = aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_CalcTangentSpace; //m_importer.SetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, false); m_importer.SetPropertyBool(AI_CONFIG_PP_PTV_KEEP_HIERARCHY, true); - m_importer.SetProgressHandler(new AssimpProgressHandler(progress)); + m_importer.SetProgressHandler(new AssimpTaskProgress(progress)); m_scene = m_importer.ReadFile(filepath.u8string(), flags); if (!m_scene || (m_scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE) || !m_scene->mRootNode) { this->messenger()->emitError(m_importer.GetErrorString()); @@ -366,10 +344,10 @@ bool AssimpReader::readFile(const FilePath& filepath, TaskProgress* progress) } else if (mesh->mPrimitiveTypes & aiPrimitiveType_LINE) { // TODO Create and add a Poly_Polygon3D object - this->messenger()->emitWarning(AssimpReaderI18N::textIdTr("LINE primitives not supported yet")); + this->messenger()->emitWarning(AssimpI18N::textIdTr("LINE primitives not supported yet")); } else { - this->messenger()->emitWarning(AssimpReaderI18N::textIdTr("Some primitive not supported")); + this->messenger()->emitWarning(AssimpI18N::textIdTr("Some primitive not supported")); } } @@ -500,7 +478,7 @@ OccHandle AssimpReader::findOccTexture( // Report warning "texture not found" MessageStream msgWarning = this->messenger()->warning(); - msgWarning << fmt::format(AssimpReaderI18N::textIdTr("Texture not found: {}\nTried:"), strFilepath); + msgWarning << fmt::format(AssimpI18N::textIdTr("Texture not found: {}\nTried:"), strFilepath); for (const FilePath& fp : textureFilepathCandidates) msgWarning << "\n " << filepathCanonical(fp).make_preferred().u8string(); diff --git a/src/io_assimp/io_assimp_task_progress.cpp b/src/io_assimp/io_assimp_task_progress.cpp new file mode 100644 index 00000000..7a7a6929 --- /dev/null +++ b/src/io_assimp/io_assimp_task_progress.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** Copyright (c) 2024, Fougue Ltd. +** All rights reserved. +** See license at https://github.com/fougue/mayo/blob/master/LICENSE.txt +****************************************************************************/ + +#include "io_assimp_task_progress.h" + +#include "../base/task_progress.h" + +namespace Mayo::IO { + +AssimpTaskProgress::AssimpTaskProgress(TaskProgress* progress) + : m_progress(progress) +{ +} + +bool AssimpTaskProgress::Update(float percent) +{ + if (TaskProgress::isAbortRequested(m_progress)) + return false; + + if (percent > 0) + m_progress->setValue(percent * 100); + + return true; +} + +} // namespace Mayo::IO diff --git a/src/io_assimp/io_assimp_task_progress.h b/src/io_assimp/io_assimp_task_progress.h new file mode 100644 index 00000000..b6d5e661 --- /dev/null +++ b/src/io_assimp/io_assimp_task_progress.h @@ -0,0 +1,25 @@ +/**************************************************************************** +** Copyright (c) 2024, Fougue Ltd. +** All rights reserved. +** See license at https://github.com/fougue/mayo/blob/master/LICENSE.txt +****************************************************************************/ + +#pragma once + +#include +namespace Mayo { class TaskProgress; } + +namespace Mayo::IO { + +// Provides assimp progress handler over Mayo::TaskProgress object +class AssimpTaskProgress : public Assimp::ProgressHandler { +public: + AssimpTaskProgress(TaskProgress* progress); + + bool Update(float percent = -1.f) override; + +private: + TaskProgress* m_progress = nullptr; +}; + +} // namespace Mayo::IO diff --git a/src/io_assimp/io_assimp_writer.cpp b/src/io_assimp/io_assimp_writer.cpp index 234a3f17..4c18a0c8 100644 --- a/src/io_assimp/io_assimp_writer.cpp +++ b/src/io_assimp/io_assimp_writer.cpp @@ -5,22 +5,32 @@ ****************************************************************************/ #include "io_assimp_writer.h" + +#include "io_assimp_i18n.h" +#include "io_assimp_task_progress.h" + #include "../base/caf_utils.h" #include "../base/filepath_conv.h" #include "../base/io_system.h" #include "../base/mesh_access.h" #include "../base/mesh_utils.h" +#include "../base/messenger.h" +#include "../base/meta_enum.h" #include "../base/string_conv.h" #include "../base/task_progress.h" #include "../base/tkernel_utils.h" +#include #include #include #include #include +#include +#include #include +#include #include #include @@ -111,6 +121,65 @@ ai_MeshPtr createAssimpMesh(const IMeshAccess& mesh) return ai_mesh; } +// Helper function to return `str` converted to lower case characters +// The input string `str` is assumed to be encoded with the classic "C" locale +std::string stringToLowerCase_c(std::string_view str) +{ + auto charToLowerCase_c = [](char c) { + return std::tolower(c, std::locale::classic()); + }; + std::string strLc; + std::transform(str.cbegin(), str.cend(), strLc.begin(), charToLowerCase_c); + return strLc; +} + +// Helper function to return suffix string(utf8) from filepath `fp` +// Note: any starting '.' character is removed from the result string +std::string filepathSuffix(const FilePath& fp) +{ + std::string suffix = stringToLowerCase_c(fp.extension().u8string()); + if (!suffix.empty() && suffix.front() == '.') + suffix.erase(suffix.begin()); + + return suffix; +} + +// Helper function to retrieve the Format corresponding to `suffix` +// Assumes that `suffix` is lowercase and does not start with '.' +Format findFormatFromFileSuffix(std::string_view suffix) +{ + for (auto format : MetaEnum::values()) { + auto formatSuffixes = formatFileSuffixes(format); + auto itSuffix = std::find(formatSuffixes.begin(), formatSuffixes.end(), suffix); + if (itSuffix != formatSuffixes.end()) + return format; + } + + return Format_Unknown; +} + +// Helper function to find the identifier of the Assimp export format corresponding to `format` +std::string findAssimpExportFormatId(const Assimp::Exporter& exporter, Format format) +{ + const size_t formatCount = exporter.GetExportFormatCount(); + for (size_t i = 0; i < formatCount; ++i) { + const aiExportFormatDesc* exportFormat = exporter.GetExportFormatDescription(i); + if (exportFormat == nullptr) + continue; //Skip + + if (exportFormat->id && findFormatFromFileSuffix(exportFormat->id) == format) + return exportFormat->id; + + if (exportFormat->fileExtension) { + const std::string exportFormatSuffix = stringToLowerCase_c(exportFormat->fileExtension); + if (findFormatFromFileSuffix(exportFormatSuffix) == format) + return exportFormat->id; + } + } + + return {}; +} + } // namespace AssimpWriter::~AssimpWriter() @@ -125,7 +194,10 @@ bool AssimpWriter::transfer(Span appItems, TaskProgress* // Find materials std::unordered_set> setVisMaterial; System::traverseUniqueItems(appItems, [&](const DocumentTreeNode& treeNode) { - + auto visMatTool = treeNode.document()->xcaf().visMaterialTool(); + auto visMat = visMatTool->GetShapeMaterial(treeNode.label()); + if (visMat) + setVisMaterial.insert(visMat); }); // Find meshes @@ -221,7 +293,26 @@ bool AssimpWriter::transfer(Span appItems, TaskProgress* bool AssimpWriter::writeFile(const FilePath& fp, TaskProgress* progress) { progress = progress ? progress : &TaskProgress::null(); - return false; + Assimp::Exporter exporter; + exporter.SetProgressHandler(new AssimpTaskProgress(progress)); + + const std::string fileSuffix = filepathSuffix(fp); + const Format format = findFormatFromFileSuffix(fileSuffix); + const std::string aiFormatId = findAssimpExportFormatId(exporter, format); + if (aiFormatId.empty()) { + this->messenger()->emitError( + fmt::format(AssimpI18N::textIdTr("No Assimp export format corresponding to file suffix '{}'"), fileSuffix) + ); + return false; + } + + const aiReturn errExport = exporter.Export(m_scene, aiFormatId, fp.u8string()); + if (errExport != aiReturn_SUCCESS) { + this->messenger()->emitError(exporter.GetErrorString()); + return false; + } + + return true; } std::unique_ptr AssimpWriter::createProperties(PropertyGroup* parentGroup)