Files
test/source/blender/animrig/intern/anim_rna.cc
Bastien Montagne 1e00054195 Cleanup: Replace 'void' MEM_[cm]allocN with templated, type-safe MEM_[cm]allocN<T>.
The main issue of 'type-less' standard C allocations is that there is no check on
allocated type possible.

This is a serious source of annoyance (and crashes) when making some
low-level structs non-trivial, as tracking down all usages of these
structs in higher-level other structs and their allocation is... really
painful.

`MEM_[cm]allocN<T>` templates on the other hand do check that the
given type is trivial, at build time (static assert), which makes such issue...
trivial to catch.

NOTE: New code should strive to use `MEM_new` (i.e. allocation and
construction) as much as possible, even for trivial PoD types.

Pull Request: https://projects.blender.org/blender/blender/pulls/134452
2025-03-07 10:44:49 +01:00

178 lines
5.0 KiB
C++

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup animrig
*/
#include <fmt/format.h>
#include "ANIM_rna.hh"
#include "BLI_listbase.h"
#include "BLI_string.h"
#include "BLI_vector.hh"
#include "DNA_object_types.h"
#include "RNA_access.hh"
#include "RNA_path.hh"
#include "RNA_prototypes.hh"
namespace blender::animrig {
Vector<float> get_rna_values(PointerRNA *ptr, PropertyRNA *prop)
{
Vector<float> values;
if (RNA_property_array_check(prop)) {
const int length = RNA_property_array_length(ptr, prop);
switch (RNA_property_type(prop)) {
case PROP_BOOLEAN: {
bool *tmp_bool = MEM_malloc_arrayN<bool>(length, __func__);
RNA_property_boolean_get_array(ptr, prop, tmp_bool);
for (int i = 0; i < length; i++) {
values.append(float(tmp_bool[i]));
}
MEM_freeN(tmp_bool);
break;
}
case PROP_INT: {
int *tmp_int = MEM_malloc_arrayN<int>(length, __func__);
RNA_property_int_get_array(ptr, prop, tmp_int);
for (int i = 0; i < length; i++) {
values.append(float(tmp_int[i]));
}
MEM_freeN(tmp_int);
break;
}
case PROP_FLOAT: {
values.reinitialize(length);
RNA_property_float_get_array(ptr, prop, values.data());
break;
}
default:
values.reinitialize(length);
break;
}
}
else {
switch (RNA_property_type(prop)) {
case PROP_BOOLEAN:
values.append(float(RNA_property_boolean_get(ptr, prop)));
break;
case PROP_INT:
values.append(float(RNA_property_int_get(ptr, prop)));
break;
case PROP_FLOAT:
values.append(RNA_property_float_get(ptr, prop));
break;
case PROP_ENUM:
values.append(float(RNA_property_enum_get(ptr, prop)));
break;
default:
values.append(0.0f);
}
}
return values;
}
StringRef get_rotation_mode_path(const eRotationModes rotation_mode)
{
switch (rotation_mode) {
case ROT_MODE_QUAT:
return "rotation_quaternion";
case ROT_MODE_AXISANGLE:
return "rotation_axis_angle";
default:
return "rotation_euler";
}
}
static bool is_idproperty_keyable(const IDProperty *id_prop, PointerRNA *ptr, PropertyRNA *prop)
{
/* While you can cast the IDProperty* to a PropertyRNA* and pass it to the RNA_* 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(id_prop->type,
eIDPropertyType::IDP_BOOLEAN,
eIDPropertyType::IDP_INT,
eIDPropertyType::IDP_FLOAT,
eIDPropertyType::IDP_DOUBLE))
{
return true;
}
if (id_prop->type == eIDPropertyType::IDP_ARRAY) {
if (ELEM(id_prop->subtype,
eIDPropertyType::IDP_BOOLEAN,
eIDPropertyType::IDP_INT,
eIDPropertyType::IDP_FLOAT,
eIDPropertyType::IDP_DOUBLE))
{
return true;
}
}
return false;
}
Vector<RNAPath> get_keyable_id_property_paths(const PointerRNA &ptr)
{
IDProperty *properties;
if (ptr.type == &RNA_PoseBone) {
const bPoseChannel *pchan = static_cast<bPoseChannel *>(ptr.data);
properties = pchan->prop;
}
else if (ptr.type == &RNA_Object) {
const Object *ob = static_cast<Object *>(ptr.data);
properties = ob->id.properties;
}
else {
/* Pointer type not supported. */
return {};
}
if (!properties) {
return {};
}
blender::Vector<RNAPath> paths;
LISTBASE_FOREACH (const IDProperty *, id_prop, &properties->data.group) {
PointerRNA resolved_ptr;
PropertyRNA *resolved_prop;
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 add-on.
* 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);
/* ID properties can be named the same as internal properties, for example `scale`. In that
* case they would resolve, but it wouldn't be the correct property. `RNA_property_is_runtime`
* catches that case. */
if (!is_resolved || !RNA_property_is_runtime(resolved_prop)) {
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(id_prop, &resolved_ptr, resolved_prop)) {
paths.append({path});
}
}
return paths;
}
} // namespace blender::animrig