Accessing custom data in cpp?

When you do it in Python, adding custom data is super easy, take any object and add an attribute like a key to a dict some_object['custom_data'] = 21.56

Now how do I access such a value in cpp? I know you can access a custom property called “cycles_curves” if it’s hard defined like so:

PointerRNA cycles_data = RNA_pointer_get(&b_ob_data.ptr, "cycles_curves");

But I would like to access an attribute of an object (or let’s call it a key in a dict) with something like this:

vector<float> custom_data;
RNA_float_get_array(b_ob_data, "custom_data", custom_data);

I get this error:

blender/intern/cycles/blender/blender_curves.cpp:201:5: error: no matching function for call to 'RNA_float_get_array'
    RNA_float_get_array(b_ob_data, "custom_data", custom_data);

Why would RNA_pointer_get() be accessible and RNA_float_get_array() not? I don’t see what to import in this case.

Besides I found this code in keyframing.c (in /source/blender) that could be relevant for retrieving custom data.

case PROP_INT:
        tmp_int = MEM_malloc_arrayN(sizeof(*tmp_int), length, __func__);
        RNA_property_int_get_array(ptr, prop, tmp_int);
        for (int i = 0; i < length; i++) {
          values[i] = (float)tmp_int[i];
        }
        MEM_freeN(tmp_int);
        break;

If any blender/cycles cpp guru could share some insights, that’d be great.

1 Like

Is error: no matching function for call the complete error message? Normally that means the argument types don’t match, and then the compiler tells you which ones don’t match. The first one needs is expected to be a PointerRNA* and the third a float*. See how the get_float4 utility function works.

But it’s easier if you explain what exactly you are trying to do, since the solution might be easier or harder depending on that.

You’re right, there’s another part to the error. It’s just drowned in the many OSSpinlock warnings I got at compile time on MacOS (I don’t know how to get rid of them).

/blender/source/blender/makesrna/RNA_access.h:1161:6: note: candidate function not viable: no known conversion from 'PointerRNA' to
      'PointerRNA *' for 1st argument; take the address of the argument with &
void RNA_float_get_array(PointerRNA *ptr, const char *name, float *values);
     ^

As I wrote in my intro, I’m trying to access custom data stored in objects with Cycles. I want to check that attribute exists and to retrieve its value (in my case a list/array of floats).

The below code does not work because the compiler wants that I initialize custom_data but it shows you what I would like to do.

BL::ID b_ob_data = b_ob->data();
float custom_data[]; // or vector<float> custom_data;
// To add: call some function to check that attribute "custom_data" exists
// Also: what happens if custom data is not a list of floats? Is there a way to check the type of input?
RNA_float_get_array(&b_ob_data.ptr, "custom_data", custom_data);

And the error:

/blender/intern/cycles/blender/blender_curves.cpp:191:9: error: definition of variable with array type needs an explicit size or an initializer
  float custom_data[];
        ^

Ideally, I would like to access Python lists within Cycles in cpp and these lists can have various sizes so I cannot initialize the above array with a size since

There is a difference between the properties that a user can add in “Custom Properties” panels, and properties that are defined in a script using bpy.props.

Only the latter are accessible through the RNA API. For the former, you’d need to use the API in BKE_idprop.h.

For RNA, you can get the array length with RNA_struct_find_property() and RNA_property_array_length(), there does not appear to be a convenience function to look it up by name with one function call.

OK thanks, it really helps that you mention these methods. The code seems to work on its own but I’m in a third case you did not mention: retrieving dynamic data added to an object with Python.

Here’s how I’d like to do it: some_object['custom_data'] = [0.15, 0.8, 0.21] then I want to retrieve this using RNA_struct_find_property() but it doesn’t find it.

Is there a way to get this type of value? It’s stored somewhere in the .blend file so I guess it should be. Using bpy.props properties means that I cannot store a list or dict.

Alternatively, I suppose I could save and read from an external file…

Such values can be accessed using the functions in BKE_idprop.h.

Something like this (untested):

IDProperty *id_props = IDP_GetProperties(&object->id, false);
IDProperty *prop = IDP_GetPropertyFromGroup(id_props, "custom_data");
if (prop && prop->type == IDP_ARRAY && prop->subtype == IDP_FLOAT) {
  float *values = (float*)prop->data.pointer;
}
1 Like

Thanks, it got me started. I got it working but had to figure out a few things, like the fact that prop->type and prop->subtype are empty, no matter what type of value is set (list, single float, single int, string) so I cannot check data type which is a problem in cpp.

/* 
This works but it's not safe to use as such because of data types
that could be different from float/double. prop->type yields nothing 
so there's no way to check data type and choose appropriate casting.
However the type of data is read correctly by readfile.c when opening 
the .blend file. It seems lost on the way to Cycles rendering.

Also, this only works for data read from the .blend file (settings new
custom values won't be updated in Cycles unless you save, reload
and run the render/preview again).  
*/
ID *id = (ID *)b_ob->ptr.owner_id;
IDProperty *id_props = IDP_GetProperties(id, false);
IDProperty *prop = IDP_GetPropertyFromGroup(id_props, "custom_data");
std::cout << "Prop name: " << prop->name << std::endl;
std::cout << "Prop len: " << prop->len << std::endl;
std::cout << "Prop type: " << prop->type << std::endl;
std::cout << "Prop subtype: " << prop->subtype << std::endl;
std::cout << "Prop flag: " << prop->flag << std::endl;
std::cout << "Prop saved: " << prop->saved << std::endl;
double *values = (double*)prop->data.pointer;
for (unsigned int i = 0; i < prop->len; i++) {
    auto value = *(values + i);
    std::cout << "Custom value " << i << ": " << value << std::endl;
}

Here’s how I tested it, which also reveals a 6-digit limit in storing floats. I hope I’m doing something wrong and this is not by design, because then I don’t see the point of having to use double.

import math.pi
obj = bpy.data.objects['Cube']
obj['custom_data'] = [452.01, 1/3, 0.5, 0.000515, 0.117, 897621545.1, 90654.008, math.pi*100]

Then

  • hit “play” to start the script
  • save your .blend file
  • reopen the file from Blender (File > Open > …)
  • start render or preview render

This is what cpp outputs in the console:

Prop name: custom_data
Prop len: 8
Prop total len: 8
Prop type:
Prop subtype:
Prop flag: 0
Prop saved: 0
Custom value 0: 452.01
Custom value 1: 0.333333
Custom value 2: 0.5
Custom value 3: 0.000515
Custom value 4: 0.117
Custom value 5: 8.97622e+08
Custom value 6: 90654
Custom value 7: 314.159

When you store an list of float in Python, it is stored as a double internally with two ints (?) so it took me a while to figure out why retrieving values with type float in cpp yielded different results from what was set in Python.

I’ve been trying to rewrite in many ways the above loop to accomodate auto values. I also tried with sizeof to find the proper step for pointer iteration. The only way to have a result guaranteed under control so far is to call *IDP_GetPropertyTypeFromGroup() instead of *IDP_GetPropertyFromGroup() and to mention what type you want but this does not handle cases when say, a list of strings is set as custom_data instead of a list of floats.

Last thing, is there a way to tell Cycles (or Blender?) that an object should be updated? So far I can only load custom values that are saved within the .blend file. Modifying such values at runtime will not trigger an update.