Compare commits

...

13 Commits

Author SHA1 Message Date
9d410ea853 Merge branch 'temp-T90535-usd-alab-material-import' into temp-usd-udim-import 2022-01-05 20:50:40 -05:00
3ca34d49bc USD import: fix compile error and warning.
Removed unnecessary assert which was causing a compile
warning.  Also, removed call to std::optional::value(),
which is not supported in macOS versions prior to 10.14.1.
2022-01-05 20:48:00 -05:00
40d78aaea3 Merge branch 'temp-T90535-usd-alab-material-import' into temp-usd-udim-import 2022-01-05 19:33:37 -05:00
95d6c29fbf USD import: use new UDIM virtual filepath API.
Applied Jesse Yurkovich's patch to update the UDIM
tile loading code to conform to the new virtual
filepath specification requirements.
2022-01-05 19:30:19 -05:00
d173583576 Merge remote-tracking branch 'origin/master' into temp-T90535-usd-alab-material-import 2022-01-05 17:10:53 -05:00
e7f8221296 Merge remote-tracking branch 'origin/master' into temp-usd-udim-import 2022-01-05 16:08:16 -05:00
dc31e24388 USD material import format fixes. 2021-12-28 21:55:38 -05:00
405d362fe6 USD material import improvements.
Refactored get_udim_tiles() based on review by
Sybren and Jesse.
2021-12-28 21:32:56 -05:00
b474c5da30 USD material import improvements.
Per review by Sybren, added find_existing_material()
utility function and comments.
2021-12-28 19:03:39 -05:00
4d0b45fcdc USD material import improvements.
Per suggestions from Jesse and Sybren in their
reviews.
2021-12-28 15:37:26 -05:00
ab1ce34961 USD import: fixed material reader comments.
Fixed typos in material reader code, as noted in
the patch review comments.
2021-12-22 17:10:50 -05:00
f382e3d5cb This review fixes USD Preview Surface import bugs reported in T90535 (missing materials in Animal Logic's ALab).
Differential Revision: https://developer.blender.org/D13297
2021-11-19 19:08:55 -05:00
05c4982f71 USD Preview Surface material import improvements.
Updates to address issues importing USD Preview Surface
materials in Animal Logic's ALab scene, as described in
T90535:

Added support for importing UDIM textures.

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

Modify: Create a unique name for the imported material.
Skip: Keep the existing material and discard the imported material.

Previously, the default behavior was Skip.  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.

Finally, if no materials with purpose "render" are assigned to
the USD primitive, the importer will now fall back on converting
assigned materials with purpose "preview".
2021-11-19 13:48:02 -05:00
5 changed files with 211 additions and 31 deletions

View File

@@ -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_UNIQUE_NAME,
"UNIQUE_NAME",
0,
"Unique Name",
"Create a unique name for the imported 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 };
@@ -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,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_UNIQUE_NAME,
"Material Name Collision",
"Behavior when the name of an imported material conflicts with an existing material");
}
#endif /* WITH_USD */

View File

@@ -25,8 +25,11 @@
#include "BKE_node.h"
#include "BKE_node_tree_update.h"
#include "BLI_fileops.h"
#include "BLI_math_vector.h"
#include "BLI_path_util.h"
#include "BLI_string.h"
#include "BLI_vector.hh"
#include "DNA_material_types.h"
@@ -35,6 +38,7 @@
#include <pxr/usd/usdShade/shader.h>
#include <iostream>
#include <optional>
#include <vector>
namespace usdtokens {
@@ -110,6 +114,65 @@ 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();
}
static bool is_udim_path(const std::string &path)
{
return path.find("<UDIM>") != std::string::npos;
}
/* For the given UDIM path (assumed to contain the UDIM token), returns an array
* containing valid tile indices.
* Returns std::nullopt if no tiles were found. */
static std::optional<blender::Vector<int>> get_udim_tiles(const std::string &file_path)
{
char base_udim_path[FILE_MAX];
BLI_strncpy(base_udim_path, file_path.c_str(), sizeof(base_udim_path));
blender::Vector<int> udim_tiles;
/* Extract the tile numbers from all files on disk. */
ListBase tiles = {nullptr, nullptr};
int tile_start, tile_range;
bool result = BKE_image_get_tile_info(base_udim_path, &tiles, &tile_start, &tile_range);
if (result) {
LISTBASE_FOREACH (LinkData *, tile, &tiles) {
int tile_number = POINTER_AS_INT(tile->data);
udim_tiles.append(tile_number);
}
}
BLI_freelistN(&tiles);
if (udim_tiles.is_empty()) {
return std::nullopt;
}
return udim_tiles;
}
/* Add tiles with the given indices to the given image. */
static void add_udim_tiles(Image *image, const blender::Vector<int> &indices)
{
image->source = IMA_SRC_TILED;
for (int tile_number : indices) {
BKE_image_add_tile(image, tile_number, nullptr);
}
}
/* Returns true if the given shader may have opacity < 1.0, based
* on heuristics. */
static bool needs_blend(const pxr::UsdShadeShader &usd_shader)
@@ -616,6 +679,26 @@ 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 will store the
* UDIM tile indices. */
std::optional<blender::Vector<int>> udim_tiles;
if (is_udim_path(file_path)) {
udim_tiles = get_udim_tiles(file_path);
}
if (file_path.empty()) {
std::cerr << "WARNING: Couldn't resolve image asset '" << asset_path
<< "' for Texture Image node." << std::endl;
@@ -630,6 +713,12 @@ void USDMaterialReader::load_tex_image(const pxr::UsdShadeShader &usd_shader,
return;
}
if (udim_tiles) {
/* Not calling udim_tiles.value(), which is not
* supported in macOS versions prior to 10.14.1. */
add_udim_tiles(image, *udim_tiles);
}
tex_image->id = &image->id;
/* Set texture color space.

View File

@@ -76,45 +76,90 @@ static void build_mat_map(const Main *bmain, std::map<std::string, Material *> *
}
}
static pxr::UsdShadeMaterial compute_bound_material(const pxr::UsdPrim &prim)
{
pxr::UsdShadeMaterialBindingAPI api = pxr::UsdShadeMaterialBindingAPI(prim);
pxr::UsdShadeMaterial mtl = api.ComputeBoundMaterial();
if (!mtl) {
/* Check for a preview material as fallback. */
mtl = api.ComputeBoundMaterial(pxr::UsdShadeTokens->preview);
}
return mtl;
}
/* 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_UNIQUE_NAME) {
/* 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 {
std::cout
<< "WARNING: Couldn't find previously assigned Blender material for USD material "
<< usd_mat_path << std::endl;
}
}
}
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;
}
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()) {
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. */
@@ -136,11 +181,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_UNIQUE_NAME) {
/* 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 +196,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;
}
}
}
@@ -695,11 +743,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;
}
@@ -728,10 +773,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()) {
@@ -749,7 +793,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,

View File

@@ -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 is 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),

View File

@@ -31,6 +31,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_UNIQUE_NAME = 0,
USD_MTL_NAME_COLLISION_REFERENCE_EXISTING = 1,
} eUSDMtlNameCollisionMode;
struct USDExportParams {
bool export_animation;
bool export_hair;
@@ -69,6 +76,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.