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
178 lines
5.0 KiB
C++
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
|