Compare commits

...

4 Commits

Author SHA1 Message Date
8703e17ace USD import: fixed compiler warnings.
Removed unused function and assignment in
a conditional expression.
2022-10-06 10:33:43 -04:00
689e6a1be7 USD import: fix fake users for unbound materials.
Now adding fake users in the import end job function,
to ensure use counts are valid.
2022-10-05 23:40:59 -04:00
092fe1ef29 USD IO format fixes. 2022-10-05 23:11:43 -04:00
05e8eeda3c USD import unbound materials.
This addresses issue T97195.

Added a new Unbound Materials USD import option,
to import materials not assigned to any geometry.
2022-10-05 23:06:21 -04:00
8 changed files with 173 additions and 53 deletions

View File

@@ -381,6 +381,8 @@ static int wm_usd_import_exec(bContext *C, wmOperator *op)
const bool import_proxy = RNA_boolean_get(op->ptr, "import_proxy");
const bool import_render = RNA_boolean_get(op->ptr, "import_render");
const bool import_unbound_materials = RNA_boolean_get(op->ptr, "import_unbound_materials");
const bool import_usd_preview = RNA_boolean_get(op->ptr, "import_usd_preview");
const bool set_material_blend = RNA_boolean_get(op->ptr, "set_material_blend");
@@ -427,7 +429,8 @@ static int wm_usd_import_exec(bContext *C, wmOperator *op)
.import_usd_preview = import_usd_preview,
.set_material_blend = set_material_blend,
.light_intensity_scale = light_intensity_scale,
.mtl_name_collision_mode = mtl_name_collision_mode};
.mtl_name_collision_mode = mtl_name_collision_mode,
.import_unbound_materials = import_unbound_materials};
STRNCPY(params.prim_path_mask, prim_path_mask);
@@ -471,6 +474,7 @@ static void wm_usd_import_draw(bContext *UNUSED(C), wmOperator *op)
uiItemR(col, ptr, "import_guide", 0, NULL, ICON_NONE);
uiItemR(col, ptr, "import_proxy", 0, NULL, ICON_NONE);
uiItemR(col, ptr, "import_render", 0, NULL, ICON_NONE);
uiItemR(col, ptr, "import_unbound_materials", 0, NULL, ICON_NONE);
col = uiLayoutColumnWithHeading(box, true, IFACE_("Options"));
uiItemR(col, ptr, "set_frame_range", 0, NULL, ICON_NONE);
@@ -579,6 +583,12 @@ void WM_OT_usd_import(struct wmOperatorType *ot)
RNA_def_boolean(ot->srna, "import_render", true, "Render", "Import final render geometry");
RNA_def_boolean(ot->srna,
"import_unbound_materials",
false,
"Unbound Materials",
"Include materials not bound to any geometry");
RNA_def_boolean(ot->srna,
"import_usd_preview",
false,

View File

@@ -225,6 +225,10 @@ static void import_startjob(void *customdata, short *stop, short *do_update, flo
data->archive = archive;
if (data->params.import_unbound_materials) {
archive->import_all_materials(data->bmain);
}
archive->collect_readers(data->bmain);
*data->do_update = true;
@@ -355,6 +359,10 @@ static void import_endjob(void *customdata)
DEG_id_tag_update(&data->scene->id, ID_RECALC_BASE_FLAGS);
DEG_relations_tag_update(data->bmain);
if (data->params.import_unbound_materials) {
data->archive->fake_users_for_unbound_materials();
}
}
WM_set_locked_interface(data->wm, false);

View File

@@ -757,4 +757,50 @@ void USDMaterialReader::convert_usd_primvar_reader_float2(
link_nodes(ntree, uv_map, "UV", dest_node, dest_socket_name);
}
void build_material_map(const Main *bmain, std::map<std::string, Material *> *r_mat_map)
{
if (r_mat_map == nullptr) {
return;
}
Material *material = static_cast<Material *>(bmain->materials.first);
for (; material; material = static_cast<Material *>(material->id.next)) {
(*r_mat_map)[pxr::TfMakeValidIdentifier(material->id.name + 2)] = material;
}
}
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;
}
/* 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;
}
} // namespace blender::io::usd

View File

@@ -6,6 +6,8 @@
#include <pxr/usd/usdShade/material.h>
#include <map>
struct Main;
struct Material;
struct bNode;
@@ -129,4 +131,30 @@ class USDMaterialReader {
NodePlacementContext *r_ctx) const;
};
/* Utility functions. */
/**
* Returns a map containing all the Blender materials which allows a fast
* lookup of the material object by name. Note that the material name key
* might be modified to be a valid USD identifier, to match materials
* imported from USD.
*/
void build_material_map(const Main *bmain, std::map<std::string, Material *> *r_mat_map);
/**
* Returns an existing Blender material that corresponds to the USD material with the given path.
* Returns null if no such material exists.
*
* \param mat_map Map a Blender material name to the material object.
* \param usd_path_to_mat_name Map a USD material path to the imported Blender material name.
*
* The usd_path_to_mat_name is needed to determine the name of the Blender
* material imported from a USD path in the case when a unique name was generated
* for the material due to a name collision.
*/
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);
} // namespace blender::io::usd

View File

@@ -48,20 +48,6 @@ static const pxr::TfToken normalsPrimvar("normals", pxr::TfToken::Immortal);
} // namespace usdtokens
namespace utils {
/* Very similar to #blender::io::alembic::utils. */
static void build_mat_map(const Main *bmain, std::map<std::string, Material *> *r_mat_map)
{
if (r_mat_map == nullptr) {
return;
}
Material *material = static_cast<Material *>(bmain->materials.first);
for (; material; material = static_cast<Material *>(material->id.next)) {
/* We have to do this because the stored material name is coming directly from USD. */
(*r_mat_map)[pxr::TfMakeValidIdentifier(material->id.name + 2)] = material;
}
}
static pxr::UsdShadeMaterial compute_bound_material(const pxr::UsdPrim &prim)
{
@@ -83,42 +69,6 @@ static pxr::UsdShadeMaterial compute_bound_material(const pxr::UsdPrim &prim)
return mtl;
}
/* Returns an existing Blender material that corresponds to the USD material 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;
}
/* 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,
@@ -141,7 +91,7 @@ static void assign_materials(Main *bmain,
it != mat_index_map.end();
++it) {
Material *assigned_mat = find_existing_material(
Material *assigned_mat = blender::io::usd::find_existing_material(
it->first, params, mat_name_to_mat, usd_path_to_mat_name);
if (!assigned_mat) {
/* Blender material doesn't exist, so create it now. */
@@ -810,7 +760,7 @@ void USDMeshReader::readFaceSetsSample(Main *bmain, Mesh *mesh, const double mot
material_indices.finish();
/* Build material name map if it's not built yet. */
if (this->settings_->mat_name_to_mat.empty()) {
utils::build_mat_map(bmain, &this->settings_->mat_name_to_mat);
build_material_map(bmain, &this->settings_->mat_name_to_mat);
}
utils::assign_materials(bmain,
object_,

View File

@@ -5,6 +5,7 @@
#include "usd_reader_camera.h"
#include "usd_reader_curve.h"
#include "usd_reader_light.h"
#include "usd_reader_material.h"
#include "usd_reader_mesh.h"
#include "usd_reader_nurbs.h"
#include "usd_reader_prim.h"
@@ -12,12 +13,14 @@
#include "usd_reader_xform.h"
#include <pxr/pxr.h>
#include <pxr/usd/usd/primRange.h>
#include <pxr/usd/usdGeom/camera.h>
#include <pxr/usd/usdGeom/curves.h>
#include <pxr/usd/usdGeom/mesh.h>
#include <pxr/usd/usdGeom/nurbsCurves.h>
#include <pxr/usd/usdGeom/scope.h>
#include <pxr/usd/usdGeom/xform.h>
#include <pxr/usd/usdShade/material.h>
#if PXR_VERSION >= 2111
# include <pxr/usd/usdLux/boundableLightBase.h>
@@ -31,6 +34,10 @@
#include "BLI_sort.hh"
#include "BLI_string.h"
#include "BKE_lib_id.h"
#include "DNA_material_types.h"
namespace blender::io::usd {
USDStageReader::USDStageReader(pxr::UsdStageRefPtr stage,
@@ -294,6 +301,72 @@ void USDStageReader::collect_readers(Main *bmain)
collect_readers(bmain, root);
}
/* Iterate through stage and import each material prim. */
void USDStageReader::import_all_materials(Main *bmain)
{
if (!valid()) {
return;
}
/* Build the material name map if it's not built yet. */
if (settings_.mat_name_to_mat.empty()) {
build_material_map(bmain, &settings_.mat_name_to_mat);
}
USDMaterialReader mtl_reader(params_, bmain);
PXR_NS::UsdPrimRange range = stage_->TraverseAll();
for (const auto &prim : range) {
if (prim.IsA<pxr::UsdShadeMaterial>()) {
pxr::UsdShadeMaterial usd_mtl(prim);
if (!usd_mtl) {
continue;
}
Material *blend_mtl = blender::io::usd::find_existing_material(
prim.GetPath(), params_, settings_.mat_name_to_mat, settings_.usd_path_to_mat_name);
if (blend_mtl) {
/* The material already exists. */
continue;
}
/* Add the material now. */
blend_mtl = mtl_reader.add_material(usd_mtl);
if (blend_mtl) {
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, so we don't import the material again if the
* material is shared. */
const std::string mtl_name = pxr::TfMakeValidIdentifier(blend_mtl->id.name + 2);
settings_.mat_name_to_mat[mtl_name] = blend_mtl;
settings_.usd_path_to_mat_name[prim.GetPath().GetAsString()] = mtl_name;
}
}
}
}
}
/* Add fake users for any imported materials with
* no users. This is typically required for unbound
* materials. */
void USDStageReader::fake_users_for_unbound_materials()
{
std::map<std::string, std::string>::const_iterator mat_name_it =
settings_.usd_path_to_mat_name.begin();
for (; mat_name_it != settings_.usd_path_to_mat_name.end(); ++mat_name_it) {
std::map<std::string, Material *>::iterator mat_it = settings_.mat_name_to_mat.find(
mat_name_it->second);
if (mat_it != settings_.mat_name_to_mat.end()) {
Material *mat = mat_it->second;
if (mat->id.us == 0) {
id_fake_user_set(&mat->id);
}
}
}
}
void USDStageReader::clear_readers()
{
for (USDPrimReader *reader : readers_) {

View File

@@ -40,6 +40,10 @@ class USDStageReader {
void collect_readers(struct Main *bmain);
void import_all_materials(struct Main *bmain);
void fake_users_for_unbound_materials();
bool valid() const;
pxr::UsdStageRefPtr stage()

View File

@@ -65,6 +65,7 @@ struct USDImportParams {
bool set_material_blend;
float light_intensity_scale;
eUSDMtlNameCollisionMode mtl_name_collision_mode;
bool import_unbound_materials;
};
/* The USD_export takes a as_background_job parameter, and returns a boolean.