Fix #142084: Importing broken USD skeletons can crash

The repro file in question contained invalid paths for the USD Skeleton.
This was not detected in our import code which proceeded to create bones
with empty (`""`), incorrect, names and subsequently allowed the
rest-pose setup to use non-existent bones during processing.

The primary fix here is to ensure that all incoming joint paths are both
valid and unique[1]. A secondary fix is made to the rest-pose function
to use our joint-to-bone map to ensure we are using the correct bone
names.

[1] https://openusd.org/release/api/class_usd_skel_skeleton.html#aa6bf8297f4aae6de9fbf1b784c524d30

Pull Request: https://projects.blender.org/blender/blender/pulls/142133
This commit is contained in:
Jesse Yurkovich
2025-07-17 19:49:29 +02:00
committed by Jesse Yurkovich
parent 1d2c1d2fa7
commit 84367f8cb9

View File

@@ -665,6 +665,7 @@ static void set_rest_pose(Main *bmain,
bArmature *arm,
const pxr::VtArray<pxr::GfMatrix4d> &bind_xforms,
const pxr::VtTokenArray &joint_order,
const blender::Map<pxr::TfToken, std::string> &joint_to_bone_map,
const pxr::UsdSkelTopology &skel_topology,
const pxr::UsdSkelSkeletonQuery &skel_query)
{
@@ -678,9 +679,13 @@ static void set_rest_pose(Main *bmain,
int64_t i = 0;
for (const pxr::TfToken &joint : joint_order) {
const pxr::SdfPath joint_path(joint);
const std::string &name = joint_path.GetName();
bPoseChannel *pchan = BKE_pose_channel_find_name(arm_obj->pose, name.c_str());
const std::string *name = joint_to_bone_map.lookup_ptr(joint);
if (name == nullptr) {
/* This joint doesn't correspond to any bone we created. Skip. */
continue;
}
bPoseChannel *pchan = BKE_pose_channel_find_name(arm_obj->pose, name->c_str());
pxr::GfMatrix4d xf = rest_xforms.AsConst()[i];
pxr::GfMatrix4d bind_xf = bind_xforms[i];
@@ -734,6 +739,23 @@ void import_skeleton(Main *bmain,
return;
}
/* Each joint path should be valid and unique. */
blender::Set<pxr::TfToken> unique_joint_paths;
unique_joint_paths.reserve(joint_order.size());
const bool all_valid_paths = std::all_of(
joint_order.cbegin(), joint_order.cend(), [&unique_joint_paths](const pxr::TfToken &val) {
const bool is_valid = pxr::SdfPath::IsValidPathString(val);
return is_valid && unique_joint_paths.add(val);
});
if (!all_valid_paths) {
BKE_reportf(reports,
RPT_WARNING,
"%s: USD joint order array contains invalid or duplicated paths for skeleton %s",
__func__,
skel.GetPath().GetAsString().c_str());
return;
}
bArmature *arm = static_cast<bArmature *>(arm_obj->data);
/* Set the armature to edit mode when creating the bones. */
@@ -962,7 +984,8 @@ void import_skeleton(Main *bmain,
ED_armature_from_edit(bmain, arm);
ED_armature_edit_free(arm);
set_rest_pose(bmain, arm_obj, arm, bind_xforms, joint_order, skel_topology, skel_query);
set_rest_pose(
bmain, arm_obj, arm, bind_xforms, joint_order, joint_to_bone_map, skel_topology, skel_query);
if (import_anim && valid_skeleton) {
import_skeleton_curves(bmain, arm_obj, skel_query, joint_to_bone_map, reports);