[Bf-blender-cvs] [f6268f921ab] master: USD import: Handle material name collisions

Michael Kowalski noreply at git.blender.org
Fri Jun 10 16:20:00 CEST 2022


Commit: f6268f921ab0777707bf2feb7809aff4cde07c20
Author: Michael Kowalski
Date:   Fri Jun 10 10:12:41 2022 -0400
Branches: master
https://developer.blender.org/rBf6268f921ab0777707bf2feb7809aff4cde07c20

USD import: Handle material name collisions

This is a partial fix for T90535.

Added Material Name Collision USD import menu option, to specify
the behavior when USD materials in different namespaces have the
same name.

The Material Name Collision menu options are

- Make Unique: Import each USD material as a unique Blender material.
- Reference Existing: If a material with the same name already
exists, reference that instead of importing.

Previously, the default behavior was to always keep the existing
material. This was causing an issue in the ALab scene, where
dozens of different USD materials all have the same name,
usdpreviewsurface1, so that only one instance of these materials
would be imported.

Reviewed by: Sybren

Differential Revision: https://developer.blender.org/D14869

===================================================================

M	source/blender/editors/io/io_usd.c
M	source/blender/io/usd/intern/usd_reader_mesh.cc
M	source/blender/io/usd/intern/usd_reader_prim.h
M	source/blender/io/usd/usd.h

===================================================================

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, &params, 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 &params,
+    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 &params,
-                             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 U

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list