diff --git a/source/blender/editors/io/io_obj.cc b/source/blender/editors/io/io_obj.cc index 12eefa343d6..36173e357e2 100644 --- a/source/blender/editors/io/io_obj.cc +++ b/source/blender/editors/io/io_obj.cc @@ -403,6 +403,7 @@ static int wm_obj_import_exec(bContext *C, wmOperator *op) import_params.use_split_groups = RNA_boolean_get(op->ptr, "use_split_groups"); import_params.import_vertex_groups = RNA_boolean_get(op->ptr, "import_vertex_groups"); import_params.validate_meshes = RNA_boolean_get(op->ptr, "validate_meshes"); + RNA_string_get(op->ptr, "collection_separator", import_params.collection_separator); import_params.relative_paths = ((U.flag & USER_RELPATHS) != 0); import_params.clear_selection = true; @@ -465,6 +466,7 @@ static void ui_obj_import_settings(uiLayout *layout, PointerRNA *imfptr) uiItemR(col, imfptr, "use_split_groups", UI_ITEM_NONE, nullptr, ICON_NONE); uiItemR(col, imfptr, "import_vertex_groups", UI_ITEM_NONE, nullptr, ICON_NONE); uiItemR(col, imfptr, "validate_meshes", UI_ITEM_NONE, nullptr, ICON_NONE); + uiItemR(col, imfptr, "collection_separator", UI_ITEM_NONE, nullptr, ICON_NONE); } static void wm_obj_import_draw(bContext *C, wmOperator *op) @@ -542,6 +544,12 @@ void WM_OT_obj_import(wmOperatorType *ot) false, "Validate Meshes", "Check imported mesh objects for invalid data (slow)"); + RNA_def_string(ot->srna, + "collection_separator", + nullptr, + 2, + "Path Separator", + "Character used to seperate objects name into hierarchical structure"); /* Only show .obj or .mtl files by default. */ prop = RNA_def_string(ot->srna, "filter_glob", "*.obj;*.mtl", 0, "Extension Filter", ""); diff --git a/source/blender/io/wavefront_obj/IO_wavefront_obj.hh b/source/blender/io/wavefront_obj/IO_wavefront_obj.hh index 1bc7474f633..6416fecee1b 100644 --- a/source/blender/io/wavefront_obj/IO_wavefront_obj.hh +++ b/source/blender/io/wavefront_obj/IO_wavefront_obj.hh @@ -75,6 +75,7 @@ struct OBJImportParams { bool validate_meshes; bool relative_paths; bool clear_selection; + char collection_separator[1]; }; /** diff --git a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc index 96ababe7ac6..4ce89ace1bf 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc @@ -25,6 +25,19 @@ namespace blender::io::obj { using std::string; +char COLLECTION_SEPARATOR; + +static void deconstruct_name(Geometry *const geometry) +{ + if (!COLLECTION_SEPARATOR) { + return; + } + split(geometry->geometry_name_, COLLECTION_SEPARATOR, geometry->collection_path_); + + geometry->geometry_name_ = geometry->collection_path_.back(); + geometry->collection_path_.pop_back(); +} + /** * Based on the properties of the given Geometry instance, create a new Geometry instance * or return the previous one. @@ -46,23 +59,29 @@ static Geometry *create_geometry(Geometry *const prev_geometry, /* After the creation of a Geometry instance, at least one element has been found in the OBJ * file that indicates that it is a mesh (faces or edges). */ if (!prev_geometry->face_elements_.is_empty() || !prev_geometry->edges_.is_empty()) { - return new_geometry(); + Geometry *ng = new_geometry(); + deconstruct_name(ng); + return ng; } if (new_type == GEOM_MESH) { /* A Geometry created initially with a default name now found its name. */ prev_geometry->geometry_name_ = name; + deconstruct_name(prev_geometry); return prev_geometry; } if (new_type == GEOM_CURVE) { /* The object originally created is not a mesh now that curve data * follows the vertex coordinates list. */ prev_geometry->geom_type_ = GEOM_CURVE; + deconstruct_name(prev_geometry); return prev_geometry; } } if (prev_geometry && prev_geometry->geom_type_ == GEOM_CURVE) { - return new_geometry(); + Geometry *ng = new_geometry(); + deconstruct_name(ng); + return ng; } return new_geometry(); @@ -497,6 +516,7 @@ void OBJParser::parse(Vector> &r_all_geometries, /* Use the filename as the default name given to the initial object. */ char ob_name[FILE_MAXFILE]; STRNCPY(ob_name, BLI_path_basename(import_params_.filepath)); + COLLECTION_SEPARATOR = import_params_.collection_separator[0]; BLI_path_extension_strip(ob_name); Geometry *curr_geom = create_geometry(nullptr, GEOM_MESH, ob_name, r_all_geometries); diff --git a/source/blender/io/wavefront_obj/importer/obj_import_objects.hh b/source/blender/io/wavefront_obj/importer/obj_import_objects.hh index d6cd6393557..022834d7d65 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_objects.hh +++ b/source/blender/io/wavefront_obj/importer/obj_import_objects.hh @@ -88,6 +88,7 @@ enum eGeometryType { struct Geometry { eGeometryType geom_type_ = GEOM_MESH; std::string geometry_name_; + std::vector collection_path_; Map group_indices_; Vector group_order_; Map material_indices_; diff --git a/source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc index 0ce80e8e0fe..20662f5eecc 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc @@ -12,6 +12,7 @@ * the minimum spec, use an external library. */ #include "fast_float.h" #include +#include namespace blender::io::obj { @@ -133,4 +134,18 @@ const char *parse_int(const char *p, const char *end, int fallback, int &dst, bo return res.ptr; } +void split(const std::string &s, const char delim, std::vector &tokens) +{ + tokens.clear(); + + std::stringstream ss(s); + std::string item; + + while (std::getline(ss, item, delim)) { + if (!item.empty()) { + tokens.push_back(item); + } + } +} + } // namespace blender::io::obj diff --git a/source/blender/io/wavefront_obj/importer/obj_import_string_utils.hh b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.hh index efb5e49c752..f5b12dd2f8d 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_string_utils.hh +++ b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.hh @@ -89,4 +89,11 @@ const char *parse_floats(const char *p, int count, bool require_trailing_space = false); +/* + * Split a string. Borrowed from the Alembic string util code. + */ +void split(const std::string &s, + char delim, + std::vector &tokens); + } // namespace blender::io::obj diff --git a/source/blender/io/wavefront_obj/importer/obj_importer.cc b/source/blender/io/wavefront_obj/importer/obj_importer.cc index 6ac329bc8e7..7a052320413 100644 --- a/source/blender/io/wavefront_obj/importer/obj_importer.cc +++ b/source/blender/io/wavefront_obj/importer/obj_importer.cc @@ -68,7 +68,30 @@ static void geometry_to_blender_objects(Main *bmain, obj = curve_ob_from_geometry.create_curve(bmain, import_params); } if (obj != nullptr) { - BKE_collection_object_add(bmain, lc->collection, obj); + Collection *target_collection = lc->collection; + std::vector::iterator iter; + + for (iter = geometry->collection_path_.begin(); iter != geometry->collection_path_.end(); ++iter) { + std::string sub_collection_name = *iter; + bool found_named = false; + + LISTBASE_FOREACH (CollectionChild *, collection_child, &target_collection->children) { + std::string loop_collection_name(collection_child->collection->id.name); + if ((GS(collection_child->collection->id.name) == ID_GR) + && loop_collection_name.substr(2) == sub_collection_name) { + target_collection = collection_child->collection; + found_named = true; + break; + } + } + + if (!found_named) { + Collection *sub_collection = BKE_collection_add( + bmain, target_collection, sub_collection_name.c_str()); + target_collection = sub_collection; + } + } + BKE_collection_object_add(bmain, target_collection, obj); objects.append(obj); } }