diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index bdeae1897..f55363519 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -390,6 +390,8 @@ set(lisbslic3r_sources if (APPLE) list(APPEND lisbslic3r_sources MacUtils.mm + Format/ModelIO.hpp + Format/ModelIO.mm ) endif () @@ -505,6 +507,12 @@ if(NOT WIN32) endif() endif() +if (APPLE) + find_library(FOUNDATION Foundation REQUIRED) + find_library(MODELIO ModelIO REQUIRED) + target_link_libraries(libslic3r ${FOUNDATION} ${MODELIO}) +endif () + if (TARGET OpenVDB::openvdb) target_link_libraries(libslic3r OpenVDB::openvdb) endif() diff --git a/src/libslic3r/Format/ModelIO.hpp b/src/libslic3r/Format/ModelIO.hpp new file mode 100644 index 000000000..a0751f2d9 --- /dev/null +++ b/src/libslic3r/Format/ModelIO.hpp @@ -0,0 +1,19 @@ +#include + +namespace Slic3r { + /** + * Uses ModelIO to convert supported model types to a temporary STL + * that can then be consumed by the existing STL loader + * @param input_file The File to load + * @return Path to the temporary file, or an empty string if conversion failed + */ + std::string make_temp_stl_with_modelio(const std::string &input_file); + + /** + * Convenience function to delete the file. + * No return value since success isn't required + * @param temp_file File path to delete + */ + void delete_temp_file(const std::string &temp_file); +} + diff --git a/src/libslic3r/Format/ModelIO.mm b/src/libslic3r/Format/ModelIO.mm new file mode 100644 index 000000000..7d1d4e726 --- /dev/null +++ b/src/libslic3r/Format/ModelIO.mm @@ -0,0 +1,27 @@ +#include "ModelIO.hpp" +#import + +namespace Slic3r { + + std::string make_temp_stl_with_modelio(const std::string &input_file) + { + NSURL *input_url = [NSURL fileURLWithPath:[NSString stringWithUTF8String:input_file.c_str()]]; + MDLAsset *asset = [[MDLAsset alloc] initWithURL:input_url]; + + NSString *tmp_file_name = [[[NSUUID UUID] UUIDString] stringByAppendingPathExtension:@"stl"]; + NSURL *tmp_file_url = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:tmp_file_name]]; + + if ([asset exportAssetToURL:tmp_file_url]) { + std::string output_file = std::string([[tmp_file_url path] UTF8String]); + return output_file; + } + + return std::string(); + } + void delete_temp_file(const std::string &temp_file) + { + NSString *file_path = [NSString stringWithUTF8String:temp_file.c_str()]; + [[NSFileManager defaultManager] removeItemAtPath:file_path error:NULL]; + } + +} // namespace Slic3r \ No newline at end of file diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index a4d2dbcfc..2e0514ca8 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -40,6 +40,11 @@ // Transtltion #include "I18N.hpp" +// ModelIO support +#ifdef __APPLE__ +#include "Format/ModelIO.hpp" +#endif + #define _L(s) Slic3r::I18N::translate(s) namespace Slic3r { @@ -195,6 +200,18 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c //FIXME options & LoadStrategy::CheckVersion ? //BBS: is_xxx is used for is_bbs_3mf when load 3mf result = load_bbs_3mf(input_file.c_str(), config, config_substitutions, &model, plate_data, project_presets, is_xxx, file_version, proFn, options, project, plate_id); +#ifdef __APPLE__ + else if (boost::algorithm::iends_with(input_file, ".usd") || boost::algorithm::iends_with(input_file, ".usda") || + boost::algorithm::iends_with(input_file, ".usdc") || boost::algorithm::iends_with(input_file, ".usdz") || + boost::algorithm::iends_with(input_file, ".abc") || boost::algorithm::iends_with(input_file, ".ply")) { + std::string temp_stl = make_temp_stl_with_modelio(input_file); + if (temp_stl.empty()) { + throw Slic3r::RuntimeError("Failed to convert asset to STL via ModelIO."); + } + result = load_stl(temp_stl.c_str(), &model); + delete_temp_file(temp_stl); + } +#endif else throw Slic3r::RuntimeError(_L("Unknown file format. Input file must have .stl, .obj, .amf(.xml) extension.")); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index be12705d4..8f70a0a27 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -739,7 +739,11 @@ static const FileWildcards file_wildcards_by_type[FT_SIZE] = { /* FT_AMF */ { "AMF files"sv, { ".amf"sv, ".zip.amf"sv, ".xml"sv } }, /* FT_3MF */ { "3MF files"sv, { ".3mf"sv } }, /* FT_GCODE */ { "G-code files"sv, { ".gcode"sv } }, +#ifdef __APPLE__ + /* FT_MODEL */ { "Supported files"sv, { ".3mf"sv, ".stl"sv, ".stp"sv, ".step"sv, ".svg"sv, ".amf"sv, ".obj"sv , ".usd"sv, ".usda"sv, ".usdc"sv, ".usdz"sv, ".abc"sv, ".ply"sv} }, +#else /* FT_MODEL */ {"Supported files"sv, {".3mf"sv, ".stl"sv, ".stp"sv, ".step"sv, ".svg"sv, ".amf"sv, ".obj"sv }}, +#endif /* FT_PROJECT */ { "Project files"sv, { ".3mf"sv} }, /* FT_GALLERY */ { "Known files"sv, { ".stl"sv, ".obj"sv } }, @@ -3644,7 +3648,11 @@ void GUI_App::import_model(wxWindow *parent, wxArrayString& input_files) const { input_files.Clear(); wxFileDialog dialog(parent ? parent : GetTopWindow(), - _L("Choose one or more files (3mf/step/stl/svg/obj/amf):"), +#ifdef __APPLE__ + _L("Choose one or more files (3mf/step/stl/svg/obj/amf/usd*/abc/ply):"), +#else + _L("Choose one or more files (3mf/step/stl/svg/obj/amf):"), +#endif from_u8(app_config->get_last_dir()), "", file_wildcards(FT_MODEL), wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST);