Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/blender/editors/io/io_usd.c28
-rw-r--r--source/blender/io/usd/intern/usd_reader_material.cc149
-rw-r--r--source/blender/io/usd/intern/usd_reader_mesh.cc84
-rw-r--r--source/blender/io/usd/intern/usd_reader_prim.h7
-rw-r--r--source/blender/io/usd/usd.h6
5 files changed, 251 insertions, 23 deletions
diff --git a/source/blender/editors/io/io_usd.c b/source/blender/editors/io/io_usd.c
index 4e2ccea36ab..39f09014a61 100644
--- a/source/blender/editors/io/io_usd.c
+++ b/source/blender/editors/io/io_usd.c
@@ -73,6 +73,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_MODIFY,
+ "MODIFY",
+ 0,
+ "Modify",
+ "Create a unique name for the imported material"},
+ {USD_MTL_NAME_COLLISION_SKIP,
+ "SKIP",
+ 0,
+ "Skip",
+ "Keep the existing material and discard the imported material"},
+ {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 };
@@ -318,6 +332,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;
@@ -356,7 +373,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);
@@ -399,6 +417,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"));
@@ -519,6 +538,13 @@ 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_MODIFY,
+ "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_material.cc b/source/blender/io/usd/intern/usd_reader_material.cc
index 317dfd2a62b..c09b8ee1e2d 100644
--- a/source/blender/io/usd/intern/usd_reader_material.cc
+++ b/source/blender/io/usd/intern/usd_reader_material.cc
@@ -24,7 +24,9 @@
#include "BKE_material.h"
#include "BKE_node.h"
+#include "BLI_fileops.h"
#include "BLI_math_vector.h"
+#include "BLI_path_util.h"
#include "BLI_string.h"
#include "DNA_material_types.h"
@@ -109,6 +111,132 @@ static void link_nodes(
nodeAddLink(ntree, source, source_socket, dest, dest_socket);
}
+/* Returns a layer handle retrieved from the given attribute's property specs.
+ * Note that the returned handle may be invalid if no layer could be found. */
+static pxr::SdfLayerHandle get_layer_handle(const pxr::UsdAttribute &Attribute)
+{
+ for (auto PropertySpec : Attribute.GetPropertyStack(pxr::UsdTimeCode::EarliestTime())) {
+ if (PropertySpec->HasDefaultValue() ||
+ PropertySpec->GetLayer()->GetNumTimeSamplesForPath(PropertySpec->GetPath()) > 0) {
+ return PropertySpec->GetLayer();
+ }
+ }
+
+ return pxr::SdfLayerHandle();
+}
+
+/* If the given file path contains a UDIM token, examine files
+ * on disk to determine the indices of the UDIM tiles that are available
+ * to load. Returns the path to the file corresponding to the lowest tile
+ * index and an array containing valid tile indices in 'r_first_tile_path'
+ * and 'r_tile_indices', respctively. The function returns true if the
+ * given arguments are valid, if 'file_path' is a UDIM path and
+ * if any tiles were found on disk; it returns false otherwise. */
+static bool get_udim_tiles(const std::string &file_path,
+ std::string *r_first_tile_path,
+ std::vector<int> *r_tile_indices)
+{
+ if (file_path.empty()) {
+ return false;
+ }
+
+ if (!(r_first_tile_path && r_tile_indices)) {
+ return false;
+ }
+
+ /* Check if we have a UDIM path. */
+ std::size_t udim_token_offset = file_path.find("<UDIM>");
+
+ if (udim_token_offset == std::string::npos) {
+ /* No UDIM token. */
+ return false;
+ }
+
+ /* Create a dummy UDIM path by replacing the '<UDIM>' token
+ * with an arbitrary index, since this is the format expected
+ * as input to the call BLI_path_sequence_decode(). We use the
+ * index 1001, but this will be rplaced by the actual index
+ * of the first tile found on disk. */
+ std::string base_udim_path(file_path);
+ base_udim_path.replace(udim_token_offset, 6, "1001");
+
+ /* Exctract the file and directory names from the path. */
+ char filename[FILE_MAX], dirname[FILE_MAXDIR];
+ BLI_split_dirfile(base_udim_path.c_str(), dirname, filename, sizeof(dirname), sizeof(filename));
+
+ /* Split the base and head portions of the file name. */
+ ushort digits = 0;
+ char base_head[FILE_MAX], base_tail[FILE_MAX];
+ base_head[0] = '\0';
+ base_tail[0] = '\0';
+ int id = BLI_path_sequence_decode(filename, base_head, base_tail, &digits);
+
+ /* As a sanity check, confirm that we got our original index. */
+ if (id != 1001) {
+ return false;
+ }
+
+ /* Iterate over the directory contents to find files
+ * with matching names and with the expected index format
+ * for UDIMS. */
+ struct direntry *dir = nullptr;
+ uint totfile = BLI_filelist_dir_contents(dirname, &dir);
+
+ if (!dir) {
+ return false;
+ }
+
+ for (int i = 0; i < totfile; ++i) {
+ if (!(dir[i].type & S_IFREG)) {
+ continue;
+ }
+
+ char head[FILE_MAX], tail[FILE_MAX];
+ head[0] = '\0';
+ tail[0] = '\0';
+ int id = BLI_path_sequence_decode(dir[i].relname, head, tail, &digits);
+
+ if (digits == 0 || digits > 4 || !(STREQLEN(base_head, head, FILE_MAX)) ||
+ !(STREQLEN(base_tail, tail, FILE_MAX))) {
+ continue;
+ }
+
+ if (id < 1001 || id >= IMA_UDIM_MAX) {
+ continue;
+ }
+
+ r_tile_indices->push_back(id);
+ }
+
+ BLI_filelist_free(dir, totfile);
+
+ if (r_tile_indices->empty()) {
+ return false;
+ }
+
+ std::sort(r_tile_indices->begin(), r_tile_indices->end());
+
+ /* Finally, use the lowest index we found to create the first tile path. */
+ (*r_first_tile_path) = file_path;
+ r_first_tile_path->replace(udim_token_offset, 6, std::to_string(r_tile_indices->front()));
+
+ return true;
+}
+
+/* Add tiles with the given indices to the given image. */
+static void add_udim_tiles(Image *image, const std::vector<int> &indices)
+{
+ if (!image || indices.empty()) {
+ return;
+ }
+
+ image->source = IMA_SRC_TILED;
+
+ for (int i = 0; i < indices.size(); ++i) {
+ BKE_image_add_tile(image, indices[i], nullptr);
+ }
+}
+
/* Returns true if the given shader may have opacity < 1.0, based
* on heuristics. */
static bool needs_blend(const pxr::UsdShadeShader &usd_shader)
@@ -622,6 +750,23 @@ void USDMaterialReader::load_tex_image(const pxr::UsdShadeShader &usd_shader,
const pxr::SdfAssetPath &asset_path = file_val.Get<pxr::SdfAssetPath>();
std::string file_path = asset_path.GetResolvedPath();
if (file_path.empty()) {
+ /* No resolved path, so use the asset path (usually
+ * necessary for UDIM paths). */
+ file_path = asset_path.GetAssetPath();
+
+ /* Texture paths are frequently relative to the USD, so get
+ * the absolute path. */
+ if (pxr::SdfLayerHandle layer_handle = get_layer_handle(file_input.GetAttr())) {
+ file_path = layer_handle->ComputeAbsolutePath(file_path);
+ }
+ }
+
+ /* If this is a UDIM texture, this array will store the
+ * UDIM tile indices. */
+ std::vector<int> udim_tile_indices;
+ get_udim_tiles(file_path, &file_path, &udim_tile_indices);
+
+ if (file_path.empty()) {
std::cerr << "WARNING: Couldn't resolve image asset '" << asset_path
<< "' for Texture Image node." << std::endl;
return;
@@ -635,6 +780,10 @@ void USDMaterialReader::load_tex_image(const pxr::UsdShadeShader &usd_shader,
return;
}
+ if (!udim_tile_indices.empty()) {
+ add_udim_tiles(image, udim_tile_indices);
+ }
+
tex_image->id = &image->id;
/* Set texture color space.
diff --git a/source/blender/io/usd/intern/usd_reader_mesh.cc b/source/blender/io/usd/intern/usd_reader_mesh.cc
index 5c8bd88e87a..a7cd0d07d72 100644
--- a/source/blender/io/usd/intern/usd_reader_mesh.cc
+++ b/source/blender/io/usd/intern/usd_reader_mesh.cc
@@ -80,41 +80,61 @@ 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;
}
- bool can_assign = true;
std::map<pxr::SdfPath, int>::const_iterator it = mat_index_map.begin();
- int matcount = 0;
- for (; it != mat_index_map.end(); ++it, matcount++) {
+ for (; it != mat_index_map.end(); ++it) {
if (!BKE_object_material_slot_add(bmain, ob)) {
- can_assign = false;
- break;
+ std::cout << "WARNING: couldn't create slot for material " << it->first << " on object "
+ << ob->id.name << std::endl;
+ return;
}
}
- if (!can_assign) {
- return;
- }
-
- /* TODO(kevin): use global map? */
+ /* TODO(makowalski): use global map? */
std::map<std::string, Material *> mat_map;
build_mat_map(bmain, &mat_map);
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()) {
+ if (params.mtl_name_collision_mode == USD_MTL_NAME_COLLISION_MODIFY) {
+ /* 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(it->first.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 *>::iterator mat_iter = mat_map.find(mat_name);
+ if (mat_iter != mat_map.end()) {
+ assigned_mat = mat_iter->second;
+ }
+ else {
+ std::cout
+ << "WARNING: Couldn't find previously assigned Blender material for USD material "
+ << it->first << std::endl;
+ }
+ }
+ }
+ else {
+ std::string mat_name = it->first.GetName();
+ std::map<std::string, Material *>::iterator mat_iter = mat_map.find(mat_name);
+
+ if (mat_iter != mat_map.end()) {
+ assigned_mat = mat_iter->second;
+ }
+ }
+
+ if (!assigned_mat) {
/* Blender material doesn't exist, so create it now. */
/* Look up the USD material. */
@@ -136,11 +156,14 @@ static void assign_materials(Main *bmain,
continue;
}
+ 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_MODIFY) {
+ /* 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) {
@@ -148,7 +171,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;
}
}
}
@@ -702,6 +725,11 @@ void USDMeshReader::assign_facesets_to_mpoly(double motionSampleTime,
pxr::UsdShadeMaterial subset_mtl = subset_api.ComputeBoundMaterial();
if (!subset_mtl) {
+ /* Check for a preview material as fallback. */
+ subset_mtl = subset_api.ComputeBoundMaterial(pxr::UsdShadeTokens->preview);
+ }
+
+ if (!subset_mtl) {
continue;
}
@@ -731,7 +759,14 @@ 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 = api.ComputeBoundMaterial();
+
+ if (!mtl) {
+ /* Check for a preview material as fallback. */
+ mtl = api.ComputeBoundMaterial(pxr::UsdShadeTokens->preview);
+ }
+
+ if (mtl) {
pxr::SdfPath mtl_path = mtl.GetPath();
@@ -750,7 +785,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 5aff52f011f..b5d795ac3bd 100644
--- a/source/blender/io/usd/intern/usd_reader_prim.h
+++ b/source/blender/io/usd/intern/usd_reader_prim.h
@@ -24,6 +24,9 @@
#include <pxr/usd/usd/prim.h>
+#include <map>
+#include <string>
+
struct Main;
struct Object;
@@ -50,6 +53,10 @@ struct ImportSettings {
CacheFile *cache_file;
+ /* Map a USD matrial prim path to a Blender material name.
+ * This map might be updated by readers during stage traversal. */
+ 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 2a036c3d398..275b7fdc3b6 100644
--- a/source/blender/io/usd/usd.h
+++ b/source/blender/io/usd/usd.h
@@ -31,6 +31,11 @@ struct CacheReader;
struct Object;
struct bContext;
+typedef enum eUSDMtlNameCollisionMode {
+ USD_MTL_NAME_COLLISION_MODIFY = 0,
+ USD_MTL_NAME_COLLISION_SKIP = 1,
+} eUSDMtlNameCollisionMode;
+
struct USDExportParams {
bool export_animation;
bool export_hair;
@@ -69,6 +74,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.