We have learned how a modifier is constructed in the previous post.

In this post we will learn how a modifier is applied.

We have already known that a modifier defines a derived ModifierTypeInfo structure. Take Array modifier as an example, its ModifierTypeInfo is defined in the file source/blender/modifiers/intern/MOD_array.c.

The applyModifier function in ModifierTypeInfo seems to be the place where actual modifier execution happens. We can set a break point in it and do some tracing.

Following is a call stack for the applyModifier function:

arrayModifier_doArray(ArrayModifierData * amd, Scene * scene, Object * ob, DerivedMesh * dm, ModifierApplyFlag flag)
applyModifier(ModifierData * md, Object * ob, DerivedMesh * dm, ModifierApplyFlag flag)
modwrap_applyModifier(ModifierData * md, Object * ob, DerivedMesh * dm, ModifierApplyFlag flag)
mesh_calc_modifiers(Scene * scene, Object * ob, float[3] * inputVertexCos, DerivedMesh * * deform_r, DerivedMesh * * final_r, int useRenderParams, int useDeform, int needMapping, unsigned __int64 dataMask, int index, int useCache, int build_shapekey_layers)
mesh_build_data(Scene * scene, Object * ob, unsigned __int64 dataMask, int build_shapekey_layers, int needMapping)
makeDerivedMesh(Scene * scene, Object * ob, BMEditMesh * em, unsigned __int64 dataMask, int build_shapekey_layers)
BKE_object_handle_data_update(EvaluationContext * eval_ctx, Scene * scene, Object * ob)
BKE_object_handle_update_ex(EvaluationContext * eval_ctx, Scene * scene, Object * ob, RigidBodyWorld * rbw, const bool do_proxy_update)
scene_update_object_func(TaskPool * pool, void * taskdata, int threadid)
BLI_task_pool_work_and_wait(TaskPool * pool)
scene_update_objects(EvaluationContext * eval_ctx, Main * bmain, Scene * scene, Scene * scene_parent)
scene_update_tagged_recursive(EvaluationContext * eval_ctx, Main * bmain, Scene * scene, Scene * scene_parent)
BKE_scene_update_tagged(EvaluationContext * eval_ctx, Main * bmain, Scene * scene)
wm_event_do_notifiers(bContext * C)
WM_main(bContext * C)
main(int argc, const unsigned char * * UNUSED_argv_c)

In the function mesh_calc_modifiers we see a loop through all modifiers:

for (; md; md = md->next, curr = curr->next) {
    // ...
    ndm = modwrap_applyModifier(md, ob, dm, app_flags);

    if (ndm) {
        /* if the modifier returned a new dm, release the old one */
        if (dm && dm != ndm) dm->release(dm);

        dm = ndm;
        // ...
    // ...

Inside this loop each modifier gets executed in modwrap_applyModifier function.

We notice that a DerivedMesh(file source/blender/blenkernel/BKE_DerivedMesh.h) object(variable dm in the code) is passed to the modifier and then another DerivedMesh object(variable ndm in the code) is returned back to the caller. We can then conclude that the DerivedMesh is the media that gets passed through modifier stack. Here is the document for Derivedmesh.

Also note that the caller of a modifier always own the DerivedMesh object. If we construct a new DerivedMesh in our modifier like what the Array modifier does, the new derived mesh will be moved to the caller, and the old one will be released.

Now we go back to the function arrayModifier_doArray, where actual modifier action happens. This is a long function. I try to provide an overview of what it does.

In the beginning of arrayModifier_doArray it collects options and parameters from the ArrayModifierData(variable amd in following source code). For example:

const bool use_merge = (amd->flags & MOD_ARR_MERGE) != 0;
// ...
const bool use_offset_ob = ((amd->offset_type & MOD_ARR_OFF_OBJ) && amd->offset_ob);
// ...
count = amd->count;

Then it reads in the DerivedMesh which is passed from previous modifier in the modifier stack:

chunk_nverts = dm->getNumVerts(dm);
chunk_nedges = dm->getNumEdges(dm);
chunk_nloops = dm->getNumLoops(dm);
chunk_npolys = dm->getNumPolys(dm);
// ...
src_mvert = dm->getVertArray(dm);

After all information has been ready, a new derived mesh will be constructed:

result = CDDM_from_template(dm, result_nverts, result_nedges, 0, result_nloops, result_npolys);

Then we can write to this new derived mesh:

/* copy customdata to original geometry */
DM_copy_vert_data(dm, result, 0, 0, chunk_nverts);
DM_copy_edge_data(dm, result, 0, 0, chunk_nedges);
DM_copy_loop_data(dm, result, 0, 0, chunk_nloops);
DM_copy_poly_data(dm, result, 0, 0, chunk_npolys);
// ...

Finally the modifier finishes by returning the new derived mesh: return result;.

Now we have learned almost all about Blender modifier system, except that the dependency graph related functions in a modifier implementation. I leave these things to future posts.