diff options
Diffstat (limited to 'source/blender/blenkernel/intern/modifier.c')
-rw-r--r-- | source/blender/blenkernel/intern/modifier.c | 507 |
1 files changed, 459 insertions, 48 deletions
diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c index 6185644f5a0..3c8b685a0e0 100644 --- a/source/blender/blenkernel/intern/modifier.c +++ b/source/blender/blenkernel/intern/modifier.c @@ -24,6 +24,9 @@ * \ingroup bke */ +/* Allow using deprecated functionality for .blend file I/O. */ +#define DNA_DEPRECATED_ALLOW + #include <float.h> #include <math.h> #include <stdarg.h> @@ -34,10 +37,15 @@ #include "MEM_guardedalloc.h" #include "DNA_armature_types.h" +#include "DNA_cloth_types.h" +#include "DNA_fluid_types.h" #include "DNA_gpencil_modifier_types.h" #include "DNA_mesh_types.h" +#include "DNA_object_fluidsim_types.h" +#include "DNA_object_force_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "DNA_screen_types.h" #include "BLI_linklist.h" #include "BLI_listbase.h" @@ -53,6 +61,8 @@ #include "BKE_appdir.h" #include "BKE_editmesh.h" #include "BKE_editmesh_cache.h" +#include "BKE_effect.h" +#include "BKE_fluid.h" #include "BKE_global.h" #include "BKE_gpencil_modifier.h" #include "BKE_idtype.h" @@ -63,6 +73,7 @@ #include "BKE_mesh_wrapper.h" #include "BKE_multires.h" #include "BKE_object.h" +#include "BKE_pointcache.h" /* may move these, only for BKE_modifier_path_relbase */ #include "BKE_main.h" @@ -73,6 +84,8 @@ #include "MOD_modifiertypes.h" +#include "BLO_read_write.h" + #include "CLG_log.h" static CLG_LogRef LOG = {"bke.modifier"}; @@ -130,6 +143,11 @@ void BKE_modifier_type_panel_id(ModifierType type, char *r_idname) strcat(r_idname, mti->name); } +void BKE_modifier_panel_expand(ModifierData *md) +{ + md->ui_expand_flag |= UI_PANEL_DATA_EXPAND_ROOT; +} + /***/ ModifierData *BKE_modifier_new(int type) @@ -177,9 +195,6 @@ void BKE_modifier_free_ex(ModifierData *md, const int flag) if (mti->foreachIDLink) { mti->foreachIDLink(md, NULL, modifier_free_data_id_us_cb, NULL); } - else if (mti->foreachObjectLink) { - mti->foreachObjectLink(md, NULL, (ObjectWalkFunc)modifier_free_data_id_us_cb, NULL); - } } if (mti->freeData) { @@ -247,15 +262,12 @@ bool BKE_modifier_is_preview(ModifierData *md) ModifierData *BKE_modifiers_findby_type(Object *ob, ModifierType type) { - ModifierData *md = ob->modifiers.first; - - for (; md; md = md->next) { + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { if (md->type == type) { - break; + return md; } } - - return md; + return NULL; } ModifierData *BKE_modifiers_findby_name(Object *ob, const char *name) @@ -265,55 +277,28 @@ ModifierData *BKE_modifiers_findby_name(Object *ob, const char *name) void BKE_modifiers_clear_errors(Object *ob) { - ModifierData *md = ob->modifiers.first; - /* int qRedraw = 0; */ - - for (; md; md = md->next) { + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { if (md->error) { MEM_freeN(md->error); md->error = NULL; - - /* qRedraw = 1; */ - } - } -} - -void BKE_modifiers_foreach_object_link(Object *ob, ObjectWalkFunc walk, void *userData) -{ - ModifierData *md = ob->modifiers.first; - - for (; md; md = md->next) { - const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); - - if (mti->foreachObjectLink) { - mti->foreachObjectLink(md, ob, walk, userData); } } } void BKE_modifiers_foreach_ID_link(Object *ob, IDWalkFunc walk, void *userData) { - ModifierData *md = ob->modifiers.first; - - for (; md; md = md->next) { + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); if (mti->foreachIDLink) { mti->foreachIDLink(md, ob, walk, userData); } - else if (mti->foreachObjectLink) { - /* each Object can masquerade as an ID, so this should be OK */ - ObjectWalkFunc fp = (ObjectWalkFunc)walk; - mti->foreachObjectLink(md, ob, fp, userData); - } } } void BKE_modifiers_foreach_tex_link(Object *ob, TexWalkFunc walk, void *userData) { - ModifierData *md = ob->modifiers.first; - - for (; md; md = md->next) { + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); if (mti->foreachTexLink) { @@ -374,9 +359,6 @@ void BKE_modifier_copydata_ex(ModifierData *md, ModifierData *target, const int if (mti->foreachIDLink) { mti->foreachIDLink(target, NULL, modifier_copy_data_id_us_cb, NULL); } - else if (mti->foreachObjectLink) { - mti->foreachObjectLink(target, NULL, (ObjectWalkFunc)modifier_copy_data_id_us_cb, NULL); - } } if (flag & LIB_ID_CREATE_NO_MAIN) { @@ -425,7 +407,7 @@ bool BKE_modifier_is_non_geometrical(ModifierData *md) return (mti->type == eModifierTypeType_NonGeometrical); } -void BKE_modifier_set_error(ModifierData *md, const char *_format, ...) +void BKE_modifier_set_error(const Object *ob, ModifierData *md, const char *_format, ...) { char buffer[512]; va_list ap; @@ -442,7 +424,16 @@ void BKE_modifier_set_error(ModifierData *md, const char *_format, ...) md->error = BLI_strdup(buffer); - CLOG_STR_ERROR(&LOG, md->error); +#ifndef NDEBUG + if ((md->mode & eModifierMode_Virtual) == 0) { + /* Ensure correct object is passed in. */ + const Object *ob_orig = (Object *)DEG_get_original_id((ID *)&ob->id); + const ModifierData *md_orig = md->orig_modifier_data ? md->orig_modifier_data : md; + BLI_assert(BLI_findindex(&ob_orig->modifiers, md_orig) != -1); + } +#endif + + CLOG_ERROR(&LOG, "Object: \"%s\", Modifier: \"%s\", %s", ob->id.name + 2, md->name, md->error); } /* used for buttons, to find out if the 'draw deformed in editmode' option is @@ -461,7 +452,6 @@ int BKE_modifiers_get_cage_index(struct Scene *scene, ModifierData *md = (is_virtual) ? BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData) : ob->modifiers.first; - int i, cageIndex = -1; if (r_lastPossibleCageIndex) { /* ensure the value is initialized */ @@ -469,7 +459,8 @@ int BKE_modifiers_get_cage_index(struct Scene *scene, } /* Find the last modifier acting on the cage. */ - for (i = 0; md; i++, md = md->next) { + int cageIndex = -1; + for (int i = 0; md; i++, md = md->next) { const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); bool supports_mapping; @@ -962,7 +953,7 @@ const char *BKE_modifier_path_relbase(Main *bmain, Object *ob) return ID_BLEND_PATH(bmain, &ob->id); } - /* last resort, better then using "" which resolves to the current + /* last resort, better than using "" which resolves to the current * working directory */ return BKE_tempdir_session(); } @@ -973,7 +964,7 @@ const char *BKE_modifier_path_relbase_from_global(Object *ob) return ID_BLEND_PATH_FROM_GLOBAL(&ob->id); } - /* last resort, better then using "" which resolves to the current + /* last resort, better than using "" which resolves to the current * working directory */ return BKE_tempdir_session(); } @@ -1137,3 +1128,423 @@ void BKE_modifier_check_uuids_unique_and_report(const Object *object) BLI_gset_free(used_uuids, NULL); } + +void BKE_modifier_blend_write(BlendWriter *writer, ListBase *modbase) +{ + if (modbase == NULL) { + return; + } + + LISTBASE_FOREACH (ModifierData *, md, modbase) { + const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); + if (mti == NULL) { + return; + } + + BLO_write_struct_by_name(writer, mti->structName, md); + + if (md->type == eModifierType_Cloth) { + ClothModifierData *clmd = (ClothModifierData *)md; + + BLO_write_struct(writer, ClothSimSettings, clmd->sim_parms); + BLO_write_struct(writer, ClothCollSettings, clmd->coll_parms); + BLO_write_struct(writer, EffectorWeights, clmd->sim_parms->effector_weights); + BKE_ptcache_blend_write(writer, &clmd->ptcaches); + } + else if (md->type == eModifierType_Fluid) { + FluidModifierData *fmd = (FluidModifierData *)md; + + if (fmd->type & MOD_FLUID_TYPE_DOMAIN) { + BLO_write_struct(writer, FluidDomainSettings, fmd->domain); + + if (fmd->domain) { + BKE_ptcache_blend_write(writer, &(fmd->domain->ptcaches[0])); + + /* create fake pointcache so that old blender versions can read it */ + fmd->domain->point_cache[1] = BKE_ptcache_add(&fmd->domain->ptcaches[1]); + fmd->domain->point_cache[1]->flag |= PTCACHE_DISK_CACHE | PTCACHE_FAKE_SMOKE; + fmd->domain->point_cache[1]->step = 1; + + BKE_ptcache_blend_write(writer, &(fmd->domain->ptcaches[1])); + + if (fmd->domain->coba) { + BLO_write_struct(writer, ColorBand, fmd->domain->coba); + } + + /* cleanup the fake pointcache */ + BKE_ptcache_free_list(&fmd->domain->ptcaches[1]); + fmd->domain->point_cache[1] = NULL; + + BLO_write_struct(writer, EffectorWeights, fmd->domain->effector_weights); + } + } + else if (fmd->type & MOD_FLUID_TYPE_FLOW) { + BLO_write_struct(writer, FluidFlowSettings, fmd->flow); + } + else if (fmd->type & MOD_FLUID_TYPE_EFFEC) { + BLO_write_struct(writer, FluidEffectorSettings, fmd->effector); + } + } + else if (md->type == eModifierType_Fluidsim) { + FluidsimModifierData *fluidmd = (FluidsimModifierData *)md; + + BLO_write_struct(writer, FluidsimSettings, fluidmd->fss); + } + else if (md->type == eModifierType_DynamicPaint) { + DynamicPaintModifierData *pmd = (DynamicPaintModifierData *)md; + + if (pmd->canvas) { + BLO_write_struct(writer, DynamicPaintCanvasSettings, pmd->canvas); + + /* write surfaces */ + LISTBASE_FOREACH (DynamicPaintSurface *, surface, &pmd->canvas->surfaces) { + BLO_write_struct(writer, DynamicPaintSurface, surface); + } + /* write caches and effector weights */ + LISTBASE_FOREACH (DynamicPaintSurface *, surface, &pmd->canvas->surfaces) { + BKE_ptcache_blend_write(writer, &(surface->ptcaches)); + + BLO_write_struct(writer, EffectorWeights, surface->effector_weights); + } + } + if (pmd->brush) { + BLO_write_struct(writer, DynamicPaintBrushSettings, pmd->brush); + BLO_write_struct(writer, ColorBand, pmd->brush->paint_ramp); + BLO_write_struct(writer, ColorBand, pmd->brush->vel_ramp); + } + } + else if (md->type == eModifierType_Collision) { + +#if 0 + CollisionModifierData *collmd = (CollisionModifierData *)md; + // TODO: CollisionModifier should use pointcache + // + have proper reset events before enabling this + writestruct(wd, DATA, MVert, collmd->numverts, collmd->x); + writestruct(wd, DATA, MVert, collmd->numverts, collmd->xnew); + writestruct(wd, DATA, MFace, collmd->numfaces, collmd->mfaces); +#endif + } + + if (mti->blendWrite != NULL) { + mti->blendWrite(writer, md); + } + } +} + +/* TODO(sergey): Find a better place for this. + * + * Unfortunately, this can not be done as a regular do_versions() since the modifier type is + * set to NONE, so the do_versions code wouldn't know where the modifier came from. + * + * The best approach seems to have the functionality in versioning_280.c but still call the + * function from #BKE_modifier_blend_read_data(). + */ + +/* Domain, inflow, ... */ +static void modifier_ensure_type(FluidModifierData *fluid_modifier_data, int type) +{ + fluid_modifier_data->type = type; + BKE_fluid_modifier_free(fluid_modifier_data); + BKE_fluid_modifier_create_type_data(fluid_modifier_data); +} + +/** + * \note The old_modifier_data is NOT linked. + * This means that in order to access sub-data pointers #BLO_read_get_new_data_address is to be + * used. + */ +static ModifierData *modifier_replace_with_fluid(BlendDataReader *reader, + Object *object, + ListBase *modifiers, + ModifierData *old_modifier_data) +{ + ModifierData *new_modifier_data = BKE_modifier_new(eModifierType_Fluid); + FluidModifierData *fluid_modifier_data = (FluidModifierData *)new_modifier_data; + + if (old_modifier_data->type == eModifierType_Fluidsim) { + FluidsimModifierData *old_fluidsim_modifier_data = (FluidsimModifierData *)old_modifier_data; + FluidsimSettings *old_fluidsim_settings = BLO_read_get_new_data_address( + reader, old_fluidsim_modifier_data->fss); + switch (old_fluidsim_settings->type) { + case OB_FLUIDSIM_ENABLE: + modifier_ensure_type(fluid_modifier_data, 0); + break; + case OB_FLUIDSIM_DOMAIN: + modifier_ensure_type(fluid_modifier_data, MOD_FLUID_TYPE_DOMAIN); + BKE_fluid_domain_type_set(object, fluid_modifier_data->domain, FLUID_DOMAIN_TYPE_LIQUID); + break; + case OB_FLUIDSIM_FLUID: + modifier_ensure_type(fluid_modifier_data, MOD_FLUID_TYPE_FLOW); + BKE_fluid_flow_type_set(object, fluid_modifier_data->flow, FLUID_FLOW_TYPE_LIQUID); + /* No need to emit liquid far away from surface. */ + fluid_modifier_data->flow->surface_distance = 0.0f; + break; + case OB_FLUIDSIM_OBSTACLE: + modifier_ensure_type(fluid_modifier_data, MOD_FLUID_TYPE_EFFEC); + BKE_fluid_effector_type_set( + object, fluid_modifier_data->effector, FLUID_EFFECTOR_TYPE_COLLISION); + break; + case OB_FLUIDSIM_INFLOW: + modifier_ensure_type(fluid_modifier_data, MOD_FLUID_TYPE_FLOW); + BKE_fluid_flow_type_set(object, fluid_modifier_data->flow, FLUID_FLOW_TYPE_LIQUID); + BKE_fluid_flow_behavior_set(object, fluid_modifier_data->flow, FLUID_FLOW_BEHAVIOR_INFLOW); + /* No need to emit liquid far away from surface. */ + fluid_modifier_data->flow->surface_distance = 0.0f; + break; + case OB_FLUIDSIM_OUTFLOW: + modifier_ensure_type(fluid_modifier_data, MOD_FLUID_TYPE_FLOW); + BKE_fluid_flow_type_set(object, fluid_modifier_data->flow, FLUID_FLOW_TYPE_LIQUID); + BKE_fluid_flow_behavior_set( + object, fluid_modifier_data->flow, FLUID_FLOW_BEHAVIOR_OUTFLOW); + break; + case OB_FLUIDSIM_PARTICLE: + /* "Particle" type objects not being used by Mantaflow fluid simulations. + * Skip this object, secondary particles can only be enabled through the domain object. */ + break; + case OB_FLUIDSIM_CONTROL: + /* "Control" type objects not being used by Mantaflow fluid simulations. + * Use guiding type instead which is similar. */ + modifier_ensure_type(fluid_modifier_data, MOD_FLUID_TYPE_EFFEC); + BKE_fluid_effector_type_set( + object, fluid_modifier_data->effector, FLUID_EFFECTOR_TYPE_GUIDE); + break; + } + } + else if (old_modifier_data->type == eModifierType_Smoke) { + SmokeModifierData *old_smoke_modifier_data = (SmokeModifierData *)old_modifier_data; + modifier_ensure_type(fluid_modifier_data, old_smoke_modifier_data->type); + if (fluid_modifier_data->type == MOD_FLUID_TYPE_DOMAIN) { + BKE_fluid_domain_type_set(object, fluid_modifier_data->domain, FLUID_DOMAIN_TYPE_GAS); + } + else if (fluid_modifier_data->type == MOD_FLUID_TYPE_FLOW) { + BKE_fluid_flow_type_set(object, fluid_modifier_data->flow, FLUID_FLOW_TYPE_SMOKE); + } + else if (fluid_modifier_data->type == MOD_FLUID_TYPE_EFFEC) { + BKE_fluid_effector_type_set( + object, fluid_modifier_data->effector, FLUID_EFFECTOR_TYPE_COLLISION); + } + } + + /* Replace modifier data in the stack. */ + new_modifier_data->next = old_modifier_data->next; + new_modifier_data->prev = old_modifier_data->prev; + if (new_modifier_data->prev != NULL) { + new_modifier_data->prev->next = new_modifier_data; + } + if (new_modifier_data->next != NULL) { + new_modifier_data->next->prev = new_modifier_data; + } + if (modifiers->first == old_modifier_data) { + modifiers->first = new_modifier_data; + } + if (modifiers->last == old_modifier_data) { + modifiers->last = new_modifier_data; + } + + /* Free old modifier data. */ + MEM_freeN(old_modifier_data); + + return new_modifier_data; +} + +void BKE_modifier_blend_read_data(BlendDataReader *reader, ListBase *lb, Object *ob) +{ + BLO_read_list(reader, lb); + + LISTBASE_FOREACH (ModifierData *, md, lb) { + BKE_modifier_session_uuid_generate(md); + + md->error = NULL; + md->runtime = NULL; + + /* Modifier data has been allocated as a part of data migration process and + * no reading of nested fields from file is needed. */ + bool is_allocated = false; + + if (md->type == eModifierType_Fluidsim) { + BLO_reportf_wrap( + BLO_read_data_reports(reader), + RPT_WARNING, + TIP_("Possible data loss when saving this file! %s modifier is deprecated (Object: %s)"), + md->name, + ob->id.name + 2); + md = modifier_replace_with_fluid(reader, ob, lb, md); + is_allocated = true; + } + else if (md->type == eModifierType_Smoke) { + BLO_reportf_wrap( + BLO_read_data_reports(reader), + RPT_WARNING, + TIP_("Possible data loss when saving this file! %s modifier is deprecated (Object: %s)"), + md->name, + ob->id.name + 2); + md = modifier_replace_with_fluid(reader, ob, lb, md); + is_allocated = true; + } + + const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); + + /* if modifiers disappear, or for upward compatibility */ + if (mti == NULL) { + md->type = eModifierType_None; + } + + if (is_allocated) { + /* All the fields has been properly allocated. */ + } + else if (md->type == eModifierType_Cloth) { + ClothModifierData *clmd = (ClothModifierData *)md; + + clmd->clothObject = NULL; + clmd->hairdata = NULL; + + BLO_read_data_address(reader, &clmd->sim_parms); + BLO_read_data_address(reader, &clmd->coll_parms); + + BKE_ptcache_blend_read_data(reader, &clmd->ptcaches, &clmd->point_cache, 0); + + if (clmd->sim_parms) { + if (clmd->sim_parms->presets > 10) { + clmd->sim_parms->presets = 0; + } + + clmd->sim_parms->reset = 0; + + BLO_read_data_address(reader, &clmd->sim_parms->effector_weights); + + if (!clmd->sim_parms->effector_weights) { + clmd->sim_parms->effector_weights = BKE_effector_add_weights(NULL); + } + } + + clmd->solver_result = NULL; + } + else if (md->type == eModifierType_Fluid) { + + FluidModifierData *fmd = (FluidModifierData *)md; + + if (fmd->type == MOD_FLUID_TYPE_DOMAIN) { + fmd->flow = NULL; + fmd->effector = NULL; + BLO_read_data_address(reader, &fmd->domain); + fmd->domain->fmd = fmd; + + fmd->domain->fluid = NULL; + fmd->domain->fluid_mutex = BLI_rw_mutex_alloc(); + fmd->domain->tex_density = NULL; + fmd->domain->tex_color = NULL; + fmd->domain->tex_shadow = NULL; + fmd->domain->tex_flame = NULL; + fmd->domain->tex_flame_coba = NULL; + fmd->domain->tex_coba = NULL; + fmd->domain->tex_field = NULL; + fmd->domain->tex_velocity_x = NULL; + fmd->domain->tex_velocity_y = NULL; + fmd->domain->tex_velocity_z = NULL; + fmd->domain->tex_wt = NULL; + fmd->domain->mesh_velocities = NULL; + BLO_read_data_address(reader, &fmd->domain->coba); + + BLO_read_data_address(reader, &fmd->domain->effector_weights); + if (!fmd->domain->effector_weights) { + fmd->domain->effector_weights = BKE_effector_add_weights(NULL); + } + + BKE_ptcache_blend_read_data( + reader, &(fmd->domain->ptcaches[0]), &(fmd->domain->point_cache[0]), 1); + + /* Manta sim uses only one cache from now on, so store pointer convert */ + if (fmd->domain->ptcaches[1].first || fmd->domain->point_cache[1]) { + if (fmd->domain->point_cache[1]) { + PointCache *cache = BLO_read_get_new_data_address(reader, fmd->domain->point_cache[1]); + if (cache->flag & PTCACHE_FAKE_SMOKE) { + /* Manta-sim/smoke was already saved in "new format" and this cache is a fake one. */ + } + else { + printf( + "High resolution manta cache not available due to pointcache update. Please " + "reset the simulation.\n"); + } + BKE_ptcache_free(cache); + } + BLI_listbase_clear(&fmd->domain->ptcaches[1]); + fmd->domain->point_cache[1] = NULL; + } + } + else if (fmd->type == MOD_FLUID_TYPE_FLOW) { + fmd->domain = NULL; + fmd->effector = NULL; + BLO_read_data_address(reader, &fmd->flow); + fmd->flow->fmd = fmd; + fmd->flow->mesh = NULL; + fmd->flow->verts_old = NULL; + fmd->flow->numverts = 0; + BLO_read_data_address(reader, &fmd->flow->psys); + } + else if (fmd->type == MOD_FLUID_TYPE_EFFEC) { + fmd->flow = NULL; + fmd->domain = NULL; + BLO_read_data_address(reader, &fmd->effector); + if (fmd->effector) { + fmd->effector->fmd = fmd; + fmd->effector->verts_old = NULL; + fmd->effector->numverts = 0; + fmd->effector->mesh = NULL; + } + else { + fmd->type = 0; + fmd->flow = NULL; + fmd->domain = NULL; + fmd->effector = NULL; + } + } + } + else if (md->type == eModifierType_DynamicPaint) { + DynamicPaintModifierData *pmd = (DynamicPaintModifierData *)md; + + if (pmd->canvas) { + BLO_read_data_address(reader, &pmd->canvas); + pmd->canvas->pmd = pmd; + pmd->canvas->flags &= ~MOD_DPAINT_BAKING; /* just in case */ + + if (pmd->canvas->surfaces.first) { + BLO_read_list(reader, &pmd->canvas->surfaces); + + LISTBASE_FOREACH (DynamicPaintSurface *, surface, &pmd->canvas->surfaces) { + surface->canvas = pmd->canvas; + surface->data = NULL; + BKE_ptcache_blend_read_data(reader, &(surface->ptcaches), &(surface->pointcache), 1); + + BLO_read_data_address(reader, &surface->effector_weights); + if (surface->effector_weights == NULL) { + surface->effector_weights = BKE_effector_add_weights(NULL); + } + } + } + } + if (pmd->brush) { + BLO_read_data_address(reader, &pmd->brush); + pmd->brush->pmd = pmd; + BLO_read_data_address(reader, &pmd->brush->psys); + BLO_read_data_address(reader, &pmd->brush->paint_ramp); + BLO_read_data_address(reader, &pmd->brush->vel_ramp); + } + } + + if (mti->blendRead != NULL) { + mti->blendRead(reader, md); + } + } +} + +void BKE_modifier_blend_read_lib(BlendLibReader *reader, Object *ob) +{ + BKE_modifiers_foreach_ID_link(ob, BKE_object_modifiers_lib_link_common, reader); + + /* If linking from a library, clear 'local' library override flag. */ + if (ob->id.lib != NULL) { + LISTBASE_FOREACH (ModifierData *, mod, &ob->modifiers) { + mod->flag &= ~eModifierFlag_OverrideLibrary_Local; + } + } +} |