diff options
author | Aras Pranckevicius <aras@nesnausk.org> | 2022-05-10 18:59:46 +0300 |
---|---|---|
committer | Aras Pranckevicius <aras@nesnausk.org> | 2022-05-10 19:12:02 +0300 |
commit | 6f7959f55fd2b3d291f63f0601d179bf581c5b28 (patch) | |
tree | d62268f86f09840cfa29a7dd3f8231fee32032e7 /source/blender/io | |
parent | 9c2613d1b6d034b4d80367df06ec4cd08d9bbbaa (diff) | |
parent | 3bc037a7eb8d214aac9d755f8b2dd0e04cdf3a85 (diff) |
Merge branch 'blender-v3.2-release'
Diffstat (limited to 'source/blender/io')
12 files changed, 206 insertions, 22 deletions
diff --git a/source/blender/io/common/CMakeLists.txt b/source/blender/io/common/CMakeLists.txt index 02bd5b2b81f..95ffbe19ada 100644 --- a/source/blender/io/common/CMakeLists.txt +++ b/source/blender/io/common/CMakeLists.txt @@ -7,6 +7,8 @@ set(INC ../../blenlib ../../depsgraph ../../makesdna + ../../../../intern/guardedalloc + ) set(INC_SYS @@ -17,9 +19,12 @@ set(SRC intern/dupli_parent_finder.cc intern/dupli_persistent_id.cc intern/object_identifier.cc + intern/path_util.cc IO_abstract_hierarchy_iterator.h IO_dupli_persistent_id.hh + IO_path_util.hh + IO_path_util_types.h IO_types.h intern/dupli_parent_finder.hh ) diff --git a/source/blender/io/common/IO_path_util.hh b/source/blender/io/common/IO_path_util.hh new file mode 100644 index 00000000000..ac2f935523e --- /dev/null +++ b/source/blender/io/common/IO_path_util.hh @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#pragma once + +#include "BLI_string_ref.hh" +#include "BLI_set.hh" + +#include "IO_path_util_types.h" + +namespace blender::io { + +/** + * Return a filepath relative to a destination directory, for use with + * exporters. + * + * When PATH_REFERENCE_COPY mode is used, the file path pair (source + * path, destination path) is added to the `copy_set`. + * + * Equivalent of bpy_extras.io_utils.path_reference. + */ +std::string path_reference(StringRefNull filepath, + StringRefNull base_src, + StringRefNull base_dst, + ePathReferenceMode mode, + Set<std::pair<std::string, std::string>> *copy_set = nullptr); + +/** Execute copying files of path_reference. */ +void path_reference_copy(const Set<std::pair<std::string, std::string>> ©_set); + +} // namespace blender::io diff --git a/source/blender/io/common/IO_path_util_types.h b/source/blender/io/common/IO_path_util_types.h new file mode 100644 index 00000000000..0233f601a81 --- /dev/null +++ b/source/blender/io/common/IO_path_util_types.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#pragma once + +/** Method used to reference paths. Equivalent of bpy_extras.io_utils.path_reference_mode. */ +typedef enum { + /** Use Relative paths with subdirectories only. */ + PATH_REFERENCE_AUTO = 0, + /** Always write absolute paths. */ + PATH_REFERENCE_ABSOLUTE = 1, + /** Write relative paths where possible. */ + PATH_REFERENCE_RELATIVE = 2, + /** Match Absolute/Relative setting with input path. */ + PATH_REFERENCE_MATCH = 3, + /** Filename only. */ + PATH_REFERENCE_STRIP = 4, + /** Copy the file to the destination path. */ + PATH_REFERENCE_COPY = 5, +} ePathReferenceMode; diff --git a/source/blender/io/common/intern/path_util.cc b/source/blender/io/common/intern/path_util.cc new file mode 100644 index 00000000000..2b9a1d67b44 --- /dev/null +++ b/source/blender/io/common/intern/path_util.cc @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "IO_path_util.hh" + +#include "BLI_fileops.h" +#include "BLI_path_util.h" + +namespace blender::io { + +std::string path_reference(StringRefNull filepath, + StringRefNull base_src, + StringRefNull base_dst, + ePathReferenceMode mode, + Set<std::pair<std::string, std::string>> *copy_set) +{ + const bool is_relative = BLI_path_is_rel(filepath.c_str()); + char filepath_abs[PATH_MAX]; + BLI_strncpy(filepath_abs, filepath.c_str(), PATH_MAX); + BLI_path_abs(filepath_abs, base_src.c_str()); + BLI_path_normalize(nullptr, filepath_abs); + + /* Figure out final mode to be used. */ + if (mode == PATH_REFERENCE_MATCH) { + mode = is_relative ? PATH_REFERENCE_RELATIVE : PATH_REFERENCE_ABSOLUTE; + } + else if (mode == PATH_REFERENCE_AUTO) { + mode = BLI_path_contains(base_dst.c_str(), filepath_abs) ? PATH_REFERENCE_RELATIVE : + PATH_REFERENCE_ABSOLUTE; + } + else if (mode == PATH_REFERENCE_COPY) { + char filepath_cpy[PATH_MAX]; + BLI_path_join(filepath_cpy, PATH_MAX, base_dst.c_str(), BLI_path_basename(filepath_abs), nullptr); + copy_set->add(std::make_pair(filepath_abs, filepath_cpy)); + BLI_strncpy(filepath_abs, filepath_cpy, PATH_MAX); + mode = PATH_REFERENCE_RELATIVE; + } + + /* Now we know the final path mode. */ + if (mode == PATH_REFERENCE_ABSOLUTE) { + return filepath_abs; + } + else if (mode == PATH_REFERENCE_RELATIVE) { + char rel_path[PATH_MAX]; + BLI_strncpy(rel_path, filepath_abs, PATH_MAX); + BLI_path_rel(rel_path, base_dst.c_str()); + /* Can't always find relative path (e.g. between different drives). */ + if (!BLI_path_is_rel(rel_path)) { + return filepath_abs; + } + return rel_path + 2; /* Skip blender's internal "//" prefix. */ + } + else if (mode == PATH_REFERENCE_STRIP) { + return BLI_path_basename(filepath_abs); + } + BLI_assert_msg(false, "Invalid path reference mode"); + return filepath_abs; +} + +void path_reference_copy(const Set<std::pair<std::string, std::string>> ©_set) +{ + for (const auto © : copy_set) { + const char *src = copy.first.c_str(); + const char *dst = copy.second.c_str(); + if (!BLI_exists(src)) { + fprintf(stderr, "Missing source file '%s', not copying\n", src); + continue; + } + if (0 == BLI_path_cmp_normalized(src, dst)) { + continue; /* Source and dest are the same. */ + } + if (!BLI_make_existing_file(dst)) { + fprintf(stderr, "Can't make directory for '%s', not copying\n", dst); + continue; + } + if (!BLI_copy(src, dst)) { + fprintf(stderr, "Can't copy '%s' to '%s'\n", src, dst); + continue; + } + } +} + +} // namespace blender::io diff --git a/source/blender/io/wavefront_obj/CMakeLists.txt b/source/blender/io/wavefront_obj/CMakeLists.txt index a7c4253f4d3..f7958ef4ec6 100644 --- a/source/blender/io/wavefront_obj/CMakeLists.txt +++ b/source/blender/io/wavefront_obj/CMakeLists.txt @@ -4,6 +4,7 @@ set(INC . ./exporter ./importer + ../common ../../blenkernel ../../blenlib ../../bmesh @@ -57,6 +58,7 @@ set(SRC set(LIB bf_blenkernel + bf_io_common ) if(WITH_TBB) diff --git a/source/blender/io/wavefront_obj/IO_wavefront_obj.h b/source/blender/io/wavefront_obj/IO_wavefront_obj.h index 8b71ec750c0..f7bf678310f 100644 --- a/source/blender/io/wavefront_obj/IO_wavefront_obj.h +++ b/source/blender/io/wavefront_obj/IO_wavefront_obj.h @@ -9,6 +9,7 @@ #include "BKE_context.h" #include "BLI_path_util.h" #include "DEG_depsgraph.h" +#include "IO_path_util_types.h" #ifdef __cplusplus extern "C" { @@ -37,6 +38,8 @@ static const int TOTAL_AXES = 3; struct OBJExportParams { /** Full path to the destination .OBJ file. */ char filepath[FILE_MAX]; + /** Pretend that destination file folder is this, if non-empty. Used only for tests. */ + char file_base_for_tests[FILE_MAX]; /** Full path to current blender file (used for comments in output). */ const char *blen_filepath; @@ -62,6 +65,7 @@ struct OBJExportParams { bool export_materials; bool export_triangulated_mesh; bool export_curves_as_nurbs; + ePathReferenceMode path_mode; /* Grouping options. */ bool export_object_groups; diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc index 194583e71fe..b027df73b1e 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc @@ -13,6 +13,8 @@ #include "BLI_path_util.h" #include "BLI_task.hh" +#include "IO_path_util.hh" + #include "obj_export_mesh.hh" #include "obj_export_mtl.hh" #include "obj_export_nurbs.hh" @@ -530,7 +532,11 @@ void MTLWriter::write_bsdf_properties(const MTLMaterial &mtl_material) void MTLWriter::write_texture_map( const MTLMaterial &mtl_material, - const Map<const eMTLSyntaxElement, tex_map_XX>::Item &texture_map) + const Map<const eMTLSyntaxElement, tex_map_XX>::Item &texture_map, + const char *blen_filedir, + const char *dest_dir, + ePathReferenceMode path_mode, + Set<std::pair<std::string, std::string>> ©_set) { std::string options; /* Option strings should have their own leading spaces. */ @@ -546,7 +552,11 @@ void MTLWriter::write_texture_map( #define SYNTAX_DISPATCH(eMTLSyntaxElement) \ if (texture_map.key == eMTLSyntaxElement) { \ - fmt_handler_.write<eMTLSyntaxElement>(options, texture_map.value.image_path); \ + std::string path = path_reference( \ + texture_map.value.image_path.c_str(), blen_filedir, dest_dir, path_mode, ©_set); \ + /* Always emit forward slashes for cross-platform compatibility. */ \ + std::replace(path.begin(), path.end(), '\\', '/'); \ + fmt_handler_.write<eMTLSyntaxElement>(options, path.c_str()); \ return; \ } @@ -561,25 +571,35 @@ void MTLWriter::write_texture_map( BLI_assert(!"This map type was not written to the file."); } -void MTLWriter::write_materials() +void MTLWriter::write_materials(const char *blen_filepath, + ePathReferenceMode path_mode, + const char *dest_dir) { if (mtlmaterials_.size() == 0) { return; } + + char blen_filedir[PATH_MAX]; + BLI_split_dir_part(blen_filepath, blen_filedir, PATH_MAX); + BLI_path_slash_native(blen_filedir); + BLI_path_normalize(nullptr, blen_filedir); + std::sort(mtlmaterials_.begin(), mtlmaterials_.end(), [](const MTLMaterial &a, const MTLMaterial &b) { return a.name < b.name; }); + Set<std::pair<std::string, std::string>> copy_set; for (const MTLMaterial &mtlmat : mtlmaterials_) { fmt_handler_.write<eMTLSyntaxElement::string>("\n"); fmt_handler_.write<eMTLSyntaxElement::newmtl>(mtlmat.name); write_bsdf_properties(mtlmat); - for (const Map<const eMTLSyntaxElement, tex_map_XX>::Item &texture_map : - mtlmat.texture_maps.items()) { - if (!texture_map.value.image_path.empty()) { - write_texture_map(mtlmat, texture_map); + for (const auto &tex : mtlmat.texture_maps.items()) { + if (tex.value.image_path.empty()) { + continue; } + write_texture_map(mtlmat, tex, blen_filedir, dest_dir, path_mode, copy_set); } } + path_reference_copy(copy_set); } Vector<int> MTLWriter::add_materials(const OBJMesh &mesh_to_export) diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh index 96f7d434338..77da7b44276 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh +++ b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh @@ -9,6 +9,7 @@ #include "DNA_meshdata_types.h" #include "BLI_map.hh" +#include "BLI_set.hh" #include "BLI_vector.hh" #include "IO_wavefront_obj.h" @@ -181,7 +182,9 @@ class MTLWriter : NonMovable, NonCopyable { * For consistency of output from run to run (useful for testing), * the materials are sorted by name before writing. */ - void write_materials(); + void write_materials(const char *blen_filepath, + ePathReferenceMode path_mode, + const char *dest_dir); StringRefNull mtl_file_path() const; /** * Add the materials of the given object to #MTLWriter, de-duplicating @@ -203,6 +206,10 @@ class MTLWriter : NonMovable, NonCopyable { * Write a texture map in the form "map_XX -s 1. 1. 1. -o 0. 0. 0. [-bm 1.] path/to/image". */ void write_texture_map(const MTLMaterial &mtl_material, - const Map<const eMTLSyntaxElement, tex_map_XX>::Item &texture_map); + const Map<const eMTLSyntaxElement, tex_map_XX>::Item &texture_map, + const char *blen_filedir, + const char *dest_dir, + ePathReferenceMode mode, + Set<std::pair<std::string, std::string>> ©_set); }; } // namespace blender::io::obj diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc b/source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc index c48d5a5f7f0..4ed148ec64e 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc @@ -113,8 +113,7 @@ static const bNode *get_node_of_type(Span<const nodes::OutputSocketRef *> socket /** * From a texture image shader node, get the image's filepath. - * Returned filepath is stripped of initial "//". If packed image is found, - * only the file "name" is returned. + * If packed image is found, only the file "name" is returned. */ static const char *get_image_filepath(const bNode *tex_node) { @@ -134,9 +133,6 @@ static const char *get_image_filepath(const bNode *tex_node) "directory as the .MTL file.\n", path); } - if (path[0] == '/' && path[1] == '/') { - path += 2; - } return path; } diff --git a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc index 78b709c884a..b6e636b389d 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc @@ -284,7 +284,16 @@ void export_frame(Depsgraph *depsgraph, const OBJExportParams &export_params, co std::move(exportable_as_mesh), *frame_writer, mtl_writer.get(), export_params); if (mtl_writer) { mtl_writer->write_header(export_params.blen_filepath); - mtl_writer->write_materials(); + char dest_dir[PATH_MAX]; + if (export_params.file_base_for_tests[0] == '\0') { + BLI_split_dir_part(export_params.filepath, dest_dir, PATH_MAX); + } + else { + BLI_strncpy(dest_dir, export_params.file_base_for_tests, PATH_MAX); + } + BLI_path_slash_native(dest_dir); + BLI_path_normalize(nullptr, dest_dir); + mtl_writer->write_materials(export_params.blen_filepath, export_params.path_mode, dest_dir); } write_nurbs_curve_objects(std::move(exportable_as_nurbs), *frame_writer); } diff --git a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc index f74bfc155dd..8c49af90a82 100644 --- a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc +++ b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc @@ -11,12 +11,15 @@ #include "BKE_appdir.h" #include "BKE_blender_version.h" +#include "BKE_main.h" #include "BLI_fileops.h" #include "BLI_index_range.hh" #include "BLI_string_utf8.h" #include "BLI_vector.hh" +#include "BLO_readfile.h" + #include "DEG_depsgraph.h" #include "obj_export_file_writer.hh" @@ -259,11 +262,12 @@ class obj_exporter_regression_test : public obj_exporter_test { std::string tempdir = std::string(BKE_tempdir_base()); std::string out_file_path = tempdir + BLI_path_basename(golden_obj.c_str()); strncpy(params.filepath, out_file_path.c_str(), FILE_MAX - 1); - params.blen_filepath = blendfile.c_str(); + params.blen_filepath = bfile->main->filepath; + std::string golden_file_path = blender::tests::flags_test_asset_dir() + "/" + golden_obj; + BLI_split_dir_part(golden_file_path.c_str(), params.file_base_for_tests, PATH_MAX); export_frame(depsgraph, params, out_file_path.c_str()); std::string output_str = read_temp_file_in_string(out_file_path); - std::string golden_file_path = blender::tests::flags_test_asset_dir() + "/" + golden_obj; std::string golden_str = read_temp_file_in_string(golden_file_path); bool are_equal = strings_equal_after_first_lines(output_str, golden_str); if (save_failing_test_output && !are_equal) { @@ -432,19 +436,26 @@ TEST_F(obj_exporter_regression_test, cubes_positioned) _export.params); } -/* Note: texture paths in the resulting mtl file currently are always - * as they are stored in the source .blend file; not relative to where - * the export is done. When that is properly fixed, the expected .mtl - * file should be updated. */ -TEST_F(obj_exporter_regression_test, cubes_with_textures) +TEST_F(obj_exporter_regression_test, cubes_with_textures_strip) { OBJExportParamsDefault _export; + _export.params.path_mode = PATH_REFERENCE_STRIP; compare_obj_export_to_golden("io_tests/blend_geometry/cubes_with_textures.blend", "io_tests/obj/cubes_with_textures.obj", "io_tests/obj/cubes_with_textures.mtl", _export.params); } +TEST_F(obj_exporter_regression_test, cubes_with_textures_relative) +{ + OBJExportParamsDefault _export; + _export.params.path_mode = PATH_REFERENCE_RELATIVE; + compare_obj_export_to_golden("io_tests/blend_geometry/cubes_with_textures.blend", + "io_tests/obj/cubes_with_textures_rel.obj", + "io_tests/obj/cubes_with_textures_rel.mtl", + _export.params); +} + TEST_F(obj_exporter_regression_test, suzanne_all_data) { OBJExportParamsDefault _export; diff --git a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh index 6a821e0b1bf..ef27a65fb4b 100644 --- a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh +++ b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh @@ -11,6 +11,7 @@ struct OBJExportParamsDefault { OBJExportParamsDefault() { params.filepath[0] = '\0'; + params.file_base_for_tests[0] = '\0'; params.blen_filepath = ""; params.export_animation = false; params.start_frame = 0; @@ -26,6 +27,7 @@ struct OBJExportParamsDefault { params.export_uv = true; params.export_normals = true; params.export_materials = true; + params.path_mode = PATH_REFERENCE_AUTO; params.export_triangulated_mesh = false; params.export_curves_as_nurbs = false; |