Files
test2/source/blender/io/usd/intern/usd_armature_utils.cc
Jesse Yurkovich 9ad2c7df0b USD: implement native Unicode support
Make use of USD's new UTF-8 support to allow our import/export code to
accept and generate appropriate USD files. This has been a long standing
shortcoming since USD's introduction, with incomplete and complicated
DCC-specific workarounds often attempted.

Summary of changes
- Export gets a new "Allow Unicode" option defaulting to "false". The
  new Unicode USD files are not backward compatible. DCCs using older
  versions of USD (before 24.03) will not be able to load such files so
  we want to provide this as an opt-in option for now.
- Every location which used to call either `USDHierarchyIterator::make_valid_name`
  or `pxr::TfMakeValidIdentifier` will now go through a new `make_safe_name`
  API instead
- Export code is responsible for passing in the `allow_unicode` option
- Import code will always pass in `true` meaning Blender will happily
  accept both existing and new Unicode USD files

Strangely, USD does not provide a convenient way of making valid UTF-8
identifiers and they left their old API unchanged. We had to roll our
own per their advice: https://forum.aousd.org/t/how-to-make-a-unicode-identifier-valid/1435

Pull Request: https://projects.blender.org/blender/blender/pulls/122471
2024-06-04 20:53:57 +02:00

195 lines
5.0 KiB
C++

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "usd_armature_utils.hh"
#include "usd_utils.hh"
#include "BKE_armature.hh"
#include "BKE_modifier.hh"
#include "BLI_listbase.h"
#include "BLI_vector.hh"
#include "DEG_depsgraph.hh"
#include "DEG_depsgraph_query.hh"
#include "DNA_action_types.h"
#include "DNA_armature_types.h"
namespace blender::io::usd {
/* Recursively invoke the 'visitor' function on the given bone and its children. */
static void visit_bones(const Bone *bone, FunctionRef<void(const Bone *)> visitor)
{
if (!(bone && visitor)) {
return;
}
visitor(bone);
LISTBASE_FOREACH (const Bone *, child, &bone->childbase) {
visit_bones(child, visitor);
}
}
const ModifierData *get_enabled_modifier(const Object &obj,
ModifierType type,
const Depsgraph *depsgraph)
{
BLI_assert(depsgraph);
Scene *scene = DEG_get_input_scene(depsgraph);
eEvaluationMode mode = DEG_get_mode(depsgraph);
LISTBASE_FOREACH (ModifierData *, md, &obj.modifiers) {
if (!BKE_modifier_is_enabled(scene, md, mode)) {
continue;
}
if (md->type == type) {
return md;
}
}
return nullptr;
}
/* Return the armature modifier on the given object. Return null if no armature modifier
* can be found. */
static const ArmatureModifierData *get_armature_modifier(const Object &obj,
const Depsgraph *depsgraph)
{
const ArmatureModifierData *mod = reinterpret_cast<const ArmatureModifierData *>(
get_enabled_modifier(obj, eModifierType_Armature, depsgraph));
return mod;
}
void visit_bones(const Object *ob_arm, FunctionRef<void(const Bone *)> visitor)
{
if (!(ob_arm && ob_arm->type == OB_ARMATURE && ob_arm->data)) {
return;
}
bArmature *armature = (bArmature *)ob_arm->data;
LISTBASE_FOREACH (const Bone *, bone, &armature->bonebase) {
visit_bones(bone, visitor);
}
}
void get_armature_bone_names(const Object *ob_arm,
const bool use_deform,
Vector<std::string> &r_names)
{
Map<StringRef, const Bone *> deform_map;
if (use_deform) {
init_deform_bones_map(ob_arm, &deform_map);
}
auto visitor = [&](const Bone *bone) {
if (use_deform && !deform_map.contains(bone->name)) {
return;
}
r_names.append(bone->name);
};
visit_bones(ob_arm, visitor);
}
pxr::TfToken build_usd_joint_path(const Bone *bone, bool allow_unicode)
{
std::string path(bone->name);
const Bone *parent = bone->parent;
while (parent) {
path = parent->name + std::string("/") + path;
parent = parent->parent;
}
return pxr::TfToken(make_safe_name(path, allow_unicode));
}
void create_pose_joints(pxr::UsdSkelAnimation &skel_anim,
const Object &obj,
const Map<StringRef, const Bone *> *deform_map,
bool allow_unicode)
{
BLI_assert(obj.pose);
pxr::VtTokenArray joints;
const bPose *pose = obj.pose;
LISTBASE_FOREACH (const bPoseChannel *, pchan, &pose->chanbase) {
if (pchan->bone) {
if (deform_map && !deform_map->contains(pchan->bone->name)) {
/* If deform_map is passed in, assume we're going deform-only.
* Bones not found in the map should be skipped. */
continue;
}
joints.push_back(build_usd_joint_path(pchan->bone, allow_unicode));
}
}
skel_anim.GetJointsAttr().Set(joints);
}
const Object *get_armature_modifier_obj(const Object &obj, const Depsgraph *depsgraph)
{
const ArmatureModifierData *mod = get_armature_modifier(obj, depsgraph);
return mod ? mod->object : nullptr;
}
bool is_armature_modifier_bone_name(const Object &obj,
const StringRefNull name,
const Depsgraph *depsgraph)
{
const ArmatureModifierData *arm_mod = get_armature_modifier(obj, depsgraph);
if (!arm_mod || !arm_mod->object || !arm_mod->object->data) {
return false;
}
bArmature *arm = static_cast<bArmature *>(arm_mod->object->data);
return BKE_armature_find_bone_name(arm, name.c_str());
}
bool can_export_skinned_mesh(const Object &obj, const Depsgraph *depsgraph)
{
return get_enabled_modifier(obj, eModifierType_Armature, depsgraph) != nullptr;
}
void init_deform_bones_map(const Object *obj, Map<StringRef, const Bone *> *deform_map)
{
if (!deform_map) {
return;
}
deform_map->clear();
auto deform_visitor = [&](const Bone *bone) {
if (!bone) {
return;
}
const bool deform = !(bone->flag & BONE_NO_DEFORM);
if (deform) {
deform_map->add(bone->name, bone);
}
};
visit_bones(obj, deform_visitor);
/* Get deform parents */
for (const auto &item : deform_map->items()) {
BLI_assert(item.value);
for (const Bone *parent = item.value->parent; parent; parent = parent->parent) {
deform_map->add(parent->name, parent);
}
}
}
} // namespace blender::io::usd