Fix #121349: Baking adds keys on custom properties that are non-keyable

When keying custom properties that are defined by an addon,
you can't use square brackets. The GUI buttons already reflect that.
The baking code and the keyframe insertion code didn't respect
that and so were able to key properties that are defined as non-keyable.

## Solutions
I've solved the issue on the C++ side by resolving
the path twice, once without and in case that didn't work the
second time with brackets. While that does solve the issue
this feels really dirty and I feel like I am misusing the system here.
**However it is absolutely needed**.
When resolving a path with brackets to a property defined
by an addon, you get an `IDProperty` disguised as a `PropertyRNA`
which will not have the correct flags set.

By checking if a property `is_animatable` in python, all that do not have that can be skipped.
Also making sure the path is passed without brackets in the correct cases.

Pull Request: https://projects.blender.org/blender/blender/pulls/121520
This commit is contained in:
Christoph Lendenfeld
2024-05-10 17:03:52 +02:00
committed by Christoph Lendenfeld
parent fa201712e1
commit eee32726c7
3 changed files with 39 additions and 14 deletions

View File

@@ -246,11 +246,19 @@ def bake_action_iter(
if frame is None or not custom_props:
return
for key, value in custom_props.items():
if key in obj.bl_rna.properties and not obj.bl_rna.properties[key].is_animatable:
continue
obj[key] = value
if key in obj.bl_rna.properties:
rna_path = key
else:
rna_path = f'["{bpy.utils.escape_identifier(key)}"]'
try:
obj.keyframe_insert(f'["{bpy.utils.escape_identifier(key)}"]', frame=frame, group=group_name)
obj.keyframe_insert(rna_path, frame=frame, group=group_name)
except TypeError:
# Non animatable properties (datablocks, etc) cannot be keyed.
# The is_animatable check above is per property. A property in isolation
# may be considered animatable, but it could be owned by a data-block that
# itself cannot be animated.
continue
def pose_frame_info(obj):

View File

@@ -220,8 +220,13 @@ def RKS_GEN_custom_props(_ksi, _context, ks, data):
# ignore special "_RNA_UI" used for UI editing
if cprop_name == "_RNA_UI":
continue
if cprop_name in data.bl_rna.properties and not data.bl_rna.properties[cprop_name].is_animatable:
continue
prop_path = '["{:s}"]'.format(bpy.utils.escape_identifier(cprop_name))
if cprop_name in data.bl_rna.properties:
prop_path = cprop_name
else:
prop_path = '["{:s}"]'.format(bpy.utils.escape_identifier(cprop_name))
try:
rna_property = data.path_resolve(prop_path, False)

View File

@@ -224,13 +224,16 @@ static int insert_key_with_keyingset(bContext *C, wmOperator *op, KeyingSet *ks)
return OPERATOR_FINISHED;
}
static bool is_idproperty_keyable(IDProperty *prop, const PropertyRNA *property_rna)
static bool is_idproperty_keyable(IDProperty *id_prop, PointerRNA *ptr, PropertyRNA *prop)
{
if (RNA_property_is_runtime(property_rna)) {
/* While you can cast the IDProperty* to a PropertyRNA* and pass it to the functions, this
* does not work because it will not have the right flags set. Instead the resolved
* PointerRNA and PropertyRNA need to be passed. */
if (!RNA_property_anim_editable(ptr, prop)) {
return false;
}
if (ELEM(prop->type,
if (ELEM(id_prop->type,
eIDPropertyType::IDP_BOOLEAN,
eIDPropertyType::IDP_INT,
eIDPropertyType::IDP_FLOAT,
@@ -239,8 +242,8 @@ static bool is_idproperty_keyable(IDProperty *prop, const PropertyRNA *property_
return true;
}
if (prop->type == eIDPropertyType::IDP_ARRAY) {
if (ELEM(prop->subtype,
if (id_prop->type == eIDPropertyType::IDP_ARRAY) {
if (ELEM(id_prop->subtype,
eIDPropertyType::IDP_BOOLEAN,
eIDPropertyType::IDP_INT,
eIDPropertyType::IDP_FLOAT,
@@ -305,18 +308,27 @@ static blender::Vector<std::string> construct_rna_paths(PointerRNA *ptr)
}
if (insert_channel_flags & USER_ANIM_KEY_CHANNEL_CUSTOM_PROPERTIES) {
if (properties) {
LISTBASE_FOREACH (IDProperty *, prop, &properties->data.group) {
char name_escaped[MAX_IDPROP_NAME * 2];
BLI_str_escape(name_escaped, prop->name, sizeof(name_escaped));
std::string path = fmt::format("[\"{}\"]", name_escaped);
LISTBASE_FOREACH (IDProperty *, id_prop, &properties->data.group) {
PointerRNA resolved_ptr;
PropertyRNA *resolved_prop;
const bool is_resolved = RNA_path_resolve_property(
std::string path = id_prop->name;
/* Resolving the path twice, once as RNA property (without brackets, "propname"), and once
* as ID property (with brackets, '["propname"]'). This is required to support IDProperties
* that have been defined as part of an addon. Those need to be animated through an RNA
* path without the brackets. */
bool is_resolved = RNA_path_resolve_property(
ptr, path.c_str(), &resolved_ptr, &resolved_prop);
if (!is_resolved) {
char name_escaped[MAX_IDPROP_NAME * 2];
BLI_str_escape(name_escaped, id_prop->name, sizeof(name_escaped));
path = fmt::format("[\"{}\"]", name_escaped);
is_resolved = RNA_path_resolve_property(
ptr, path.c_str(), &resolved_ptr, &resolved_prop);
}
if (!is_resolved) {
continue;
}
if (is_idproperty_keyable(prop, resolved_prop)) {
if (is_idproperty_keyable(id_prop, &resolved_ptr, resolved_prop)) {
paths.append(path);
}
}