diff options
-rw-r--r-- | source/blender/editors/io/io_usd.c | 29 | ||||
-rw-r--r-- | source/blender/io/usd/intern/usd_reader_mesh.cc | 86 | ||||
-rw-r--r-- | source/blender/io/usd/intern/usd_reader_prim.h | 10 | ||||
-rw-r--r-- | source/blender/io/usd/usd.h | 8 |
4 files changed, 112 insertions, 21 deletions
diff --git a/source/blender/editors/io/io_usd.c b/source/blender/editors/io/io_usd.c index 45d658d5b25..a59cdf60243 100644 --- a/source/blender/editors/io/io_usd.c +++ b/source/blender/editors/io/io_usd.c @@ -57,6 +57,20 @@ const EnumPropertyItem rna_enum_usd_export_evaluation_mode_items[] = { {0, NULL, 0, NULL, NULL}, }; +const EnumPropertyItem rna_enum_usd_mtl_name_collision_mode_items[] = { + {USD_MTL_NAME_COLLISION_MAKE_UNIQUE, + "MAKE_UNIQUE", + 0, + "Make Unique", + "Import each USD material as a unique Blender material"}, + {USD_MTL_NAME_COLLISION_REFERENCE_EXISTING, + "REFERENCE_EXISTING", + 0, + "Reference Existing", + "If a material with the same name already exists, reference that instead of importing"}, + {0, NULL, 0, NULL, NULL}, +}; + /* Stored in the wmOperator's customdata field to indicate it should run as a background job. * This is set when the operator is invoked, and not set when it is only executed. */ enum { AS_BACKGROUND_JOB = 1 }; @@ -371,6 +385,9 @@ static int wm_usd_import_exec(bContext *C, wmOperator *op) const float light_intensity_scale = RNA_float_get(op->ptr, "light_intensity_scale"); + const eUSDMtlNameCollisionMode mtl_name_collision_mode = RNA_enum_get(op->ptr, + "mtl_name_collision_mode"); + /* TODO(makowalski): Add support for sequences. */ const bool is_sequence = false; int offset = 0; @@ -409,7 +426,8 @@ static int wm_usd_import_exec(bContext *C, wmOperator *op) .use_instancing = use_instancing, .import_usd_preview = import_usd_preview, .set_material_blend = set_material_blend, - .light_intensity_scale = light_intensity_scale}; + .light_intensity_scale = light_intensity_scale, + .mtl_name_collision_mode = mtl_name_collision_mode}; const bool ok = USD_import(C, filename, ¶ms, as_background_job); @@ -452,6 +470,7 @@ static void wm_usd_import_draw(bContext *UNUSED(C), wmOperator *op) uiItemR(col, ptr, "relative_path", 0, NULL, ICON_NONE); uiItemR(col, ptr, "create_collection", 0, NULL, ICON_NONE); uiItemR(box, ptr, "light_intensity_scale", 0, NULL, ICON_NONE); + uiItemR(box, ptr, "mtl_name_collision_mode", 0, NULL, ICON_NONE); box = uiLayoutBox(layout); col = uiLayoutColumnWithHeading(box, true, IFACE_("Experimental")); @@ -575,6 +594,14 @@ void WM_OT_usd_import(struct wmOperatorType *ot) "Scale for the intensity of imported lights", 0.0001f, 1000.0f); + + RNA_def_enum( + ot->srna, + "mtl_name_collision_mode", + rna_enum_usd_mtl_name_collision_mode_items, + USD_MTL_NAME_COLLISION_MAKE_UNIQUE, + "Material Name Collision", + "Behavior when the name of an imported material conflicts with an existing material"); } #endif /* WITH_USD */ diff --git a/source/blender/io/usd/intern/usd_reader_mesh.cc b/source/blender/io/usd/intern/usd_reader_mesh.cc index 2c86bfff4da..368d0e1bab9 100644 --- a/source/blender/io/usd/intern/usd_reader_mesh.cc +++ b/source/blender/io/usd/intern/usd_reader_mesh.cc @@ -62,11 +62,56 @@ static void build_mat_map(const Main *bmain, std::map<std::string, Material *> * } } +static pxr::UsdShadeMaterial compute_bound_material(const pxr::UsdPrim &prim) +{ + return pxr::UsdShadeMaterialBindingAPI(prim).ComputeBoundMaterial(); +} + +/* Returns an existing Blender material that corresponds to the USD + * material with 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<std::string, Material *> &mat_map, + const std::map<std::string, std::string> &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<std::string, std::string>::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<std::string, Material *>::const_iterator mat_iter = mat_map.find(mat_name); + if (mat_iter != mat_map.end()) { + return mat_iter->second; + } + else { + /* 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<std::string, Material *>::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<pxr::SdfPath, int> &mat_index_map, const USDImportParams ¶ms, - pxr::UsdStageRefPtr stage) + pxr::UsdStageRefPtr stage, + std::map<std::string, std::string> &usd_path_to_mat_name) { if (!(stage && bmain && ob)) { return; @@ -94,13 +139,10 @@ static void assign_materials(Main *bmain, blender::io::usd::USDMaterialReader mat_reader(params, bmain); for (it = mat_index_map.begin(); it != mat_index_map.end(); ++it) { - std::string mat_name = it->first.GetName(); - - std::map<std::string, Material *>::iterator mat_iter = mat_map.find(mat_name); - - Material *assigned_mat = nullptr; - if (mat_iter == mat_map.end()) { + Material *assigned_mat = find_existing_material( + it->first, params, mat_map, usd_path_to_mat_name); + if (!assigned_mat) { /* Blender material doesn't exist, so create it now. */ /* Look up the USD material. */ @@ -122,11 +164,14 @@ static void assign_materials(Main *bmain, continue; } + const std::string mat_name = pxr::TfMakeValidIdentifier(assigned_mat->id.name + 2); mat_map[mat_name] = assigned_mat; - } - else { - /* We found an existing Blender material. */ - assigned_mat = mat_iter->second; + + 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. */ + usd_path_to_mat_name[it->first.GetAsString()] = mat_name; + } } if (assigned_mat) { @@ -134,7 +179,7 @@ static void assign_materials(Main *bmain, } else { /* This shouldn't happen. */ - std::cout << "WARNING: Couldn't assign material " << mat_name << std::endl; + std::cout << "WARNING: Couldn't assign material " << it->first << std::endl; } } } @@ -710,11 +755,8 @@ void USDMeshReader::assign_facesets_to_mpoly(double motionSampleTime, int current_mat = 0; if (!subsets.empty()) { for (const pxr::UsdGeomSubset &subset : subsets) { - pxr::UsdShadeMaterialBindingAPI subset_api = pxr::UsdShadeMaterialBindingAPI( - subset.GetPrim()); - - pxr::UsdShadeMaterial subset_mtl = subset_api.ComputeBoundMaterial(); + pxr::UsdShadeMaterial subset_mtl = utils::compute_bound_material(subset.GetPrim()); if (!subset_mtl) { continue; } @@ -743,10 +785,9 @@ void USDMeshReader::assign_facesets_to_mpoly(double motionSampleTime, } if (r_mat_map->empty()) { - pxr::UsdShadeMaterialBindingAPI api = pxr::UsdShadeMaterialBindingAPI(prim_); - - if (pxr::UsdShadeMaterial mtl = api.ComputeBoundMaterial()) { + pxr::UsdShadeMaterial mtl = utils::compute_bound_material(prim_); + if (mtl) { pxr::SdfPath mtl_path = mtl.GetPath(); if (!mtl_path.IsEmpty()) { @@ -764,7 +805,12 @@ void USDMeshReader::readFaceSetsSample(Main *bmain, Mesh *mesh, const double mot std::map<pxr::SdfPath, int> mat_map; assign_facesets_to_mpoly(motionSampleTime, mesh->mpoly, mesh->totpoly, &mat_map); - utils::assign_materials(bmain, object_, mat_map, this->import_params_, this->prim_.GetStage()); + utils::assign_materials(bmain, + object_, + mat_map, + this->import_params_, + this->prim_.GetStage(), + this->settings_->usd_path_to_mat_name); } Mesh *USDMeshReader::read_mesh(Mesh *existing_mesh, diff --git a/source/blender/io/usd/intern/usd_reader_prim.h b/source/blender/io/usd/intern/usd_reader_prim.h index fdf6247de16..f2df00accf6 100644 --- a/source/blender/io/usd/intern/usd_reader_prim.h +++ b/source/blender/io/usd/intern/usd_reader_prim.h @@ -7,6 +7,9 @@ #include <pxr/usd/usd/prim.h> +#include <map> +#include <string> + struct Main; struct Object; @@ -33,6 +36,13 @@ struct ImportSettings { CacheFile *cache_file; + /* Map a USD material prim path to a Blender material name. + * This map is updated by readers during stage traversal. + * This field is mutable because it is used to keep track + * of what the importer is doing. This is necessary even + * when all the other import settings are to remain const. */ + mutable std::map<std::string, std::string> usd_path_to_mat_name; + ImportSettings() : do_convert_mat(false), from_up(0), diff --git a/source/blender/io/usd/usd.h b/source/blender/io/usd/usd.h index 2e4dcb0da94..a07315d8b4e 100644 --- a/source/blender/io/usd/usd.h +++ b/source/blender/io/usd/usd.h @@ -15,6 +15,13 @@ struct CacheReader; struct Object; struct bContext; +/* Behavior when the name of an imported material + * conflicts with an existing material. */ +typedef enum eUSDMtlNameCollisionMode { + USD_MTL_NAME_COLLISION_MAKE_UNIQUE = 0, + USD_MTL_NAME_COLLISION_REFERENCE_EXISTING = 1, +} eUSDMtlNameCollisionMode; + struct USDExportParams { bool export_animation; bool export_hair; @@ -57,6 +64,7 @@ struct USDImportParams { bool import_usd_preview; bool set_material_blend; float light_intensity_scale; + eUSDMtlNameCollisionMode mtl_name_collision_mode; }; /* The USD_export takes a as_background_job parameter, and returns a boolean. |