In this and following several posts I will write about Blender’s modifier system. I expect following questions to be answered:

  1. How is modifiers’ source code organized in the source tree?
  2. How is a modifier registered to Blender system?
  3. Where and when does a modifier operate?
  4. How are modifiers combined together?

It seems that the last question is too broad. Let me elaborate to make it more specific.

As you know, Blender organizes all modifiers in a stack. Then multiple modifiers can be combined and chained together. Two adjacent modifiers in the stack must agree in a data format so that the processed data from previous modifier can be handed over to the next one. So what is the protocol of data format which is used among different modifiers?

Now let us get started with above questions in mind.

First we start with the function where a modifier is added to an object. Hover the cursor on the Array modifier icon from the Add Modifier menu in the modifiers panel, you can see the Python code in the pop-up: bpy.ops.object.modifier_add(type="ARRAY"). This is an operator named modifier_add. Search this name in the source code you will find the operator registration function OBJECT_OT_modifier_add. And then you can locate the actual execution function of this operator: modifier_add_exec(file source/blender/editors/object/object_modifier.c). We add a break point in modifier_add_exec to trace the construction of a modifier.

Now we try to add an Array modifier and step into the ED_object_modifier_add from modifier_add_exec.

In ED_object_modifier_add,

  1. Information of a modifier is retrieved by calling function modifierType_getInfo(file source/blender/blenkernel/intern/modifier.c) on a given ModifierType(file source/blender/makesdna/DNA_modifier_types.h). This information is stored in struct ModifierTypeInfo(file source/blender/blenkernel/BKE_modifier.h). You can register your own modifiers by providing informations for them in this file.

  2. A new modifier is constructed in function modifier_new(file source/blender/blenkernel/intern/modifier.c). In this function, a ModifierData structure(file source/blender/makesdna/DNA_modifier_types.h) is allocated through MEM_callocN(mti->structSize, mti->structName).

    Note that a “derived” ModifierData is actually allocated here. The allocator uses a structure size recored in ModifierTypeInfo(mti variable in the code), which usually is different from ModifierData’s.

    For Array modifier, we can see its derived ModifierData is named as ArrayModifierData, which is defined in the file source/blender/makesdna/DNA_modifier_types.h.

    After that, the modifier gets initialized through if (mti->initData) mti->initData(md);.

    The function initData is a function pointer you could set in the ModifierTypeInfo structure. By stepping into this function you can reach the definition of Array modifier’s initializer, which resides in the file source/blender/modifiers/intern/MOD_array.c. During the initialization stage, one modifier’s “derived” ModifierData is initialized.

    In the file MOD_array.c there are also many other functions. These functions could be set to the ModifierTypeInfo structure when you define the modifier.

    Along with the file MOD_array.c there are many other files with a prefix of MOD_. These are the sources of other modifiers. They reside in the folder source/blender/modifiers/intern.

Speak in the Object-Oriented glossary, the ModifierTypeInfo is the common interface that modifiers should derive from.

Now the definition and construction of a modifier are finished.

In the next part I will dive into the execution code of the Array modifier.