From 05e8eeda3c95c960b2481b85cd836194d78c95ad Mon Sep 17 00:00:00 2001 From: Michael Kowalski Date: Wed, 5 Oct 2022 23:06:21 -0400 Subject: USD import unbound materials. This addresses issue T97195. Added a new Unbound Materials USD import option, to import materials not assigned to any geometry. --- source/blender/editors/io/io_usd.c | 9 ++- source/blender/io/usd/intern/usd_capi_import.cc | 8 +++ .../blender/io/usd/intern/usd_reader_material.cc | 48 +++++++++++++++ source/blender/io/usd/intern/usd_reader_material.h | 29 +++++++++ source/blender/io/usd/intern/usd_reader_mesh.cc | 40 +----------- source/blender/io/usd/intern/usd_reader_stage.cc | 72 ++++++++++++++++++++++ source/blender/io/usd/intern/usd_reader_stage.h | 4 ++ source/blender/io/usd/usd.h | 1 + 8 files changed, 172 insertions(+), 39 deletions(-) (limited to 'source/blender') diff --git a/source/blender/editors/io/io_usd.c b/source/blender/editors/io/io_usd.c index eb80cabcd7f..5fe8ff7a16c 100644 --- a/source/blender/editors/io/io_usd.c +++ b/source/blender/editors/io/io_usd.c @@ -381,6 +381,8 @@ static int wm_usd_import_exec(bContext *C, wmOperator *op) const bool import_proxy = RNA_boolean_get(op->ptr, "import_proxy"); const bool import_render = RNA_boolean_get(op->ptr, "import_render"); + const bool import_unbound_materials = RNA_boolean_get(op->ptr, "import_unbound_materials"); + const bool import_usd_preview = RNA_boolean_get(op->ptr, "import_usd_preview"); const bool set_material_blend = RNA_boolean_get(op->ptr, "set_material_blend"); @@ -427,7 +429,8 @@ static int wm_usd_import_exec(bContext *C, wmOperator *op) .import_usd_preview = import_usd_preview, .set_material_blend = set_material_blend, .light_intensity_scale = light_intensity_scale, - .mtl_name_collision_mode = mtl_name_collision_mode}; + .mtl_name_collision_mode = mtl_name_collision_mode, + .import_unbound_materials = import_unbound_materials}; STRNCPY(params.prim_path_mask, prim_path_mask); @@ -471,6 +474,7 @@ static void wm_usd_import_draw(bContext *UNUSED(C), wmOperator *op) uiItemR(col, ptr, "import_guide", 0, NULL, ICON_NONE); uiItemR(col, ptr, "import_proxy", 0, NULL, ICON_NONE); uiItemR(col, ptr, "import_render", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "import_unbound_materials", 0, NULL, ICON_NONE); col = uiLayoutColumnWithHeading(box, true, IFACE_("Options")); uiItemR(col, ptr, "set_frame_range", 0, NULL, ICON_NONE); @@ -579,6 +583,9 @@ void WM_OT_usd_import(struct wmOperatorType *ot) RNA_def_boolean(ot->srna, "import_render", true, "Render", "Import final render geometry"); + RNA_def_boolean(ot->srna, "import_unbound_materials", false, + "Unbound Materials", "Include materials not bound to any geometry"); + RNA_def_boolean(ot->srna, "import_usd_preview", false, diff --git a/source/blender/io/usd/intern/usd_capi_import.cc b/source/blender/io/usd/intern/usd_capi_import.cc index 5808c6bc77a..29f3792792b 100644 --- a/source/blender/io/usd/intern/usd_capi_import.cc +++ b/source/blender/io/usd/intern/usd_capi_import.cc @@ -225,8 +225,16 @@ static void import_startjob(void *customdata, short *stop, short *do_update, flo data->archive = archive; + if (data->params.import_unbound_materials) { + archive->import_all_materials(data->bmain); + } + archive->collect_readers(data->bmain); + if (data->params.import_unbound_materials) { + archive->fake_users_for_unbound_materials(); + } + *data->do_update = true; *data->progress = 0.2f; diff --git a/source/blender/io/usd/intern/usd_reader_material.cc b/source/blender/io/usd/intern/usd_reader_material.cc index d1af4553083..6416f6861d4 100644 --- a/source/blender/io/usd/intern/usd_reader_material.cc +++ b/source/blender/io/usd/intern/usd_reader_material.cc @@ -757,4 +757,52 @@ void USDMaterialReader::convert_usd_primvar_reader_float2( link_nodes(ntree, uv_map, "UV", dest_node, dest_socket_name); } + +void build_material_map(const Main *bmain, std::map *r_mat_map) +{ + if (r_mat_map == nullptr) { + return; + } + + Material *material = static_cast(bmain->materials.first); + + for (; material; material = static_cast(material->id.next)) { + (*r_mat_map)[pxr::TfMakeValidIdentifier(material->id.name + 2)] = material; + } +} + +Material *find_existing_material( + const pxr::SdfPath &usd_mat_path, + const USDImportParams ¶ms, + const std::map &mat_map, + const std::map &usd_path_to_mat_name) +{ + if (params.mtl_name_collision_mode == USD_MTL_NAME_COLLISION_MAKE_UNIQUE) { + /* Check if we've already created the Blender material with a modified name. */ + std::map::const_iterator path_to_name_iter = + usd_path_to_mat_name.find(usd_mat_path.GetAsString()); + + if (path_to_name_iter != usd_path_to_mat_name.end()) { + std::string mat_name = path_to_name_iter->second; + std::map::const_iterator mat_iter = mat_map.find(mat_name); + if (mat_iter != mat_map.end()) { + return mat_iter->second; + } + /* We can't find the Blender material which was previously created for this USD + * material, which should never happen. */ + BLI_assert_unreachable(); + } + } + else { + std::string mat_name = usd_mat_path.GetName(); + std::map::const_iterator mat_iter = mat_map.find(mat_name); + + if (mat_iter != mat_map.end()) { + return mat_iter->second; + } + } + + return nullptr; +} + } // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_material.h b/source/blender/io/usd/intern/usd_reader_material.h index 24d80e99c38..abd810b9936 100644 --- a/source/blender/io/usd/intern/usd_reader_material.h +++ b/source/blender/io/usd/intern/usd_reader_material.h @@ -6,6 +6,8 @@ #include +#include + struct Main; struct Material; struct bNode; @@ -69,6 +71,7 @@ class USDMaterialReader { Main *bmain_; public: + USDMaterialReader(const USDImportParams ¶ms, Main *bmain); Material *add_material(const pxr::UsdShadeMaterial &usd_material) const; @@ -129,4 +132,30 @@ class USDMaterialReader { NodePlacementContext *r_ctx) const; }; +/* Utility functions. */ + +/** + * Returns a map containing all the Blender materials which allows a fast + * lookup of the material object by name. Note that the material name key + * might be modified to be a valid USD identifier, to match materials + * imported from USD. + */ +void build_material_map(const Main *bmain, std::map *r_mat_map); + +/** + * Returns an existing Blender material that corresponds to the USD material with the given path. + * Returns null if no such material exists. + * + * \param mat_map Map a Blender material name to the material object. + * \param usd_path_to_mat_name Map a USD material path to the imported Blender material name. + * + * The usd_path_to_mat_name is needed to determine the name of the Blender + * material imported from a USD path in the case when a unique name was generated + * for the material due to a name collision. + */ +Material *find_existing_material(const pxr::SdfPath &usd_mat_path, + const USDImportParams ¶ms, + const std::map &mat_map, + const std::map &usd_path_to_mat_name); + } // namespace blender::io::usd diff --git a/source/blender/io/usd/intern/usd_reader_mesh.cc b/source/blender/io/usd/intern/usd_reader_mesh.cc index 77c79852141..3dbdd5d486b 100644 --- a/source/blender/io/usd/intern/usd_reader_mesh.cc +++ b/source/blender/io/usd/intern/usd_reader_mesh.cc @@ -83,42 +83,6 @@ static pxr::UsdShadeMaterial compute_bound_material(const pxr::UsdPrim &prim) return mtl; } -/* Returns an existing Blender material that corresponds to the USD material with the given path. - * Returns null if no such material exists. */ -static Material *find_existing_material( - const pxr::SdfPath &usd_mat_path, - const USDImportParams ¶ms, - const std::map &mat_map, - const std::map &usd_path_to_mat_name) -{ - if (params.mtl_name_collision_mode == USD_MTL_NAME_COLLISION_MAKE_UNIQUE) { - /* Check if we've already created the Blender material with a modified name. */ - std::map::const_iterator path_to_name_iter = - usd_path_to_mat_name.find(usd_mat_path.GetAsString()); - - if (path_to_name_iter != usd_path_to_mat_name.end()) { - std::string mat_name = path_to_name_iter->second; - std::map::const_iterator mat_iter = mat_map.find(mat_name); - if (mat_iter != mat_map.end()) { - return mat_iter->second; - } - /* We can't find the Blender material which was previously created for this USD - * material, which should never happen. */ - BLI_assert_unreachable(); - } - } - else { - std::string mat_name = usd_mat_path.GetName(); - std::map::const_iterator mat_iter = mat_map.find(mat_name); - - if (mat_iter != mat_map.end()) { - return mat_iter->second; - } - } - - return nullptr; -} - static void assign_materials(Main *bmain, Object *ob, const std::map &mat_index_map, @@ -141,7 +105,7 @@ static void assign_materials(Main *bmain, it != mat_index_map.end(); ++it) { - Material *assigned_mat = find_existing_material( + Material *assigned_mat = blender::io::usd::find_existing_material( it->first, params, mat_name_to_mat, usd_path_to_mat_name); if (!assigned_mat) { /* Blender material doesn't exist, so create it now. */ @@ -810,7 +774,7 @@ void USDMeshReader::readFaceSetsSample(Main *bmain, Mesh *mesh, const double mot material_indices.finish(); /* Build material name map if it's not built yet. */ if (this->settings_->mat_name_to_mat.empty()) { - utils::build_mat_map(bmain, &this->settings_->mat_name_to_mat); + build_material_map(bmain, &this->settings_->mat_name_to_mat); } utils::assign_materials(bmain, object_, diff --git a/source/blender/io/usd/intern/usd_reader_stage.cc b/source/blender/io/usd/intern/usd_reader_stage.cc index df75be849e2..89dd7098f6c 100644 --- a/source/blender/io/usd/intern/usd_reader_stage.cc +++ b/source/blender/io/usd/intern/usd_reader_stage.cc @@ -5,6 +5,7 @@ #include "usd_reader_camera.h" #include "usd_reader_curve.h" #include "usd_reader_light.h" +#include "usd_reader_material.h" #include "usd_reader_mesh.h" #include "usd_reader_nurbs.h" #include "usd_reader_prim.h" @@ -12,12 +13,14 @@ #include "usd_reader_xform.h" #include +#include #include #include #include #include #include #include +#include #if PXR_VERSION >= 2111 # include @@ -31,6 +34,10 @@ #include "BLI_sort.hh" #include "BLI_string.h" +#include "BKE_lib_id.h" + +#include "DNA_material_types.h" + namespace blender::io::usd { USDStageReader::USDStageReader(pxr::UsdStageRefPtr stage, @@ -294,6 +301,71 @@ void USDStageReader::collect_readers(Main *bmain) collect_readers(bmain, root); } +/* Iterate through stage and import each material prim. */ +void USDStageReader::import_all_materials(Main *bmain) +{ + if (!valid()) { + return; + } + + /* Build the material name map if it's not built yet. */ + if (settings_.mat_name_to_mat.empty()) { + build_material_map(bmain, &settings_.mat_name_to_mat); + } + + USDMaterialReader mtl_reader(params_, bmain); + + PXR_NS::UsdPrimRange range = stage_->TraverseAll(); + for (const auto &prim : range) { + if (prim.IsA()) { + pxr::UsdShadeMaterial usd_mtl(prim); + if (!usd_mtl) { + continue; + } + + Material *blend_mtl = blender::io::usd::find_existing_material( + prim.GetPath(), params_, settings_.mat_name_to_mat, settings_.usd_path_to_mat_name); + + if (blend_mtl) { + /* The material already exists. */ + continue; + } + + /* Add the material now. */ + if (blend_mtl = mtl_reader.add_material(usd_mtl)) { + + if (params_.mtl_name_collision_mode == USD_MTL_NAME_COLLISION_MAKE_UNIQUE) { + /* Record the name of the Blender material we created for the USD material + * with the given path, so we don't import the material again if the + * material is shared. */ + const std::string mtl_name = pxr::TfMakeValidIdentifier(blend_mtl->id.name + 2); + settings_.mat_name_to_mat[mtl_name] = blend_mtl; + + settings_.usd_path_to_mat_name[prim.GetPath().GetAsString()] = mtl_name; + } + } + } + } +} + +/* Add fake users for any imported materials with + * no users. This is typically required for unbound + * materials. */ +void USDStageReader::fake_users_for_unbound_materials() +{ + std::map::const_iterator mat_name_it = settings_.usd_path_to_mat_name.begin(); + for (; mat_name_it != settings_.usd_path_to_mat_name.end(); ++mat_name_it) { + std::map::iterator mat_it = settings_.mat_name_to_mat.find( + mat_name_it->second); + if (mat_it != settings_.mat_name_to_mat.end()) { + Material *mat = mat_it->second; + if (mat->id.us == 0) { + id_fake_user_set(&mat->id); + } + } + } +} + void USDStageReader::clear_readers() { for (USDPrimReader *reader : readers_) { diff --git a/source/blender/io/usd/intern/usd_reader_stage.h b/source/blender/io/usd/intern/usd_reader_stage.h index 5f4a343f874..8ad84ddb372 100644 --- a/source/blender/io/usd/intern/usd_reader_stage.h +++ b/source/blender/io/usd/intern/usd_reader_stage.h @@ -40,6 +40,10 @@ class USDStageReader { void collect_readers(struct Main *bmain); + void import_all_materials(struct Main *bmain); + + void fake_users_for_unbound_materials(); + bool valid() const; pxr::UsdStageRefPtr stage() diff --git a/source/blender/io/usd/usd.h b/source/blender/io/usd/usd.h index 3494d8ffdc3..19b6770b101 100644 --- a/source/blender/io/usd/usd.h +++ b/source/blender/io/usd/usd.h @@ -65,6 +65,7 @@ struct USDImportParams { bool set_material_blend; float light_intensity_scale; eUSDMtlNameCollisionMode mtl_name_collision_mode; + bool import_unbound_materials; }; /* The USD_export takes a as_background_job parameter, and returns a boolean. -- cgit v1.2.3