Compare commits
13 Commits
blender-pr
...
temp-usd-u
Author | SHA1 | Date | |
---|---|---|---|
9d410ea853 | |||
3ca34d49bc | |||
40d78aaea3 | |||
95d6c29fbf | |||
d173583576 | |||
e7f8221296 | |||
dc31e24388 | |||
405d362fe6 | |||
b474c5da30 | |||
4d0b45fcdc | |||
ab1ce34961 | |||
f382e3d5cb | |||
05c4982f71 |
@@ -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, ¶ms, 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 */
|
||||
|
@@ -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.
|
||||
|
@@ -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 ¶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_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 ¶ms,
|
||||
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,
|
||||
|
@@ -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),
|
||||
|
@@ -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.
|
||||
|
Reference in New Issue
Block a user