Dev:Source/Data System/ID Property
Generic Properties for Blender Library Data
Introduction
Blender now supports storing custom data in all library data (e.g. objects, materials, textures, geometric data such as meshes, curves, metaballs, scenes and more). Now render export, game export and just about any other type of script that needs it can store data inside the .blend attached to library data.
The system is called ID properties, because the properties are stored inside the unique ID structure all library data have.
How It Works
Properties consist of these basic types:
- Array: a simple one-dimensional array of either floats or ints
- String: a string
- Group: a group of other properties
- Float: a floating-point number.
- Int: an integer number.
In addition, all properties (obviously) have a name.
Standards
There is a page dedicated to ID property standards at BlenderDev/PropertyStandards. Please work together with other authors to form a standard for your script, the idea is to avoid every single script having its own set of unique properties it uses.
Python Tutorial
Using ID properties is really very easy from a python point of view. The following example code shows the simplicity:
import Blender
from Blender import Material
mat = Material.Get("Material")
# get a property
try:
TranslucencyFactor = mat.properties['SomeRenderer']['TransFac']
except:
TranslucencyFactor = 0.0
mat.properties['SomeRenderer']={}
mat.properties['SomeRenderer']['TransFac'] = 0.0
# some more code to demonstrate how to make groups, arrays, etc.
mat.properties['a float array'] = [0.0, 1.0, 2.0]
mat.properties['a group'] = {"IntArray": [1, 2, 3], "AnotherGroup": {"int": 1}}
mat.properties['a string'] = "Sdfdsfsd"
mat.properties['a float'] = 0.0
mat.properties['an int'] = 0
# to get a properties type, use type()
if type(mat.properties['a float array']) is Blender.Types.IDArrayType:
print "It's an array!"
if type(mat.properties['an int']) is int:
print "It's an int!"
# iterate over a group
for property in mat.properties['a group']:
print property
print type(property)
theint = mat.properties['an int']
thestring = mat.properties['a string']
As you can see, properties can be created from simple python types such as ints, floats, dicts and lists. Getting the data from a property is as simple as accessing its .data member.
C API
The C API for ID properties is prototyped in the header file source/blender/blenkernel/BKE_idprop.h. Using the C API is a lot different then using the python API; it's a lot low-level. Eventually some sort of ID validation system will be set up, similar to XML. For now, there's just the low-level functions.
Base Struct
Here is the ID Property struct and the changed ID struct:
typedef struct IDPropertyData {
void *pointer;
ListBase group;
int val, pad;
} IDPropertyData;
typedef struct IDProperty {
struct IDProperty *next, *prev;
char name[32];
char type, subtype;
short flag;
IDPropertyData data;
int len; /* array length, also (this is important!) string length + 1.
the idea is to be able to reuse array realloc functions on strings.*/
/*totallen is total length of allocated array/string, including a buffer.
Note that the buffering is mild; the code comes from python's list implementation.*/
int totallen; /*strings and arrays are both buffered, though the buffer isn't
saved. at least it won't be when I write that code. :)*/
int saved; /*saved is used to indicate if this struct has been saved yet.
seemed like a good idea as a pad var was needed anyway :)*/
} IDProperty;
#define MAX_IDPROP_NAME 32
#define DEFAULT_ALLOC_FOR_NULL_STRINGS 64
/*->type*/
#define IDP_STRING 0
#define IDP_INT 1
#define IDP_FLOAT 2
#define IDP_VECTOR 3
#define IDP_MATRIX 4
#define IDP_ARRAY 5
#define IDP_GROUP 6
#define IDP_ID 7
typedef struct ID {
void *next, *prev;
struct ID *newid;
struct Library *lib;
char name[24];
short us;
/**
* LIB_... flags report on status of the datablock this ID belongs
* to.
*/
short flag;
int icon_id;
IDProperty *properties;
} ID;
Getting the ID Properties From an ID
To get the ID properties from an ID (specifically, the IDProperty of type Group that's attached to an ID), use the following function:
struct IDProperty *IDP_GetProperties(struct ID *id, int create_if_needed);
Passing create_if_needed as 1 will automatically create and attach an IDProperty struct of type Group to the ID if needed, otherwise it'll return NULL if the ID has no such struct.
Creating an ID Property
Creating an ID property is easy; first you create a IDPropertyTemplate and set the appropriate values. IDPropertyTemplate is a union, which makes this very easy. To set the data for a float property, just set .f to the float; .i for int; .str for string; .array.len and .array.type (either IDP_FLOAT or IDP_INT) for array; and nothing for a new group.
Once you've created the template union, you then feed it to the IDP_New function, along with the ID property type and its name. Here's an example:
IDPropertyTemplate val;
IDProperty *group, *idgroup, *color;
group = IDP_New(IDP_GROUP, val, "group1"); //groups don't need a template.
val.array.len = 4
val.array.type = IDP_FLOAT;
color = IDP_New(IDP_ARRAY, val, "color1");
idgroup = IDP_GetProperties(some_id, 1);
IDP_AddToGroup(group, color);
IDP_AddToGroup(idgroup, group);
Finding an ID Property Inside a Group
To find an ID property, use the following function:
IDProperty *IDP_GetPropertyFromGroup(struct IDProperty *prop, char *name);
Iterating Over The ID Properties in a Group
To iterate over the ID Properties inside a Group property, allocate a void* pointer and set it to an iterator using this function:
void *IDP_GetGroupIterator(struct IDProperty *prop);
Then you can iterate with this function:
void *IDP_GroupIterNext(void *vself);
Note that the iterator is automatically freed at the end of iteration, but if iteration is stopped early you can deallocate it with:
void IDP_FreeIterBeforeEnd(void *vself);
An example of using it:
IDProperty *prop;
void *iter = IDP_GetGroupIterator(some_group);
for (prop=GroupIterNext(iter);prop;prop=GroupIterNext(iter)) {
. . .
}