/* SPDX-License-Identifier: GPL-2.0-or-later * Copyright 2021 Tangent Animation and. NVIDIA Corporation. All rights reserved. */ #include "usd_reader_stage.h" #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" #include "usd_reader_volume.h" #include "usd_reader_xform.h" #include #include #include #include #include #include #include #include #include #if PXR_VERSION >= 2111 # include # include #else # include #endif #include #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, const USDImportParams ¶ms, const ImportSettings &settings) : stage_(stage), params_(params), settings_(settings) { } USDStageReader::~USDStageReader() { clear_readers(); } bool USDStageReader::valid() const { return stage_; } USDPrimReader *USDStageReader::create_reader_if_allowed(const pxr::UsdPrim &prim) { if (params_.import_cameras && prim.IsA()) { return new USDCameraReader(prim, params_, settings_); } if (params_.import_curves && prim.IsA()) { return new USDCurvesReader(prim, params_, settings_); } if (params_.import_curves && prim.IsA()) { return new USDNurbsReader(prim, params_, settings_); } if (params_.import_meshes && prim.IsA()) { return new USDMeshReader(prim, params_, settings_); } #if PXR_VERSION >= 2111 if (params_.import_lights && (prim.IsA() || prim.IsA())) { #else if (params_.import_lights && prim.IsA()) { #endif return new USDLightReader(prim, params_, settings_); } if (params_.import_volumes && prim.IsA()) { return new USDVolumeReader(prim, params_, settings_); } if (prim.IsA()) { return new USDXformReader(prim, params_, settings_); } return nullptr; } USDPrimReader *USDStageReader::create_reader(const pxr::UsdPrim &prim) { if (prim.IsA()) { return new USDCameraReader(prim, params_, settings_); } if (prim.IsA()) { return new USDCurvesReader(prim, params_, settings_); } if (prim.IsA()) { return new USDNurbsReader(prim, params_, settings_); } if (prim.IsA()) { return new USDMeshReader(prim, params_, settings_); } #if PXR_VERSION >= 2111 if (prim.IsA() || prim.IsA()) { #else if (prim.IsA()) { #endif return new USDLightReader(prim, params_, settings_); } if (prim.IsA()) { return new USDVolumeReader(prim, params_, settings_); } if (prim.IsA()) { return new USDXformReader(prim, params_, settings_); } return nullptr; } bool USDStageReader::include_by_visibility(const pxr::UsdGeomImageable &imageable) const { if (!params_.import_visible_only) { /* Invisible prims are allowed. */ return true; } pxr::UsdAttribute visibility_attr = imageable.GetVisibilityAttr(); if (!visibility_attr) { /* No visibility attribute, so allow. */ return true; } /* Include if the prim has an animating visibility attribute or is not invisible. */ if (visibility_attr.ValueMightBeTimeVarying()) { return true; } pxr::TfToken visibility; visibility_attr.Get(&visibility); return visibility != pxr::UsdGeomTokens->invisible; } bool USDStageReader::include_by_purpose(const pxr::UsdGeomImageable &imageable) const { if (params_.import_guide && params_.import_proxy && params_.import_render) { /* The options allow any purpose, so we trivially include the prim. */ return true; } pxr::UsdAttribute purpose_attr = imageable.GetPurposeAttr(); if (!purpose_attr) { /* No purpose attribute, so trivially include the prim. */ return true; } pxr::TfToken purpose; purpose_attr.Get(&purpose); if (purpose == pxr::UsdGeomTokens->guide) { return params_.import_guide; } if (purpose == pxr::UsdGeomTokens->proxy) { return params_.import_proxy; } if (purpose == pxr::UsdGeomTokens->render) { return params_.import_render; } return true; } /* Determine if the given reader can use the parent of the encapsulated USD prim * to compute the Blender object's transform. If so, the reader is appropriately * flagged and the function returns true. Otherwise, the function returns false. */ static bool merge_with_parent(USDPrimReader *reader) { USDXformReader *xform_reader = dynamic_cast(reader); if (!xform_reader) { return false; } /* Check if the Xform reader is already merged. */ if (xform_reader->use_parent_xform()) { return false; } /* Only merge if the parent is an Xform. */ if (!xform_reader->prim().GetParent().IsA()) { return false; } /* Don't merge Xform and Scope prims. */ if (xform_reader->prim().IsA() || xform_reader->prim().IsA()) { return false; } /* Don't merge if the prim has authored transform ops. */ if (xform_reader->prim_has_xform_ops()) { return false; } /* Flag the Xform reader as merged. */ xform_reader->set_use_parent_xform(true); return true; } USDPrimReader *USDStageReader::collect_readers(Main *bmain, const pxr::UsdPrim &prim) { if (prim.IsA()) { pxr::UsdGeomImageable imageable(prim); if (!include_by_purpose(imageable)) { return nullptr; } if (!include_by_visibility(imageable)) { return nullptr; } } pxr::Usd_PrimFlagsPredicate filter_predicate = pxr::UsdPrimDefaultPredicate; if (params_.import_instance_proxies) { filter_predicate = pxr::UsdTraverseInstanceProxies(filter_predicate); } pxr::UsdPrimSiblingRange children = prim.GetFilteredChildren(filter_predicate); std::vector child_readers; for (const auto &childPrim : children) { if (USDPrimReader *child_reader = collect_readers(bmain, childPrim)) { child_readers.push_back(child_reader); } } if (prim.IsPseudoRoot()) { return nullptr; } /* Check if we can merge an Xform with its child prim. */ if (child_readers.size() == 1) { USDPrimReader *child_reader = child_readers.front(); if (merge_with_parent(child_reader)) { return child_reader; } } USDPrimReader *reader = create_reader_if_allowed(prim); if (!reader) { return nullptr; } readers_.push_back(reader); reader->incref(); /* Set each child reader's parent. */ for (USDPrimReader *child_reader : child_readers) { child_reader->parent(reader); } return reader; } void USDStageReader::collect_readers(Main *bmain) { if (!valid()) { return; } clear_readers(); /* Iterate through the stage. */ pxr::UsdPrim root = stage_->GetPseudoRoot(); std::string prim_path_mask(params_.prim_path_mask); if (!prim_path_mask.empty()) { pxr::UsdPrim prim = stage_->GetPrimAtPath(pxr::SdfPath(prim_path_mask)); if (prim.IsValid()) { root = prim; } else { std::cerr << "WARNING: Prim Path Mask " << prim_path_mask << " does not specify a valid prim.\n"; } } stage_->SetInterpolationType(pxr::UsdInterpolationType::UsdInterpolationTypeHeld); 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 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. */ if (blend_mtl = mtl_reader.add_material(usd_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::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::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_) { if (!reader) { continue; } reader->decref(); if (reader->refcount() == 0) { delete reader; } } readers_.clear(); } void USDStageReader::sort_readers() { blender::parallel_sort( readers_.begin(), readers_.end(), [](const USDPrimReader *a, const USDPrimReader *b) { const char *na = a ? a->name().c_str() : ""; const char *nb = b ? b->name().c_str() : ""; return BLI_strcasecmp(na, nb) < 0; }); } } // Namespace blender::io::usd