From BlenderWiki

Jump to: navigation, search

Introduction

This projects aims to allow adding of custom data to vertices, edges, faces, and unify handling of data attached to them, like UV's and colors. This will be first implemented for meshes, but should be general enough to also work for lattices, NURBS, ..

Below is the code documentation on the current implementation.

Brecht

Custom Element Data

The CustomData API provides a generic way to manipulate per-element custom data in meshes. Each unit of custom data is called a layer. Each layer is identified by a type, which has a size and a set of data-handling functions associated with it.

This makes it possible to add new data layers to meshes without making modifications all over the Blender code. Note that this API does not make it possible for external plugins or python scripts to define their own layer types. A special ID Property layer type could be implemented for that to work.

The CustomData module was implemented by Ben Batt for the Modifier Stack Upgrade, and later extended by Brecht Van Lommel for use beyond the the modifier stack and DerivedMesh. Much of the content has been taken from the modifier stack upgrade documentation.

Functionality

Storage

Custom Data may be stored in two ways. The first is in a separate array for each type of layer, which is used in most cases. In edit mode, the data is stored in a memory block per element. The API for the latter type of storage is more limited, as it is used only for one specific case.

Adding Layers

When adding a layer, the arrays can be allocated or assigned in different ways:

  • Assign: assign a given pointer is used.
  • Calloc: allocate blank memory.
  • Default: allocate memory and set it to the default value.
  • Reference: assign a given pointer, but dont free it.
  • Duplicate: duplicate the memory of a given pointer.

Besides that there are also a number of flags that can be set:

  • No Copy: don't copy this layer, e.g. restricting the layers copied in the modifier stack.
  • No Free: don't free the array of this layer.
  • Temporary: for derivedmesh, layer is only temporary, and will be freed on derivedmesh release.
Copying and Merging

The API provides a way to copy or merge an existing CustomData struct into another, specifying a different layer size. The new layer data may be allocated with the same options as for adding layers. A mask is also provided that indicates which layer types should be or shouldn't be copied.

Editing Custom Data

For array storage reading or writing the contents of a particular layer may be done directly on the array. For reading or writing in edit mode storage, a pointer to the data for an element is returned by the API. There are also functions for copying or interpolating the data from all layers from one collection of layers to another. See the function reference

Usage

Custom Data in Mesh

The Mesh datablock uses the CustomData API to store all per-element layers, including the vertices, edges and faces themselfs. Adding, freeing, copying, and interpolating layers must be done through the CustomData API.

typedef struct Mesh {
   ...
    struct MFace *mface;
    struct MTFace *mtface;
    struct MVert *mvert;
    struct MEdge *medge;
    struct MDeformVert *dvert;
    struct MCol *mcol;
    struct MSticky *msticky;
   ...    
    struct CustomData vdata, edata, fdata;
    ...
} Mesh;

A CustomData struct for vertices, edges and faces contains a list of data layers. The Mesh struct contains duplicates of some of the pointers to the data, and these must be kept consistent with the pointers in the CustomData struct at all times. Using mesh_update_customdata_pointers these pointers can be updated to point to the data as stored in the CustomData structs.

Mesh Custom Data Storage

Custom Data in DerivedMesh

DerivedMesh also uses the CustomData API. All DerivedMesh backends stores the data associated with vertices, edges and faces in a CustomData struct, but only the CDDerivedMesh backend stores the vertices, edges and faces themselves there.

Custom Data in EditMesh

EditMesh stores its custom data in blocks per EditVert, EditEdge and EditFace. The special CustomData_em functions must be used to work with custom data in edit mode.

EditMesh Custom Data Storage

Internals

CustomData structure

The CustomData structure stores a collection of custom data layers associated with a mesh element type.

typedef struct CustomData {
    CustomDataLayer *layers;  /* CustomDataLayers, ordered by type */
    int totlayer, maxlayer;   /* number of layers, size of layers array */
    int totsize, pad;         /* in editmode, total size of all data layers */
} CustomData;
CustomDataLayer structure
typedef struct CustomDataLayer {
    int type;       /* type of data in layer */
    int offset;     /* in editmode, offset of layer in block */
    int flag;       /* general purpose flag */
    int active;     /* number of the active layer of this type */
    void *data;     /* layer data */
} CustomDataLayer;
Layer Type Info

The CustomDataLayer.type field is passed to the layerType_getInfo function, where it is used as an index into the LAYERTYPEINFO array (an array of LayerTypeInfo structures). It can hold any value of any of the defined types, such as CD_MVERT, CD_MDEFORMVERT or CD_MTFACE.

LayerTypeInfo structure

The LayerTypeInfo structure holds information about a layer type, including its size and various functions which are used to access or modify it. The LAYERTYPEINFO array is a static array of LayerTypeInfo structures indexed by type ID and accessed via layerType_getInfo(). When a new layer type is added, a new entry must be added to this array, giving the size and data manipulation functions (if needed) for the new type.

typedef struct LayerTypeInfo {
   int size; /* the memory size of one element of this layer's data */
 
   /* a function to copy count elements of this layer's data
    * (deep copy if appropriate)
    * size should be the size of one element of this layer's data (e.g.
    * LayerTypeInfo.size)
    */
   void (*copy)(const void *source, void *dest, int count, int size);
 
   /* a function to free any dynamically allocated components of this
    * layer's data (note the data pointer itself should not be freed)
    * size should be the size of one element of this layer's data (e.g.
    * LayerTypeInfo.size)
    */
   void (*free)(void *data, int count, int size);
 
   /* a function to interpolate between count source elements of this
    * layer's data and store the result in dest
    * if weights == NULL or sub_weights == NULL, they should default to 1
    *
    * weights gives the weight for each element in sources
    * sub_weights gives the sub-element weights for each element in sources
    *    (there should be (sub element count)^2 weights per element)
    * count gives the number of elements in sources
    */
   void (*interp)(void **sources, float *weights, float *sub_weights,
                  int count, void *dest);
 
    /* a function to swap the data in corners of the element */
    void (*swap)(void *data, int *corner_indices);
 
    /* a function to set a layer's data to default values. if NULL, the
       default is assumed to be all zeros */
    void (*set_default)(void *data, int count);
} LayerTypeInfo;
 
const LayerTypeInfo LAYERTYPEINFO[LAYERTYPE_NUMTYPES] = {
   {sizeof(MVert), NULL, NULL, NULL, NULL, NULL},
   ...
    {sizeof(MDeformVert), "MDeformVert", 1, layerCopy_mdeformvert,
     layerFree_mdeformvert, layerInterp_mdeformvert, NULL, NULL},
   ...
    {sizeof(MTFace), "MTFace", 1, layerCopy_tface, NULL, layerInterp_tface,
     layerSwap_tface, layerDefault_tface},
   ...
};
 
const LayerTypeInfo *layerType_getInfo(int type);

Function Reference

These functions provide a generic interface to a custom element data stack.

void CustomData_copy
 void CustomData_copy(const CustomData *source, CustomData *dest,
                     CustomDataMask mask, int alloctype, int totelem);

Initialises a CustomData object with the same layer setup as source, and totelem elements.

  • Mask is a bitfield where (mask & (1 << (layer type))) indicates if a layer should be copied or not.
  • Alloctype must be one of the above.
void CustomData_merge
 void CustomData_merge(const CustomData *source, CustomData *dest,
                      CustomDataMask mask, int alloctype, int totelem);

Same as the above, except that this will preserve existing layers, and only add the layers that were not there yet.

void CustomData_free
 void CustomData_free(CustomData *data, int totelem);

Frees data associated with a CustomData object (doesn't free the object itself, though).

void CustomData_free_temporary
 void CustomData_free_temporary(CustomData *data, int totelem);

Frees all layers with CD_FLAG_TEMPORARY.

void *CustomData_add_layer
 void *CustomData_add_layer(CustomData *data, int type, int alloctype,
                           void *layer, int totelem);

Adds a data layer of the given type to the CustomData object, optionally backed by an external data array. The different allocation types are as defined above.

  • Returns the data of the layer.
  • In editmode, use EM_add_data_layer instead of this function.
int CustomData_free_layer
 int CustomData_free_layer(CustomData *data, int type, int totelem);

Frees the active or first data layer with the give type.

  • Returns 1 on succes, 0 if no layer with the given type is found
  • In editmode, use EM_free_data_layer instead of this function
void CustomData_free_layers
 void CustomData_free_layers(CustomData *data, int type, int totelem);

Same as above, but free all layers with type.

int CustomData_has_layer
 int CustomData_has_layer(const CustomData *data, int type);
  • Returns 1 if a layer with the specified type exists.
int CustomData_number_of_layers
 int CustomData_number_of_layers(const CustomData *data, int type);
  • Returns the number of layers with this type.
void *CustomData_duplicate_referenced_layer
 void *CustomData_duplicate_referenced_layer(CustomData *data, int type);
  • Duplicate data of a layer with flag CD_FLAG_NOFREE, and remove that flag.
void CustomData_set_only_copy
 void CustomData_set_only_copy(const CustomData *data,
                              CustomDataMask mask);

Set the CD_FLAG_NOCOPY flag in custom data layers where the mask is zero for the layer type, so only layer types specified by the mask will be copied.

void CustomData_copy_data
 void CustomData_copy_data(const CustomData *source,
                          CustomData *dest, int source_index,
                          int dest_index, int count);
 void CustomData_em_copy_data(const CustomData *source,
                            CustomData *dest, void *src_block,
                            void **dest_block);

Copies data from one CustomData object to another objects need not be compatible, each source layer is copied to the first dest layer of correct type (if there is none, the layer is skipped).

void CustomData_free_elem
 void CustomData_free_elem(CustomData *data, int index, int count);

Frees count data elements on each layer starting at index in a CustomData object.

void CustomData_interp
 void CustomData_interp(const CustomData *source, CustomData *dest,
                      int *src_indices, float *weights, float *sub_weights,
                      int count, int dest_index);
 void CustomData_em_interp(CustomData *data,  void **src_blocks,
                          float *weights, float *sub_weights, int count,
                          void *dest_block);

Interpolates data from one CustomData object to another.

  • The objects need not be compatible, each source layer is interpolated to the first dest layer of the correct type (if there is none, the layer is skipped).
  • If weights == NULL or sub_weights == NULL, they default to all 1's.
  • src_indices gives the source elements to interpolate from.
  • weights gives the weight for each source element.
  • sub_weights is an array of matrices of weights for sub-elements (matrices should be source->subElems * source->subElems in size).
  • count gives the number of source elements to interpolate from.
  • dest_index gives the dest element to write the interpolated value to.
void *CustomData_get
 void *CustomData_get(const CustomData *data, int index, int type);
 void *CustomData_em_get(const CustomData *data, void *block, int type);

Gets a pointer to the data element at index from the first or active layer of the specified type.

  • Returns NULL if there is no layer of the specified type.
void *CustomData_get_layer
 void *CustomData_get_layer(const CustomData *data, int type);
 void *CustomData_get_layer_n(const CustomData *data, int type, int n);

Gets a pointer to the first/active or nth layer of the specified type.

  • Returns NULL if there is no layer of the specified type.
void CustomData_set
 void CustomData_set(const CustomData *data, int index, int type, void *source);
 void CustomData_em_set(struct CustomData *data, void *block, int type,
                       void *source);

Copies the data from source to the data element at index in the first layer of the specified type.

  • No effect if there is no layer of the specified type.
void CustomData_set_layer*
 void *CustomData_set_layer(const CustomData *data, int type, void *ptr);
 void CustomData_set_layer_active(CustomData *data, int type, int n);
 void CustomData_set_layer_flag(CustomData *data, int type, int flag);
  • Set the pointer of to the first layer of type. the old data is not freed.
  • Sets the nth layer of type as active.
  • Adds flag to the layer flags.
void CustomData_to/from_em_block
 void CustomData_to_em_block(const CustomData *source,
                            CustomData *dest, int index, void **block);
 void CustomData_from_em_block(const CustomData *source,
                              CustomData *dest, void *block, int index);

Copy to and from editmesh storage.