37#include <pxr/base/gf/matrix4f.h>
38#include <pxr/base/vt/array.h>
39#include <pxr/base/vt/types.h>
40#include <pxr/usd/sdf/types.h>
41#include <pxr/usd/usdGeom/primvarsAPI.h>
42#include <pxr/usd/usdGeom/subset.h>
43#include <pxr/usd/usdShade/materialBindingAPI.h>
44#include <pxr/usd/usdShade/tokens.h>
45#include <pxr/usd/usdSkel/bindingAPI.h>
54static const pxr::TfToken
st(
"st", pxr::TfToken::Immortal);
55static const pxr::TfToken
UVMap(
"UVMap", pxr::TfToken::Immortal);
64 const pxr::UsdShadeMaterialBindingAPI api = pxr::UsdShadeMaterialBindingAPI(prim);
70 pxr::UsdShadeMaterial mtl;
71 switch (mtl_purpose) {
73 mtl = api.ComputeBoundMaterial(pxr::UsdShadeTokens->full);
76 mtl = api.ComputeBoundMaterial(pxr::UsdShadeTokens->preview);
80 mtl = api.ComputeBoundMaterial(pxr::UsdShadeTokens->preview);
83 mtl = api.ComputeBoundMaterial(pxr::UsdShadeTokens->allPurpose);
94 pxr::UsdStageRefPtr
stage,
99 if (!(
stage && bmain && ob)) {
109 for (
const auto item : mat_index_map.
items()) {
111 item.key,
params, mat_name_to_mat, usd_path_to_mat_name);
116 pxr::UsdPrim prim =
stage->GetPrimAtPath(item.key);
117 pxr::UsdShadeMaterial usd_mat(prim);
121 &
LOG,
"Couldn't construct USD material from prim %s", item.key.GetAsString().c_str());
130 "Couldn't create Blender material from USD material %s",
131 item.key.GetAsString().c_str());
150 CLOG_WARN(&
LOG,
"Couldn't assign material %s", item.key.GetAsString().c_str());
167 is_left_handed_(
false),
168 is_time_varying_(
false),
169 is_initial_load_(
false)
185 is_initial_load_ =
true;
189 Mesh *read_mesh = this->read_mesh(mesh,
params,
nullptr);
191 is_initial_load_ =
false;
192 if (read_mesh != mesh) {
196 readFaceSetsSample(bmain, mesh, motionSampleTime);
198 if (mesh_prim_.GetPointsAttr().ValueMightBeTimeVarying() ||
199 mesh_prim_.GetVelocitiesAttr().ValueMightBeTimeVarying())
201 is_time_varying_ =
true;
204 if (is_time_varying_) {
209 pxr::TfToken subdivScheme;
210 mesh_prim_.GetSubdivisionSchemeAttr().Get(&subdivScheme, motionSampleTime);
212 if (subdivScheme == pxr::UsdGeomTokens->catmullClark) {
230 return bool(mesh_prim_);
238 mesh_prim_.GetFaceVertexIndicesAttr().Get(&face_indices_, motionSampleTime);
239 mesh_prim_.GetFaceVertexCountsAttr().Get(&face_counts_, motionSampleTime);
240 mesh_prim_.GetPointsAttr().Get(&positions_, motionSampleTime);
242 pxr::UsdGeomPrimvarsAPI primvarsAPI(mesh_prim_);
249 if (primvar.HasValue()) {
250 primvar.ComputeFlattened(&normals_, motionSampleTime);
251 normal_interpolation_ = primvar.GetInterpolation();
254 mesh_prim_.GetNormalsAttr().Get(&normals_, motionSampleTime);
255 normal_interpolation_ = mesh_prim_.GetNormalsInterpolation();
258 return positions_.size() != existing_mesh->
verts_num ||
259 face_counts_.size() != existing_mesh->
faces_num ||
260 face_indices_.size() != existing_mesh->
corners_num;
263void USDMeshReader::read_mpolys(
Mesh *mesh)
const
270 for (
int i = 0; i < face_counts_.size(); i++) {
271 const int face_size = face_counts_[i];
273 face_offsets[i] = loop_index;
278 if (is_left_handed_) {
279 int loop_end_index = loop_index + (face_size - 1);
280 for (
int f = 0; f < face_size; ++f, ++loop_index) {
281 corner_verts[loop_index] = face_indices_[loop_end_index - f];
285 for (
int f = 0; f < face_size; ++f, ++loop_index) {
286 corner_verts[loop_index] = face_indices_[loop_index];
294void USDMeshReader::read_uv_data_primvar(
Mesh *mesh,
295 const pxr::UsdGeomPrimvar &primvar,
296 const double motionSampleTime)
299 pxr::UsdGeomPrimvar::StripPrimvarsName(primvar.GetName()).GetString());
302 if (usd_uvs.empty()) {
306 const pxr::TfToken varying_type = primvar.GetInterpolation();
308 pxr::UsdGeomTokens->vertex,
309 pxr::UsdGeomTokens->faceVarying,
310 pxr::UsdGeomTokens->varying));
312 if ((varying_type == pxr::UsdGeomTokens->faceVarying && usd_uvs.size() != mesh->corners_num) ||
313 (varying_type == pxr::UsdGeomTokens->vertex && usd_uvs.size() != mesh->verts_num) ||
314 (varying_type == pxr::UsdGeomTokens->varying && usd_uvs.size() != mesh->verts_num))
318 "USD Import: UV attribute value '%s' count inconsistent with interpolation type",
319 primvar.GetName().GetText());
330 "USD Import: couldn't add UV attribute '%s'",
331 primvar.GetBaseName().GetText());
335 if (varying_type == pxr::UsdGeomTokens->faceVarying) {
336 if (is_left_handed_) {
338 const OffsetIndices faces = mesh->faces();
339 for (
const int i : faces.index_range()) {
341 for (
int j : face.index_range()) {
342 const int rev_index = face.
last(j);
343 uv_data.
span[face.start() + j] =
float2(usd_uvs[rev_index][0], usd_uvs[rev_index][1]);
348 for (
int i = 0; i < uv_data.
span.size(); ++i) {
349 uv_data.
span[i] =
float2(usd_uvs[i][0], usd_uvs[i][1]);
355 const Span<int> corner_verts = mesh->corner_verts();
356 BLI_assert(mesh->verts_num == usd_uvs.size());
357 for (
int i = 0; i < uv_data.
span.size(); ++i) {
359 int vi = corner_verts[i];
360 uv_data.
span[i] =
float2(usd_uvs[vi][0], usd_uvs[vi][1]);
367void USDMeshReader::read_vertex_creases(
Mesh *mesh,
const double motionSampleTime)
369 pxr::VtIntArray corner_indices;
370 if (!mesh_prim_.GetCornerIndicesAttr().Get(&corner_indices, motionSampleTime)) {
374 pxr::VtIntArray corner_sharpnesses;
375 if (!mesh_prim_.GetCornerSharpnessesAttr().Get(&corner_sharpnesses, motionSampleTime)) {
380 if (corner_indices.size() > mesh->verts_num) {
385 if (corner_indices.size() != corner_sharpnesses.size()) {
387 &
LOG,
"Vertex crease and sharpnesses count mismatch for mesh %s",
prim_path_.c_str());
395 for (
size_t i = 0; i < corner_indices.size(); i++) {
396 creases.
span[corner_indices[i]] = corner_sharpnesses[i];
401void USDMeshReader::read_velocities(
Mesh *mesh,
const double motionSampleTime)
403 pxr::VtVec3fArray velocities;
404 mesh_prim_.GetVelocitiesAttr().Get(&velocities, motionSampleTime);
406 if (!velocities.empty()) {
412 velocity.
span.copy_from(usd_data.cast<
float3>());
417void USDMeshReader::process_normals_vertex_varying(
Mesh *mesh)
419 if (normals_.empty()) {
423 if (normals_.size() != mesh->verts_num) {
432void USDMeshReader::process_normals_face_varying(
Mesh *mesh)
const
434 if (normals_.empty()) {
439 if (normals_.size() != mesh->corners_num) {
446 const OffsetIndices faces = mesh->faces();
447 for (
const int i : faces.index_range()) {
449 for (
int j : face.index_range()) {
450 const int corner = face.
start() + j;
452 int usd_index = face.start();
453 if (is_left_handed_) {
454 usd_index += face.size() - 1 - j;
467void USDMeshReader::process_normals_uniform(
Mesh *mesh)
const
469 if (normals_.empty()) {
474 if (normals_.size() != mesh->faces_num) {
481 const OffsetIndices faces = mesh->faces();
482 for (
const int i : faces.index_range()) {
483 for (
const int corner : faces[i]) {
493 const double motionSampleTime,
503 mesh->tag_positions_changed();
505 read_vertex_creases(mesh, motionSampleTime);
510 if (normal_interpolation_ == pxr::UsdGeomTokens->faceVarying) {
511 process_normals_face_varying(mesh);
513 else if (normal_interpolation_ == pxr::UsdGeomTokens->uniform) {
514 process_normals_uniform(mesh);
520 normal_interpolation_ == pxr::UsdGeomTokens->vertex)
522 process_normals_vertex_varying(mesh);
530 read_velocities(mesh, motionSampleTime);
531 read_custom_data(settings, mesh, motionSampleTime, new_mesh);
535void USDMeshReader::read_custom_data(
const ImportSettings *settings,
537 const double motionSampleTime,
540 if (!(mesh && mesh_prim_ && mesh->corners_num > 0)) {
544 pxr::UsdGeomPrimvarsAPI pv_api = pxr::UsdGeomPrimvarsAPI(mesh_prim_);
545 std::vector<pxr::UsdGeomPrimvar> primvars = pv_api.GetPrimvarsWithValues();
547 pxr::TfToken active_color_name;
548 pxr::TfToken active_uv_set_name;
551 for (pxr::UsdGeomPrimvar &pv : primvars) {
552 if (!pv.HasValue()) {
555 "Skipping primvar %s, mesh %s -- no value",
556 pv.GetName().GetText(),
561 if (!pv.GetAttr().GetTypeName().IsArray()) {
566 const pxr::SdfValueTypeName type = pv.GetTypeName();
567 const pxr::TfToken varying_type = pv.GetInterpolation();
568 const pxr::TfToken name = pxr::UsdGeomPrimvar::StripPrimvarsName(pv.GetPrimvarName());
572 if (!new_mesh && primvar_varying_map_.
contains(name) && !primvar_varying_map_.
lookup(name)) {
577 if (
ELEM(name,
"velocity")) {
582 pxr::SdfValueTypeNames->StringArray,
583 pxr::SdfValueTypeNames->QuatfArray,
584 pxr::SdfValueTypeNames->QuatdArray,
585 pxr::SdfValueTypeNames->QuathArray))
598 active_color_name =
name;
606 else if (
ELEM(varying_type,
607 pxr::UsdGeomTokens->vertex,
608 pxr::UsdGeomTokens->faceVarying,
609 pxr::UsdGeomTokens->varying) &&
617 active_uv_set_name =
name;
619 this->read_uv_data_primvar(mesh, pv, motionSampleTime);
631 if (!primvar_varying_map_.
contains(name)) {
632 bool might_be_time_varying = pv.ValueMightBeTimeVarying();
633 primvar_varying_map_.
add(name, might_be_time_varying);
634 if (might_be_time_varying) {
635 is_time_varying_ =
true;
640 if (!active_color_name.IsEmpty()) {
645 if (!active_uv_set_name.IsEmpty()) {
647 &mesh->corner_data,
CD_PROP_FLOAT2, active_uv_set_name.GetText());
648 if (layer_index > -1) {
655void USDMeshReader::assign_facesets_to_material_indices(
double motionSampleTime,
659 if (r_mat_map ==
nullptr) {
669 const std::vector<pxr::UsdGeomSubset> subsets = pxr::UsdGeomSubset::GetAllGeomSubsets(
673 if (!subsets.empty()) {
674 for (
const pxr::UsdGeomSubset &subset : subsets) {
675 pxr::UsdPrim subset_prim = subset.GetPrim();
682 pxr::SdfPath subset_mtl_path = subset_mtl.GetPath();
683 if (subset_mtl_path.IsEmpty()) {
687 pxr::TfToken element_type;
688 subset.GetElementTypeAttr().Get(&element_type, motionSampleTime);
689 if (element_type != pxr::UsdGeomTokens->face) {
691 "UsdGeomSubset '%s' uses unsupported elementType: %s",
692 subset_prim.GetName().GetText(),
693 element_type.GetText());
697 const int mat_idx = r_mat_map->
lookup_or_add(subset_mtl_path, 1 + current_mat++);
698 const int max_element_idx = std::max(0,
int(material_indices.
size() - 1));
701 subset.GetIndicesAttr().Get(&indices, motionSampleTime);
703 int bad_element_count = 0;
704 for (
const int element_idx : indices) {
705 const int safe_element_idx = std::clamp(element_idx, 0, max_element_idx);
706 bad_element_count += (safe_element_idx != element_idx) ? 1 : 0;
707 material_indices[safe_element_idx] = mat_idx - 1;
710 if (bad_element_count > 0) {
712 "UsdGeomSubset '%s' contains invalid indices; material assignment may be "
713 "incorrect (%d were out of range)",
714 subset_prim.GetName().GetText(),
723 pxr::SdfPath mtl_path = mtl.GetPath();
725 if (!mtl_path.IsEmpty()) {
726 r_mat_map->
add(mtl.GetPath(), 1);
732void USDMeshReader::readFaceSetsSample(
Main *bmain,
Mesh *mesh,
const double motionSampleTime)
743 this->assign_facesets_to_material_indices(motionSampleTime, material_indices.
span, &mat_map);
744 material_indices.
finish();
753 this->
prim_.GetStage(),
754 this->settings_->mat_name_to_mat,
755 this->settings_->usd_path_to_mat_name);
758Mesh *USDMeshReader::read_mesh(
Mesh *existing_mesh,
763 return existing_mesh;
766 mesh_prim_.GetOrientationAttr().Get(&orientation_);
767 if (orientation_ == pxr::UsdGeomTokens->leftHanded) {
768 is_left_handed_ =
true;
771 Mesh *active_mesh = existing_mesh;
772 bool new_mesh =
false;
781 settings.read_flag |=
params.read_flags;
786 existing_mesh, positions_.size(), 0, face_counts_.size(), face_indices_.size());
790 &settings, active_mesh,
params.motion_sample_time, new_mesh || is_initial_load_);
801 assign_facesets_to_material_indices(
802 params.motion_sample_time, material_indices.
span, &mat_map);
803 material_indices.
finish();
807 if (settings.validate_meshes) {
818 const char **r_err_str)
821 Mesh *new_mesh = read_mesh(existing_mesh,
params, r_err_str);
823 if (new_mesh != existing_mesh) {
837 pxr::UsdSkelBindingAPI skel_api(
prim_);
839 if (pxr::UsdSkelSkeleton skel = skel_api.GetInheritedSkeleton()) {
840 return skel.GetPath().GetAsString();
846std::optional<XformResult> USDMeshReader::get_local_usd_xform(
const float time)
const
855 pxr::UsdSkelBindingAPI skel_api = pxr::UsdSkelBindingAPI(
prim_);
856 if (pxr::UsdAttribute xf_attr = skel_api.GetGeomBindTransformAttr()) {
857 if (xf_attr.HasAuthoredValue()) {
858 pxr::GfMatrix4d bind_xf;
859 if (skel_api.GetGeomBindTransformAttr().Get(&bind_xf)) {
864 return XformResult(pxr::GfMatrix4f(bind_xf),
true);
869 "%s: Couldn't compute geom bind transform for %s",
871 prim_.GetPath().GetAsString().c_str());
void BKE_id_attributes_default_color_set(struct ID *id, const char *name)
void BKE_id_attributes_active_color_set(struct ID *id, const char *name)
CustomData interface, see also DNA_customdata_types.h.
void CustomData_set_layer_render_index(CustomData *data, eCustomDataType type, int n)
int CustomData_get_named_layer_index(const CustomData *data, eCustomDataType type, blender::StringRef name)
void CustomData_set_layer_active_index(CustomData *data, eCustomDataType type, int n)
General operations, lookup, etc. for materials.
void BKE_object_material_assign_single_obdata(struct Main *bmain, struct Object *ob, struct Material *ma, short act)
void BKE_mesh_set_custom_normals(Mesh *mesh, float(*r_custom_loop_normals)[3])
Mesh * BKE_mesh_new_nomain_from_template(const Mesh *me_src, int verts_num, int edges_num, int faces_num, int corners_num)
bool BKE_mesh_validate(Mesh *mesh, bool do_verbose, bool cddata_check_mask)
void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, Mesh *mesh_dst, Object *ob)
Mesh * BKE_mesh_add(Main *bmain, const char *name)
void BKE_mesh_set_custom_normals_from_verts(Mesh *mesh, float(*r_custom_vert_normals)[3])
General operations, lookup, etc. for blender objects.
Object * BKE_object_add_only_object(Main *bmain, int type, const char *name) ATTR_RETURNS_NONNULL
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
#define BLI_STATIC_ASSERT(a, msg)
#define CLOG_WARN(clg_ref,...)
@ MOD_MESHSEQ_READ_ATTRIBUTES
Object is a sort of wrapper for general info.
constexpr int64_t last(const int64_t n=0) const
constexpr int64_t start() const
Value & lookup_or_add_default(const Key &key)
bool add(const Key &key, const Value &value)
const Value & lookup(const Key &key) const
ItemIterator items() const
bool contains(const Key &key) const
Value & lookup_or_add(const Key &key, const Value &value)
constexpr int64_t size() const
constexpr void copy_from(Span< T > values) const
Span< NewT > constexpr cast() const
void add_cache_modifier()
void add_subdiv_modifier()
Material * add_material(const pxr::UsdShadeMaterial &usd_material) const
void read_object_data(Main *bmain, double motionSampleTime) override
void create_object(Main *bmain, double motionSampleTime) override
bool valid() const override
USDMeshReader(const pxr::UsdPrim &prim, const USDImportParams &import_params, const ImportSettings &settings)
std::string get_skeleton_path() const
void read_geometry(bke::GeometrySet &geometry_set, USDMeshReadParams params, const char **r_err_str) override
bool topology_changed(const Mesh *existing_mesh, double motionSampleTime) override
const ImportSettings * settings_
const USDImportParams & import_params_
const std::string & name() const
ReportList * reports() const
void mesh_calc_edges(Mesh &mesh, bool keep_existing_edges, bool select_new_edges)
To convert_value(const From value)
void build_material_map(const Main *bmain, blender::Map< std::string, Material * > *r_mat_map)
pxr::VtArray< T > get_primvar_array(const pxr::UsdGeomPrimvar &primvar, const pxr::UsdTimeCode timecode)
std::string make_safe_name(const std::string &name, bool allow_unicode)
Material * find_existing_material(const pxr::SdfPath &usd_mat_path, const USDImportParams ¶ms, const blender::Map< std::string, Material * > &mat_map, const blender::Map< std::string, std::string > &usd_path_to_mat_name)
USDMeshReadParams create_mesh_read_params(const double motion_sample_time, const int read_flags)
std::tuple< pxr::GfMatrix4f, bool > XformResult
@ USD_MTL_PURPOSE_PREVIEW
void import_blendshapes(Main *bmain, Object *mesh_obj, const pxr::UsdPrim &prim, ReportList *reports, const bool import_anim)
std::optional< eCustomDataType > convert_usd_type_to_blender(const pxr::SdfValueTypeName usd_type)
void read_generic_mesh_primvar(Mesh *mesh, const pxr::UsdGeomPrimvar &primvar, const double motionSampleTime, const bool is_left_handed)
@ USD_MTL_NAME_COLLISION_MAKE_UNIQUE
void import_mesh_skel_bindings(Main *bmain, Object *mesh_obj, const pxr::UsdPrim &prim, ReportList *reports)
VecBase< float, 2 > float2
static const pxr::TfToken st("st", pxr::TfToken::Immortal)
static const pxr::TfToken UVMap("UVMap", pxr::TfToken::Immortal)
static const pxr::TfToken normalsPrimvar("normals", pxr::TfToken::Immortal)
const pxr::TfToken displayColor("displayColor", pxr::TfToken::Immortal)
static pxr::UsdShadeMaterial compute_bound_material(const pxr::UsdPrim &prim, eUSDMtlPurpose mtl_purpose)
static void assign_materials(Main *bmain, Object *ob, const blender::Map< pxr::SdfPath, int > &mat_index_map, const blender::io::usd::USDImportParams ¶ms, pxr::UsdStageRefPtr stage, blender::Map< std::string, Material * > &mat_name_to_mat, blender::Map< std::string, std::string > &usd_path_to_mat_name)
void replace_mesh(Mesh *mesh, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
Mesh * get_mesh_for_write()
MutableVArraySpan< T > span
blender::Map< std::string, Material * > mat_name_to_mat
eUSDMtlPurpose mtl_purpose