© The Author(s), under exclusive license to APress Media, LLC , part of Springer Nature 2021
B. E. HollisterCore Blender Developmenthttps://doi.org/10.1007/978-1-4842-6415-7_6

6. Blender “RNA” and the Data API

Brad E. Hollister1  
(1)
Computer Science Department, CSUDH, Carson, CA, USA
 

Blender “DNA” was described by example in Chapter 2, when we talked about Blender state and serialization. This chapter covers how the Blender codebase sets “DNA” variables via “RNA” properties.1

An additional aspect to the makesrna module is that multiple source files are generated during the build process. They are then fed back into the source code compiled as the runtime Blender executable. This means that makesrna is distinct from other Blender modules. It is implemented in this way, so that objects of “RNA” properties, that is, structs definitions with property data values such as initial settings, value ranges, and accessor function pointers, are not hand-coded. The makesrna module allows Blender developers to incorporate new “DNA” types by adding required wrapper code. In addition, a paradigm of the codebase is to “bake-in” elements of data, for runtime efficiency. This was another design consideration of the makesrna module, taking its direction from makesdna. However, makedna uses code generation to a lesser extent than makesrna, as will be seen.

The Blender makesrna Module

makesrna is somewhat non-standard in the Blender codebase. We explore its various parts, in the next few sections. There is a distinction between the Data API and the broader makesrna module API. As usual, the makerna API function names start with an RNA_* prefix.

Note

Some RNA_* functions are used more by non-runtime portions of the makesrna module for generating Data API backend functions (generated makesrna source code). The Data API RNA_* interface is provided in [repository root]/source/blender/makesrna/RNA_access.h, whereas the makesrna RNA_* API interface, used heavily by the non-runtime makesrna code, is contained in [repository root]/source/blender/makesrna/RNA_define.h.

Runtime vs. Non-runtime Code

The makesrna module has both a runtime and non-runtime portion. The non-runtime portion is dedicated to source code generation.

An individual file in the repository’s makesrna module may include both runtime and non-runtime code. Runtime code becomes part of the Blender executable. Non-runtime code is compiled as part of the tools that generate runtime code.

Note

This chapter references generated files which are created by a build and written to the build directory. Therefore, we preface each path with either [repository root] or [build root].

During the build process, the makesrna program is run. It is compiled from [repository root]/source/blender/makesrna/intern/makesrna.c. makesrna.c contains no runtime code. Many of the files in [repository root]/source/blender/makesrna/intern/ have a separate runtime and non-runtime portion that is included (or excluded) from compilation by the preprocessor.

At the top of every generated source file is the macro #define RNA_RUNTIME. The source code in generated files belongs to the runtime. By defining the RNA_RUNTIME macro, the generated files affect which part of a repository source file is compiled. As such, many of the source files in the repository are compiled twice, first for the non-runtime and then for the runtime after RNA_RUNTIME is defined by the generated source.

Repository “RNA” Code

We will look at the makesrna module’s generated code, after first inspecting the repository’s “RNA” code. Figure 6-1 shows the repository’s makesrna module top-level directory.
../images/495914_1_En_6_Chapter/495914_1_En_6_Fig1_HTML.jpg
Figure 6-1

The makesrna top-level directory contents

The primary source files in the top-level directory of the makesrna are RNA_types.h, RNA_enum_types.h, RNA_access.h, and RNA_define.h.2 This layout is similar to many Blender modules, in that there is an intern subdirectory containing the implementation of the module’s API and functions used internally by the module itself.

RNA_types.h

“RNA” types used directly outside of makesrna are defined in RNA_types.h . For example, PointerRNA is used extensively in the python module. However, StructRNA is only used indirectly via the PointerRNA type and therefore defined in [repository root]/source/blender/makesrna/intern/rna_internal_types.h.

RNA_enum_types.h

RNA_enum_types.h prototypes RNA_* API calls that return enumerations using EnumPropertyItem (Listing 6-1). It is not responsible for defining enums such as PropertyType, which are instead defined in RNA_types.h.
/**
 * This struct is are typically defined in arrays which define an *enum* for RNA,
 * which is used by the RNA API both for user-interface and the Python API.
 */
typedef struct EnumPropertyItem {
  /** The internal value of the enum, not exposed to users. */
  int value;
  /**
   * Note that identifiers must be unique within the array,
   * by convention they're upper case with underscores for separators.
   * - An empty string is used to define menu separators.
   * - NULL denotes the end of the array of items.
   */
  const char *identifier;
  /** Optional icon, typically 'ICON_NONE' */
  int icon;
  /** Name displayed in the interface. */
  const char *name;
  /** Longer description used in the interface. */
  const char *description;
} EnumPropertyItem;
Listing 6-1

The EnumPropertyItem struct defined in RNA_types.h

RNA_access.h

RNA_access.h contains prototypes for the runtime Data API. These functions allow access to properties held by a PointerRNA’s StructRNA field. Example prototypes for property access to float data are shown in Listing 6-2. StructRNA has pointers to RNAProperty objects (e.g., FloatRNAProperty, IntRNAProperty, etc.). Property objects have accessors in the generated runtime files, comprising the Data API’s backend.
...
float RNA_property_float_get(PointerRNA *ptr, PropertyRNA *prop);
void RNA_property_float_set(PointerRNA *ptr, PropertyRNA *prop, float value);
void RNA_property_float_get_array(PointerRNA *ptr, PropertyRNA *prop, float *values);
...
Listing 6-2

Example of float property Data API accessors, prototyped in [repository root]/source/blender/makesrna/RNA_access.h

RNA_define.h

RNA_define.h is a set of function prototypes for creating instances of “RNA” structs used by both runtime and non-runtime portions of makesrna. These functions are named with an RNA_def_* prefix and implemented in [repository root]/source/blender/makesrna/intern/rna_access.c.

Client code of the prototyped API in RNA_define.h is a binary program compiled from [repository root]/source/blender/makesrna/intern/makesrna.c. makesrna.c is analogous to makesdna.c, from Chapter 2. It is responsible for generating source files for the runtime Data API backend, for each “DNA” type.

The intern Subdirectory Contents

makesrna’s intern subdirectory contains source files prefixed by rna_*. There is an rna_*.c source file for every “DNA” type that is wrapped by “RNA.” rna_*_api.c functions implement callbacks triggered when an object is updated by the Data API, usually in response to values changed in the user interface or Python API call via an operator.

There are two parts to these files, a runtime and a non-runtime portion. The runtime portion is used for additional Data API backend functionality not written to the generated source files. This code is responsible for “refine” functions. These functions return a StructRNA, which represents a “DNA” type refinement, for example, a point light.

The non-runtime calls RNA_def_* API, setting fields on either BlenderRNA or StructRNA objects for a “DNA” type. There is an upcoming example for the Light struct (“DNA”) later in the chapter. That example will discuss makesrna/intern/rna_light.c.

“RNA” Types

There are two categories of “RNA.” The first type is accessed only through the RNA_* API and implemented in rna_internal_types.c. The second type of “RNA” is defined in RNA_types.h and manipulated by functions defined outside of makesrna. For instance, PointerRNA is from RNA_types.h and therefore accessed by code external to makesrna—mostly in the python module. The following descriptions are of the main “RNA” types.

StructRNA
StructRNA is shown in Listing 6-3 and Figure 6-2. It is used for defining the properties on “RNA” wrappers of “DNA” type data.
/* changes to this struct require updating rna_generate_struct in makesrna.c */
struct StructRNA {
  /* structs are containers of properties */
  ContainerRNA cont;
  /* unique identifier, keep after 'cont' */
  const char *identifier;
  /** Python type, this is a subtype of #pyrna_struct_Type
   * but used so each struct can have its own type which is useful for subclassing RNA. */
  void *py_type;
  void *blender_type;
  /* various options */
  int flag;
  /* Each StructRNA type can define own tags which properties can set
   * (PropertyRNA.tags) for changed behavior based on struct-type. */
  const EnumPropertyItem *prop_tag_defines;
  /* user readable name */
  const char *name;
  /* single line description, displayed in the tooltip for example */
  const char *description;
  /* context for translation */
  const char *translation_context;
  /* icon ID */
  int icon;
  /* property that defines the name */
  PropertyRNA *nameproperty;
  /* property to iterate over properties */
  PropertyRNA *iteratorproperty;
  /* struct this is derivedfrom */
  struct StructRNA *base;
  /* only use for nested structs, where both the parent and child access
   * the same C Struct but nesting is used for grouping properties.
   * The parent property is used so we know NULL checks are not needed,
   * and that this struct will never exist without its parent */
  struct StructRNA *nested;
  /* function to give the more specific type */
  StructRefineFunc refine;
  /* function to find path to this struct in an ID */
  StructPathFunc path;
  /* function to register/unregister subclasses */
  StructRegisterFunc reg;
  StructUnregisterFunc unreg;
  StructInstanceFunc instance;
  /* callback to get id properties */
  IDPropertiesFunc idproperties;
  /* functions of this struct */
  ListBase functions;
};
Listing 6-3

StructRNA from rna_internal_types.h. Used in both the “RNA” runtime and makesrna.c

../images/495914_1_En_6_Chapter/495914_1_En_6_Fig2_HTML.jpg
Figure 6-2

Collaboration between StructRNA and PropertyRNA objects . Other aggregations are clipped

Note

“RNA” is used to encapsulate information pertaining to “DNA,” as used in operators for data definitions and settings.

StructRNA objects are written to the generated source. RNA_Light is one such StructRNA object. We can see its definition in Listing 6-4. Here, we can see values that populate the fields of StructRNA. Its accessor functions are defined in rna_light.c (repository code) and in the generated source file rna_light_gen.c.
StructRNA RNA_Light = {
      {(ContainerRNA *)&RNA_PointLight, (ContainerRNA *)&RNA_ShapeKeyBezierPoint,
      NULL,
      {(PropertyRNA *)&rna_Light_type, (PropertyRNA *)&rna_Light_animation_data}},
      "Light", NULL, NULL, 519, NULL, "Light",
      "Light data-block for lighting a scene",
      "Light", 164,
      (PropertyRNA *)&rna_ID_name, (PropertyRNA *)&rna_ID_rna_properties,
      &RNA_ID,
      NULL,
      rna_Light_refine,
      NULL,
      NULL,
      NULL,
      rna_ID_instance,
      rna_ID_idprops,
      {NULL, NULL}
};
Listing 6-4

RNA_Light definition from [build root]/source/blender/makerna/intern/rna_light_gen.c. rna_Light_type is in boldface, as it is important for later discussion

ContainerRNA
As discussed, StructRNA and PropertyRNA are implemented in [repository root]/source/blender/makesrna/intern/rna_internal_types.h. StructRNA has a field named cont, of type ContainerRNA, shown in Listing 6-5.
typedef struct ContainerRNA {
  void *next, *prev;
  struct GHash *prophash;
  ListBase properties;
} ContainerRNA;
Listing 6-5

The ContainerRNA data type implemented in [repository root]/source/blender/makesrna/intern/rna_internal_types.h. The next and prev fields point to StructRNA objects at the location of their “base” types, which are ContainerRNA objects. These are properties of an object’s underlying “DNA” type

BlenderRNA
The top-level “RNA” type called BlenderRNA (Listing 6-6) is instanced using RNA_create(). RNA_create() is prototyped in RNA_define.h (Listing 6-7). A BlenderRNA object wraps the “DNA” type called SDNA. SDNA was discussed in Chapter 2 and is defined in [repository root]/source/blender/makesdna/DNA_sdna_types.h.
/* Blender RNA
 *
 * Root RNA data structure that lists all struct types. */
struct BlenderRNA {
  ListBase structs;
  /* A map of structs: {StructRNA.identifier -> StructRNA}
   * These are ensured to have unique names (with STRUCT_PUBLIC_NAMESPACE enabled). */
  struct GHash *structs_map;
  /* Needed because types with an empty identifier aren't included in 'structs_map'. */
  unsigned int structs_len;
};
Listing 6-6

The BlenderRNA implementation from [repository root]/source/blender/makesrna/intern/rna_internal_types.h

BlenderRNA *RNA_create(void)
{
  BlenderRNA *brna;
  brna = MEM_callocN(sizeof(BlenderRNA), "BlenderRNA");
  const char *error_message = NULL;
  BLI_listbase_clear(&DefRNA.structs);
  brna->structs_map = BLI_ghash_str_new_ex(__func__, 2048);
...
  DefRNA.sdna = DNA_sdna_from_data(DNAstr, DNAlen, false, false, &error_message);
...
  return brna;
}
Listing 6-7

RNA_create() from [repository root]/source/blender/makesrna/intern/rna_define.c

The “RNA” wrapper for the Main struct (blendfile data) is shown in Listing 6-8. This definition is from the generated file [build root]/source/blender/makerna/intern/rna_main_gen.c.
StructRNA RNA_BlendData = {
      {(ContainerRNA *)&RNA_BlendDataCameras, (ContainerRNA *)&RNA_LineStyleTextureSlot,
      NULL,
      {(PropertyRNA *)&rna_BlendData_rna_properties, (PropertyRNA *)&rna_BlendData_lightprobes}},
      "BlendData", NULL, NULL, 516, NULL, "Blendfile Data",
      "Main data structure representing a .blend file and all its data-blocks",
      "*", 15,
      NULL, (PropertyRNA *)&rna_BlendData_rna_properties,
      NULL,
      NULL,
      NULL,
      NULL,
      NULL,
      NULL,
      NULL,
      NULL,
      {NULL, NULL}
};
Listing 6-8

Definition of RNA_BlendData

PointerRNA
The definition of PointerRNA is shown in Listing 6-9 and Figure 6-3. In Listing 6-10, we see the implementation of RNA_main_pointer_create(). This function creates the PointerRNA used in the “RNA” Data API. Notice the RNA_BlendData variable, which is the definition for BlenderRNA. This RNA_* function adds the Main struct, which maintains Blender state.
typedef struct PointerRNA {
  struct ID *owner_id;
  struct StructRNA *type;
  void *data;
} PointerRNA;
Listing 6-9

PointerRNA type defined in [repository root]/source/blender/makesrna/RNA_types.h. The data field is a pointer to the “DNA” struct that the “RNA” is wrapping

../images/495914_1_En_6_Chapter/495914_1_En_6_Fig3_HTML.jpg
Figure 6-3

Illustration of PointerRNA and its aggregation of ID struct

void RNA_main_pointer_create(struct Main *main, PointerRNA *r_ptr)
{
  r_ptr->owner_id = NULL;
  r_ptr->type = &RNA_BlendData;
  r_ptr->data = main;
}
Listing 6-10

The PointerRNA defined for the Main struct ([repository root]/source/blender/makesrna/intern/rna_access.c). PointerRNA encapsulates “RNA” types used outside of the “RNA” API. RNA_BlendData is a variable defined by generated code in [build root]/source/blender/makesrna/intern/rna_main_gen.c

PropertyRNA
PropertyRNA is the “base” type for all “RNA” property types. Listing 6-11 shows the PropertyRNA definition. Importantly, the mapping from “RNA” to “DNA” is done through an intermediary StructRNA, the type for the srna field in PropertyRNA.
struct PropertyRNA {
  struct PropertyRNA *next, *prev;
  ...
  /* unique identifier */
  const char *identifier;
  ...
  /* This is used for accessing props/functions of this property
   * any property can have this but should only be used for collections and arrays
   * since python will convert int/bool/pointer's */
  struct StructRNA *srna; /* attributes attached directly to this collection */
  /* python handle to hold all callbacks
   * (in a pointer array at the moment, may later be a tuple) */
  void *py_data;
};
Listing 6-11

The PropertyRNA struct definition from [repository root]/source/blender/makesrna/intern/rna_internal_types.h. The identifier and srna fields are in boldface

The backend Data API—not the interface—is part of the generated source. These functions populate a PropertyRNA object attached to the StructRNA, representing the corresponding “DNA” type. For instance, in Listing 6-12 we see struct BoolPropertyRNA. The function pointers get and set (of types PropBooleanGetFunc and PropBooleanSetFunc, respectively) are assigned accessors from generated code.
typedef struct BoolPropertyRNA {
  PropertyRNA property;
  PropBooleanGetFunc get;
  PropBooleanSetFunc set;
  PropBooleanArrayGetFunc getarray;
  PropBooleanArraySetFunc setarray;
  PropBooleanGetFuncEx get_ex;
  PropBooleanSetFuncEx set_ex;
  PropBooleanArrayGetFuncEx getarray_ex;
  PropBooleanArraySetFuncEx setarray_ex;
  bool defaultvalue;
  const bool *defaultarray;
} BoolPropertyRNA;
Listing 6-12

The struct BoolPropertyRNA definition from [repository root]/source/blender/makesrna/intern/rna_internal_types.h

In Listing 6-13, from [repository root]/source/blender/makesrna/intern/rna_light_gen.c, we can see that rna_Light_type is represented with an “RNA” enumeration property. This translates to an EnumPropertyRNA field added to the corresponding StructRNA definition for that light type. rna_Light_type is added to RNA_Light from Listing 6-4.
EnumPropertyRNA rna_Light_type = {
      {(PropertyRNA *)&rna_Light_distance, NULL,
      -1, "type", 3, 0, 0, 0, 0, "Type",
      "Type of Light",
      0, "Light",
      PROP_ENUM, PROP_NONE | PROP_UNIT_NONE, NULL, 0, {0, 0, 0}, 0,
      rna_Light_draw_update, 0, NULL, NULL, rna_property_override_diff_default, rna_property_override_store_default, rna_property_override_apply_default,
      0, -1, NULL},
      Light_type_get, Light_type_set, NULL, NULL, NULL, NULL, rna_Light_type_items, 4, 0
};
Listing 6-13

An EnumPropertyRNA object defined in [repository root]/source/blender/makesrna/intern/rna_light_gen.c. This is the rna_Light_type EnumPropertyRNA object shown as a property defined on RNA_Light (a StructRNA object)

Generated “RNA” Code

As we have been alluding to, there is a substantial amount of code in makesrna that is generated. The generated code is written to the build directory and mirrors the rna_*_.c files from the intern subdirectory of the repository’s makesrna module.

This code contains the appropriate “RNA” structs populated with values for extents, enumerations, and backend Data API functions. It is the RNA_* API that uses the functions in the generated code to directly interface “DNA.”

The makesrna.c file is compiled to an executable and generates rna_*_gen.c files during the build process. Is there a list specifying each file and “DNA” type to be wrapped by the struct definitions from the generated source?

Turns out there is. Listing 6-14 shows PROCESS_ITEMS[], filled with RNAProcessItem objects. Each RNAProcessItem’s define field is a function pointer to an RNA_def_* function, called by the makesrna executable to generate struct definitions for the “RNA” Data API. The RNA_def_* function is defined in a repository source file, in the filename field of the RNAProcessItem object.
typedef struct RNAProcessItem {
  const char *filename;
  const char *api_filename;
  void (*define)(BlenderRNA *brna);
} RNAProcessItem;
static RNAProcessItem PROCESS_ITEMS[] = {
    {"rna_rna.c", NULL, RNA_def_rna},
    {"rna_ID.c", NULL, RNA_def_ID},
    {"rna_texture.c", "rna_texture_api.c", RNA_def_texture},
...
       {"rna_light.c", NULL, RNA_def_light},
...
Listing 6-14

RNAProcessItems and PROCESS_ITEMS[] from [repository root]/source/blender/makesrna/intern/makesrna.c. The RNAProcessItem for RNA_def_light is in boldface

Listing 6-15 shows code from [repository root]/source/blender/makesrna/intern/rna_light.c. We can see the use of RNA_def_struct(), RNA_def_struct_refine(), and RNA_def_struct_ui_text() for writing out a definition of the StructRNA variable RNA_Light in [build root]/source/blender/makesrna/intern/rna_light_gen.c. The makesrna executable calls RNA_def_light() from its list of RNAProcessItem objects. The rna_def_* functions are not runtime code. They are used only by the generation phase during the build, to produce accessor and “RNA” properties’ runtime code for the Data API.
void RNA_def_light(BlenderRNA *brna)
{
  rna_def_light(brna);
  rna_def_point_light(brna);
  rna_def_area_light(brna);
  rna_def_spot_light(brna);
  rna_def_sun_light(brna);
}
static void rna_def_light(BlenderRNA *brna)
{
  StructRNA *srna;
 ...
  srna = RNA_def_struct(brna, "Light", "ID");
  RNA_def_struct_sdna(srna, "Light");
  RNA_def_struct_refine_func(srna, "rna_Light_refine");
  RNA_def_struct_ui_text(srna, "Light", "Light
...
Listing 6-15

Example of RNA_define.h’s API for creating “RNA” from Light “DNA”

Setting Blender “DNA” Using “RNA”

The Data API provides a way for operators to update Blender “DNA.” Operators use “RNA” properties to interface Blender “DNA.” “RNA” properties map to “DNA” fields they abstract. This section’s example is of an operator adding a point light object to a Blender scene. The example illustrates use of the Data API and its related “RNA” structs.

wmOperatorType

Blender operators are more than callback functions. We will cover them, in more detail, in Chapter 7. For now, they are represented by a wmOperatorType (Listing 6-16) and implemented in [repository root]/source/blender/windowmanager/WM_types.h.
typedef struct wmOperatorType {
  /** Text for UI, undo. */
  const char *name;
  /** Unique identifier. */
  const char *idname;
...
  /** rna for properties */
  struct StructRNA *srna;
...
  /**
   * Default rna property to use for generic invoke functions.
   * menus, enum search... etc. Example: Enum 'type' for a Delete menu.
   *
   * When assigned a string/number property,
   * immediately edit the value when used in a popup. see: #UI_BUT_ACTIVATE_ON_INIT.
   */
  PropertyRNA *prop;
...
  /** RNA integration */
  ExtensionRNA ext;
...
} wmOperatorType;
Listing 6-16

The wmOperatorType implementation, showing relevant “RNA” related fields. The srna field is shown in boldface. srna points to a StructRNA

The wmOperatorType struct contains groups of related “RNA” properties derived from the underlying “DNA” type it acts upon. The operator may either “get” or “set” these individual properties via the Data API.

Adding a Light

We just covered the preliminaries. We now discuss the data structures and function calls for extracting settings information in an operator using the Data API and “RNA.”

The Blender Python API function bpy.ops.object.light_add() creates a new light object. It is an operator invoked using bpy.ops (the operators submodule of bpy).

Note

Operators are implemented in C code, but it is possible to write operators in Python as well. Python-based operators are run by the Blender Python extended interpreter. In this chapter, however, we show the Blender Python API calling a C based operator.

A call stack for this operator’s execution is shown in Figure 6-4.
../images/495914_1_En_6_Chapter/495914_1_En_6_Fig4_HTML.jpg
Figure 6-4

Call stack starting from pyop_call() in [repository root]/source/blender/python/intern/bpy_operator.c

Relevant “RNA”

In this example, the operator’s action simply creates a new point light. The pertinent attributes (“properties”) are ones associated with a point light. Only a subset of attributes are relevant in the “DNA” Light, for example, its area_shape field is not used.

In this case, these properties are those of the global variable RNA_PointLight, a StructRNA. RNA_PointLight is defined in the “RNA” generated code. Its definition is shown in Listing 6-17.
StructRNA RNA_PointLight = {
      {(ContainerRNA *)&RNA_AreaLight, (ContainerRNA *)&RNA_Light,
      NULL,
      {(PropertyRNA *)&rna_PointLight_energy, (PropertyRNA *)&rna_PointLight_contact_shadow_thickness}},
      "PointLight", NULL, NULL, 519, NULL, "Point Light",
      "Omnidirectional point Light",
      "*", 298,
      (PropertyRNA *)&rna_ID_name, (PropertyRNA *)&rna_ID_rna_properties,
      &RNA_Light,
      NULL,
      rna_Light_refine,
      NULL,
      NULL,
      NULL,
      rna_ID_instance,
      rna_ID_idprops,
      {NULL, NULL}
};
Listing 6-17

The RNA_PointLight global variable defined in [build root]/source/blender/makesrna/intern/rna_light_gen.c. The cont field (ContainerRNA type) assignment is shown in boldface, along with the FloatPropertyRNA type rna_PointLight_energy, just one of the multiple “RNA” properties

Relevant “RNA” properties are stored by wmOperatorType’s srna field, a pointer to a StructRNA. Note that each of the “DNA” types has a set of global StructRNA variables defined in the “RNA” generated code. For the “DNA” type instance, Light from [repository root]/source/blender/makesdna/DNA_light_types.h has its StructRNA definitions in [build root]/source/blender/makesrna/intern/rna_light_gen.c. We just saw an example of this with RNA_PointLight. A StructRNA’s “RNA” properties are each represented by a PropertyRNA object. There is a “derived” PropertyRNA type for each “RNA” property: BoolPropertyRNA, IntPropertyRNA, etc.

Note

Recall that there is no language mechanism in C that explicitly enables inheritance. It is approximated by including the base type as a header in the “derived” struct. We saw this with PyObject in Chapter 5.

rna_PointLight_energy, a FloatPropertyRNA, is shown in Listing 6-18. It illustrates “getter” and “setter” functions for that property.
/* Point Light */
FloatPropertyRNA rna_PointLight_energy = {
      {(PropertyRNA *)&rna_PointLight_falloff_type, NULL,
      -1, "energy", 3, 0, 0, 4, 0, "Power",
      "Amount of light emitted",
      0, "*",
      PROP_FLOAT, PROP_POWER | PROP_UNIT_POWER, NULL, 0, {0, 0, 0}, 0,
      rna_Light_draw_update, 0, NULL, NULL, rna_property_override_diff_default, rna_property_override_store_default, rna_property_override_apply_default,
      offsetof(Light, energy), 5, NULL},
      PointLight_energy_get, PointLight_energy_set, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0.0f, 1000000.0f, -FLT_MAX, FLT_MAX, 10.0f, 5, 10.0f, NULL
};
float PointLight_energy_get(PointerRNA *ptr)
{
    Light *data = (Light *)(ptr->data);
    return (float)(data->energy);
}
void PointLight_energy_set(PointerRNA *ptr, float value)
{
    Light *data = (Light *)(ptr->data);
    data->energy = value;
}
Listing 6-18

The rna_PointLight_energy “RNA” property, from [build root]/source/blender/makesrna/intern/rna_light_gen.c. The associated “getter” and “setter” for this property are also shown. These accessor functions directly manipulate the Light “DNA” struct

The light object “types” are all eventually represented by the general Light struct (a “DNA” type). Each, however, is represented only by a subset of Light’s fields. Thus, the related properties for each light “type” (e.g., point, area, etc.) are chained together in the cont field of StructRNA, for access during an operator’s action.

The container of properties will be used to set the correct variables in Light. Recalling Listing 6-17, notice the container of properties in RNA_PointLight’s cont field.

Calling the Data API

Starting at pyop_call() in Listing 6-19, a PointerRNA object is created to wrap the operator’s properties. A PointerRNA contains a StructRNA object.
static PyObject *pyop_call(PyObject *UNUSED(self), PyObject *args)
{
  wmOperatorType *ot;
  int error_val = 0;
  PointerRNA ptr;
...
  ot = WM_operatortype_find(opname, true);
...
    WM_operator_properties_create_ptr(&ptr, ot);
    WM_operator_properties_sanitize(&ptr, 0);
...
        operator_ret = WM_operator_call_py(C, ot, context, &ptr, reports, is_undo);
...
Listing 6-19

Start of the operator in pyop_call() from [repository root]/source/blender/python/intern/bpy_operator.c. A PointerRNA is created

We see that a PointerRNA object is defined in a call to WM_operator_properties_create_ptr(), using the correct operator type ot, retrieved from WM_operatortype_find().

WM_operator_call_py() is passed the ptr variable with the appropriate StructRNA properties for the operator type obtained earlier by WM_operatortype_find().

The execution path eventually reaches wm_operator_invoke() after a few prior calls seen in Figure 6-4. The PointerRNA is passed to each of these calls, eventually landing in wm_operator_invoke(). It is here that a wmOperator (Listing 6-20) is created.
static int wm_operator_invoke(bContext *C,
                              wmOperatorType *ot,
                              wmEvent *event,
                              PointerRNA *properties,
                              ReportList *reports,
                              const bool poll_only,
                              bool use_last_properties)
{
...
    wmOperator *op = wm_operator_create(wm, ot, properties, reports);
...
      retval = op->type->exec(C, op);
...
Listing 6-20

wm_operator_invoke() from [repository root]/source/blender/windowmanager/intern/wm_event_system.c

The wmOperator returned from wm_operator_create() houses a PointerRNA. The PointerRNA itself contains the proper StructRNA with our “RNA” properties for the operator. struct wmOperator is shown in Listing 6-21.
typedef struct wmOperator {
...
  /** Rna pointer to access properties. */
  struct PointerRNA *ptr;
...
} wmOperator;
Listing 6-21

Elided struct wmOperator defined in [repository root]/source/blender/makesdna/DNA_windowmanager_types.h

We now reach object_light_add_exec(), shown in Figure 6-4. object_light_add_exec() is the first function in the operator execution path that uses the Data API. Here, a “DNA” type Light is pointed to by the variable la. See Listing 6-22.
static int object_light_add_exec(bContext *C, wmOperator *op)
{
  Object *ob;
  Light *la;
...
  float loc[3], rot[3];
... ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, NULL, &local_view_bits, NULL))
...
  ob = ED_object_add_type(C, OB_LAMP, get_light_defname(type), loc, rot, false, local_view_bits);
...
  return OPERATOR_FINISHED;
}
Listing 6-22

Snippet from the object_light_add_exec() definition, in [repository root]/source/blender/editors/object/object_add.c

In object_light_add_exec(), we make a call to ED_object_add_generic_get_opts(), using the Data API to extract options such as the intended location for the light.
bool ED_object_add_generic_get_opts(bContext *C,
                        wmOperator *op,
                        const char view_align_axis,
                        float loc[3],
                        float rot[3],
                        bool *enter_editmode,
                        ushort *local_view_bits,
                        bool *is_view_aligned)
{
...
   /* Location! */
  {
    float _loc[3];
    if (!loc) {
      loc = _loc;
    }
    if (RNA_struct_property_is_set(op->ptr, "location")) {
      RNA_float_get_array(op->ptr, "location", loc);
    }
...
Listing 6-23

ED_object_add_generic_get_opts() obtains settings required by wmOperator, using the Data API. Here, we see the Data API call RNA_float_get_array() for the location variable loc. The loc variable is gathered from the PointerRNA data field, which points to an ID (struct header) contained in the Object struct (not shown) of the Light “DNA” object

“DNA” via the blenkernel Module

For completion of an operator’s action, we look at ED_object_add_type(). This function is called by object_light_add_exec() and uses the BKE_* API. We do not trace the code to BKE_light_add(). However, this operator eventually inserts the new light into the appropriate “collection” (a linked list of objects using ListBase as the field type) in the Main struct. ED_object_add_type() passes settings garnered by the Data API and passes them to the blenkernel API. See Listing 6-24.

Note

Up to this point in the call stack, we have wended through different modules. We can see the editors module plays a key role in operator actions. We will look more at the editors module in Chapter 7 and 8.

Object *ED_object_add_type(bContext *C,
                           int type,
                           const char *name,
                           const float loc[3],
                           const float rot[3],
                           bool enter_editmode,
                           ushort local_view_bits)
{
  Main *bmain = CTX_data_main(C);
  Scene *scene = CTX_data_scene(C);
  ViewLayer *view_layer = CTX_data_view_layer(C);
  Object *ob;
...
   ob = BKE_object_add(bmain, scene, view_layer, type, name);
...
  return ob;
}
Listing 6-24

ED_object_add_type() from [repository root]/source/blender/editors/object/object_add.c. We now have the proper settings from the “RNA” and can use the copied data to write to “DNA.” Notice the CTX_* API from the blenkernel module, to get bmain, scene, and view_Layer from the bContext parameter

The python Module and Blender “RNA”

We just saw an example of a Blender Python API operator call. But how does the Blender Python API connect to the “RNA” Data API in the first place?

We already know how the Blender Python API extensions are created in the python module from Chapter 5. There, we had shown that each extension module is represented by a PyObject “derived” type, using PyTypeObject from the CPython library to define the classes and methods (or functions) that compose a Blender Python API module.

Now, we need to investigate the specific PyObject plus PyTypeObject derivatives with connections to Blender “RNA.” We will do this for the bpy.data Python API submodule, which provides access to the Main struct of the Blender “DNA.” (The mechanism for doing this is similar for other Blender Python API connections to Blender “RNA,” such as the bpy.ops.object.light_add() call.) For access to the Main “DNA” struct, we need pyrna_struct_getattro() and pyrna_struct_setattro()—their function definitions provided in [repository root]/source/blender/python/intern/bpy_rna.c.

Both are invoked by pyop_call(). The path to their execution is similar to the call stack in Figure 6-4. However, these functions need to be connected to the respective PyObject. How does this happen?

The particular PyObject we are interested in is called BPy_StructRNA . It is shown in Listing 6-25 and the collaboration diagram in Figure 6-5.
typedef struct {
  PyObject_HEAD /* required python macro   */
...
  PointerRNA ptr;
...
} BPy_StructRNA;
Listing 6-25

struct BPy_StructRNA from [repository root]/source/blender/python/intern/bpy_rna.h. The struct is elided to reveal the PointerRNA field, shown in boldface. We know struct BPy_StructRNA is a PyObject because it contains a PyObject_HEAD

../images/495914_1_En_6_Chapter/495914_1_En_6_Fig5_HTML.jpg
Figure 6-5

The BPy_StructRNA collaboration diagram

We see that BPy_StructRNA really serves to hold the PointerRNA object, which encapsulates the underlying StructRNA. The StructRNA allows the execution path to lead to the backend Data API functions that access the Blender “DNA.” As shown previously in this chapter, we need to point the data field of PointerRNA , to its “DNA” object.

Listing 6-26 shows the process for the Main struct. This is a python module API function that returns a struct BPy_StructRNA object (a PyObject “derived” type).
/* 'bpy.data' from Python. */
static PointerRNA *rna_module_ptr = NULL;
PyObject *BPY_rna_module(void)
{
  BPy_StructRNA *pyrna;
  PointerRNA ptr;
  /* For now, return the base RNA type rather than a real module. */
  RNA_main_pointer_create(G_MAIN, &ptr);
  pyrna = (BPy_StructRNA*)pyrna_struct_CreatePyObject(&ptr);
  rna_module_ptr = &pyrna->ptr;
  return (PyObject *)pyrna;
}
Listing 6-26

Wrapping the Main struct with a PointerRNA object and then creating the corresponding struct BPy_StructRNA to register as a PyObject. This will provide access to the embedded Python interpreter. This is performed by BPY_rna_module(), from [repository root]/source/blender/python/intern/bpy_rna.c

In Listing 6-27, we see the pyrna_struct_getattro() and pyrna_struct_setattro() functions and where they are added to the PyTypeObject derived type. Recall that the PyTypeObject fields are numerous and its methods are added as arrays of PyGetSetDef, depending on the nature of the methods or functions being added.
PyTypeObject pyrna_struct_Type = {
    PyVarObject_HEAD_INIT(NULL, 0) "bpy_struct", /* tp_name */
    sizeof(BPy_StructRNA),                       /* tp_basicsize */
    ...
    (getattrofunc)pyrna_struct_getattro, /* getattrofunc tp_getattro; */
    (setattrofunc)pyrna_struct_setattro, /* setattrofunc tp_setattro; */
    ...
    NULL,
};
Listing 6-27

The struct BPy_StructRNA functions for access to the Main struct “DNA” are defined in [repository root]/source/blender/python/intern/bpy_rna.c. The tp_getattro and tp_setattro function pointer fields of PyTypeObject are used for pyrna_struct_getattro() and pyrna_struct_setattro(), respectively

Summary

This chapter covered “RNA” and the Data API. We have seen that the makerna module is different from other Blender modules. The purpose of makesrna is to implement wrappers for “DNA” types, so that operators can interface “DNA” using value ranges, accessor functions, etc.

Much of the code is generated from function calls using the makesrna API, that is, functions with the prefix RNA_def_*. The other makesrna API functions form the “RNA” Data API interface. They are prototyped in the RNA_access.h file.

Generated and repository source files, together, provide the backend for the Data API. This code performs the data manipulations on “DNA” structs.

Finally, we saw how the Blender Python API, as implemented in the python module, connects the PyObjects which represent the extended Blender CPython to the “RNA” interface of the Data API.