diff options
author | Campbell Barton <ideasman42@gmail.com> | 2016-06-23 00:52:58 +0300 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2016-06-23 00:52:58 +0300 |
commit | 3b0a5dd15884aed00d988d73a72f4946673f8be9 (patch) | |
tree | 07f488e24eb2b9f9b3dfce9d15ac66dabd74b7e4 | |
parent | 97ee7f8609bed555ff4bd371b7e25d13b2d6a0d6 (diff) | |
parent | 7547c6a250cd6f36c9894605b822380a1261febf (diff) |
Merge branch 'master' into blender2.8
109 files changed, 2498 insertions, 1514 deletions
diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp index 1a9adb08194..5e53b66f710 100644 --- a/intern/cycles/render/nodes.cpp +++ b/intern/cycles/render/nodes.cpp @@ -581,7 +581,7 @@ void EnvironmentTextureNode::compile(OSLCompiler& compiler) compiler.parameter("color_space", "sRGB"); compiler.parameter(this, "interpolation"); - compiler.parameter(this, "is_float"); + compiler.parameter("is_float", is_float); compiler.parameter("use_alpha", !alpha_out->links.empty()); compiler.add(this, "node_environment_texture"); } diff --git a/source/blender/blenkernel/BKE_DerivedMesh.h b/source/blender/blenkernel/BKE_DerivedMesh.h index 8ccc4a6eb0e..606488f85cc 100644 --- a/source/blender/blenkernel/BKE_DerivedMesh.h +++ b/source/blender/blenkernel/BKE_DerivedMesh.h @@ -694,7 +694,7 @@ DerivedMesh *mesh_create_derived_render( CustomDataMask dataMask); DerivedMesh *getEditDerivedBMesh( - struct BMEditMesh *em, struct Object *ob, + struct BMEditMesh *em, struct Object *ob, CustomDataMask data_mask, float (*vertexCos)[3]); DerivedMesh *mesh_create_derived_index_render( @@ -723,7 +723,7 @@ DerivedMesh *mesh_create_derived_physics( CustomDataMask dataMask); DerivedMesh *editbmesh_get_derived_base( - struct Object *, struct BMEditMesh *em); + struct Object *ob, struct BMEditMesh *em, CustomDataMask data_mask); DerivedMesh *editbmesh_get_derived_cage( struct Scene *scene, struct Object *, struct BMEditMesh *em, CustomDataMask dataMask); diff --git a/source/blender/blenkernel/BKE_animsys.h b/source/blender/blenkernel/BKE_animsys.h index 6524afff051..772e08589c1 100644 --- a/source/blender/blenkernel/BKE_animsys.h +++ b/source/blender/blenkernel/BKE_animsys.h @@ -62,7 +62,7 @@ struct AnimData *BKE_animdata_add_id(struct ID *id); bool BKE_animdata_set_action(struct ReportList *reports, struct ID *id, struct bAction *act); /* Free AnimData */ -void BKE_animdata_free(struct ID *id); +void BKE_animdata_free(struct ID *id, const bool do_id_user); /* Copy AnimData */ struct AnimData *BKE_animdata_copy(struct AnimData *adt, const bool do_action); diff --git a/source/blender/blenkernel/BKE_curve.h b/source/blender/blenkernel/BKE_curve.h index 061270b8b41..23b3128f328 100644 --- a/source/blender/blenkernel/BKE_curve.h +++ b/source/blender/blenkernel/BKE_curve.h @@ -66,7 +66,6 @@ typedef struct CurveCache { #define CU_DO_2DFILL(cu) ((((cu)->flag & CU_3D) == 0) && (((cu)->flag & (CU_FRONT | CU_BACK)) != 0)) /* ** Curve ** */ -void BKE_curve_unlink(struct Curve *cu); void BKE_curve_free(struct Curve *cu); void BKE_curve_editfont_free(struct Curve *cu); void BKE_curve_init(struct Curve *cu); diff --git a/source/blender/blenkernel/BKE_group.h b/source/blender/blenkernel/BKE_group.h index 9056e48cf50..ae6e52b613b 100644 --- a/source/blender/blenkernel/BKE_group.h +++ b/source/blender/blenkernel/BKE_group.h @@ -41,7 +41,6 @@ struct Object; struct Scene; void BKE_group_free(struct Group *group); -void BKE_group_unlink(struct Main *bmain, struct Group *group); struct Group *BKE_group_add(struct Main *bmain, const char *name); struct Group *BKE_group_copy(struct Group *group); bool BKE_group_object_add(struct Group *group, struct Object *ob, struct Scene *scene, struct Base *base); diff --git a/source/blender/blenkernel/BKE_library.h b/source/blender/blenkernel/BKE_library.h index 727818383b1..2e225775af0 100644 --- a/source/blender/blenkernel/BKE_library.h +++ b/source/blender/blenkernel/BKE_library.h @@ -60,13 +60,15 @@ void BKE_libblock_relink(struct ID *id); void BKE_libblock_rename(struct Main *bmain, struct ID *id, const char *name) ATTR_NONNULL(); void BLI_libblock_ensure_unique_name(struct Main *bmain, const char *name) ATTR_NONNULL(); +struct ID *BKE_libblock_find_name_ex(struct Main *bmain, const short type, const char *name) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +struct ID *BKE_libblock_find_name(const short type, const char *name) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); + +/* library_remap.c (keep here since they're general functions) */ void BKE_libblock_free(struct Main *bmain, void *idv) ATTR_NONNULL(); void BKE_libblock_free_ex(struct Main *bmain, void *idv, bool do_id_user) ATTR_NONNULL(); void BKE_libblock_free_us(struct Main *bmain, void *idv) ATTR_NONNULL(); void BKE_libblock_free_data(struct Main *bmain, struct ID *id) ATTR_NONNULL(); - -struct ID *BKE_libblock_find_name_ex(struct Main *bmain, const short type, const char *name) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -struct ID *BKE_libblock_find_name(const short type, const char *name) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +void BKE_libblock_delete(struct Main *bmain, void *idv) ATTR_NONNULL(); void BKE_id_lib_local_paths(struct Main *bmain, struct Library *lib, struct ID *id); void id_lib_extern(struct ID *id); @@ -82,7 +84,6 @@ void id_fake_user_clear(struct ID *id); bool id_make_local(struct ID *id, bool test); bool id_single_user(struct bContext *C, struct ID *id, struct PointerRNA *ptr, struct PropertyRNA *prop); bool id_copy(struct ID *id, struct ID **newid, bool test); -bool id_unlink(struct ID *id, int test); void id_sort_by_name(struct ListBase *lb, struct ID *id); bool new_id(struct ListBase *lb, struct ID *id, const char *name); @@ -119,16 +120,11 @@ void BKE_main_lib_objects_recalc_all(struct Main *bmain); /* (MAX_ID_NAME - 2) + 3 */ void BKE_id_ui_prefix(char name[66 + 1], const struct ID *id); +void BKE_library_free(struct Library *lib); + void BKE_library_make_local( struct Main *bmain, const struct Library *lib, const bool untagged_only, const bool set_fake); -typedef void (*BKE_library_free_window_manager_cb)(struct bContext *, struct wmWindowManager *); -typedef void (*BKE_library_free_notifier_reference_cb)(const void *); -typedef void (*BKE_library_free_editor_id_reference_cb)(const struct ID *); - -void BKE_library_callback_free_window_manager_set(BKE_library_free_window_manager_cb func); -void BKE_library_callback_free_notifier_reference_set(BKE_library_free_notifier_reference_cb func); -void BKE_library_callback_free_editor_id_reference_set(BKE_library_free_editor_id_reference_cb func); /* use when "" is given to new_id() */ #define ID_FALLBACK_NAME N_("Untitled") diff --git a/source/blender/blenkernel/BKE_library_query.h b/source/blender/blenkernel/BKE_library_query.h index 2e73be576f9..c89dce99caa 100644 --- a/source/blender/blenkernel/BKE_library_query.h +++ b/source/blender/blenkernel/BKE_library_query.h @@ -32,6 +32,7 @@ */ struct ID; +struct Main; /* Tips for the callback for cases it's gonna to modify the pointer. */ enum { diff --git a/source/blender/blenkernel/BKE_library_remap.h b/source/blender/blenkernel/BKE_library_remap.h new file mode 100644 index 00000000000..e85a3e60751 --- /dev/null +++ b/source/blender/blenkernel/BKE_library_remap.h @@ -0,0 +1,76 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ +#ifndef __BKE_LIBRARY_REMAP_H__ +#define __BKE_LIBRARY_REMAP_H__ + +/** \file BKE_library_remap.h + * \ingroup bke + */ +#ifdef __cplusplus +extern "C" { +#endif + +#include "BLI_compiler_attrs.h" + +/* BKE_libblock_free, delete are declared in BKE_library.h for convenience. */ + +/* Also IDRemap->flag. */ +enum { + /* Do not remap indirect usages of IDs (that is, when user is some linked data). */ + ID_REMAP_SKIP_INDIRECT_USAGE = 1 << 0, + /* This flag should always be set, *except for 'unlink' scenarios* (only relevant when new_id == NULL). + * Basically, when unset, NEVER_NULL ID usages will keep pointing to old_id, but (if needed) old_id user count + * will still be decremented. This is mandatory for 'delete ID' case, but in all other situation this would lead + * to invalid user counts! */ + ID_REMAP_SKIP_NEVER_NULL_USAGE = 1 << 1, + /* This tells the callback func to flag with LIB_DOIT all IDs using target one with a 'never NULL' pointer + * (like e.g. Object->data). */ + ID_REMAP_FLAG_NEVER_NULL_USAGE = 1 << 2, + /* This tells the callback func to force setting IDs using target one with a 'never NULL' pointer to NULL. + * WARNING! Use with extreme care, this will leave database in broken state! */ + ID_REMAP_FORCE_NEVER_NULL_USAGE = 1 << 3, +}; + +/* Note: Requiring new_id to be non-null, this *may* not be the case ultimately, but makes things simpler for now. */ +void BKE_libblock_remap_locked( + struct Main *bmain, void *old_idv, void *new_idv, + const short remap_flags) ATTR_NONNULL(1, 2); +void BKE_libblock_remap( + struct Main *bmain, void *old_idv, void *new_idv, + const short remap_flags) ATTR_NONNULL(1, 2); + +void BKE_libblock_unlink(struct Main *bmain, void *idv, const bool do_flag_never_null) ATTR_NONNULL(); + +void BKE_libblock_relink_ex(void *idv, void *old_idv, void *new_idv, const bool us_min_never_null) ATTR_NONNULL(1); + + +typedef void (*BKE_library_free_window_manager_cb)(struct bContext *, struct wmWindowManager *); +typedef void (*BKE_library_free_notifier_reference_cb)(const void *); +typedef void (*BKE_library_remap_editor_id_reference_cb)(struct ID *, struct ID *); + +void BKE_library_callback_free_window_manager_set(BKE_library_free_window_manager_cb func); +void BKE_library_callback_free_notifier_reference_set(BKE_library_free_notifier_reference_cb func); +void BKE_library_callback_remap_editor_id_reference_set(BKE_library_remap_editor_id_reference_cb func); + +#ifdef __cplusplus +} +#endif + +#endif /* __BKE_LIBRARY_REMAP_H__ */
\ No newline at end of file diff --git a/source/blender/blenkernel/BKE_linestyle.h b/source/blender/blenkernel/BKE_linestyle.h index e3eead4102c..e343cd29622 100644 --- a/source/blender/blenkernel/BKE_linestyle.h +++ b/source/blender/blenkernel/BKE_linestyle.h @@ -79,8 +79,6 @@ void BKE_linestyle_geometry_modifier_move(FreestyleLineStyle *linestyle, LineSty void BKE_linestyle_modifier_list_color_ramps(FreestyleLineStyle *linestyle, ListBase *listbase); char *BKE_linestyle_path_to_color_ramp(FreestyleLineStyle *linestyle, struct ColorBand *color_ramp); -void BKE_linestyle_target_object_unlink(FreestyleLineStyle *linestyle, struct Object *ob); - bool BKE_linestyle_use_textures(FreestyleLineStyle *linestyle, const bool use_shading_nodes); void BKE_linestyle_default_shader(const struct bContext *C, FreestyleLineStyle *linestyle); diff --git a/source/blender/blenkernel/BKE_mask.h b/source/blender/blenkernel/BKE_mask.h index 25893d54dca..f3d12b5a8cc 100644 --- a/source/blender/blenkernel/BKE_mask.h +++ b/source/blender/blenkernel/BKE_mask.h @@ -125,8 +125,7 @@ struct Mask *BKE_mask_new(struct Main *bmain, const char *name); struct Mask *BKE_mask_copy_nolib(struct Mask *mask); struct Mask *BKE_mask_copy(struct Mask *mask); -void BKE_mask_free_nolib(struct Mask *mask); -void BKE_mask_free(struct Main *bmain, struct Mask *mask); +void BKE_mask_free(struct Mask *mask); void BKE_mask_coord_from_frame(float r_co[2], const float co[2], const float frame_size[2]); void BKE_mask_coord_from_movieclip(struct MovieClip *clip, struct MovieClipUser *user, float r_co[2], const float co[2]); diff --git a/source/blender/blenkernel/BKE_mball.h b/source/blender/blenkernel/BKE_mball.h index 0574b88bef3..b8258455c65 100644 --- a/source/blender/blenkernel/BKE_mball.h +++ b/source/blender/blenkernel/BKE_mball.h @@ -38,7 +38,6 @@ struct Object; struct Scene; struct MetaElem; -void BKE_mball_unlink(struct MetaBall *mb); void BKE_mball_free(struct MetaBall *mb); void BKE_mball_init(struct MetaBall *mb); struct MetaBall *BKE_mball_add(struct Main *bmain, const char *name); diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index d8d869015a3..9330f41d19a 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -81,8 +81,7 @@ int poly_get_adj_loops_from_vert( int BKE_mesh_edge_other_vert(const struct MEdge *e, int v); -void BKE_mesh_unlink(struct Mesh *me); -void BKE_mesh_free(struct Mesh *me, int unlink); +void BKE_mesh_free(struct Mesh *me); void BKE_mesh_init(struct Mesh *me); struct Mesh *BKE_mesh_add(struct Main *bmain, const char *name); struct Mesh *BKE_mesh_copy_ex(struct Main *bmain, struct Mesh *me); diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 4b92c1f7a3a..bf198c9b86b 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -334,7 +334,6 @@ void ntreeInitDefault(struct bNodeTree *ntree); struct bNodeTree *ntreeAddTree(struct Main *bmain, const char *name, const char *idname); /* copy/free funcs, need to manage ID users */ -void ntreeFreeTree_ex(struct bNodeTree *ntree, const bool do_id_user); void ntreeFreeTree(struct bNodeTree *ntree); struct bNodeTree *ntreeCopyTree_ex(struct bNodeTree *ntree, struct Main *bmain, const bool do_id_user); struct bNodeTree *ntreeCopyTree(struct bNodeTree *ntree); diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index 7f0234a1bc8..612c758139a 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -62,7 +62,6 @@ void BKE_object_free_curve_cache(struct Object *ob); void BKE_object_update_base_layer(struct Scene *scene, struct Object *ob); void BKE_object_free(struct Object *ob); -void BKE_object_free_ex(struct Object *ob, bool do_id_user); void BKE_object_free_derived_caches(struct Object *ob); void BKE_object_free_caches(struct Object *object); @@ -76,7 +75,6 @@ void BKE_object_free_modifiers(struct Object *ob); void BKE_object_make_proxy(struct Object *ob, struct Object *target, struct Object *gob); void BKE_object_copy_proxy_drivers(struct Object *ob, struct Object *target); -void BKE_object_unlink(struct Main *bmain, struct Object *ob); bool BKE_object_exists_check(struct Object *obtest); bool BKE_object_is_in_editmode(struct Object *ob); bool BKE_object_is_in_editmode_vgroup(struct Object *ob); diff --git a/source/blender/blenkernel/BKE_sca.h b/source/blender/blenkernel/BKE_sca.h index 1f2bb6c3a2b..e8b37a36b56 100644 --- a/source/blender/blenkernel/BKE_sca.h +++ b/source/blender/blenkernel/BKE_sca.h @@ -67,7 +67,6 @@ void clear_sca_new_poins_ob(struct Object *ob); void clear_sca_new_poins(void); void set_sca_new_poins_ob(struct Object *ob); void set_sca_new_poins(void); -void sca_remove_ob_poin(struct Object *obt, struct Object *ob); void sca_move_sensor(struct bSensor *sens_to_move, struct Object *ob, int move_up); void sca_move_controller(struct bController *cont_to_move, struct Object *ob, int move_up); diff --git a/source/blender/blenkernel/BKE_scene.h b/source/blender/blenkernel/BKE_scene.h index 12bfc07e958..ccb7dc8e015 100644 --- a/source/blender/blenkernel/BKE_scene.h +++ b/source/blender/blenkernel/BKE_scene.h @@ -100,10 +100,11 @@ struct Scene *BKE_scene_set_name(struct Main *bmain, const char *name); struct Scene *BKE_scene_copy(struct Scene *sce, int type); void BKE_scene_groups_relink(struct Scene *sce); -void BKE_scene_unlink(struct Main *bmain, struct Scene *sce, struct Scene *newsce); struct Object *BKE_scene_camera_find(struct Scene *sc); +#ifdef DURIAN_CAMERA_SWITCH struct Object *BKE_scene_camera_switch_find(struct Scene *scene); // DURIAN_CAMERA_SWITCH +#endif int BKE_scene_camera_switch_update(struct Scene *scene); char *BKE_scene_find_marker_name(struct Scene *scene, int frame); diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h index d05df3470b5..14e978b23f2 100644 --- a/source/blender/blenkernel/BKE_screen.h +++ b/source/blender/blenkernel/BKE_screen.h @@ -99,6 +99,9 @@ typedef struct SpaceType { /* return context data */ int (*context)(const struct bContext *, const char *, struct bContextDataResult *); + /* Used when we want to replace an ID by another (or NULL). */ + void (*id_remap)(struct ScrArea *, struct SpaceLink *, struct ID *, struct ID *); + /* region type definitions */ ListBase regiontypes; @@ -274,8 +277,8 @@ void BKE_spacedata_freelist(ListBase *lb); void BKE_spacedata_copylist(ListBase *lb1, ListBase *lb2); void BKE_spacedata_draw_locks(int set); -void BKE_spacedata_callback_id_unref_set(void (*func)(struct SpaceLink *sl, const struct ID *)); -void BKE_spacedata_id_unref(struct SpaceLink *sl, const struct ID *id); +void BKE_spacedata_callback_id_remap_set(void (*func)(struct ScrArea *, struct SpaceLink *, struct ID *, struct ID *)); +void BKE_spacedata_id_unref(struct ScrArea *sa, struct SpaceLink *sl, struct ID *id); /* area/regions */ struct ARegion *BKE_area_region_copy(struct SpaceType *st, struct ARegion *ar); diff --git a/source/blender/blenkernel/BKE_sound.h b/source/blender/blenkernel/BKE_sound.h index 67db2537c8f..18d9fe061a8 100644 --- a/source/blender/blenkernel/BKE_sound.h +++ b/source/blender/blenkernel/BKE_sound.h @@ -72,8 +72,6 @@ struct bSound *BKE_sound_new_buffer(struct Main *bmain, struct bSound *source); struct bSound *BKE_sound_new_limiter(struct Main *bmain, struct bSound *source, float start, float end); #endif -void BKE_sound_delete(struct Main *bmain, struct bSound *sound); - void BKE_sound_cache(struct bSound *sound); void BKE_sound_delete_cache(struct bSound *sound); diff --git a/source/blender/blenkernel/BKE_world.h b/source/blender/blenkernel/BKE_world.h index 8d7ab230919..0be61fe0229 100644 --- a/source/blender/blenkernel/BKE_world.h +++ b/source/blender/blenkernel/BKE_world.h @@ -37,7 +37,6 @@ struct Main; struct World; void BKE_world_free(struct World *sc); -void BKE_world_free_ex(struct World *sc, bool do_id_user); void BKE_world_init(struct World *wrld); struct World *add_world(struct Main *bmian, const char *name); struct World *BKE_world_copy(struct World *wrld); diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index e5ebce371b6..df715531cb7 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -122,6 +122,7 @@ set(SRC intern/library.c intern/library_idmap.c intern/library_query.c + intern/library_remap.c intern/linestyle.c intern/mask.c intern/mask_evaluate.c @@ -240,6 +241,7 @@ set(SRC BKE_library.h BKE_library_idmap.h BKE_library_query.h + BKE_library_remap.h BKE_linestyle.h BKE_main.h BKE_mask.h diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c index d590a35bb57..79e500f8ceb 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.c +++ b/source/blender/blenkernel/intern/DerivedMesh.c @@ -1433,10 +1433,12 @@ static void calc_weightpaint_vert_array( Object *ob, DerivedMesh *dm, int const draw_flag, DMWeightColorInfo *dm_wcinfo, unsigned char (*r_wtcol_v)[4]) { - MDeformVert *dv = DM_get_vert_data_layer(dm, CD_MDEFORMVERT); - int numVerts = dm->getNumVerts(dm); + BMEditMesh *em = (dm->type == DM_TYPE_EDITBMESH) ? BKE_editmesh_from_object(ob) : NULL; + const int numVerts = dm->getNumVerts(dm); - if (dv && (ob->actdef != 0)) { + if ((ob->actdef != 0) && + (CustomData_has_layer(em ? &em->bm->vdata : &dm->vertData, CD_MDEFORMVERT))) + { unsigned char (*wc)[4] = r_wtcol_v; unsigned int i; @@ -1455,8 +1457,30 @@ static void calc_weightpaint_vert_array( } } - for (i = numVerts; i != 0; i--, wc++, dv++) { - calc_weightpaint_vert_color((unsigned char *)wc, dv, dm_wcinfo, defbase_tot, defbase_act, defbase_sel, defbase_sel_tot, draw_flag); + /* editmesh won't have deform verts unless modifiers require it, + * avoid having to create an array of deform-verts only for drawing + * by reading from the bmesh directly. */ + if (em) { + BMIter iter; + BMVert *eve; + const int cd_dvert_offset = CustomData_get_offset(&em->bm->vdata, CD_MDEFORMVERT); + BLI_assert(cd_dvert_offset != -1); + + BM_ITER_MESH_INDEX (eve, &iter, em->bm, BM_VERTS_OF_MESH, i) { + const MDeformVert *dv = BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset); + calc_weightpaint_vert_color( + (unsigned char *)wc, dv, dm_wcinfo, + defbase_tot, defbase_act, defbase_sel, defbase_sel_tot, draw_flag); + wc++; + } + } + else { + const MDeformVert *dv = DM_get_vert_data_layer(dm, CD_MDEFORMVERT); + for (i = numVerts; i != 0; i--, wc++, dv++) { + calc_weightpaint_vert_color( + (unsigned char *)wc, dv, dm_wcinfo, + defbase_tot, defbase_act, defbase_sel, defbase_sel_tot, draw_flag); + } } if (defbase_sel) { @@ -2282,7 +2306,7 @@ static void editbmesh_calc_modifiers( modifiers_clearErrors(ob); if (r_cage && cageIndex == -1) { - *r_cage = getEditDerivedBMesh(em, ob, NULL); + *r_cage = getEditDerivedBMesh(em, ob, dataMask, NULL); } md = modifiers_getVirtualModifierList(ob, &virtualModifierData); @@ -2448,7 +2472,7 @@ static void editbmesh_calc_modifiers( } else { *r_cage = getEditDerivedBMesh( - em, ob, + em, ob, mask, deformedVerts ? MEM_dupallocN(deformedVerts) : NULL); } } @@ -2484,7 +2508,7 @@ static void editbmesh_calc_modifiers( } else { /* this is just a copy of the editmesh, no need to calc normals */ - *r_final = getEditDerivedBMesh(em, ob, deformedVerts); + *r_final = getEditDerivedBMesh(em, ob, dataMask, deformedVerts); deformedVerts = NULL; /* In this case, we should never have weight-modifying modifiers in stack... */ @@ -2847,9 +2871,9 @@ DerivedMesh *editbmesh_get_derived_cage(Scene *scene, Object *obedit, BMEditMesh return em->derivedCage; } -DerivedMesh *editbmesh_get_derived_base(Object *obedit, BMEditMesh *em) +DerivedMesh *editbmesh_get_derived_base(Object *obedit, BMEditMesh *em, CustomDataMask data_mask) { - return getEditDerivedBMesh(em, obedit, NULL); + return getEditDerivedBMesh(em, obedit, data_mask, NULL); } /***/ diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index df9b9683687..46ee8a4d888 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -159,22 +159,19 @@ void BKE_action_make_local(bAction *act) /* .................................. */ +/** Free (or release) any data used by this action (does not free the action itself). */ void BKE_action_free(bAction *act) -{ - /* sanity check */ - if (act == NULL) - return; - +{ + /* No animdata here. */ + /* Free F-Curves */ free_fcurves(&act->curves); /* Free groups */ - if (act->groups.first) - BLI_freelistN(&act->groups); + BLI_freelistN(&act->groups); /* Free pose-references (aka local markers) */ - if (act->markers.first) - BLI_freelistN(&act->markers); + BLI_freelistN(&act->markers); } /* .................................. */ diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index 7e01b88fc3b..b7e8bc62470 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -215,7 +215,7 @@ bool BKE_animdata_set_action(ReportList *reports, ID *id, bAction *act) /* Freeing -------------------------------------------- */ /* Free AnimData used by the nominated ID-block, and clear ID-block's AnimData pointer */ -void BKE_animdata_free(ID *id) +void BKE_animdata_free(ID *id, const bool do_id_user) { /* Only some ID-blocks have this info for now, so we cast the * types that do to be of type IdAdtTemplate @@ -226,12 +226,14 @@ void BKE_animdata_free(ID *id) /* check if there's any AnimData to start with */ if (adt) { - /* unlink action (don't free, as it's in its own list) */ - if (adt->action) - id_us_min(&adt->action->id); - /* same goes for the temporarily displaced action */ - if (adt->tmpact) - id_us_min(&adt->tmpact->id); + if (do_id_user) { + /* unlink action (don't free, as it's in its own list) */ + if (adt->action) + id_us_min(&adt->action->id); + /* same goes for the temporarily displaced action */ + if (adt->tmpact) + id_us_min(&adt->tmpact->id); + } /* free nla data */ free_nladata(&adt->nla_tracks); @@ -291,7 +293,7 @@ bool BKE_animdata_copy_id(ID *id_to, ID *id_from, const bool do_action) if ((id_to && id_from) && (GS(id_to->name) != GS(id_from->name))) return false; - BKE_animdata_free(id_to); + BKE_animdata_free(id_to, true); adt = BKE_animdata_from_id(id_from); if (adt) { diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index 04b4733fd44..038993777cf 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -120,30 +120,25 @@ void BKE_armature_bonelist_free(ListBase *lb) BLI_freelistN(lb); } +/** Free (or release) any data used by this armature (does not free the armature itself). */ void BKE_armature_free(bArmature *arm) { - if (arm) { - BKE_armature_bonelist_free(&arm->bonebase); + BKE_animdata_free(&arm->id, false); - /* free editmode data */ - if (arm->edbo) { - BLI_freelistN(arm->edbo); + BKE_armature_bonelist_free(&arm->bonebase); - MEM_freeN(arm->edbo); - arm->edbo = NULL; - } + /* free editmode data */ + if (arm->edbo) { + BLI_freelistN(arm->edbo); - /* free sketch */ - if (arm->sketch) { - freeSketch(arm->sketch); - arm->sketch = NULL; - } + MEM_freeN(arm->edbo); + arm->edbo = NULL; + } - /* free animation data */ - if (arm->adt) { - BKE_animdata_free(&arm->id); - arm->adt = NULL; - } + /* free sketch */ + if (arm->sketch) { + freeSketch(arm->sketch); + arm->sketch = NULL; } } diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index da7863096e3..a3e006a162f 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -202,22 +202,18 @@ Brush *BKE_brush_copy(Brush *brush) return brushn; } -/* not brush itself */ +/** Free (or release) any data used by this brush (does not free the brush itself). */ void BKE_brush_free(Brush *brush) { - id_us_min((ID *)brush->mtex.tex); - id_us_min((ID *)brush->mask_mtex.tex); - id_us_min((ID *)brush->paint_curve); - - if (brush->icon_imbuf) + if (brush->icon_imbuf) { IMB_freeImBuf(brush->icon_imbuf); - - BKE_previewimg_free(&(brush->preview)); + } curvemapping_free(brush->curve); - if (brush->gradient) - MEM_freeN(brush->gradient); + MEM_SAFE_FREE(brush->gradient); + + BKE_previewimg_free(&(brush->preview)); } /** diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index 96bac2c2f41..eabee742327 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -153,9 +153,10 @@ void BKE_camera_make_local(Camera *cam) } } +/** Free (or release) any data used by this camera (does not free the camera itself). */ void BKE_camera_free(Camera *ca) { - BKE_animdata_free((ID *)ca); + BKE_animdata_free((ID *)ca, false); } /******************************** Camera Usage *******************************/ diff --git a/source/blender/blenkernel/intern/crazyspace.c b/source/blender/blenkernel/intern/crazyspace.c index c8de0786697..56df8e51eba 100644 --- a/source/blender/blenkernel/intern/crazyspace.c +++ b/source/blender/blenkernel/intern/crazyspace.c @@ -39,6 +39,7 @@ #include "DNA_meshdata_types.h" #include "BLI_utildefines.h" +#include "BLI_linklist.h" #include "BLI_math.h" #include "BKE_crazyspace.h" @@ -275,7 +276,13 @@ int BKE_crazyspace_get_first_deform_matrices_editbmesh(Scene *scene, Object *ob, if (mti->type == eModifierTypeType_OnlyDeform && mti->deformMatricesEM) { if (!defmats) { - dm = getEditDerivedBMesh(em, ob, NULL); + const int required_mode = eModifierMode_Realtime | eModifierMode_Editmode; + CustomDataMask data_mask = CD_MASK_BAREMESH; + CDMaskLink *datamasks = modifiers_calcDataMasks(scene, ob, md, data_mask, required_mode, NULL, 0); + data_mask = datamasks->mask; + BLI_linklist_free((LinkNode *)datamasks, NULL); + + dm = getEditDerivedBMesh(em, ob, data_mask, NULL); deformedVerts = editbmesh_get_vertex_cos(em, &numVerts); defmats = MEM_mallocN(sizeof(*defmats) * numVerts, "defmats"); diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index 8afb451f768..dec6ff22360 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -69,36 +69,6 @@ static int cu_isectLL(const float v1[3], const float v2[3], const float v3[3], c short cox, short coy, float *lambda, float *mu, float vec[3]); -void BKE_curve_unlink(Curve *cu) -{ - int a; - - for (a = 0; a < cu->totcol; a++) { - if (cu->mat[a]) - id_us_min(&cu->mat[a]->id); - cu->mat[a] = NULL; - } - if (cu->vfont) - id_us_min(&cu->vfont->id); - cu->vfont = NULL; - - if (cu->vfontb) - id_us_min(&cu->vfontb->id); - cu->vfontb = NULL; - - if (cu->vfonti) - id_us_min(&cu->vfonti->id); - cu->vfonti = NULL; - - if (cu->vfontbi) - id_us_min(&cu->vfontbi->id); - cu->vfontbi = NULL; - - if (cu->key) - id_us_min(&cu->key->id); - cu->key = NULL; -} - /* frees editcurve entirely */ void BKE_curve_editfont_free(Curve *cu) { @@ -136,26 +106,21 @@ void BKE_curve_editNurb_free(Curve *cu) } } -/* don't free curve itself */ +/** Free (or release) any data used by this curve (does not free the curve itself). */ void BKE_curve_free(Curve *cu) { + BKE_animdata_free((ID *)cu, false); + BKE_nurbList_free(&cu->nurb); BKE_curve_editfont_free(cu); BKE_curve_editNurb_free(cu); - BKE_curve_unlink(cu); - BKE_animdata_free((ID *)cu); - - if (cu->mat) - MEM_freeN(cu->mat); - if (cu->str) - MEM_freeN(cu->str); - if (cu->strinfo) - MEM_freeN(cu->strinfo); - if (cu->bb) - MEM_freeN(cu->bb); - if (cu->tb) - MEM_freeN(cu->tb); + + MEM_SAFE_FREE(cu->mat); + MEM_SAFE_FREE(cu->str); + MEM_SAFE_FREE(cu->strinfo); + MEM_SAFE_FREE(cu->bb); + MEM_SAFE_FREE(cu->tb); } void BKE_curve_init(Curve *cu) diff --git a/source/blender/blenkernel/intern/editderivedmesh.c b/source/blender/blenkernel/intern/editderivedmesh.c index c1013342bd9..1aba76baa2c 100644 --- a/source/blender/blenkernel/intern/editderivedmesh.c +++ b/source/blender/blenkernel/intern/editderivedmesh.c @@ -2226,16 +2226,17 @@ static CustomData *bmDm_getPolyDataLayout(DerivedMesh *dm) return &bmdm->em->bm->pdata; } - +/** + * \note This may be called per-draw, + * avoid allocating large arrays where possible and keep this a thin wrapper for #BMesh. + */ DerivedMesh *getEditDerivedBMesh( - BMEditMesh *em, - Object *UNUSED(ob), + BMEditMesh *em, struct Object *UNUSED(ob), + CustomDataMask data_mask, float (*vertexCos)[3]) { EditDerivedBMesh *bmdm = MEM_callocN(sizeof(*bmdm), __func__); BMesh *bm = em->bm; - const int cd_dvert_offset = CustomData_get_offset(&bm->vdata, CD_MDEFORMVERT); - const int cd_skin_offset = CustomData_get_offset(&bm->vdata, CD_MVERT_SKIN); bmdm->em = em; @@ -2304,6 +2305,9 @@ DerivedMesh *getEditDerivedBMesh( bmdm->vertexCos = (const float (*)[3])vertexCos; bmdm->dm.deformedOnly = (vertexCos != NULL); + const int cd_dvert_offset = (data_mask & CD_MASK_MDEFORMVERT) ? + CustomData_get_offset(&bm->vdata, CD_MDEFORMVERT) : -1; + if (cd_dvert_offset != -1) { BMIter iter; BMVert *eve; @@ -2317,6 +2321,9 @@ DerivedMesh *getEditDerivedBMesh( } } + const int cd_skin_offset = (data_mask & CD_MASK_MVERT_SKIN) ? + CustomData_get_offset(&bm->vdata, CD_MVERT_SKIN) : -1; + if (cd_skin_offset != -1) { BMIter iter; BMVert *eve; diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c index 787b915e41b..824683f5b99 100644 --- a/source/blender/blenkernel/intern/effect.c +++ b/source/blender/blenkernel/intern/effect.c @@ -137,9 +137,6 @@ void free_partdeflect(PartDeflect *pd) if (!pd) return; - if (pd->tex) - id_us_min(&pd->tex->id); - if (pd->rng) BLI_rng_free(pd->rng); diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c index 812d1c66923..5e1f8814ed6 100644 --- a/source/blender/blenkernel/intern/font.c +++ b/source/blender/blenkernel/intern/font.c @@ -95,10 +95,9 @@ void BKE_vfont_free_data(struct VFont *vfont) } } +/** Free (or release) any data used by this font (does not free the font itself). */ void BKE_vfont_free(struct VFont *vf) { - if (vf == NULL) return; - BKE_vfont_free_data(vf); if (vf->packedfile) { diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index f3eb5430bce..af1bcd0c545 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -113,16 +113,13 @@ void free_gpencil_layers(ListBase *list) } /* Free all of GPencil datablock's related data, but not the block itself */ +/** Free (or release) any data used by this grease pencil (does not free the gpencil itself). */ void BKE_gpencil_free(bGPdata *gpd) { + BKE_animdata_free(&gpd->id, false); + /* free layers */ free_gpencil_layers(&gpd->layers); - - /* free animation data */ - if (gpd->adt) { - BKE_animdata_free(&gpd->id); - gpd->adt = NULL; - } } /* -------- Container Creation ---------- */ diff --git a/source/blender/blenkernel/intern/group.c b/source/blender/blenkernel/intern/group.c index 6026913d247..95c7730431a 100644 --- a/source/blender/blenkernel/intern/group.c +++ b/source/blender/blenkernel/intern/group.c @@ -59,69 +59,19 @@ static void free_group_object(GroupObject *go) MEM_freeN(go); } - +/** Free (or release) any data used by this group (does not free the group itself). */ void BKE_group_free(Group *group) { /* don't free group itself */ GroupObject *go; - BKE_previewimg_free(&group->preview); + /* No animdata here. */ while ((go = BLI_pophead(&group->gobject))) { free_group_object(go); } -} -void BKE_group_unlink(Main *bmain, Group *group) -{ - Material *ma; - Object *ob; - Scene *sce; - SceneRenderLayer *srl; - - for (ma = bmain->mat.first; ma; ma = ma->id.next) { - if (ma->group == group) - ma->group = NULL; - } - for (ma = bmain->mat.first; ma; ma = ma->id.next) { - if (ma->group == group) - ma->group = NULL; - } - for (sce = bmain->scene.first; sce; sce = sce->id.next) { - Base *base = sce->base.first; - - /* ensure objects are not in this group */ - for (; base; base = base->next) { - if (BKE_group_object_unlink(group, base->object, sce, base) && - BKE_group_object_find(NULL, base->object) == NULL) - { - base->object->flag &= ~OB_FROMGROUP; - base->flag &= ~OB_FROMGROUP; - } - } - - for (srl = sce->r.layers.first; srl; srl = srl->next) { - FreestyleLineSet *lineset; - - if (srl->light_override == group) - srl->light_override = NULL; - for (lineset = srl->freestyleConfig.linesets.first; lineset; lineset = lineset->next) { - if (lineset->group == group) - lineset->group = NULL; - } - } - } - - for (ob = bmain->object.first; ob; ob = ob->id.next) { - - if (ob->dup_group == group) { - ob->dup_group = NULL; - } - } - - /* group stays in library, but no members */ - BKE_group_free(group); - group->id.us = 0; + BKE_previewimg_free(&group->preview); } Group *BKE_group_add(Main *bmain, const char *name) diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 0b2c844cb2c..14a445649ad 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -324,20 +324,16 @@ void BKE_image_free_buffers(Image *ima) ima->ok = IMA_OK; } -/* called by library too, do not free ima itself */ +/** Free (or release) any data used by this image (does not free the image itself). */ void BKE_image_free(Image *ima) { int a; + /* Also frees animdata. */ BKE_image_free_buffers(ima); image_free_packedfiles(ima); - BKE_icon_id_delete(&ima->id); - ima->id.icon_id = 0; - - BKE_previewimg_free(&ima->preview); - for (a = 0; a < IMA_MAX_RENDER_SLOT; a++) { if (ima->renders[a]) { RE_FreeRenderResult(ima->renders[a]); @@ -346,7 +342,10 @@ void BKE_image_free(Image *ima) } BKE_image_free_views(ima); - MEM_freeN(ima->stereo3d_format); + MEM_SAFE_FREE(ima->stereo3d_format); + + BKE_icon_id_delete(&ima->id); + BKE_previewimg_free(&ima->preview); } /* only image block itself */ diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c index 362f41335d2..2517e2cc197 100644 --- a/source/blender/blenkernel/intern/key.c +++ b/source/blender/blenkernel/intern/key.c @@ -74,11 +74,13 @@ #define IPO_BEZTRIPLE 100 #define IPO_BPOINT 101 + +/** Free (or release) any data used by this shapekey (does not free the key itself). */ void BKE_key_free(Key *key) { KeyBlock *kb; - - BKE_animdata_free((ID *)key); + + BKE_animdata_free((ID *)key, false); while ((kb = BLI_pophead(&key->block))) { if (kb->data) diff --git a/source/blender/blenkernel/intern/lamp.c b/source/blender/blenkernel/intern/lamp.c index 49a573489ef..692b703f721 100644 --- a/source/blender/blenkernel/intern/lamp.c +++ b/source/blender/blenkernel/intern/lamp.c @@ -234,7 +234,7 @@ void BKE_lamp_free(Lamp *la) MEM_freeN(mtex); } - BKE_animdata_free((ID *)la); + BKE_animdata_free((ID *)la, false); curvemapping_free(la->curfalloff); diff --git a/source/blender/blenkernel/intern/lattice.c b/source/blender/blenkernel/intern/lattice.c index b350e932281..58c0a567116 100644 --- a/source/blender/blenkernel/intern/lattice.c +++ b/source/blender/blenkernel/intern/lattice.c @@ -300,24 +300,27 @@ Lattice *BKE_lattice_copy(Lattice *lt) return ltn; } +/** Free (or release) any data used by this lattice (does not free the lattice itself). */ void BKE_lattice_free(Lattice *lt) { - if (lt->def) MEM_freeN(lt->def); - if (lt->dvert) BKE_defvert_array_free(lt->dvert, lt->pntsu * lt->pntsv * lt->pntsw); + BKE_animdata_free(<->id, false); + + MEM_SAFE_FREE(lt->def); + if (lt->dvert) { + BKE_defvert_array_free(lt->dvert, lt->pntsu * lt->pntsv * lt->pntsw); + lt->dvert = NULL; + } if (lt->editlatt) { Lattice *editlt = lt->editlatt->latt; - if (editlt->def) MEM_freeN(editlt->def); - if (editlt->dvert) BKE_defvert_array_free(editlt->dvert, lt->pntsu * lt->pntsv * lt->pntsw); + if (editlt->def) + MEM_freeN(editlt->def); + if (editlt->dvert) + BKE_defvert_array_free(editlt->dvert, lt->pntsu * lt->pntsv * lt->pntsw); MEM_freeN(editlt); MEM_freeN(lt->editlatt); - } - - /* free animation data */ - if (lt->adt) { - BKE_animdata_free(<->id); - lt->adt = NULL; + lt->editlatt = NULL; } } diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index 64c9ddb5b38..5f7bded4262 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -84,7 +84,6 @@ #include "BKE_context.h" #include "BKE_curve.h" #include "BKE_depsgraph.h" -#include "BKE_fcurve.h" #include "BKE_font.h" #include "BKE_global.h" #include "BKE_group.h" @@ -92,7 +91,6 @@ #include "BKE_idcode.h" #include "BKE_idprop.h" #include "BKE_image.h" -#include "BKE_ipo.h" #include "BKE_key.h" #include "BKE_lamp.h" #include "BKE_lattice.h" @@ -103,15 +101,11 @@ #include "BKE_material.h" #include "BKE_main.h" #include "BKE_mball.h" -#include "BKE_movieclip.h" #include "BKE_mask.h" #include "BKE_node.h" #include "BKE_object.h" -#include "BKE_paint.h" #include "BKE_packedFile.h" #include "BKE_speaker.h" -#include "BKE_sound.h" -#include "BKE_screen.h" #include "BKE_scene.h" #include "BKE_text.h" #include "BKE_texture.h" @@ -124,10 +118,6 @@ #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" -#ifdef WITH_PYTHON -#include "BPY_extern.h" -#endif - /* GS reads the memory pointed at in a specific ordering. * only use this definition, makes little and big endian systems * work fine, in conjunction with MAKE_ID */ @@ -181,7 +171,6 @@ void id_us_ensure_real(ID *id) } } -/* Unused currently... */ void id_us_clear_real(ID *id) { if (id && (id->tag & LIB_TAG_EXTRAUSER)) { @@ -231,9 +220,7 @@ void id_us_min(ID *id) if (id->us <= limit) { printf("ID user decrement error: %s (from '%s'): %d <= %d\n", id->name, id->lib ? id->lib->filepath : "[Main]", id->us, limit); - /* We cannot assert here, because of how we 'delete' datablocks currently (setting their usercount to zero), - * this is weak but it's how it works for now. */ - /* BLI_assert(0); */ + BLI_assert(0); id->us = limit; } else { @@ -455,37 +442,6 @@ bool id_copy(ID *id, ID **newid, bool test) return false; } -bool id_unlink(ID *id, int test) -{ - Main *mainlib = G.main; - short type = GS(id->name); - - switch (type) { - case ID_TXT: - if (test) return true; - BKE_text_unlink(mainlib, (Text *)id); - break; - case ID_GR: - if (test) return true; - BKE_group_unlink(mainlib, (Group *)id); - break; - case ID_OB: - if (test) return true; - BKE_object_unlink(mainlib, (Object *)id); - break; - } - - if (id->us == 0) { - if (test) return true; - - BKE_libblock_free(mainlib, id); - - return true; - } - - return false; -} - bool id_single_user(bContext *C, ID *id, PointerRNA *ptr, PropertyRNA *prop) { ID *newid = NULL; @@ -1095,231 +1051,12 @@ void BKE_libblock_relink(ID *id) BKE_library_foreach_ID_link(id, id_relink_looper, NULL, 0); } -static void BKE_library_free(Library *lib) +void BKE_library_free(Library *lib) { if (lib->packedfile) freePackedFile(lib->packedfile); } -static BKE_library_free_window_manager_cb free_windowmanager_cb = NULL; - -void BKE_library_callback_free_window_manager_set(BKE_library_free_window_manager_cb func) -{ - free_windowmanager_cb = func; -} - -static BKE_library_free_notifier_reference_cb free_notifier_reference_cb = NULL; - -void BKE_library_callback_free_notifier_reference_set(BKE_library_free_notifier_reference_cb func) -{ - free_notifier_reference_cb = func; -} - -static BKE_library_free_editor_id_reference_cb free_editor_id_reference_cb = NULL; - -void BKE_library_callback_free_editor_id_reference_set(BKE_library_free_editor_id_reference_cb func) -{ - free_editor_id_reference_cb = func; -} - -static void animdata_dtar_clear_cb(ID *UNUSED(id), AnimData *adt, void *userdata) -{ - ChannelDriver *driver; - FCurve *fcu; - - /* find the driver this belongs to and update it */ - for (fcu = adt->drivers.first; fcu; fcu = fcu->next) { - driver = fcu->driver; - - if (driver) { - DriverVar *dvar; - for (dvar = driver->variables.first; dvar; dvar = dvar->next) { - DRIVER_TARGETS_USED_LOOPER(dvar) - { - if (dtar->id == userdata) - dtar->id = NULL; - } - DRIVER_TARGETS_LOOPER_END - } - } - } -} - -void BKE_libblock_free_data(Main *bmain, ID *id) -{ - if (id->properties) { - IDP_FreeProperty(id->properties); - MEM_freeN(id->properties); - } - - /* this ID may be a driver target! */ - BKE_animdata_main_cb(bmain, animdata_dtar_clear_cb, (void *)id); -} - -/* used in headerbuttons.c image.c mesh.c screen.c sound.c and library.c */ -void BKE_libblock_free_ex(Main *bmain, void *idv, bool do_id_user) -{ - ID *id = idv; - short type = GS(id->name); - ListBase *lb = which_libbase(bmain, type); - - DAG_id_type_tag(bmain, type); - -#ifdef WITH_PYTHON - BPY_id_release(id); -#endif - - switch (type) { /* GetShort from util.h */ - case ID_SCE: - BKE_scene_free((Scene *)id); - break; - case ID_LI: - BKE_library_free((Library *)id); - break; - case ID_OB: - BKE_object_free_ex((Object *)id, do_id_user); - break; - case ID_ME: - BKE_mesh_free((Mesh *)id, 1); - break; - case ID_CU: - BKE_curve_free((Curve *)id); - break; - case ID_MB: - BKE_mball_free((MetaBall *)id); - break; - case ID_MA: - BKE_material_free((Material *)id); - break; - case ID_TE: - BKE_texture_free((Tex *)id); - break; - case ID_IM: - BKE_image_free((Image *)id); - break; - case ID_LT: - BKE_lattice_free((Lattice *)id); - break; - case ID_LA: - BKE_lamp_free((Lamp *)id); - break; - case ID_CA: - BKE_camera_free((Camera *) id); - break; - case ID_IP: - BKE_ipo_free((Ipo *)id); - break; - case ID_KE: - BKE_key_free((Key *)id); - break; - case ID_WO: - BKE_world_free((World *)id); - break; - case ID_SCR: - BKE_screen_free((bScreen *)id); - break; - case ID_VF: - BKE_vfont_free((VFont *)id); - break; - case ID_TXT: - BKE_text_free((Text *)id); - break; - case ID_SPK: - BKE_speaker_free((Speaker *)id); - break; - case ID_SO: - BKE_sound_free((bSound *)id); - break; - case ID_GR: - BKE_group_free((Group *)id); - break; - case ID_AR: - BKE_armature_free((bArmature *)id); - break; - case ID_AC: - BKE_action_free((bAction *)id); - break; - case ID_NT: - ntreeFreeTree_ex((bNodeTree *)id, do_id_user); - break; - case ID_BR: - BKE_brush_free((Brush *)id); - break; - case ID_WM: - if (free_windowmanager_cb) - free_windowmanager_cb(NULL, (wmWindowManager *)id); - break; - case ID_GD: - BKE_gpencil_free((bGPdata *)id); - break; - case ID_MC: - BKE_movieclip_free((MovieClip *)id); - break; - case ID_MSK: - BKE_mask_free(bmain, (Mask *)id); - break; - case ID_LS: - BKE_linestyle_free((FreestyleLineStyle *)id); - break; - case ID_PAL: - BKE_palette_free((Palette *)id); - break; - case ID_PC: - BKE_paint_curve_free((PaintCurve *)id); - break; - } - - /* avoid notifying on removed data */ - BKE_main_lock(bmain); - - if (free_notifier_reference_cb) { - free_notifier_reference_cb(id); - } - - if (free_editor_id_reference_cb) { - free_editor_id_reference_cb(id); - } - - BLI_remlink(lb, id); - - BKE_libblock_free_data(bmain, id); - BKE_main_unlock(bmain); - - MEM_freeN(id); -} - -void BKE_libblock_free(Main *bmain, void *idv) -{ - BKE_libblock_free_ex(bmain, idv, true); -} - -void BKE_libblock_free_us(Main *bmain, void *idv) /* test users */ -{ - ID *id = idv; - - id_us_min(id); - - /* XXX This is a temp (2.77) hack so that we keep same behavior as in 2.76 regarding groups when deleting an object. - * Since only 'user_one' usage of objects is groups, and only 'real user' usage of objects is scenes, - * removing that 'user_one' tag when there is no more real (scene) users of an object ensures it gets - * fully unlinked. - * Otherwise, there is no real way to get rid of an object anymore - better handling of this is TODO. - */ - if ((GS(id->name) == ID_OB) && (id->us == 1)) { - id_us_clear_real(id); - } - - if (id->us == 0) { - switch (GS(id->name)) { - case ID_OB: - BKE_object_unlink(bmain, (Object *)id); - break; - } - - BKE_libblock_free(bmain, id); - } -} - Main *BKE_main_new(void) { Main *bmain = MEM_callocN(sizeof(Main), "new main"); diff --git a/source/blender/blenkernel/intern/library_query.c b/source/blender/blenkernel/intern/library_query.c index 42cecfe86db..7cb5bfd1360 100644 --- a/source/blender/blenkernel/intern/library_query.c +++ b/source/blender/blenkernel/intern/library_query.c @@ -29,6 +29,7 @@ #include <stdlib.h> +#include "MEM_guardedalloc.h" #include "DNA_actuator_types.h" #include "DNA_anim_types.h" @@ -189,9 +190,24 @@ static void library_foreach_actuatorsObjectLooper( FOREACH_FINALIZE_VOID; } +static void library_foreach_nla_strip(LibraryForeachIDData *data, NlaStrip *strip) +{ + NlaStrip *substrip; + + FOREACH_CALLBACK_INVOKE(data, strip->act, IDWALK_USER); + + for (substrip = strip->strips.first; substrip; substrip = substrip->next) { + library_foreach_nla_strip(data, substrip); + } + + FOREACH_FINALIZE_VOID; +} + static void library_foreach_animationData(LibraryForeachIDData *data, AnimData *adt) { FCurve *fcu; + NlaTrack *nla_track; + NlaStrip *nla_strip; for (fcu = adt->drivers.first; fcu; fcu = fcu->next) { ChannelDriver *driver = fcu->driver; @@ -207,6 +223,15 @@ static void library_foreach_animationData(LibraryForeachIDData *data, AnimData * } } + FOREACH_CALLBACK_INVOKE(data, adt->action, IDWALK_USER); + FOREACH_CALLBACK_INVOKE(data, adt->tmpact, IDWALK_USER); + + for (nla_track = adt->nla_tracks.first; nla_track; nla_track = nla_track->next) { + for (nla_strip = nla_track->strips.first; nla_strip; nla_strip = nla_strip->next) { + library_foreach_nla_strip(data, nla_strip); + } + } + FOREACH_FINALIZE_VOID; } @@ -267,6 +292,12 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u } switch (GS(id->name)) { + case ID_LI: + { + Library *lib = (Library *) id; + CALLBACK_INVOKE(lib->parent, IDWALK_NOP); + break; + } case ID_SCE: { Scene *scene = (Scene *) id; @@ -278,7 +309,10 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u CALLBACK_INVOKE(scene->world, IDWALK_USER); CALLBACK_INVOKE(scene->set, IDWALK_NOP); CALLBACK_INVOKE(scene->clip, IDWALK_USER); - CALLBACK_INVOKE(scene->nodetree, IDWALK_NOP); + if (scene->nodetree) { + /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ + BKE_library_foreach_ID_link((ID *)scene->nodetree, callback, user_data, flag); + } /* DO NOT handle scene->basact here, it's doubling with the loop over whole scene->base later, * since basact is just a pointer to one of those items. */ CALLBACK_INVOKE(scene->obedit, IDWALK_NOP); @@ -471,7 +505,10 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u library_foreach_mtex(&data, material->mtex[i]); } } - CALLBACK_INVOKE(material->nodetree, IDWALK_NOP); + if (material->nodetree) { + /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ + BKE_library_foreach_ID_link((ID *)material->nodetree, callback, user_data, flag); + } CALLBACK_INVOKE(material->group, IDWALK_USER); break; } @@ -479,7 +516,10 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u case ID_TE: { Tex *texture = (Tex *) id; - CALLBACK_INVOKE(texture->nodetree, IDWALK_NOP); + if (texture->nodetree) { + /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ + BKE_library_foreach_ID_link((ID *)texture->nodetree, callback, user_data, flag); + } CALLBACK_INVOKE(texture->ima, IDWALK_USER); if (texture->env) { CALLBACK_INVOKE(texture->env->object, IDWALK_NOP); @@ -509,7 +549,10 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u library_foreach_mtex(&data, lamp->mtex[i]); } } - CALLBACK_INVOKE(lamp->nodetree, IDWALK_NOP); + if (lamp->nodetree) { + /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ + BKE_library_foreach_ID_link((ID *)lamp->nodetree, callback, user_data, flag); + } break; } @@ -542,7 +585,10 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u library_foreach_mtex(&data, world->mtex[i]); } } - CALLBACK_INVOKE(world->nodetree, IDWALK_NOP); + if (world->nodetree) { + /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ + BKE_library_foreach_ID_link((ID *)world->nodetree, callback, user_data, flag); + } break; } @@ -636,7 +682,10 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u library_foreach_mtex(&data, linestyle->mtex[i]); } } - CALLBACK_INVOKE(linestyle->nodetree, IDWALK_NOP); + if (linestyle->nodetree) { + /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ + BKE_library_foreach_ID_link((ID *)linestyle->nodetree, callback, user_data, flag); + } for (lsm = linestyle->color_modifiers.first; lsm; lsm = lsm->next) { if (lsm->type == LS_MODIFIER_DISTANCE_FROM_OBJECT) { @@ -705,11 +754,18 @@ typedef struct IDUsersIter { int count; /* Set by callback. */ } IDUsersIter; -static int foreach_libblock_id_users_callback(void *user_data, ID *UNUSED(self_id), ID **id_p, int UNUSED(cb_flag)) +static int foreach_libblock_id_users_callback(void *user_data, ID *UNUSED(self_id), ID **id_p, int cb_flag) { IDUsersIter *iter = user_data; if (*id_p && (*id_p == iter->id)) { +#if 0 + printf("%s uses %s (refcounted: %d, userone: %d, used_one: %d, used_one_active: %d)\n", + iter->curr_id->name, iter->id->name, (cb_flag & IDWALK_USER) ? 1 : 0, (cb_flag & IDWALK_USER_ONE) ? 1 : 0, + (iter->id->tag & LIB_TAG_EXTRAUSER) ? 1 : 0, (iter->id->tag & LIB_TAG_EXTRAUSER_SET) ? 1 : 0); +#else + UNUSED_VARS(cb_flag); +#endif iter->count++; } diff --git a/source/blender/blenkernel/intern/library_remap.c b/source/blender/blenkernel/intern/library_remap.c new file mode 100644 index 00000000000..9335d41f399 --- /dev/null +++ b/source/blender/blenkernel/intern/library_remap.c @@ -0,0 +1,764 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/blenkernel/intern/library_remap.c + * \ingroup bke + * + * Contains management of ID's and libraries remap, unlink and free logic. + */ + +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <stdlib.h> +#include <stddef.h> +#include <assert.h> + +#include "MEM_guardedalloc.h" + +/* all types are needed here, in order to do memory operations */ +#include "DNA_anim_types.h" +#include "DNA_armature_types.h" +#include "DNA_brush_types.h" +#include "DNA_camera_types.h" +#include "DNA_group_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_ipo_types.h" +#include "DNA_key_types.h" +#include "DNA_lamp_types.h" +#include "DNA_lattice_types.h" +#include "DNA_linestyle_types.h" +#include "DNA_material_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meta_types.h" +#include "DNA_movieclip_types.h" +#include "DNA_mask_types.h" +#include "DNA_node_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_speaker_types.h" +#include "DNA_sound_types.h" +#include "DNA_text_types.h" +#include "DNA_vfont_types.h" +#include "DNA_windowmanager_types.h" +#include "DNA_world_types.h" + +#include "BLI_blenlib.h" +#include "BLI_utildefines.h" + +#include "BKE_action.h" +#include "BKE_animsys.h" +#include "BKE_armature.h" +#include "BKE_brush.h" +#include "BKE_camera.h" +#include "BKE_curve.h" +#include "BKE_depsgraph.h" +#include "BKE_fcurve.h" +#include "BKE_font.h" +#include "BKE_group.h" +#include "BKE_gpencil.h" +#include "BKE_idprop.h" +#include "BKE_image.h" +#include "BKE_ipo.h" +#include "BKE_key.h" +#include "BKE_lamp.h" +#include "BKE_lattice.h" +#include "BKE_library.h" +#include "BKE_library_query.h" +#include "BKE_library_remap.h" +#include "BKE_linestyle.h" +#include "BKE_mesh.h" +#include "BKE_material.h" +#include "BKE_main.h" +#include "BKE_mball.h" +#include "BKE_movieclip.h" +#include "BKE_mask.h" +#include "BKE_node.h" +#include "BKE_object.h" +#include "BKE_paint.h" +#include "BKE_speaker.h" +#include "BKE_sound.h" +#include "BKE_screen.h" +#include "BKE_scene.h" +#include "BKE_text.h" +#include "BKE_texture.h" +#include "BKE_world.h" + +#ifdef WITH_PYTHON +#include "BPY_extern.h" +#endif + +static BKE_library_free_window_manager_cb free_windowmanager_cb = NULL; + +void BKE_library_callback_free_window_manager_set(BKE_library_free_window_manager_cb func) +{ + free_windowmanager_cb = func; +} + +static BKE_library_free_notifier_reference_cb free_notifier_reference_cb = NULL; + +void BKE_library_callback_free_notifier_reference_set(BKE_library_free_notifier_reference_cb func) +{ + free_notifier_reference_cb = func; +} + +static BKE_library_remap_editor_id_reference_cb remap_editor_id_reference_cb = NULL; + +void BKE_library_callback_remap_editor_id_reference_set(BKE_library_remap_editor_id_reference_cb func) +{ + remap_editor_id_reference_cb = func; +} + +typedef struct IDRemap { + ID *old_id; + ID *new_id; + ID *id; /* The ID in which we are replacing old_id by new_id usages. */ + short flag; + + /* 'Output' data. */ + short status; + int skipped_direct; /* Number of direct usecases that could not be remapped (e.g.: obdata when in edit mode). */ + int skipped_indirect; /* Number of indirect usecases that could not be remapped. */ + int skipped_refcounted; /* Number of skipped usecases that refcount the datablock. */ +} IDRemap; + +/* IDRemap->flag enums defined in BKE_library.h */ + +/* IDRemap->status */ +enum { + /* *** Set by callback. *** */ + ID_REMAP_IS_LINKED_DIRECT = 1 << 0, /* new_id is directly linked in current .blend. */ + ID_REMAP_IS_USER_ONE_SKIPPED = 1 << 1, /* There was some skipped 'user_one' usages of old_id. */ +}; + +static int foreach_libblock_remap_callback(void *user_data, ID *UNUSED(id_self), ID **id_p, int cb_flag) +{ + IDRemap *id_remap_data = user_data; + ID *old_id = id_remap_data->old_id; + ID *new_id = id_remap_data->new_id; + ID *id = id_remap_data->id; + + if (!old_id) { /* Used to cleanup all IDs used by a specific one. */ + BLI_assert(!new_id); + old_id = *id_p; + } + + if (*id_p && (*id_p == old_id)) { + /* Note: proxy usage implies LIB_TAG_EXTERN, so on this aspect it is direct, + * on the other hand since they get reset to lib data on file open/reload it is indirect too... + * Edit Mode is also a 'skip direct' case. */ + const bool is_obj = (GS(id->name) == ID_OB); + const bool is_proxy = (is_obj && (((Object *)id)->proxy || ((Object *)id)->proxy_group)); + const bool is_obj_editmode = (is_obj && BKE_object_is_in_editmode((Object *)id)); + /* Note that indirect data from same file as processed ID is **not** considered indirect! */ + const bool is_indirect = ((id->lib != NULL) && (id->lib != old_id->lib)); + const bool skip_indirect = (id_remap_data->flag & ID_REMAP_SKIP_INDIRECT_USAGE) != 0; + const bool is_never_null = ((cb_flag & IDWALK_NEVER_NULL) && (new_id == NULL) && + (id_remap_data->flag & ID_REMAP_FORCE_NEVER_NULL_USAGE) == 0); + const bool skip_never_null = (id_remap_data->flag & ID_REMAP_SKIP_NEVER_NULL_USAGE) != 0; + + if ((id_remap_data->flag & ID_REMAP_FLAG_NEVER_NULL_USAGE) && (cb_flag & IDWALK_NEVER_NULL)) { + id->tag |= LIB_TAG_DOIT; + } + + /* Special hack in case it's Object->data and we are in edit mode (skipped_direct too). */ + if ((is_never_null && skip_never_null) || + (is_obj_editmode && (((Object *)id)->data == *id_p)) || + (skip_indirect && (is_proxy || is_indirect))) + { + if (is_never_null || is_proxy || is_obj_editmode) { + id_remap_data->skipped_direct++; + } + else { + id_remap_data->skipped_indirect++; + } + if (cb_flag & IDWALK_USER) { + id_remap_data->skipped_refcounted++; + } + else if (cb_flag & IDWALK_USER_ONE) { + /* No need to count number of times this happens, just a flag is enough. */ + id_remap_data->status |= ID_REMAP_IS_USER_ONE_SKIPPED; + } + } + else { + if (!is_never_null) { + *id_p = new_id; + } + if (cb_flag & IDWALK_USER) { + id_us_min(old_id); + /* We do not want to handle LIB_TAG_INDIRECT/LIB_TAG_EXTERN here. */ + if (new_id) + new_id->us++; + } + else if (cb_flag & IDWALK_USER_ONE) { + id_us_ensure_real(new_id); + /* We cannot affect old_id->us directly, LIB_TAG_EXTRAUSER(_SET) are assumed to be set as needed, + * that extra user is processed in final handling... */ + } + if (!is_indirect) { + id_remap_data->status |= ID_REMAP_IS_LINKED_DIRECT; + } + } + } + + return IDWALK_RET_NOP; +} + +/** + * Execute the 'data' part of the remapping (that is, all ID pointers from other ID datablocks). + * + * Behavior differs depending on whether given \a id is NULL or not: + * - \a id NULL: \a old_id must be non-NULL, \a new_id may be NULL (unlinking \a old_id) or not + * (remapping \a old_id to \a new_id). The whole \a bmain database is checked, and all pointers to \a old_id + * are remapped to \a new_id. + * - \a id is non-NULL: + * + If \a old_id is NULL, \a new_id must also be NULL, and all ID pointers from \a id are cleared (i.e. \a id + * does not references any other datablock anymore). + * + If \a old_id is non-NULL, behavior is as with a NULL \a id, but only for given \a id. + * + * \param bmain: the Main data storage to operate on (can be NULL if \a id is non-NULL). + * \param id: the datablock to operate on (can be NULL if \a bmain is non-NULL). + * \param old_id: the datablock to dereference (may be NULL if \a id is non-NULL). + * \param new_id: the new datablock to replace \a old_id references with (may be NULL). + * \param r_id_remap_data: if non-NULL, the IDRemap struct to use (uselful to retrieve info about remapping process). + */ +static void libblock_remap_data( + Main *bmain, ID *id, ID *old_id, ID *new_id, const short remap_flags, IDRemap *r_id_remap_data) +{ + IDRemap id_remap_data; + ListBase *lb_array[MAX_LIBARRAY]; + int i; + + if (r_id_remap_data == NULL) { + r_id_remap_data = &id_remap_data; + } + r_id_remap_data->old_id = old_id; + r_id_remap_data->new_id = new_id; + r_id_remap_data->id = NULL; + r_id_remap_data->flag = remap_flags; + r_id_remap_data->status = 0; + r_id_remap_data->skipped_direct = 0; + r_id_remap_data->skipped_indirect = 0; + r_id_remap_data->skipped_refcounted = 0; + + if (id) { +#ifdef DEBUG_PRINT + printf("\tchecking id %s (%p, %p)\n", id->name, id, id->lib); +#endif + r_id_remap_data->id = id; + BKE_library_foreach_ID_link(id, foreach_libblock_remap_callback, (void *)r_id_remap_data, IDWALK_NOP); + } + else { + i = set_listbasepointers(bmain, lb_array); + + /* Note that this is a very 'bruteforce' approach, maybe we could use some depsgraph to only process + * objects actually using given old_id... sounds rather unlikely currently, though, so this will do for now. */ + + while (i--) { + ID *id_curr = lb_array[i]->first; + + for (; id_curr; id_curr = id_curr->next) { + /* Note that we cannot skip indirect usages of old_id here (if requested), we still need to check it for + * the user count handling... + * XXX No more true (except for debug usage of those skipping counters). */ + r_id_remap_data->id = id_curr; + BKE_library_foreach_ID_link( + id_curr, foreach_libblock_remap_callback, (void *)r_id_remap_data, IDWALK_NOP); + } + } + } + + /* XXX We may not want to always 'transfer' fakeuser from old to new id... Think for now it's desired behavior + * though, we can always add an option (flag) to control this later if needed. */ + if (old_id && (old_id->flag & LIB_FAKEUSER)) { + id_fake_user_clear(old_id); + id_fake_user_set(new_id); + } + + id_us_clear_real(old_id); + + if (new_id && (new_id->tag & LIB_TAG_INDIRECT) && (r_id_remap_data->status & ID_REMAP_IS_LINKED_DIRECT)) { + new_id->tag &= ~LIB_TAG_INDIRECT; + new_id->tag |= LIB_TAG_EXTERN; + } + +#ifdef DEBUG_PRINT + printf("%s: %d occurences skipped (%d direct and %d indirect ones)\n", __func__, + r_id_remap_data->skipped_direct + r_id_remap_data->skipped_indirect, + r_id_remap_data->skipped_direct, r_id_remap_data->skipped_indirect); +#endif +} + +/** + * Replace all references in given Main to \a old_id by \a new_id + * (if \a new_id is NULL, it unlinks \a old_id). + */ +void BKE_libblock_remap_locked( + Main *bmain, void *old_idv, void *new_idv, + const short remap_flags) +{ + IDRemap id_remap_data; + ID *old_id = old_idv; + ID *new_id = new_idv; + int skipped_direct, skipped_refcounted; + + BLI_assert(old_id != NULL); + BLI_assert((new_id == NULL) || GS(old_id->name) == GS(new_id->name)); + BLI_assert(old_id != new_id); + + /* Some pre-process updates. + * This is a bit ugly, but cannot see a way to avoid it. Maybe we should do a per-ID callback for this instead? + */ + if (GS(old_id->name) == ID_OB) { + Object *old_ob = (Object *)old_id; + Object *new_ob = (Object *)new_id; + + if (new_ob == NULL) { + Scene *sce; + Base *base; + + for (sce = bmain->scene.first; sce; sce = sce->id.next) { + base = BKE_scene_base_find(sce, old_ob); + + if (base) { + id_us_min((ID *)base->object); + BKE_scene_base_unlink(sce, base); + MEM_freeN(base); + } + } + } + } + + libblock_remap_data(bmain, NULL, old_id, new_id, remap_flags, &id_remap_data); + + if (free_notifier_reference_cb) { + free_notifier_reference_cb(old_id); + } + + /* We assume editors do not hold references to their IDs... This is false in some cases + * (Image is especially tricky here), editors' code is to handle refcount (id->us) itself then. */ + if (remap_editor_id_reference_cb) { + remap_editor_id_reference_cb(old_id, new_id); + } + + skipped_direct = id_remap_data.skipped_direct; + skipped_refcounted = id_remap_data.skipped_refcounted; + + /* If old_id was used by some ugly 'user_one' stuff (like Image or Clip editors...), and user count has actually + * been incremented for that, we have to decrease once more its user count... unless we had to skip + * some 'user_one' cases. */ + if ((old_id->tag & LIB_TAG_EXTRAUSER_SET) && !(id_remap_data.status & ID_REMAP_IS_USER_ONE_SKIPPED)) { + id_us_min(old_id); + old_id->tag &= ~LIB_TAG_EXTRAUSER_SET; + } + + BLI_assert(old_id->us - skipped_refcounted >= 0); + UNUSED_VARS_NDEBUG(skipped_refcounted); + + if (skipped_direct == 0) { + /* old_id is assumed to not be used directly anymore... */ + if (old_id->lib && (old_id->tag & LIB_TAG_EXTERN)) { + old_id->tag &= ~LIB_TAG_EXTERN; + old_id->tag |= LIB_TAG_INDIRECT; + } + } + + /* Some after-process updates. + * This is a bit ugly, but cannot see a way to avoid it. Maybe we should do a per-ID callback for this instead? + */ + switch (GS(old_id->name)) { + case ID_OB: + { + Object *old_ob = (Object *)old_id; + Object *new_ob = (Object *)new_id; + + if (old_ob->flag & OB_FROMGROUP) { + /* Note that for Scene's BaseObject->flag, either we: + * - unlinked old_ob (i.e. new_ob is NULL), in which case scenes' bases have been removed already. + * - remaped old_ob by new_ob, in which case scenes' bases are still valid as is. + * So in any case, no need to update them here. */ + if (BKE_group_object_find(NULL, old_ob) == NULL) { + old_ob->flag &= ~OB_FROMGROUP; + } + if (new_ob == NULL) { /* We need to remove NULL-ified groupobjects... */ + Group *group; + for (group = bmain->group.first; group; group = group->id.next) { + BKE_group_object_unlink(group, NULL, NULL, NULL); + } + } + else { + new_ob->flag |= OB_FROMGROUP; + } + } + break; + } + case ID_GR: + if (new_id == NULL) { /* Only affects us in case group was unlinked. */ + for (Scene *sce = bmain->scene.first; sce; sce = sce->id.next) { + /* Note that here we assume no object has no base (i.e. all objects are assumed instanced + * in one scene...). */ + for (Base *base = sce->base.first; base; base = base->next) { + if (base->flag & OB_FROMGROUP) { + Object *ob = base->object; + + if (ob->flag & OB_FROMGROUP) { + Group *grp = BKE_group_object_find(NULL, ob); + + /* Unlinked group (old_id) is still in bmain... */ + if (grp && (&grp->id == old_id)) { + grp = BKE_group_object_find(grp, ob); + } + if (!grp) { + ob->flag &= ~OB_FROMGROUP; + } + } + if (!(ob->flag & OB_FROMGROUP)) { + base->flag &= ~OB_FROMGROUP; + } + } + } + } + } + break; + default: + break; + } + + /* Full rebuild of DAG! */ + DAG_relations_tag_update(bmain); +} + +void BKE_libblock_remap(Main *bmain, void *old_idv, void *new_idv, const short remap_flags) +{ + BKE_main_lock(bmain); + + BKE_libblock_remap_locked(bmain, old_idv, new_idv, remap_flags); + + BKE_main_unlock(bmain); +} + +/** + * Unlink given \a id from given \a bmain (does not touch to indirect, i.e. library, usages of the ID). + * + * \param do_flag_never_null: If true, all IDs using \a idv in a 'non-NULL' way are flagged by \a LIB_TAG_DOIT flag + * (quite obviously, 'non-NULL' usages can never be unlinked by this function...). + */ +void BKE_libblock_unlink(Main *bmain, void *idv, const bool do_flag_never_null) +{ + const short remap_flags = ID_REMAP_SKIP_INDIRECT_USAGE | (do_flag_never_null ? ID_REMAP_FLAG_NEVER_NULL_USAGE : 0); + + BKE_main_lock(bmain); + + BKE_libblock_remap_locked(bmain, idv, NULL, remap_flags); + + BKE_main_unlock(bmain); +} + +/** Similar to libblock_remap, but only affects IDs used by given \a idv ID. + * + * \param old_idv: Unlike BKE_libblock_remap, can be NULL, + * in which case all ID usages by given \a idv will be cleared. + * \param us_min_never_null: If \a true and new_id is NULL, + * 'NEVER_NULL' ID usages keep their old id, but this one still gets its user count decremented + * (needed when given \a idv is going to be deleted right after being unlinked). + */ +/* Should be able to replace all _relink() funcs (constraints, rigidbody, etc.) ? */ +/* XXX Arg! Naming... :( + * _relink? avoids confusion with _remap, but is confusing with _unlink + * _remap_used_ids? + * _remap_datablocks? + * BKE_id_remap maybe? + * ... sigh + */ +void BKE_libblock_relink_ex( + void *idv, void *old_idv, void *new_idv, const bool us_min_never_null) +{ + ID *id = idv; + ID *old_id = old_idv; + ID *new_id = new_idv; + int remap_flags = us_min_never_null ? 0 : ID_REMAP_SKIP_NEVER_NULL_USAGE; + + /* No need to lock here, we are only affecting given ID. */ + + BLI_assert(id); + if (old_id) { + BLI_assert((new_id == NULL) || GS(old_id->name) == GS(new_id->name)); + BLI_assert(old_id != new_id); + } + else { + BLI_assert(new_id == NULL); + } + + libblock_remap_data(NULL, id, old_id, new_id, remap_flags, NULL); +} + +static void animdata_dtar_clear_cb(ID *UNUSED(id), AnimData *adt, void *userdata) +{ + ChannelDriver *driver; + FCurve *fcu; + + /* find the driver this belongs to and update it */ + for (fcu = adt->drivers.first; fcu; fcu = fcu->next) { + driver = fcu->driver; + + if (driver) { + DriverVar *dvar; + for (dvar = driver->variables.first; dvar; dvar = dvar->next) { + DRIVER_TARGETS_USED_LOOPER(dvar) + { + if (dtar->id == userdata) + dtar->id = NULL; + } + DRIVER_TARGETS_LOOPER_END + } + } + } +} + +void BKE_libblock_free_data(Main *bmain, ID *id) +{ + if (id->properties) { + IDP_FreeProperty(id->properties); + MEM_freeN(id->properties); + } + + /* this ID may be a driver target! */ + BKE_animdata_main_cb(bmain, animdata_dtar_clear_cb, (void *)id); +} + +/** + * used in headerbuttons.c image.c mesh.c screen.c sound.c and library.c + * + * \param do_id_user: if \a true, try to release other ID's 'references' hold by \a idv. + */ +void BKE_libblock_free_ex(Main *bmain, void *idv, const bool do_id_user) +{ + ID *id = idv; + short type = GS(id->name); + ListBase *lb = which_libbase(bmain, type); + + DAG_id_type_tag(bmain, type); + +#ifdef WITH_PYTHON + BPY_id_release(id); +#endif + + if (do_id_user) { + BKE_libblock_relink_ex(id, NULL, NULL, true); + } + + switch (type) { + case ID_SCE: + BKE_scene_free((Scene *)id); + break; + case ID_LI: + BKE_library_free((Library *)id); + break; + case ID_OB: + BKE_object_free((Object *)id); + break; + case ID_ME: + BKE_mesh_free((Mesh *)id); + break; + case ID_CU: + BKE_curve_free((Curve *)id); + break; + case ID_MB: + BKE_mball_free((MetaBall *)id); + break; + case ID_MA: + BKE_material_free((Material *)id); + break; + case ID_TE: + BKE_texture_free((Tex *)id); + break; + case ID_IM: + BKE_image_free((Image *)id); + break; + case ID_LT: + BKE_lattice_free((Lattice *)id); + break; + case ID_LA: + BKE_lamp_free((Lamp *)id); + break; + case ID_CA: + BKE_camera_free((Camera *) id); + break; + case ID_IP: /* Deprecated. */ + BKE_ipo_free((Ipo *)id); + break; + case ID_KE: + BKE_key_free((Key *)id); + break; + case ID_WO: + BKE_world_free((World *)id); + break; + case ID_SCR: + BKE_screen_free((bScreen *)id); + break; + case ID_VF: + BKE_vfont_free((VFont *)id); + break; + case ID_TXT: + BKE_text_free((Text *)id); + break; + case ID_SPK: + BKE_speaker_free((Speaker *)id); + break; + case ID_SO: + BKE_sound_free((bSound *)id); + break; + case ID_GR: + BKE_group_free((Group *)id); + break; + case ID_AR: + BKE_armature_free((bArmature *)id); + break; + case ID_AC: + BKE_action_free((bAction *)id); + break; + case ID_NT: + ntreeFreeTree((bNodeTree *)id); + break; + case ID_BR: + BKE_brush_free((Brush *)id); + break; + case ID_WM: + if (free_windowmanager_cb) + free_windowmanager_cb(NULL, (wmWindowManager *)id); + break; + case ID_GD: + BKE_gpencil_free((bGPdata *)id); + break; + case ID_MC: + BKE_movieclip_free((MovieClip *)id); + break; + case ID_MSK: + BKE_mask_free((Mask *)id); + break; + case ID_LS: + BKE_linestyle_free((FreestyleLineStyle *)id); + break; + case ID_PAL: + BKE_palette_free((Palette *)id); + break; + case ID_PC: + BKE_paint_curve_free((PaintCurve *)id); + break; + } + + /* avoid notifying on removed data */ + BKE_main_lock(bmain); + + if (free_notifier_reference_cb) { + free_notifier_reference_cb(id); + } + + if (remap_editor_id_reference_cb) { + remap_editor_id_reference_cb(id, NULL); + } + + BLI_remlink(lb, id); + + BKE_libblock_free_data(bmain, id); + BKE_main_unlock(bmain); + + MEM_freeN(id); +} + +void BKE_libblock_free(Main *bmain, void *idv) +{ + BKE_libblock_free_ex(bmain, idv, true); +} + +void BKE_libblock_free_us(Main *bmain, void *idv) /* test users */ +{ + ID *id = idv; + + id_us_min(id); + + /* XXX This is a temp (2.77) hack so that we keep same behavior as in 2.76 regarding groups when deleting an object. + * Since only 'user_one' usage of objects is groups, and only 'real user' usage of objects is scenes, + * removing that 'user_one' tag when there is no more real (scene) users of an object ensures it gets + * fully unlinked. + * Otherwise, there is no real way to get rid of an object anymore - better handling of this is TODO. + */ + if ((GS(id->name) == ID_OB) && (id->us == 1)) { + id_us_clear_real(id); + } + + if (id->us == 0) { + BKE_libblock_unlink(bmain, id, false); + + BKE_libblock_free(bmain, id); + } +} + +void BKE_libblock_delete(Main *bmain, void *idv) +{ + ListBase *lbarray[MAX_LIBARRAY]; + int base_count, i; + + base_count = set_listbasepointers(bmain, lbarray); + BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); + + /* First tag all datablocks directly from target lib. + * Note that we go forward here, since we want to check dependencies before users (e.g. meshes before objects). + * Avoids to have to loop twice. */ + for (i = 0; i < base_count; i++) { + ListBase *lb = lbarray[i]; + ID *id; + + for (id = lb->first; id; id = id->next) { + /* Note: in case we delete a library, we also delete all its datablocks! */ + if ((id == (ID *)idv) || (id->lib == (Library *)idv) || (id->tag & LIB_TAG_DOIT)) { + id->tag |= LIB_TAG_DOIT; + /* Will tag 'never NULL' users of this ID too. + * Note that we cannot use BKE_libblock_unlink() here, since it would ignore indirect (and proxy!) + * links, this can lead to nasty crashing here in second, actual deleting loop. + * Also, this will also flag users of deleted data that cannot be unlinked + * (object using deleted obdata, etc.), so that they also get deleted. */ + BKE_libblock_remap(bmain, id, NULL, ID_REMAP_FLAG_NEVER_NULL_USAGE | ID_REMAP_FORCE_NEVER_NULL_USAGE); + } + } + } + + /* In usual reversed order, such that all usage of a given ID, even 'never NULL' ones, have been already cleared + * when we reach it (e.g. Objects being processed before meshes, they'll have already released their 'reference' + * over meshes when we come to freeing obdata). */ + for (i = base_count; i--; ) { + ListBase *lb = lbarray[i]; + ID *id, *id_next; + + for (id = lb->first; id; id = id_next) { + id_next = id->next; + if (id->tag & LIB_TAG_DOIT) { + if (id->us != 0) { +#ifdef DEBUG_PRINT + printf("%s: deleting %s (%d)\n", __func__, id->name, id->us); +#endif + BLI_assert(id->us == 0); + } + BKE_libblock_free(bmain, id); + } + } + } +} diff --git a/source/blender/blenkernel/intern/linestyle.c b/source/blender/blenkernel/intern/linestyle.c index 5a1dfc04045..30dc48819e9 100644 --- a/source/blender/blenkernel/intern/linestyle.c +++ b/source/blender/blenkernel/intern/linestyle.c @@ -125,26 +125,25 @@ FreestyleLineStyle *BKE_linestyle_new(struct Main *bmain, const char *name) return linestyle; } +/** Free (or release) any data used by this linestyle (does not free the linestyle itself). */ void BKE_linestyle_free(FreestyleLineStyle *linestyle) { LineStyleModifier *m; - - MTex *mtex; int a; + BKE_animdata_free(&linestyle->id, false); + for (a = 0; a < MAX_MTEX; a++) { - mtex = linestyle->mtex[a]; - if (mtex && mtex->tex) - id_us_min(&mtex->tex->id); - if (mtex) - MEM_freeN(mtex); + MEM_SAFE_FREE(linestyle->mtex[a]); } + + /* is no lib link block, but linestyle extension */ if (linestyle->nodetree) { ntreeFreeTree(linestyle->nodetree); MEM_freeN(linestyle->nodetree); + linestyle->nodetree = NULL; } - BKE_animdata_free(&linestyle->id); while ((m = (LineStyleModifier *)linestyle->color_modifiers.first)) BKE_linestyle_color_modifier_remove(linestyle, m); while ((m = (LineStyleModifier *)linestyle->alpha_modifiers.first)) @@ -1452,33 +1451,6 @@ char *BKE_linestyle_path_to_color_ramp(FreestyleLineStyle *linestyle, ColorBand return NULL; } -void BKE_linestyle_target_object_unlink(FreestyleLineStyle *linestyle, struct Object *ob) -{ - LineStyleModifier *m; - - for (m = (LineStyleModifier *)linestyle->color_modifiers.first; m; m = m->next) { - if (m->type == LS_MODIFIER_DISTANCE_FROM_OBJECT) { - if (((LineStyleColorModifier_DistanceFromObject *)m)->target == ob) { - ((LineStyleColorModifier_DistanceFromObject *)m)->target = NULL; - } - } - } - for (m = (LineStyleModifier *)linestyle->alpha_modifiers.first; m; m = m->next) { - if (m->type == LS_MODIFIER_DISTANCE_FROM_OBJECT) { - if (((LineStyleAlphaModifier_DistanceFromObject *)m)->target == ob) { - ((LineStyleAlphaModifier_DistanceFromObject *)m)->target = NULL; - } - } - } - for (m = (LineStyleModifier *)linestyle->thickness_modifiers.first; m; m = m->next) { - if (m->type == LS_MODIFIER_DISTANCE_FROM_OBJECT) { - if (((LineStyleThicknessModifier_DistanceFromObject *)m)->target == ob) { - ((LineStyleThicknessModifier_DistanceFromObject *)m)->target = NULL; - } - } - } -} - bool BKE_linestyle_use_textures(FreestyleLineStyle *linestyle, const bool use_shading_nodes) { if (use_shading_nodes) { diff --git a/source/blender/blenkernel/intern/mask.c b/source/blender/blenkernel/intern/mask.c index 930a3c487ec..94e53755ac4 100644 --- a/source/blender/blenkernel/intern/mask.c +++ b/source/blender/blenkernel/intern/mask.c @@ -1010,63 +1010,10 @@ void BKE_mask_layer_free_list(ListBase *masklayers) } } -/** free for temp copy, but don't manage unlinking from other pointers */ -void BKE_mask_free_nolib(Mask *mask) +/** Free (or release) any data used by this mask (does not free the mask itself). */ +void BKE_mask_free(Mask *mask) { - BKE_mask_layer_free_list(&mask->masklayers); -} - -void BKE_mask_free(Main *bmain, Mask *mask) -{ - bScreen *scr; - ScrArea *area; - SpaceLink *sl; - Scene *scene; - - for (scr = bmain->screen.first; scr; scr = scr->id.next) { - for (area = scr->areabase.first; area; area = area->next) { - for (sl = area->spacedata.first; sl; sl = sl->next) { - switch (sl->spacetype) { - case SPACE_CLIP: - { - SpaceClip *sc = (SpaceClip *)sl; - - if (sc->mask_info.mask == mask) { - sc->mask_info.mask = NULL; - } - break; - } - case SPACE_IMAGE: - { - SpaceImage *sima = (SpaceImage *)sl; - - if (sima->mask_info.mask == mask) { - sima->mask_info.mask = NULL; - } - break; - } - } - } - } - } - - for (scene = bmain->scene.first; scene; scene = scene->id.next) { - if (scene->ed) { - Sequence *seq; - - SEQ_BEGIN (scene->ed, seq) - { - if (seq->mask == mask) { - seq->mask = NULL; - } - } - SEQ_END - } - } - - FOREACH_NODETREE(bmain, ntree, id) { - BKE_node_tree_unlink_id((ID *)mask, ntree); - } FOREACH_NODETREE_END + BKE_animdata_free((ID *)mask, false); /* free mask data */ BKE_mask_layer_free_list(&mask->masklayers); diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index 30f82a50ed9..db5ac54ada9 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -81,47 +81,33 @@ void init_def_material(void) BKE_material_init(&defmaterial); } -/* not material itself */ +/** Free (or release) any data used by this material (does not free the material itself). */ void BKE_material_free(Material *ma) { - BKE_material_free_ex(ma, true); -} - -/* not material itself */ -void BKE_material_free_ex(Material *ma, bool do_id_user) -{ - MTex *mtex; int a; + + BKE_animdata_free((ID *)ma, false); for (a = 0; a < MAX_MTEX; a++) { - mtex = ma->mtex[a]; - if (do_id_user && mtex && mtex->tex) - id_us_min(&mtex->tex->id); - if (mtex) - MEM_freeN(mtex); + MEM_SAFE_FREE(ma->mtex[a]); } - if (ma->ramp_col) MEM_freeN(ma->ramp_col); - if (ma->ramp_spec) MEM_freeN(ma->ramp_spec); - - BKE_animdata_free((ID *)ma); - - if (ma->preview) - BKE_previewimg_free(&ma->preview); - BKE_icon_id_delete((struct ID *)ma); - ma->id.icon_id = 0; + MEM_SAFE_FREE(ma->ramp_col); + MEM_SAFE_FREE(ma->ramp_spec); /* is no lib link block, but material extension */ if (ma->nodetree) { - ntreeFreeTree_ex(ma->nodetree, do_id_user); + ntreeFreeTree(ma->nodetree); MEM_freeN(ma->nodetree); + ma->nodetree = NULL; } - if (ma->texpaintslot) - MEM_freeN(ma->texpaintslot); + MEM_SAFE_FREE(ma->texpaintslot); + + GPU_material_free(&ma->gpumaterial); - if (ma->gpumaterial.first) - GPU_material_free(&ma->gpumaterial); + BKE_icon_id_delete((ID *)ma); + BKE_previewimg_free(&ma->preview); } void BKE_material_init(Material *ma) @@ -1840,7 +1826,7 @@ void free_matcopybuf(void) matcopybuf.ramp_spec = NULL; if (matcopybuf.nodetree) { - ntreeFreeTree_ex(matcopybuf.nodetree, false); + ntreeFreeTree(matcopybuf.nodetree); MEM_freeN(matcopybuf.nodetree); matcopybuf.nodetree = NULL; } diff --git a/source/blender/blenkernel/intern/mball.c b/source/blender/blenkernel/intern/mball.c index d7019aa8458..685cd35fc20 100644 --- a/source/blender/blenkernel/intern/mball.c +++ b/source/blender/blenkernel/intern/mball.c @@ -66,28 +66,13 @@ /* Functions */ -void BKE_mball_unlink(MetaBall *mb) +/** Free (or release) any data used by this mball (does not free the mball itself). */ +void BKE_mball_free(MetaBall *mb) { - int a; - - for (a = 0; a < mb->totcol; a++) { - if (mb->mat[a]) - id_us_min(&mb->mat[a]->id); - mb->mat[a] = NULL; - } -} + BKE_animdata_free((ID *)mb, false); + MEM_SAFE_FREE(mb->mat); -/* do not free mball itself */ -void BKE_mball_free(MetaBall *mb) -{ - BKE_mball_unlink(mb); - - if (mb->adt) { - BKE_animdata_free((ID *)mb); - mb->adt = NULL; - } - if (mb->mat) MEM_freeN(mb->mat); BLI_freelistN(&mb->elems); if (mb->disp.first) BKE_displist_free(&mb->disp); } diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index 2af78cca79f..4e47dfcce74 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -432,33 +432,11 @@ bool BKE_mesh_has_custom_loop_normals(Mesh *me) * we need a more generic method, like the expand() functions in * readfile.c */ -void BKE_mesh_unlink(Mesh *me) -{ - int a; - - if (me == NULL) return; - - if (me->mat) { - for (a = 0; a < me->totcol; a++) { - if (me->mat[a]) - id_us_min(&me->mat[a]->id); - me->mat[a] = NULL; - } - } - if (me->key) { - id_us_min(&me->key->id); - } - me->key = NULL; - - if (me->texcomesh) me->texcomesh = NULL; -} - -/* do not free mesh itself */ -void BKE_mesh_free(Mesh *me, int unlink) +/** Free (or release) any data used by this mesh (does not free the mesh itself). */ +void BKE_mesh_free(Mesh *me) { - if (unlink) - BKE_mesh_unlink(me); + BKE_animdata_free(&me->id, false); CustomData_free(&me->vdata, me->totvert); CustomData_free(&me->edata, me->totedge); @@ -466,16 +444,10 @@ void BKE_mesh_free(Mesh *me, int unlink) CustomData_free(&me->ldata, me->totloop); CustomData_free(&me->pdata, me->totpoly); - if (me->adt) { - BKE_animdata_free(&me->id); - me->adt = NULL; - } - - if (me->mat) MEM_freeN(me->mat); - - if (me->bb) MEM_freeN(me->bb); - if (me->mselect) MEM_freeN(me->mselect); - if (me->edit_btmesh) MEM_freeN(me->edit_btmesh); + MEM_SAFE_FREE(me->mat); + MEM_SAFE_FREE(me->bb); + MEM_SAFE_FREE(me->mselect); + MEM_SAFE_FREE(me->edit_btmesh); } static void mesh_tessface_clear_intern(Mesh *mesh, int free_customdata) diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c index d2bfcfb0887..5f667732b04 100644 --- a/source/blender/blenkernel/intern/movieclip.c +++ b/source/blender/blenkernel/intern/movieclip.c @@ -1248,7 +1248,7 @@ static void free_buffers(MovieClip *clip) clip->anim = NULL; } - BKE_animdata_free((ID *) clip); + BKE_animdata_free((ID *) clip, false); } void BKE_movieclip_clear_cache(MovieClip *clip) @@ -1482,8 +1482,10 @@ void BKE_movieclip_build_proxy_frame_for_ibuf(MovieClip *clip, ImBuf *ibuf, stru } } +/** Free (or release) any data used by this movie clip (does not free the clip itself). */ void BKE_movieclip_free(MovieClip *clip) { + /* Also frees animdata. */ free_buffers(clip); BKE_tracking_free(&clip->tracking); diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index 2b381f6ff0b..d78ddc41e97 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -1782,21 +1782,21 @@ static void free_localized_node_groups(bNodeTree *ntree) for (node = ntree->nodes.first; node; node = node->next) { if (node->type == NODE_GROUP && node->id) { bNodeTree *ngroup = (bNodeTree *)node->id; - ntreeFreeTree_ex(ngroup, false); + ntreeFreeTree(ngroup); MEM_freeN(ngroup); } } } -/* do not free ntree itself here, BKE_libblock_free calls this function too */ -void ntreeFreeTree_ex(bNodeTree *ntree, const bool do_id_user) +/** Free (or release) any data used by this nodetree (does not free the nodetree itself). */ +void ntreeFreeTree(bNodeTree *ntree) { bNodeTree *tntree; bNode *node, *next; bNodeSocket *sock, *nextsock; - - if (ntree == NULL) return; - + + BKE_animdata_free((ID *)ntree, false); + /* XXX hack! node trees should not store execution graphs at all. * This should be removed when old tree types no longer require it. * Currently the execution data for texture nodes remains in the tree @@ -1820,29 +1820,10 @@ void ntreeFreeTree_ex(bNodeTree *ntree, const bool do_id_user) /* unregister associated RNA types */ ntreeInterfaceTypeFree(ntree); - BKE_animdata_free((ID *)ntree); - - id_us_min((ID *)ntree->gpd); - BLI_freelistN(&ntree->links); /* do first, then unlink_node goes fast */ for (node = ntree->nodes.first; node; node = next) { next = node->next; - - /* ntreeUserIncrefID inline */ - - /* XXX, this is correct, however when freeing the entire database - * this ends up accessing freed data which isn't properly unlinking - * its self from scene nodes, SO - for now prefer invalid usercounts - * on free rather then bad memory access - Campbell */ -#if 0 - if (do_id_user) { - id_us_min(node->id); - } -#else - (void)do_id_user; -#endif - node_free_node_ex(ntree, node, false, false); } @@ -1874,11 +1855,6 @@ void ntreeFreeTree_ex(bNodeTree *ntree, const bool do_id_user) BKE_libblock_free_data(G.main, &ntree->id); } } -/* same as ntreeFreeTree_ex but always manage users */ -void ntreeFreeTree(bNodeTree *ntree) -{ - ntreeFreeTree_ex(ntree, true); -} void ntreeFreeCache(bNodeTree *ntree) { @@ -2165,7 +2141,7 @@ void ntreeLocalMerge(bNodeTree *localtree, bNodeTree *ntree) if (ntree->typeinfo->local_merge) ntree->typeinfo->local_merge(localtree, ntree); - ntreeFreeTree_ex(localtree, false); + ntreeFreeTree(localtree); MEM_freeN(localtree); } } diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 51bc0b597c7..357f01e0961 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -303,14 +303,14 @@ void BKE_object_free_derived_caches(Object *ob) if (ob->type == OB_MESH) { Mesh *me = ob->data; - if (me->bb) { + if (me && me->bb) { me->bb->flag |= BOUNDBOX_DIRTY; } } else if (ELEM(ob->type, OB_SURF, OB_CURVE, OB_FONT)) { Curve *cu = ob->data; - if (cu->bb) { + if (cu && cu->bb) { cu->bb->flag |= BOUNDBOX_DIRTY; } } @@ -347,77 +347,52 @@ void BKE_object_free_caches(Object *object) } } -/* do not free object itself */ -void BKE_object_free_ex(Object *ob, bool do_id_user) +/** Free (or release) any data used by this object (does not free the object itself). */ +void BKE_object_free(Object *ob) { - int a; - + BKE_animdata_free((ID *)ob, false); + BKE_object_free_modifiers(ob); - - /* disconnect specific data, but not for lib data (might be indirect data, can get relinked) */ - if (ob->data) { - ID *id = ob->data; - id_us_min(id); - if (id->us == 0 && id->lib == NULL) { - switch (ob->type) { - case OB_MESH: - BKE_mesh_unlink((Mesh *)id); - break; - case OB_CURVE: - BKE_curve_unlink((Curve *)id); - break; - case OB_MBALL: - BKE_mball_unlink((MetaBall *)id); - break; - } - } - ob->data = NULL; - } - if (ob->mat) { - for (a = 0; a < ob->totcol; a++) { - if (ob->mat[a]) - id_us_min(&ob->mat[a]->id); - } - MEM_freeN(ob->mat); + MEM_SAFE_FREE(ob->mat); + MEM_SAFE_FREE(ob->matbits); + MEM_SAFE_FREE(ob->iuser); + MEM_SAFE_FREE(ob->bb); + + BLI_freelistN(&ob->defbase); + if (ob->pose) { + BKE_pose_free_ex(ob->pose, false); + ob->pose = NULL; } - if (ob->matbits) MEM_freeN(ob->matbits); - ob->mat = NULL; - ob->matbits = NULL; - if (ob->iuser) MEM_freeN(ob->iuser); - ob->iuser = NULL; - if (ob->bb) MEM_freeN(ob->bb); - ob->bb = NULL; - if (ob->adt) BKE_animdata_free((ID *)ob); - if (ob->poselib) - id_us_min(&ob->poselib->id); - if (ob->gpd) - id_us_min(&ob->gpd->id); - if (ob->defbase.first) - BLI_freelistN(&ob->defbase); - if (ob->pose) - BKE_pose_free_ex(ob->pose, do_id_user); - if (ob->mpath) + if (ob->mpath) { animviz_free_motionpath(ob->mpath); + ob->mpath = NULL; + } BKE_bproperty_free_list(&ob->prop); - + free_sensors(&ob->sensors); free_controllers(&ob->controllers); free_actuators(&ob->actuators); - BKE_constraints_free_ex(&ob->constraints, do_id_user); + BKE_constraints_free_ex(&ob->constraints, false); free_partdeflect(ob->pd); BKE_rigidbody_free_object(ob); BKE_rigidbody_free_constraint(ob); - if (ob->soft) sbFree(ob->soft); - if (ob->bsoft) bsbFree(ob->bsoft); - if (ob->gpulamp.first) GPU_lamp_free(ob); + if (ob->soft) { + sbFree(ob->soft); + ob->soft = NULL; + } + if (ob->bsoft) { + bsbFree(ob->bsoft); + ob->bsoft = NULL; + } + GPU_lamp_free(ob); BKE_sculptsession_free(ob); - if (ob->pc_ids.first) BLI_freelistN(&ob->pc_ids); + BLI_freelistN(&ob->pc_ids); BLI_freelistN(&ob->lodlevels); @@ -427,337 +402,12 @@ void BKE_object_free_ex(Object *ob, bool do_id_user) if (ob->curve_cache->path) free_path(ob->curve_cache->path); MEM_freeN(ob->curve_cache); + ob->curve_cache = NULL; } BKE_previewimg_free(&ob->preview); } -void BKE_object_free(Object *ob) -{ - BKE_object_free_ex(ob, true); -} - -static void unlink_object__unlinkModifierLinks(void *userData, Object *ob, Object **obpoin, int UNUSED(cd_flag)) -{ - Object *unlinkOb = userData; - - if (*obpoin == unlinkOb) { - *obpoin = NULL; - // XXX: should this just be OB_RECALC_DATA? - DAG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); - } -} - -void BKE_object_unlink(Main *bmain, Object *ob) -{ - Object *obt; - Material *mat; - World *wrld; - bScreen *sc; - Scene *sce; - SceneRenderLayer *srl; - FreestyleLineSet *lineset; - bNodeTree *ntree; - Curve *cu; - Tex *tex; - Group *group; - Camera *camera; - bConstraint *con; - //bActionStrip *strip; // XXX animsys - ModifierData *md; - ARegion *ar; - RegionView3D *rv3d; - LodLevel *lod; - int a, found; - - unlink_controllers(&ob->controllers); - unlink_actuators(&ob->actuators); - - /* check all objects: parents en bevels and fields, also from libraries */ - /* FIXME: need to check all animation blocks (drivers) */ - obt = bmain->object.first; - while (obt) { - if (obt->proxy == ob) - obt->proxy = NULL; - if (obt->proxy_from == ob) { - obt->proxy_from = NULL; - DAG_id_tag_update(&obt->id, OB_RECALC_OB); - } - if (obt->proxy_group == ob) - obt->proxy_group = NULL; - - if (obt->parent == ob) { - obt->parent = NULL; - DAG_id_tag_update(&obt->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); - } - - modifiers_foreachObjectLink(obt, unlink_object__unlinkModifierLinks, ob); - - if (ELEM(obt->type, OB_CURVE, OB_FONT)) { - cu = obt->data; - - if (cu->bevobj == ob) { - cu->bevobj = NULL; - DAG_id_tag_update(&obt->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); - } - if (cu->taperobj == ob) { - cu->taperobj = NULL; - DAG_id_tag_update(&obt->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); - } - if (cu->textoncurve == ob) { - cu->textoncurve = NULL; - DAG_id_tag_update(&obt->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); - } - } - else if (obt->type == OB_ARMATURE && obt->pose) { - bPoseChannel *pchan; - for (pchan = obt->pose->chanbase.first; pchan; pchan = pchan->next) { - for (con = pchan->constraints.first; con; con = con->next) { - const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); - ListBase targets = {NULL, NULL}; - bConstraintTarget *ct; - - if (cti && cti->get_constraint_targets) { - cti->get_constraint_targets(con, &targets); - - for (ct = targets.first; ct; ct = ct->next) { - if (ct->tar == ob) { - ct->tar = NULL; - ct->subtarget[0] = '\0'; - DAG_id_tag_update(&obt->id, OB_RECALC_DATA); - } - } - - if (cti->flush_constraint_targets) - cti->flush_constraint_targets(con, &targets, 0); - } - } - if (pchan->custom == ob) - pchan->custom = NULL; - } - } - else if (ELEM(OB_MBALL, ob->type, obt->type)) { - if (BKE_mball_is_basis_for(obt, ob)) - DAG_id_tag_update(&obt->id, OB_RECALC_DATA); - } - - sca_remove_ob_poin(obt, ob); - - for (con = obt->constraints.first; con; con = con->next) { - const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); - ListBase targets = {NULL, NULL}; - bConstraintTarget *ct; - - if (cti && cti->get_constraint_targets) { - cti->get_constraint_targets(con, &targets); - - for (ct = targets.first; ct; ct = ct->next) { - if (ct->tar == ob) { - ct->tar = NULL; - ct->subtarget[0] = '\0'; - DAG_id_tag_update(&obt->id, OB_RECALC_DATA); - } - } - - if (cti->flush_constraint_targets) - cti->flush_constraint_targets(con, &targets, 0); - } - } - - /* object is deflector or field */ - if (ob->pd) { - if (obt->soft) - DAG_id_tag_update(&obt->id, OB_RECALC_DATA); - - /* cloth */ - for (md = obt->modifiers.first; md; md = md->next) - if (md->type == eModifierType_Cloth) - DAG_id_tag_update(&obt->id, OB_RECALC_DATA); - } - - /* strips */ -#if 0 // XXX old animation system - for (strip = obt->nlastrips.first; strip; strip = strip->next) { - if (strip->object == ob) - strip->object = NULL; - - if (strip->modifiers.first) { - bActionModifier *amod; - for (amod = strip->modifiers.first; amod; amod = amod->next) - if (amod->ob == ob) - amod->ob = NULL; - } - } -#endif // XXX old animation system - - /* levels of detail */ - for (lod = obt->lodlevels.first; lod; lod = lod->next) { - if (lod->source == ob) - lod->source = NULL; - } - - obt = obt->id.next; - } - - /* materials */ - for (mat = bmain->mat.first; mat; mat = mat->id.next) { - if (mat->nodetree) { - ntreeSwitchID(mat->nodetree, &ob->id, NULL); - } - for (a = 0; a < MAX_MTEX; a++) { - if (mat->mtex[a] && ob == mat->mtex[a]->object) { - /* actually, test for lib here... to do */ - mat->mtex[a]->object = NULL; - } - } - } - - /* node trees */ - for (ntree = bmain->nodetree.first; ntree; ntree = ntree->id.next) { - if (ntree->type == NTREE_SHADER) - ntreeSwitchID(ntree, &ob->id, NULL); - } - - /* textures */ - for (tex = bmain->tex.first; tex; tex = tex->id.next) { - if (tex->env && (ob == tex->env->object)) tex->env->object = NULL; - if (tex->pd && (ob == tex->pd->object)) tex->pd->object = NULL; - if (tex->vd && (ob == tex->vd->object)) tex->vd->object = NULL; - } - - /* worlds */ - wrld = bmain->world.first; - while (wrld) { - if (wrld->id.lib == NULL) { - for (a = 0; a < MAX_MTEX; a++) { - if (wrld->mtex[a] && ob == wrld->mtex[a]->object) - wrld->mtex[a]->object = NULL; - } - } - - wrld = wrld->id.next; - } - - /* scenes */ - sce = bmain->scene.first; - while (sce) { - if (sce->id.lib == NULL) { - if (sce->camera == ob) sce->camera = NULL; - if (sce->toolsettings->skgen_template == ob) sce->toolsettings->skgen_template = NULL; - -#ifdef DURIAN_CAMERA_SWITCH - { - TimeMarker *m; - - for (m = sce->markers.first; m; m = m->next) { - if (m->camera == ob) - m->camera = NULL; - } - } -#endif - if (sce->ed) { - Sequence *seq; - SEQ_BEGIN(sce->ed, seq) - { - if (seq->scene_camera == ob) { - seq->scene_camera = NULL; - } - } - SEQ_END - } - - for (srl = sce->r.layers.first; srl; srl = srl->next) { - for (lineset = (FreestyleLineSet *)srl->freestyleConfig.linesets.first; - lineset; lineset = lineset->next) - { - if (lineset->linestyle) { - BKE_linestyle_target_object_unlink(lineset->linestyle, ob); - } - } - } - } - - sce = sce->id.next; - } - - /* screens */ - sc = bmain->screen.first; - while (sc) { - ScrArea *sa = sc->areabase.first; - while (sa) { - SpaceLink *sl; - - for (sl = sa->spacedata.first; sl; sl = sl->next) { - if (sl->spacetype == SPACE_VIEW3D) { - View3D *v3d = (View3D *) sl; - - /* found doesn't need to be set here */ - if (v3d->ob_centre == ob) { - v3d->ob_centre = NULL; - v3d->ob_centre_bone[0] = '\0'; - } - if (v3d->localvd && v3d->localvd->ob_centre == ob) { - v3d->localvd->ob_centre = NULL; - v3d->localvd->ob_centre_bone[0] = '\0'; - } - - found = 0; - if (v3d->camera == ob) { - v3d->camera = NULL; - found = 1; - } - if (v3d->localvd && v3d->localvd->camera == ob) { - v3d->localvd->camera = NULL; - found += 2; - } - - if (found) { - if (sa->spacetype == SPACE_VIEW3D) { - for (ar = sa->regionbase.first; ar; ar = ar->next) { - if (ar->regiontype == RGN_TYPE_WINDOW) { - rv3d = (RegionView3D *)ar->regiondata; - if (found == 1 || found == 3) { - if (rv3d->persp == RV3D_CAMOB) - rv3d->persp = RV3D_PERSP; - } - if (found == 2 || found == 3) { - if (rv3d->localvd && rv3d->localvd->persp == RV3D_CAMOB) - rv3d->localvd->persp = RV3D_PERSP; - } - } - } - } - } - } -#if 0 - else if (ELEM(sl->spacetype, SPACE_OUTLINER, SPACE_BUTS, SPACE_NODE)) { - /* now handled by WM_main_remove_editor_id_reference */ - } -#endif - } - - sa = sa->next; - } - sc = sc->id.next; - } - - /* groups */ - group = bmain->group.first; - while (group) { - BKE_group_object_unlink(group, ob, NULL, NULL); - group = group->id.next; - } - - /* cameras */ - camera = bmain->camera.first; - while (camera) { - if (camera->dof_ob == ob) { - camera->dof_ob = NULL; - } - camera = camera->id.next; - } -} - /* actual check for internal data, not context or flags */ bool BKE_object_is_in_editmode(Object *ob) { @@ -3959,8 +3609,9 @@ bool BKE_object_modifier_update_subframe(Scene *scene, Object *ob, bool update_m /* was originally OB_RECALC_ALL - TODO - which flags are really needed??? */ ob->recalc |= OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME; BKE_animsys_evaluate_animdata(scene, &ob->id, ob->adt, frame, ADT_RECALC_ANIM); - if (update_mesh) + if (update_mesh) { BKE_object_handle_update(G.main->eval_ctx, scene, ob); + } else BKE_object_where_is_calc_time(scene, ob, frame); diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index 3a2663c5d48..8c1502643c5 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -298,13 +298,11 @@ void BKE_paint_brush_set(Paint *p, Brush *br) } } +/** Free (or release) any data used by this paint curve (does not free the pcurve itself). */ void BKE_paint_curve_free(PaintCurve *pc) { - if (pc->points) { - MEM_freeN(pc->points); - pc->points = NULL; - pc->tot_points = 0; - } + MEM_SAFE_FREE(pc->points); + pc->tot_points = 0; } PaintCurve *BKE_paint_curve_add(Main *bmain, const char *name) @@ -378,6 +376,7 @@ Palette *BKE_palette_add(Main *bmain, const char *name) return palette; } +/** Free (or release) any data used by this palette (does not free the palette itself). */ void BKE_palette_free(Palette *palette) { BLI_freelistN(&palette->colors); @@ -493,8 +492,6 @@ void BKE_paint_init(Scene *sce, PaintMode mode, const char col[3]) void BKE_paint_free(Paint *paint) { - id_us_min((ID *)paint->brush); - id_us_min((ID *)paint->palette); curvemapping_free(paint->cavity_curve); } diff --git a/source/blender/blenkernel/intern/sca.c b/source/blender/blenkernel/intern/sca.c index e90a39e8c0e..a468420f87d 100644 --- a/source/blender/blenkernel/intern/sca.c +++ b/source/blender/blenkernel/intern/sca.c @@ -653,77 +653,6 @@ void set_sca_new_poins(void) } } -void sca_remove_ob_poin(Object *obt, Object *ob) -{ - bSensor *sens; - bMessageSensor *ms; - bActuator *act; - bCameraActuator *ca; - bObjectActuator *oa; - bSceneActuator *sa; - bEditObjectActuator *eoa; - bPropertyActuator *pa; - bMessageActuator *ma; - bParentActuator *para; - bArmatureActuator *aa; - bSteeringActuator *sta; - - - sens= obt->sensors.first; - while (sens) { - switch (sens->type) { - case SENS_MESSAGE: - ms= sens->data; - if (ms->fromObject==ob) ms->fromObject= NULL; - } - sens= sens->next; - } - - act= obt->actuators.first; - while (act) { - switch (act->type) { - case ACT_CAMERA: - ca= act->data; - if (ca->ob==ob) ca->ob= NULL; - break; - case ACT_OBJECT: - oa= act->data; - if (oa->reference==ob) oa->reference= NULL; - break; - case ACT_PROPERTY: - pa= act->data; - if (pa->ob==ob) pa->ob= NULL; - break; - case ACT_SCENE: - sa= act->data; - if (sa->camera==ob) sa->camera= NULL; - break; - case ACT_EDIT_OBJECT: - eoa= act->data; - if (eoa->ob==ob) eoa->ob= NULL; - break; - case ACT_MESSAGE: - ma= act->data; - if (ma->toObject==ob) ma->toObject= NULL; - break; - case ACT_PARENT: - para = act->data; - if (para->ob==ob) para->ob = NULL; - break; - case ACT_ARMATURE: - aa = act->data; - if (aa->target == ob) aa->target = NULL; - if (aa->subtarget == ob) aa->subtarget = NULL; - break; - case ACT_STEERING: - sta = act->data; - if (sta->navmesh == ob) sta->navmesh = NULL; - if (sta->target == ob) sta->target = NULL; - } - act= act->next; - } -} - /* ******************** INTERFACE ******************* */ void sca_move_sensor(bSensor *sens_to_move, Object *ob, int move_up) { diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 79d5ac2ec51..4a649c2ea6d 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -356,41 +356,34 @@ void BKE_scene_groups_relink(Scene *sce) BKE_rigidbody_world_groups_relink(sce->rigidbody_world); } -/* do not free scene itself */ +/** Free (or release) any data used by this scene (does not free the scene itself). */ void BKE_scene_free(Scene *sce) { - Base *base; SceneRenderLayer *srl; + BKE_animdata_free((ID *)sce, false); + /* check all sequences */ BKE_sequencer_clear_scene_in_allseqs(G.main, sce); - base = sce->base.first; - while (base) { - id_us_min(&base->object->id); - base = base->next; - } - /* do not free objects! */ - - if (sce->gpd) { -#if 0 /* removed since this can be invalid memory when freeing everything */ - /* since the grease pencil data is freed before the scene. - * since grease pencil data is not (yet?), shared between objects - * its probably safe not to do this, some save and reload will free this. */ - id_us_min(&sce->gpd->id); -#endif - sce->gpd = NULL; - } - + sce->basact = NULL; BLI_freelistN(&sce->base); BKE_sequencer_editing_free(sce); - BKE_animdata_free((ID *)sce); BKE_keyingsets_free(&sce->keyingsets); - - if (sce->rigidbody_world) + + /* is no lib link block, but scene extension */ + if (sce->nodetree) { + ntreeFreeTree(sce->nodetree); + MEM_freeN(sce->nodetree); + sce->nodetree = NULL; + } + + if (sce->rigidbody_world) { BKE_rigidbody_free_world(sce->rigidbody_world); - + sce->rigidbody_world = NULL; + } + if (sce->r.avicodecdata) { free_avicodecdata(sce->r.avicodecdata); MEM_freeN(sce->r.avicodecdata); @@ -443,15 +436,8 @@ void BKE_scene_free(Scene *sce) if (sce->depsgraph) DEG_graph_free(sce->depsgraph); - if (sce->nodetree) { - ntreeFreeTree(sce->nodetree); - MEM_freeN(sce->nodetree); - } - - if (sce->stats) - MEM_freeN(sce->stats); - if (sce->fps_info) - MEM_freeN(sce->fps_info); + MEM_SAFE_FREE(sce->stats); + MEM_SAFE_FREE(sce->fps_info); BKE_sound_destroy_scene(sce); @@ -884,40 +870,6 @@ Scene *BKE_scene_set_name(Main *bmain, const char *name) return NULL; } -void BKE_scene_unlink(Main *bmain, Scene *sce, Scene *newsce) -{ - Scene *sce1; - bScreen *screen; - - /* check all sets */ - for (sce1 = bmain->scene.first; sce1; sce1 = sce1->id.next) - if (sce1->set == sce) - sce1->set = NULL; - - for (sce1 = bmain->scene.first; sce1; sce1 = sce1->id.next) { - bNode *node; - - if (sce1 == sce || !sce1->nodetree) - continue; - - for (node = sce1->nodetree->nodes.first; node; node = node->next) { - if (node->id == &sce->id) - node->id = NULL; - } - } - - /* all screens */ - for (screen = bmain->screen.first; screen; screen = screen->id.next) { - if (screen->scene == sce) { - screen->scene = newsce; - } - - /* editors are handled by WM_main_remove_editor_id_reference */ - } - - BKE_libblock_free(bmain, sce); -} - /* Used by metaballs, return *all* objects (including duplis) existing in the scene (including scene's sets) */ int BKE_scene_base_iter_next(EvaluationContext *eval_ctx, SceneBaseIter *iter, Scene **scene, int val, Base **base, Object **ob) @@ -1173,6 +1125,8 @@ void BKE_scene_base_unlink(Scene *sce, Base *base) BKE_rigidbody_remove_object(sce, base->object); BLI_remlink(&sce->base, base); + if (sce->basact == base) + sce->basact = NULL; } void BKE_scene_base_deselect_all(Scene *sce) diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index 139c6670f74..857bd5447c8 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -273,17 +273,18 @@ void BKE_spacedata_draw_locks(int set) } } -static void (*spacedata_id_unref_cb)(struct SpaceLink *sl, const struct ID *id) = NULL; +static void (*spacedata_id_remap_cb)(struct ScrArea *sa, struct SpaceLink *sl, ID *old_id, ID *new_id) = NULL; -void BKE_spacedata_callback_id_unref_set(void (*func)(struct SpaceLink *sl, const struct ID *)) +void BKE_spacedata_callback_id_remap_set(void (*func)(ScrArea *sa, SpaceLink *sl, ID *, ID *)) { - spacedata_id_unref_cb = func; + spacedata_id_remap_cb = func; } -void BKE_spacedata_id_unref(struct SpaceLink *sl, const struct ID *id) +/* UNUSED!!! */ +void BKE_spacedata_id_unref(struct ScrArea *sa, struct SpaceLink *sl, struct ID *id) { - if (spacedata_id_unref_cb) { - spacedata_id_unref_cb(sl, id); + if (spacedata_id_remap_cb) { + spacedata_id_remap_cb(sa, sl, id, NULL); } } @@ -358,11 +359,13 @@ void BKE_screen_area_free(ScrArea *sa) BLI_freelistN(&sa->actionzones); } -/* don't free screen itself */ +/** Free (or release) any data used by this screen (does not free the screen itself). */ void BKE_screen_free(bScreen *sc) { ScrArea *sa, *san; ARegion *ar; + + /* No animdata here. */ for (ar = sc->regionbase.first; ar; ar = ar->next) BKE_area_region_free(NULL, ar); diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c index c82f3a3af23..5ef502e0182 100644 --- a/source/blender/blenkernel/intern/sequencer.c +++ b/source/blender/blenkernel/intern/sequencer.c @@ -3092,7 +3092,7 @@ static ImBuf *seq_render_mask(const SeqRenderData *context, Mask *mask, float nr BKE_maskrasterize_handle_init(mr_handle, mask_temp, context->rectx, context->recty, true, true, true); - BKE_mask_free_nolib(mask_temp); + BKE_mask_free(mask_temp); MEM_freeN(mask_temp); BKE_maskrasterize_buffer(mr_handle, context->rectx, context->recty, maskbuf); @@ -5154,7 +5154,7 @@ Sequence *BKE_sequencer_add_sound_strip(bContext *C, ListBase *seqbasep, SeqLoad info = AUD_getInfo(sound->playback_handle); if (info.specs.channels == AUD_CHANNELS_INVALID) { - BKE_sound_delete(bmain, sound); + BKE_libblock_free(bmain, sound); #if 0 if (op) BKE_report(op->reports, RPT_ERROR, "Unsupported audio format"); diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c index b016f8a49ed..414be73e234 100644 --- a/source/blender/blenkernel/intern/sound.c +++ b/source/blender/blenkernel/intern/sound.c @@ -123,8 +123,11 @@ bSound *BKE_sound_new_file_exists(struct Main *bmain, const char *filepath) return BKE_sound_new_file_exists_ex(bmain, filepath, NULL); } +/** Free (or release) any data used by this sound (does not free the sound itself). */ void BKE_sound_free(bSound *sound) { + /* No animdata here. */ + if (sound->packedfile) { freePackedFile(sound->packedfile); sound->packedfile = NULL; @@ -148,8 +151,7 @@ void BKE_sound_free(bSound *sound) BLI_spin_end(sound->spinlock); MEM_freeN(sound->spinlock); sound->spinlock = NULL; - } - + } #endif /* WITH_AUDASPACE */ } @@ -315,15 +317,6 @@ bSound *BKE_sound_new_limiter(struct Main *bmain, bSound *source, float start, f } #endif -void BKE_sound_delete(struct Main *bmain, bSound *sound) -{ - if (sound) { - BKE_sound_free(sound); - - BKE_libblock_free(bmain, sound); - } -} - void BKE_sound_cache(bSound *sound) { sound->flags |= SOUND_FLAGS_CACHING; diff --git a/source/blender/blenkernel/intern/speaker.c b/source/blender/blenkernel/intern/speaker.c index c452065fbad..e5075a2d382 100644 --- a/source/blender/blenkernel/intern/speaker.c +++ b/source/blender/blenkernel/intern/speaker.c @@ -141,8 +141,5 @@ void BKE_speaker_make_local(Speaker *spk) void BKE_speaker_free(Speaker *spk) { - if (spk->sound) - id_us_min(&spk->sound->id); - - BKE_animdata_free((ID *)spk); + BKE_animdata_free((ID *)spk, false); } diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c index 6def9e3e503..594f9dffbee 100644 --- a/source/blender/blenkernel/intern/text.c +++ b/source/blender/blenkernel/intern/text.c @@ -171,14 +171,17 @@ void BKE_text_free_lines(Text *text) text->curl = text->sell = NULL; } +/** Free (or release) any data used by this text (does not free the text itself). */ void BKE_text_free(Text *text) { + /* No animdata here. */ + BKE_text_free_lines(text); - if (text->name) MEM_freeN(text->name); - MEM_freeN(text->undo_buf); + MEM_SAFE_FREE(text->name); + MEM_SAFE_FREE(text->undo_buf); #ifdef WITH_PYTHON - if (text->compiled) BPY_text_free_code(text); + BPY_text_free_code(text); #endif } diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c index 69152f23476..3e4f13a2c8f 100644 --- a/source/blender/blenkernel/intern/texture.c +++ b/source/blender/blenkernel/intern/texture.c @@ -556,23 +556,38 @@ int colorband_element_remove(struct ColorBand *coba, int index) /* ******************* TEX ************************ */ +/** Free (or release) any data used by this texture (does not free the texure itself). */ void BKE_texture_free(Tex *tex) { - if (tex->coba) MEM_freeN(tex->coba); - if (tex->env) BKE_texture_envmap_free(tex->env); - if (tex->pd) BKE_texture_pointdensity_free(tex->pd); - if (tex->vd) BKE_texture_voxeldata_free(tex->vd); - if (tex->ot) BKE_texture_ocean_free(tex->ot); - BKE_animdata_free((struct ID *)tex); - - BKE_previewimg_free(&tex->preview); - BKE_icon_id_delete((struct ID *)tex); - tex->id.icon_id = 0; - + BKE_animdata_free((ID *)tex, false); + + /* is no lib link block, but texture extension */ if (tex->nodetree) { ntreeFreeTree(tex->nodetree); MEM_freeN(tex->nodetree); + tex->nodetree = NULL; } + + MEM_SAFE_FREE(tex->coba); + if (tex->env) { + BKE_texture_envmap_free(tex->env); + tex->env = NULL; + } + if (tex->pd) { + BKE_texture_pointdensity_free(tex->pd); + tex->pd = NULL; + } + if (tex->vd) { + BKE_texture_voxeldata_free(tex->vd); + tex->vd = NULL; + } + if (tex->ot) { + BKE_texture_ocean_free(tex->ot); + tex->ot = NULL; + } + + BKE_icon_id_delete((ID *)tex); + BKE_previewimg_free(&tex->preview); } /* ------------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/intern/world.c b/source/blender/blenkernel/intern/world.c index 17a2e7f14fd..ec021586be5 100644 --- a/source/blender/blenkernel/intern/world.c +++ b/source/blender/blenkernel/intern/world.c @@ -52,38 +52,28 @@ #include "GPU_material.h" -void BKE_world_free_ex(World *wrld, bool do_id_user) +/** Free (or release) any data used by this world (does not free the world itself). */ +void BKE_world_free(World *wrld) { - MTex *mtex; int a; - + + BKE_animdata_free((ID *)wrld, false); + for (a = 0; a < MAX_MTEX; a++) { - mtex = wrld->mtex[a]; - if (do_id_user && mtex && mtex->tex) - id_us_min(&mtex->tex->id); - if (mtex) - MEM_freeN(mtex); + MEM_SAFE_FREE(wrld->mtex[a]); } - BKE_previewimg_free(&wrld->preview); - - BKE_animdata_free((ID *)wrld); /* is no lib link block, but world extension */ if (wrld->nodetree) { - ntreeFreeTree_ex(wrld->nodetree, do_id_user); + ntreeFreeTree(wrld->nodetree); MEM_freeN(wrld->nodetree); + wrld->nodetree = NULL; } - if (wrld->gpumaterial.first) - GPU_material_free(&wrld->gpumaterial); + GPU_material_free(&wrld->gpumaterial); BKE_icon_id_delete((struct ID *)wrld); - wrld->id.icon_id = 0; -} - -void BKE_world_free(World *wrld) -{ - BKE_world_free_ex(wrld, true); + BKE_previewimg_free(&wrld->preview); } void BKE_world_init(World *wrld) diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h index bf47682297d..c85cf128643 100644 --- a/source/blender/blenloader/BLO_readfile.h +++ b/source/blender/blenloader/BLO_readfile.h @@ -100,7 +100,8 @@ struct ID *BLO_library_link_named_part(struct Main *mainl, BlendHandle **bh, con struct ID *BLO_library_link_named_part_ex( struct Main *mainl, BlendHandle **bh, const short idcode, const char *name, const short flag, - struct Scene *scene, struct View3D *v3d); + struct Scene *scene, struct View3D *v3d, + const bool use_placeholders, const bool force_indirect); void BLO_library_link_end(struct Main *mainl, BlendHandle **bh, short flag, struct Scene *scene, struct View3D *v3d); void BLO_library_link_copypaste(struct Main *mainl, BlendHandle *bh); diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index ea373364376..3262f740b09 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -113,6 +113,7 @@ #include "BKE_action.h" #include "BKE_armature.h" +#include "BKE_blender_version.h" #include "BKE_brush.h" #include "BKE_cloth.h" #include "BKE_constraint.h" @@ -7896,6 +7897,9 @@ static void do_versions(FileData *fd, Library *lib, Main *main) blo_do_versions_260(fd, lib, main); blo_do_versions_270(fd, lib, main); + main->versionfile = BLENDER_VERSION; + main->subversionfile = BLENDER_SUBVERSION; + /* WATCH IT!!!: pointers from libdata have not been converted yet here! */ /* WATCH IT 2!: Userdef struct init see do_versions_userdef() above! */ @@ -9288,13 +9292,13 @@ static void give_base_to_groups( } } -static ID *create_placeholder(Main *mainvar, const char *idname, const short tag) +static ID *create_placeholder(Main *mainvar, const short idcode, const char *idname, const short tag) { - const short idcode = GS(idname); ListBase *lb = which_libbase(mainvar, idcode); ID *ph_id = BKE_libblock_alloc_notest(idcode); - memcpy(ph_id->name, idname, sizeof(ph_id->name)); + *((short *)ph_id->name) = idcode; + BLI_strncpy(ph_id->name + 2, idname, sizeof(ph_id->name) - 2); BKE_libblock_init_empty(ph_id); ph_id->lib = mainvar->curlib; ph_id->tag = tag | LIB_TAG_MISSING; @@ -9309,7 +9313,9 @@ static ID *create_placeholder(Main *mainvar, const char *idname, const short tag /* returns true if the item was found * but it may already have already been appended/linked */ -static ID *link_named_part(Main *mainl, FileData *fd, const short idcode, const char *name) +static ID *link_named_part( + Main *mainl, FileData *fd, const short idcode, const char *name, + const bool use_placeholders, const bool force_indirect) { BHead *bhead = find_bhead_from_code_name(fd, idcode, name); ID *id; @@ -9320,7 +9326,7 @@ static ID *link_named_part(Main *mainl, FileData *fd, const short idcode, const id = is_yet_read(fd, mainl, bhead); if (id == NULL) { /* not read yet */ - read_libblock(fd, mainl, bhead, LIB_TAG_TESTEXT, &id); + read_libblock(fd, mainl, bhead, force_indirect ? LIB_TAG_TESTIND : LIB_TAG_TESTEXT, &id); if (id) { /* sort by name in list */ @@ -9333,18 +9339,22 @@ static ID *link_named_part(Main *mainl, FileData *fd, const short idcode, const if (G.debug) printf("append: already linked\n"); oldnewmap_insert(fd->libmap, bhead->old, id, bhead->code); - if (id->tag & LIB_TAG_INDIRECT) { + if (!force_indirect && (id->tag & LIB_TAG_INDIRECT)) { id->tag &= ~LIB_TAG_INDIRECT; id->tag |= LIB_TAG_EXTERN; } } } + else if (use_placeholders) { + /* XXX flag part is weak! */ + id = create_placeholder(mainl, idcode, name, force_indirect ? LIB_TAG_INDIRECT : LIB_TAG_EXTERN); + } else { id = NULL; } /* if we found the id but the id is NULL, this is really bad */ - BLI_assert((bhead != NULL) == (id != NULL)); + BLI_assert(!((bhead != NULL) && (id == NULL))); return id; } @@ -9416,9 +9426,9 @@ void BLO_library_link_copypaste(Main *mainl, BlendHandle *bh) static ID *link_named_part_ex( Main *mainl, FileData *fd, const short idcode, const char *name, const short flag, - Scene *scene, View3D *v3d) + Scene *scene, View3D *v3d, const bool use_placeholders, const bool force_indirect) { - ID *id = link_named_part(mainl, fd, idcode, name); + ID *id = link_named_part(mainl, fd, idcode, name, use_placeholders, force_indirect); if (id && (GS(id->name) == ID_OB)) { /* loose object: give a base */ link_object_postprocess(id, scene, v3d, flag); @@ -9444,7 +9454,7 @@ static ID *link_named_part_ex( ID *BLO_library_link_named_part(Main *mainl, BlendHandle **bh, const short idcode, const char *name) { FileData *fd = (FileData*)(*bh); - return link_named_part(mainl, fd, idcode, name); + return link_named_part(mainl, fd, idcode, name, false, false); } /** @@ -9458,15 +9468,18 @@ ID *BLO_library_link_named_part(Main *mainl, BlendHandle **bh, const short idcod * \param flag Options for linking, used for instantiating. * \param scene The scene in which to instantiate objects/groups (if NULL, no instantiation is done). * \param v3d The active View3D (only to define active layers for instantiated objects & groups, can be NULL). + * \param use_placeholders If true, generate a placeholder (empty ID) if not found in current lib file. + * \param force_indirect If true, force loaded ID to be tagged as LIB_TAG_INDIRECT (used in reload context only). * \return the linked ID when found. */ ID *BLO_library_link_named_part_ex( Main *mainl, BlendHandle **bh, const short idcode, const char *name, const short flag, - Scene *scene, View3D *v3d) + Scene *scene, View3D *v3d, + const bool use_placeholders, const bool force_indirect) { FileData *fd = (FileData*)(*bh); - return link_named_part_ex(mainl, fd, idcode, name, flag, scene, v3d); + return link_named_part_ex(mainl, fd, idcode, name, flag, scene, v3d, use_placeholders, force_indirect); } static void link_id_part(ReportList *reports, FileData *fd, Main *mainvar, ID *id, ID **r_id) @@ -9506,7 +9519,7 @@ static void link_id_part(ReportList *reports, FileData *fd, Main *mainvar, ID *i /* Generate a placeholder for this ID (simplified version of read_libblock actually...). */ if (r_id) { - *r_id = is_valid ? create_placeholder(mainvar, id->name, id->tag) : NULL; + *r_id = is_valid ? create_placeholder(mainvar, GS(id->name), id->name + 2, id->tag) : NULL; } } } diff --git a/source/blender/compositor/operations/COM_MaskOperation.cpp b/source/blender/compositor/operations/COM_MaskOperation.cpp index 220b4e908a6..d6e5fdf86bb 100644 --- a/source/blender/compositor/operations/COM_MaskOperation.cpp +++ b/source/blender/compositor/operations/COM_MaskOperation.cpp @@ -94,7 +94,7 @@ void MaskOperation::initExecution() frame_iter += frame_step; } - BKE_mask_free_nolib(mask_temp); + BKE_mask_free(mask_temp); MEM_freeN(mask_temp); } } diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c index 0bd969252d5..b690695ae69 100644 --- a/source/blender/editors/animation/anim_channels_edit.c +++ b/source/blender/editors/animation/anim_channels_edit.c @@ -2115,7 +2115,7 @@ static int animchannels_clean_empty_exec(bContext *C, wmOperator *UNUSED(op)) /* remove AnimData? */ if (action_empty && nla_empty && drivers_empty) { - BKE_animdata_free(id); + BKE_animdata_free(id, true); } } diff --git a/source/blender/editors/include/ED_buttons.h b/source/blender/editors/include/ED_buttons.h index 1aee13a8ccf..636c583b828 100644 --- a/source/blender/editors/include/ED_buttons.h +++ b/source/blender/editors/include/ED_buttons.h @@ -36,6 +36,4 @@ bool ED_texture_context_check_lamp(const struct bContext *C); bool ED_texture_context_check_linestyle(const struct bContext *C); bool ED_texture_context_check_others(const struct bContext *C); -void ED_buttons_id_unref(struct SpaceButs *sbuts, const struct ID *id); - #endif /* __ED_BUTTONS_H__ */ diff --git a/source/blender/editors/include/ED_node.h b/source/blender/editors/include/ED_node.h index 7fe9a0c320c..f7b9d6b4f9e 100644 --- a/source/blender/editors/include/ED_node.h +++ b/source/blender/editors/include/ED_node.h @@ -103,8 +103,6 @@ void ED_node_set_active(struct Main *bmain, struct bNodeTree *ntree, struct bNod void ED_node_composite_job(const struct bContext *C, struct bNodeTree *nodetree, struct Scene *scene_owner); -void ED_node_id_unref(struct SpaceNode *snode, const ID *id); - /* node_ops.c */ void ED_operatormacros_node(void); diff --git a/source/blender/editors/include/ED_outliner.h b/source/blender/editors/include/ED_outliner.h index af4af8e2f5d..73ee2542247 100644 --- a/source/blender/editors/include/ED_outliner.h +++ b/source/blender/editors/include/ED_outliner.h @@ -27,10 +27,4 @@ #ifndef __ED_OUTLINER_H__ #define __ED_OUTLINER_H__ -struct ID; -struct SpaceOops; - -/* Used to check whether a given texture context is valid in current context. */ -void ED_outliner_id_unref(struct SpaceOops *so, const struct ID *id); - #endif /* __ED_OUTLINER_H__ */ diff --git a/source/blender/editors/include/ED_util.h b/source/blender/editors/include/ED_util.h index f143ea478c6..b6b80b93e0b 100644 --- a/source/blender/editors/include/ED_util.h +++ b/source/blender/editors/include/ED_util.h @@ -43,7 +43,7 @@ void ED_editors_exit(struct bContext *C); bool ED_editors_flush_edits(const struct bContext *C, bool for_render); -void ED_spacedata_id_unref(struct SpaceLink *sl, const struct ID *id); +void ED_spacedata_id_remap(struct ScrArea *sa, struct SpaceLink *sl, struct ID *old_id, struct ID *new_id); void ED_OT_flush_edits(struct wmOperatorType *ot); diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index c23c2eed890..baab6b6100d 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -403,4 +403,6 @@ void ED_view3d_shade_update(struct Main *bmain, struct Scene *scene, struct View #define V3D_IS_ZBUF(v3d) \ (((v3d)->flag & V3D_ZBUF_SELECT) && ((v3d)->drawtype > OB_WIRE)) +void ED_view3d_id_remap(struct View3D *v3d, const struct ID *old_id, struct ID *new_id); + #endif /* __ED_VIEW3D_H__ */ diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c index 8b16b2a977e..62656d75b9a 100644 --- a/source/blender/editors/mesh/editmesh_undo.c +++ b/source/blender/editors/mesh/editmesh_undo.c @@ -637,7 +637,7 @@ static void free_undo(void *um_v) MEM_freeN(me->key); } - BKE_mesh_free(me, false); + BKE_mesh_free(me); MEM_freeN(me); } diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 0ceb177164a..9a38a292656 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -1111,7 +1111,6 @@ void ED_base_object_free_and_unlink(Main *bmain, Scene *scene, Base *base) BKE_scene_base_unlink(scene, base); object_delete_check_glsl_update(base->object); BKE_libblock_free_us(bmain, base->object); - if (scene->basact == base) scene->basact = NULL; MEM_freeN(base); } @@ -1287,7 +1286,7 @@ static void make_object_duplilist_real(bContext *C, Scene *scene, Base *base, basen->object = ob; /* make sure apply works */ - BKE_animdata_free(&ob->id); + BKE_animdata_free(&ob->id, true); ob->adt = NULL; /* Proxies are not to be copied. */ @@ -1379,7 +1378,6 @@ static void make_object_duplilist_real(bContext *C, Scene *scene, Base *base, } } - /* The same how BKE_object_unlink detects which object proxies to clear. */ if (base->object->transflag & OB_DUPLIGROUP && base->object->dup_group) { for (object = bmain->object.first; object; object = object->id.next) { if (object->proxy_group == base->object) { diff --git a/source/blender/editors/object/object_group.c b/source/blender/editors/object/object_group.c index 76a8a68c42d..2b87a890f0f 100644 --- a/source/blender/editors/object/object_group.c +++ b/source/blender/editors/object/object_group.c @@ -43,6 +43,7 @@ #include "BKE_depsgraph.h" #include "BKE_group.h" #include "BKE_library.h" +#include "BKE_library_remap.h" #include "BKE_main.h" #include "BKE_report.h" #include "BKE_object.h" @@ -527,7 +528,8 @@ static int group_unlink_exec(bContext *C, wmOperator *UNUSED(op)) if (!group) return OPERATOR_CANCELLED; - BKE_group_unlink(bmain, group); + BKE_libblock_unlink(bmain, group, false); + BKE_libblock_free(bmain, group); WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, NULL); diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c index f4260a0cd33..132c3fa5438 100644 --- a/source/blender/editors/render/render_preview.c +++ b/source/blender/editors/render/render_preview.c @@ -70,6 +70,7 @@ #include "BKE_icons.h" #include "BKE_lamp.h" #include "BKE_library.h" +#include "BKE_library_remap.h" #include "BKE_main.h" #include "BKE_material.h" #include "BKE_node.h" @@ -830,7 +831,7 @@ static void shader_preview_free(void *customdata) /* get rid of copied material */ BLI_remlink(&pr_main->mat, sp->matcopy); - BKE_material_free_ex(sp->matcopy, false); + BKE_material_free(sp->matcopy); properties = IDP_GetProperties((ID *)sp->matcopy, false); if (properties) { @@ -862,7 +863,9 @@ static void shader_preview_free(void *customdata) /* get rid of copied world */ BLI_remlink(&pr_main->world, sp->worldcopy); - BKE_world_free_ex(sp->worldcopy, true); /* [#32865] - we need to unlink the texture copies, unlike for materials */ + /* T32865 - we need to unlink the texture copies, unlike for materials */ + BKE_libblock_relink_ex(sp->worldcopy, NULL, NULL, true); + BKE_world_free(sp->worldcopy); properties = IDP_GetProperties((ID *)sp->worldcopy, false); if (properties) { @@ -878,6 +881,7 @@ static void shader_preview_free(void *customdata) /* get rid of copied lamp */ BLI_remlink(&pr_main->lamp, sp->lampcopy); + BKE_libblock_relink_ex(sp->lampcopy, NULL, NULL, true); BKE_lamp_free(sp->lampcopy); properties = IDP_GetProperties((ID *)sp->lampcopy, false); diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index a459f982ada..62aeca4b9d1 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -45,6 +45,7 @@ #include "BKE_image.h" #include "BKE_global.h" #include "BKE_library.h" +#include "BKE_library_remap.h" #include "BKE_main.h" #include "BKE_node.h" #include "BKE_screen.h" @@ -1755,7 +1756,9 @@ bool ED_screen_delete_scene(bContext *C, Scene *scene) ED_screen_set_scene(C, CTX_wm_screen(C), newscene); - BKE_scene_unlink(bmain, scene, newscene); + BKE_libblock_remap(bmain, scene, newscene, ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_NEVER_NULL_USAGE); + + BKE_libblock_free(bmain, scene); return true; } diff --git a/source/blender/editors/sound/sound_ops.c b/source/blender/editors/sound/sound_ops.c index a4d9a920cbb..4931426d62e 100644 --- a/source/blender/editors/sound/sound_ops.c +++ b/source/blender/editors/sound/sound_ops.c @@ -116,7 +116,7 @@ static int sound_open_exec(bContext *C, wmOperator *op) info = AUD_getInfo(sound->playback_handle); if (info.specs.channels == AUD_CHANNELS_INVALID) { - BKE_sound_delete(bmain, sound); + BKE_libblock_free(bmain, sound); if (op->customdata) MEM_freeN(op->customdata); BKE_report(op->reports, RPT_ERROR, "Unsupported audio format"); return OPERATOR_CANCELLED; diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c index 60240109432..671d6bb083e 100644 --- a/source/blender/editors/space_action/space_action.c +++ b/source/blender/editors/space_action/space_action.c @@ -33,6 +33,7 @@ #include <stdio.h> #include "DNA_action_types.h" +#include "DNA_group_types.h" #include "DNA_scene_types.h" #include "MEM_guardedalloc.h" @@ -614,6 +615,19 @@ static void action_refresh(const bContext *C, ScrArea *sa) // XXX re-sizing y-extents of tot should go here? } +static void action_id_remap(ScrArea *UNUSED(sa), SpaceLink *slink, ID *old_id, ID *new_id) +{ + SpaceAction *sact = (SpaceAction *)slink; + + if (!ELEM(GS(old_id->name), ID_GR)) { + return; + } + + if ((ID *)sact->ads.filter_grp == old_id) { + sact->ads.filter_grp = (Group *)new_id; + } +} + /* only called once, from space/spacetypes.c */ void ED_spacetype_action(void) { @@ -631,7 +645,8 @@ void ED_spacetype_action(void) st->keymap = action_keymap; st->listener = action_listener; st->refresh = action_refresh; - + st->id_remap = action_id_remap; + /* regions: main window */ art = MEM_callocN(sizeof(ARegionType), "spacetype action region"); art->regionid = RGN_TYPE_WINDOW; diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c index 23189cb3d1e..2825af9d5cf 100644 --- a/source/blender/editors/space_buttons/buttons_context.c +++ b/source/blender/editors/space_buttons/buttons_context.c @@ -1077,33 +1077,3 @@ ID *buttons_context_id_path(const bContext *C) return NULL; } - -void ED_buttons_id_unref(SpaceButs *sbuts, const ID *id) -{ - if (sbuts->pinid == id) { - sbuts->pinid = NULL; - sbuts->flag &= ~SB_PIN_CONTEXT; - } - - if (sbuts->path) { - ButsContextPath *path = sbuts->path; - int i; - - for (i = 0; i < path->len; i++) { - if (path->ptr[i].id.data == id) { - break; - } - } - - if (i == path->len) { - /* pass */ - } - else if (i == 0) { - MEM_SAFE_FREE(sbuts->path); - } - else { - memset(&path->ptr[i], 0, sizeof(path->ptr[i]) * (path->len - i)); - path->len = i; - } - } -} diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c index 70f29bd9a30..f91a357504d 100644 --- a/source/blender/editors/space_buttons/space_buttons.c +++ b/source/blender/editors/space_buttons/space_buttons.c @@ -45,6 +45,8 @@ #include "WM_api.h" #include "WM_types.h" +#include "RNA_access.h" + #include "buttons_intern.h" /* own include */ /* ******************** default callbacks for buttons space ***************** */ @@ -382,6 +384,59 @@ static void buttons_area_listener(bScreen *UNUSED(sc), ScrArea *sa, wmNotifier * ED_area_tag_redraw(sa); } +static void buttons_id_remap(ScrArea *UNUSED(sa), SpaceLink *slink, ID *old_id, ID *new_id) +{ + SpaceButs *sbuts = (SpaceButs *)slink; + + if (sbuts->pinid == old_id) { + sbuts->pinid = new_id; + if (new_id == NULL) { + sbuts->flag &= ~SB_PIN_CONTEXT; + } + } + + if (sbuts->path) { + ButsContextPath *path = sbuts->path; + int i; + + for (i = 0; i < path->len; i++) { + if (path->ptr[i].id.data == old_id) { + break; + } + } + + if (i == path->len) { + /* pass */ + } + else if (new_id == NULL) { + if (i == 0) { + MEM_SAFE_FREE(sbuts->path); + } + else { + memset(&path->ptr[i], 0, sizeof(path->ptr[i]) * (path->len - i)); + path->len = i; + } + } + else { + RNA_id_pointer_create(new_id, &path->ptr[i]); + /* There is no easy way to check/make path downwards valid, just nullify it. + * Next redraw will rebuild this anyway. */ + i++; + memset(&path->ptr[i], 0, sizeof(path->ptr[i]) * (path->len - i)); + path->len = i; + } + } + + if (sbuts->texuser) { + ButsContextTexture *ct = sbuts->texuser; + if ((ID *)ct->texture == old_id) { + ct->texture = (Tex *)new_id; + } + BLI_freelistN(&ct->users); + ct->user = NULL; + } +} + /* only called once, from space/spacetypes.c */ void ED_spacetype_buttons(void) { @@ -399,7 +454,8 @@ void ED_spacetype_buttons(void) st->keymap = buttons_keymap; st->listener = buttons_area_listener; st->context = buttons_context; - + st->id_remap = buttons_id_remap; + /* regions: main window */ art = MEM_callocN(sizeof(ARegionType), "spacetype buttons region"); art->regionid = RGN_TYPE_WINDOW; diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c index e1d4e4fabc5..415839ab761 100644 --- a/source/blender/editors/space_clip/space_clip.c +++ b/source/blender/editors/space_clip/space_clip.c @@ -45,6 +45,7 @@ #include "BKE_context.h" #include "BKE_screen.h" +#include "BKE_library.h" #include "BKE_movieclip.h" #include "BKE_tracking.h" @@ -1512,6 +1513,25 @@ static void clip_properties_region_listener(bScreen *UNUSED(sc), ScrArea *UNUSED /********************* registration ********************/ +static void clip_id_remap(ScrArea *UNUSED(sa), SpaceLink *slink, ID *old_id, ID *new_id) +{ + SpaceClip *sclip = (SpaceClip *)slink; + + if (!ELEM(GS(old_id->name), ID_MC, ID_MSK)) { + return; + } + + if ((ID *)sclip->clip == old_id) { + sclip->clip = (MovieClip *)new_id; + id_us_ensure_real(new_id); + } + + if ((ID *)sclip->mask_info.mask == old_id) { + sclip->mask_info.mask = (Mask *)new_id; + id_us_ensure_real(new_id); + } +} + /* only called once, from space/spacetypes.c */ void ED_spacetype_clip(void) { @@ -1531,6 +1551,7 @@ void ED_spacetype_clip(void) st->context = clip_context; st->dropboxes = clip_dropboxes; st->refresh = clip_refresh; + st->id_remap = clip_id_remap; /* regions: main window */ art = MEM_callocN(sizeof(ARegionType), "spacetype clip region"); diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c index c6a8a9753d1..a7284694f64 100644 --- a/source/blender/editors/space_graph/space_graph.c +++ b/source/blender/editors/space_graph/space_graph.c @@ -33,6 +33,7 @@ #include <stdio.h> #include "DNA_anim_types.h" +#include "DNA_group_types.h" #include "DNA_scene_types.h" #include "MEM_guardedalloc.h" @@ -627,6 +628,19 @@ static void graph_refresh(const bContext *C, ScrArea *sa) } } +static void graph_id_remap(ScrArea *UNUSED(sa), SpaceLink *slink, ID *old_id, ID *new_id) +{ + SpaceIpo *sgraph = (SpaceIpo *)slink; + + if (!ELEM(GS(old_id->name), ID_GR)) { + return; + } + + if ((ID *)sgraph->ads->filter_grp == old_id) { + sgraph->ads->filter_grp = (Group *)new_id; + } +} + /* only called once, from space/spacetypes.c */ void ED_spacetype_ipo(void) { @@ -644,7 +658,8 @@ void ED_spacetype_ipo(void) st->keymap = graphedit_keymap; st->listener = graph_listener; st->refresh = graph_refresh; - + st->id_remap = graph_id_remap; + /* regions: main window */ art = MEM_callocN(sizeof(ARegionType), "spacetype graphedit region"); art->regionid = RGN_TYPE_WINDOW; diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index 168f9c0dfdf..35a658eac23 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -28,6 +28,7 @@ * \ingroup spimage */ +#include "DNA_gpencil_types.h" #include "DNA_mesh_types.h" #include "DNA_mask_types.h" #include "DNA_meshdata_types.h" @@ -44,6 +45,7 @@ #include "BKE_colortools.h" #include "BKE_context.h" #include "BKE_image.h" +#include "BKE_library.h" #include "BKE_scene.h" #include "BKE_screen.h" @@ -981,6 +983,31 @@ static void image_header_region_listener(bScreen *UNUSED(sc), ScrArea *UNUSED(sa } } +static void image_id_remap(ScrArea *UNUSED(sa), SpaceLink *slink, ID *old_id, ID *new_id) +{ + SpaceImage *simg = (SpaceImage *)slink; + + if (!ELEM(GS(old_id->name), ID_IM, ID_GD, ID_MSK)) { + return; + } + + if ((ID *)simg->image == old_id) { + simg->image = (Image *)new_id; + id_us_ensure_real(new_id); + } + + if ((ID *)simg->gpd == old_id) { + simg->gpd = (bGPdata *)new_id; + id_us_min(old_id); + id_us_plus(new_id); + } + + if ((ID *)simg->mask_info.mask == old_id) { + simg->mask_info.mask = (Mask *)new_id; + id_us_ensure_real(new_id); + } +} + /**************************** spacetype *****************************/ /* only called once, from space/spacetypes.c */ @@ -1002,7 +1029,8 @@ void ED_spacetype_image(void) st->refresh = image_refresh; st->listener = image_listener; st->context = image_context; - + st->id_remap = image_id_remap; + /* regions: main window */ art = MEM_callocN(sizeof(ARegionType), "spacetype image region"); art->regionid = RGN_TYPE_WINDOW; diff --git a/source/blender/editors/space_logic/space_logic.c b/source/blender/editors/space_logic/space_logic.c index 243a522011b..69966e9bf34 100644 --- a/source/blender/editors/space_logic/space_logic.c +++ b/source/blender/editors/space_logic/space_logic.c @@ -38,7 +38,10 @@ #include "BLI_blenlib.h" #include "BLI_utildefines.h" +#include "DNA_gpencil_types.h" + #include "BKE_context.h" +#include "BKE_library.h" #include "BKE_screen.h" #include "ED_space_api.h" @@ -300,6 +303,21 @@ static void logic_header_region_draw(const bContext *C, ARegion *ar) /**************************** spacetype *****************************/ +static void logic_id_remap(ScrArea *UNUSED(sa), SpaceLink *slink, ID *old_id, ID *new_id) +{ + SpaceLogic *slog = (SpaceLogic *)slink; + + if (!ELEM(GS(old_id->name), ID_GD)) { + return; + } + + if ((ID *)slog->gpd == old_id) { + slog->gpd = (bGPdata *)new_id; + id_us_min(old_id); + id_us_plus(new_id); + } +} + /* only called once, from space/spacetypes.c */ void ED_spacetype_logic(void) { @@ -317,7 +335,8 @@ void ED_spacetype_logic(void) st->keymap = logic_keymap; st->refresh = logic_refresh; st->context = logic_context; - + st->id_remap = logic_id_remap; + /* regions: main window */ art = MEM_callocN(sizeof(ARegionType), "spacetype logic region"); art->regionid = RGN_TYPE_WINDOW; diff --git a/source/blender/editors/space_nla/space_nla.c b/source/blender/editors/space_nla/space_nla.c index e2b36c5b5ae..3b5604087b9 100644 --- a/source/blender/editors/space_nla/space_nla.c +++ b/source/blender/editors/space_nla/space_nla.c @@ -33,6 +33,7 @@ #include <stdio.h> #include "DNA_anim_types.h" +#include "DNA_group_types.h" #include "DNA_scene_types.h" #include "MEM_guardedalloc.h" @@ -501,6 +502,19 @@ static void nla_listener(bScreen *UNUSED(sc), ScrArea *sa, wmNotifier *wmn) } } +static void nla_id_remap(ScrArea *UNUSED(sa), SpaceLink *slink, ID *old_id, ID *new_id) +{ + SpaceNla *snla = (SpaceNla *)slink; + + if (!ELEM(GS(old_id->name), ID_GR)) { + return; + } + + if ((ID *)snla->ads->filter_grp == old_id) { + snla->ads->filter_grp = (Group *)new_id; + } +} + /* only called once, from space/spacetypes.c */ void ED_spacetype_nla(void) { @@ -517,7 +531,8 @@ void ED_spacetype_nla(void) st->operatortypes = nla_operatortypes; st->listener = nla_listener; st->keymap = nla_keymap; - + st->id_remap = nla_id_remap; + /* regions: main window */ art = MEM_callocN(sizeof(ARegionType), "spacetype nla region"); art->regionid = RGN_TYPE_WINDOW; diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c index 6ae72e2a164..ffe510016ff 100644 --- a/source/blender/editors/space_node/node_edit.c +++ b/source/blender/editors/space_node/node_edit.c @@ -740,34 +740,6 @@ void ED_node_set_active(Main *bmain, bNodeTree *ntree, bNode *node) } } -void ED_node_id_unref(SpaceNode *snode, const ID *id) -{ - if (GS(id->name) == ID_SCE) { - if (snode->id == id) { - /* nasty DNA logic for SpaceNode: - * ideally should be handled by editor code, but would be bad level call - */ - bNodeTreePath *path, *path_next; - for (path = snode->treepath.first; path; path = path_next) { - path_next = path->next; - MEM_freeN(path); - } - BLI_listbase_clear(&snode->treepath); - - snode->id = NULL; - snode->from = NULL; - snode->nodetree = NULL; - snode->edittree = NULL; - } - } - else if (GS(id->name) == ID_OB) { - if (snode->from == id) { - snode->flag &= ~SNODE_PIN; - snode->from = NULL; - } - } -} - void ED_node_post_apply_transform(bContext *UNUSED(C), bNodeTree *UNUSED(ntree)) { /* XXX This does not work due to layout functions relying on node->block, diff --git a/source/blender/editors/space_node/space_node.c b/source/blender/editors/space_node/space_node.c index df484724fc5..4ef703c8994 100644 --- a/source/blender/editors/space_node/space_node.c +++ b/source/blender/editors/space_node/space_node.c @@ -28,6 +28,7 @@ * \ingroup spnode */ +#include "DNA_gpencil_types.h" #include "DNA_lamp_types.h" #include "DNA_material_types.h" #include "DNA_node_types.h" @@ -821,6 +822,41 @@ static int node_context(const bContext *C, const char *member, bContextDataResul return 0; } +static void node_id_remap(ScrArea *UNUSED(sa), SpaceLink *slink, ID *old_id, ID *new_id) +{ + SpaceNode *snode = (SpaceNode *)slink; + + if (GS(old_id->name) == ID_SCE) { + if (snode->id == old_id) { + /* nasty DNA logic for SpaceNode: + * ideally should be handled by editor code, but would be bad level call + */ + BLI_freelistN(&snode->treepath); + + /* XXX Untested in case new_id != NULL... */ + snode->id = new_id; + snode->from = NULL; + snode->nodetree = NULL; + snode->edittree = NULL; + } + } + else if (GS(old_id->name) == ID_OB) { + if (snode->from == old_id) { + if (new_id == NULL) { + snode->flag &= ~SNODE_PIN; + } + snode->from = new_id; + } + } + else if (GS(old_id->name) == ID_GD) { + if ((ID *)snode->gpd == old_id) { + snode->gpd = (bGPdata *)new_id; + id_us_min(old_id); + id_us_plus(new_id); + } + } +} + /* only called once, from space/spacetypes.c */ void ED_spacetype_node(void) { @@ -840,6 +876,7 @@ void ED_spacetype_node(void) st->refresh = node_area_refresh; st->context = node_context; st->dropboxes = node_dropboxes; + st->id_remap = node_id_remap; /* regions: main window */ art = MEM_callocN(sizeof(ARegionType), "spacetype node region"); diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index 37a40acd519..5615db1308f 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -502,6 +502,11 @@ static void namebutton_cb(bContext *C, void *tsep, char *oldname) BKE_reportf(CTX_wm_reports(C), RPT_ERROR, "Library path '%s' does not exist, correct this before saving", expanded); } + else if (lib->id.tag & LIB_TAG_MISSING) { + BKE_reportf(CTX_wm_reports(C), RPT_INFO, + "Library path '%s' is now valid, please reload the library", expanded); + lib->id.tag &= ~LIB_TAG_MISSING; + } } } else { diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c index 2627b978b40..687869ae727 100644 --- a/source/blender/editors/space_outliner/outliner_edit.c +++ b/source/blender/editors/space_outliner/outliner_edit.c @@ -29,17 +29,23 @@ * \ingroup spoutliner */ +#include <string.h> + #include "MEM_guardedalloc.h" #include "DNA_anim_types.h" #include "DNA_group_types.h" +#include "DNA_ID.h" #include "DNA_scene_types.h" #include "DNA_object_types.h" #include "DNA_material_types.h" #include "BLI_blenlib.h" #include "BLI_utildefines.h" +#include "BLI_path_util.h" #include "BLI_mempool.h" +#include "BLI_stack.h" +#include "BLI_string.h" #include "BLT_translation.h" @@ -47,7 +53,9 @@ #include "BKE_context.h" #include "BKE_depsgraph.h" #include "BKE_global.h" +#include "BKE_idcode.h" #include "BKE_library.h" +#include "BKE_library_remap.h" #include "BKE_main.h" #include "BKE_outliner_treehash.h" #include "BKE_report.h" @@ -55,6 +63,8 @@ #include "BKE_material.h" #include "BKE_group.h" +#include "../blenloader/BLO_readfile.h" + #include "ED_object.h" #include "ED_outliner.h" #include "ED_screen.h" @@ -70,6 +80,9 @@ #include "RNA_access.h" #include "RNA_define.h" +#include "RNA_enum_types.h" + +#include "GPU_material.h" #include "outliner_intern.h" @@ -291,64 +304,294 @@ void OUTLINER_OT_item_rename(wmOperatorType *ot) ot->poll = ED_operator_outliner_active; } -/* Library delete --------------------------------------------------- */ +/* ID delete --------------------------------------------------- */ -static void lib_delete(bContext *C, TreeElement *te, TreeStoreElem *tselem, ReportList *reports) +static void id_delete(bContext *C, TreeElement *te, TreeStoreElem *tselem) { - Library *lib = (Library *)tselem->id; - ListBase *lbarray[MAX_LIBARRAY]; - int a; + Main *bmain = CTX_data_main(C); + ID *id = tselem->id; - BLI_assert(te->idcode == ID_LI && lib != NULL); + BLI_assert(te->idcode != 0 && id != NULL); + BLI_assert(te->idcode != ID_LI || ((Library *)id)->parent == NULL); UNUSED_VARS_NDEBUG(te); - /* We simply set all ID from given lib (including lib itself) to zero user count. - * It is not possible to do a proper cleanup without a save/reload in current master. */ - a = set_listbasepointers(CTX_data_main(C), lbarray); - while (a--) { - ListBase *lb = lbarray[a]; - ID *id; - - for (id = lb->first; id; id = id->next) { - if (id->lib == lib) { - id_fake_user_clear(id); - id->us = 0; + BKE_libblock_delete(bmain, id); + + WM_event_add_notifier(C, NC_WINDOW, NULL); +} + +void id_delete_cb( + bContext *C, Scene *UNUSED(scene), TreeElement *te, TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, + void *UNUSED(user_data)) +{ + id_delete(C, te, tselem); +} + +static int outliner_id_delete_invoke_do(bContext *C, ReportList *reports, TreeElement *te, const float mval[2]) +{ + if (mval[1] > te->ys && mval[1] < te->ys + UI_UNIT_Y) { + TreeStoreElem *tselem = TREESTORE(te); + + if (te->idcode != 0 && tselem->id) { + if (te->idcode == ID_LI && ((Library *)tselem->id)->parent) { + BKE_reportf(reports, RPT_ERROR_INVALID_INPUT, + "Cannot delete indirectly linked library '%s'", ((Library *)tselem->id)->filepath); + return OPERATOR_CANCELLED; + } + id_delete(C, te, tselem); + return OPERATOR_FINISHED; + } + } + else { + for (te = te->subtree.first; te; te = te->next) { + int ret; + if ((ret = outliner_id_delete_invoke_do(C, reports, te, mval))) { + return ret; } } } - BKE_reportf(reports, RPT_WARNING, - "Please save and reload .blend file to complete deletion of '%s' library", - ((Library *)tselem->id)->filepath); + return 0; } -void lib_delete_cb( - bContext *C, Scene *UNUSED(scene), TreeElement *te, +static int outliner_id_delete_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + ARegion *ar = CTX_wm_region(C); + SpaceOops *soops = CTX_wm_space_outliner(C); + TreeElement *te; + float fmval[2]; + + BLI_assert(ar && soops); + + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); + + for (te = soops->tree.first; te; te = te->next) { + int ret; + + if ((ret = outliner_id_delete_invoke_do(C, op->reports, te, fmval))) { + return ret; + } + } + + return OPERATOR_CANCELLED; +} + +void OUTLINER_OT_id_delete(wmOperatorType *ot) +{ + ot->name = "Delete Datablock"; + ot->idname = "OUTLINER_OT_id_delete"; + ot->description = "Delete the ID under cursor"; + + ot->invoke = outliner_id_delete_invoke; + ot->poll = ED_operator_outliner_active; +} + +/* ID remap --------------------------------------------------- */ + +static int outliner_id_remap_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + SpaceOops *soops = CTX_wm_space_outliner(C); + + const short id_type = (short)RNA_enum_get(op->ptr, "id_type"); + ID *old_id = BLI_findlink(which_libbase(CTX_data_main(C), id_type), RNA_enum_get(op->ptr, "old_id")); + ID *new_id = BLI_findlink(which_libbase(CTX_data_main(C), id_type), RNA_enum_get(op->ptr, "new_id")); + + /* check for invalid states */ + if (soops == NULL) + return OPERATOR_CANCELLED; + + if (!(old_id && (old_id != new_id) && (GS(old_id->name) == GS(new_id->name)))) { + return OPERATOR_CANCELLED; + } + + BKE_libblock_remap(bmain, old_id, new_id, + ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_NEVER_NULL_USAGE); + + BKE_main_lib_objects_recalc_all(bmain); + + /* recreate dependency graph to include new objects */ + DAG_scene_relations_rebuild(bmain, scene); + + /* free gpu materials, some materials depend on existing objects, such as lamps so freeing correctly refreshes */ + GPU_materials_free(); + + WM_event_add_notifier(C, NC_WINDOW, NULL); + + return OPERATOR_FINISHED; +} + +static bool outliner_id_remap_find_tree_element(bContext *C, wmOperator *op, ListBase *tree, const float y) +{ + TreeElement *te; + + for (te = tree->first; te; te = te->next) { + if (y > te->ys && y < te->ys + UI_UNIT_Y) { + TreeStoreElem *tselem = TREESTORE(te); + + if (tselem->type == 0 && tselem->id) { + printf("found id %s (%p)!\n", tselem->id->name, tselem->id); + + RNA_enum_set(op->ptr, "id_type", GS(tselem->id->name)); + RNA_enum_set_identifier(C, op->ptr, "new_id", tselem->id->name + 2); + RNA_enum_set_identifier(C, op->ptr, "old_id", tselem->id->name + 2); + return true; + } + } + if (outliner_id_remap_find_tree_element(C, op, &te->subtree, y)) { + return true; + } + } + return false; +} + +static int outliner_id_remap_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + SpaceOops *soops = CTX_wm_space_outliner(C); + ARegion *ar = CTX_wm_region(C); + float fmval[2]; + + if (!RNA_property_is_set(op->ptr, RNA_struct_find_property(op->ptr, "id_type"))) { + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); + + outliner_id_remap_find_tree_element(C, op, &soops->tree, fmval[1]); + } + + return WM_operator_props_dialog_popup(C, op, 200, 100); +} + +static EnumPropertyItem *outliner_id_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free) +{ + EnumPropertyItem item_tmp = {0}, *item = NULL; + int totitem = 0; + int i = 0; + + short id_type = (short)RNA_enum_get(ptr, "id_type"); + ID *id = which_libbase(CTX_data_main(C), id_type)->first; + + for (; id; id = id->next) { + item_tmp.identifier = item_tmp.name = id->name + 2; + item_tmp.value = i++; + RNA_enum_item_add(&item, &totitem, &item_tmp); + } + + RNA_enum_item_end(&item, &totitem); + *r_free = true; + + return item; +} + +void OUTLINER_OT_id_remap(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Outliner ID data Remap"; + ot->idname = "OUTLINER_OT_id_remap"; + ot->description = ""; + + /* callbacks */ + ot->invoke = outliner_id_remap_invoke; + ot->exec = outliner_id_remap_exec; + ot->poll = ED_operator_outliner_active; + + ot->flag = 0; + + RNA_def_enum(ot->srna, "id_type", rna_enum_id_type_items, ID_OB, "ID Type", ""); + + prop = RNA_def_enum(ot->srna, "old_id", DummyRNA_NULL_items, 0, "Old ID", "Old ID to replace"); + RNA_def_property_enum_funcs_runtime(prop, NULL, NULL, outliner_id_itemf); + RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE | PROP_HIDDEN); + + ot->prop = RNA_def_enum(ot->srna, "new_id", DummyRNA_NULL_items, 0, + "New ID", "New ID to remap all selected IDs' users to"); + RNA_def_property_enum_funcs_runtime(ot->prop, NULL, NULL, outliner_id_itemf); + RNA_def_property_flag(ot->prop, PROP_ENUM_NO_TRANSLATE); +} + +void id_remap_cb( + bContext *C, Scene *UNUSED(scene), TreeElement *UNUSED(te), TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data)) { - lib_delete(C, te, tselem, CTX_wm_reports(C)); + wmOperatorType *ot = WM_operatortype_find("OUTLINER_OT_id_remap", false); + PointerRNA op_props; + + BLI_assert(tselem->id != NULL); + + WM_operator_properties_create_ptr(&op_props, ot); + + RNA_enum_set(&op_props, "id_type", GS(tselem->id->name)); + RNA_enum_set_identifier(C, &op_props, "old_id", tselem->id->name + 2); + + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &op_props); + + WM_operator_properties_free(&op_props); } -static int outliner_lib_delete_invoke_do(bContext *C, ReportList *reports, TreeElement *te, const float mval[2]) +/* Library relocate/reload --------------------------------------------------- */ + +static int lib_relocate( + bContext *C, TreeElement *te, TreeStoreElem *tselem, wmOperatorType *ot, const bool reload) +{ + PointerRNA op_props; + int ret = 0; + + BLI_assert(te->idcode == ID_LI && tselem->id != NULL); + UNUSED_VARS_NDEBUG(te); + + WM_operator_properties_create_ptr(&op_props, ot); + + RNA_string_set(&op_props, "library", tselem->id->name + 2); + + if (reload) { + Library *lib = (Library *)tselem->id; + char dir[FILE_MAXDIR], filename[FILE_MAX]; + + BLI_split_dirfile(lib->filepath, dir, filename, sizeof(dir), sizeof(filename)); + + printf("%s, %s\n", tselem->id->name, lib->filepath); + + /* We assume if both paths in lib are not the same then lib->name was relative... */ + RNA_boolean_set(&op_props, "relative_path", BLI_path_cmp(lib->filepath, lib->name) != 0); + + RNA_string_set(&op_props, "directory", dir); + RNA_string_set(&op_props, "filename", filename); + + ret = WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, &op_props); + } + else { + ret = WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &op_props); + } + + WM_operator_properties_free(&op_props); + + return ret; +} + +static int outliner_lib_relocate_invoke_do( + bContext *C, ReportList *reports, TreeElement *te, const float mval[2], const bool reload) { if (mval[1] > te->ys && mval[1] < te->ys + UI_UNIT_Y) { TreeStoreElem *tselem = TREESTORE(te); if (te->idcode == ID_LI && tselem->id) { - if (((Library *)tselem->id)->parent) { + if (((Library *)tselem->id)->parent && !reload) { BKE_reportf(reports, RPT_ERROR_INVALID_INPUT, - "Cannot delete indirectly linked library '%s'", ((Library *)tselem->id)->filepath); + "Cannot relocate indirectly linked library '%s'", ((Library *)tselem->id)->filepath); return OPERATOR_CANCELLED; } + else { + wmOperatorType *ot = WM_operatortype_find(reload ? "WM_OT_lib_reload" : "WM_OT_lib_relocate", false); - lib_delete(C, te, tselem, reports); - return OPERATOR_FINISHED; + return lib_relocate(C, te, tselem, ot, reload); + } } } else { for (te = te->subtree.first; te; te = te->next) { int ret; - if ((ret = outliner_lib_delete_invoke_do(C, reports, te, mval))) { + if ((ret = outliner_lib_relocate_invoke_do(C, reports, te, mval, reload))) { return ret; } } @@ -357,7 +600,7 @@ static int outliner_lib_delete_invoke_do(bContext *C, ReportList *reports, TreeE return 0; } -static int outliner_lib_delete_invoke(bContext *C, wmOperator *op, const wmEvent *event) +static int outliner_lib_relocate_invoke(bContext *C, wmOperator *op, const wmEvent *event) { ARegion *ar = CTX_wm_region(C); SpaceOops *soops = CTX_wm_space_outliner(C); @@ -371,7 +614,7 @@ static int outliner_lib_delete_invoke(bContext *C, wmOperator *op, const wmEvent for (te = soops->tree.first; te; te = te->next) { int ret; - if ((ret = outliner_lib_delete_invoke_do(C, op->reports, te, fmval))) { + if ((ret = outliner_lib_relocate_invoke_do(C, op->reports, te, fmval, false))) { return ret; } } @@ -379,16 +622,69 @@ static int outliner_lib_delete_invoke(bContext *C, wmOperator *op, const wmEvent return OPERATOR_CANCELLED; } -void OUTLINER_OT_lib_delete(wmOperatorType *ot) +void OUTLINER_OT_lib_relocate(wmOperatorType *ot) { - ot->name = "Delete Library"; - ot->idname = "OUTLINER_OT_lib_delete"; - ot->description = "Delete the library under cursor (needs a save/reload)"; + ot->name = "Relocate Library"; + ot->idname = "OUTLINER_OT_lib_relocate"; + ot->description = "Relocate the library under cursor"; - ot->invoke = outliner_lib_delete_invoke; + ot->invoke = outliner_lib_relocate_invoke; ot->poll = ED_operator_outliner_active; } +/* XXX This does not work with several items + * (it is only called once in the end, due to the 'deffered' filebrowser invocation through event system...). */ +void lib_relocate_cb( + bContext *C, Scene *UNUSED(scene), TreeElement *te, + TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data)) +{ + wmOperatorType *ot = WM_operatortype_find("WM_OT_lib_relocate", false); + + lib_relocate(C, te, tselem, ot, false); +} + + +static int outliner_lib_reload_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + ARegion *ar = CTX_wm_region(C); + SpaceOops *soops = CTX_wm_space_outliner(C); + TreeElement *te; + float fmval[2]; + + BLI_assert(ar && soops); + + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); + + for (te = soops->tree.first; te; te = te->next) { + int ret; + + if ((ret = outliner_lib_relocate_invoke_do(C, op->reports, te, fmval, true))) { + return ret; + } + } + + return OPERATOR_CANCELLED; +} + +void OUTLINER_OT_lib_reload(wmOperatorType *ot) +{ + ot->name = "Reload Library"; + ot->idname = "OUTLINER_OT_lib_reload"; + ot->description = "Reload the library under cursor"; + + ot->invoke = outliner_lib_reload_invoke; + ot->poll = ED_operator_outliner_active; +} + +void lib_reload_cb( + bContext *C, Scene *UNUSED(scene), TreeElement *te, + TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data)) +{ + wmOperatorType *ot = WM_operatortype_find("WM_OT_lib_reload", false); + + lib_relocate(C, te, tselem, ot, true); +} + /* ************************************************************** */ /* Setting Toggling Operators */ @@ -2079,36 +2375,3 @@ void OUTLINER_OT_group_link(wmOperatorType *ot) /* properties */ RNA_def_string(ot->srna, "object", "Object", MAX_ID_NAME, "Object", "Target Object"); } - -/******** Utils to clear any ref to freed ID... **********/ - -void ED_outliner_id_unref(SpaceOops *so, const ID *id) -{ - /* Some early out checks. */ - if (!TREESTORE_ID_TYPE(id)) { - return; /* ID type is not used by outilner... */ - } - - if (so->search_tse.id == id) { - so->search_tse.id = NULL; - } - - if (so->treestore) { - TreeStoreElem *tselem; - BLI_mempool_iter iter; - bool changed = false; - - BLI_mempool_iternew(so->treestore, &iter); - while ((tselem = BLI_mempool_iterstep(&iter))) { - if (tselem->id == id) { - tselem->id = NULL; - changed = true; - } - } - if (so->treehash && changed) { - /* rebuild hash table, because it depends on ids too */ - /* postpone a full rebuild because this can be called many times on-free */ - so->storeflag |= SO_TREESTORE_REBUILD; - } - } -} diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h index 155e41088d6..c6694b80df3 100644 --- a/source/blender/editors/space_outliner/outliner_intern.h +++ b/source/blender/editors/space_outliner/outliner_intern.h @@ -179,8 +179,17 @@ void group_toggle_selectability_cb(struct bContext *C, struct Scene *scene, Tree void group_toggle_renderability_cb(struct bContext *C, struct Scene *scene, TreeElement *te, struct TreeStoreElem *tsep, struct TreeStoreElem *tselem, void *user_data); void item_rename_cb(struct bContext *C, struct Scene *scene, TreeElement *te, struct TreeStoreElem *tsep, struct TreeStoreElem *tselem, void *user_data); +void lib_relocate_cb( + struct bContext *C, struct Scene *scene, struct TreeElement *te, + struct TreeStoreElem *tsep, struct TreeStoreElem *tselem, void *user_data); +void lib_reload_cb( + struct bContext *C, struct Scene *scene, struct TreeElement *te, + struct TreeStoreElem *tsep, struct TreeStoreElem *tselem, void *user_data); -void lib_delete_cb( +void id_delete_cb( + struct bContext *C, struct Scene *scene, struct TreeElement *te, + struct TreeStoreElem *tsep, struct TreeStoreElem *tselem, void *user_data); +void id_remap_cb( struct bContext *C, struct Scene *scene, struct TreeElement *te, struct TreeStoreElem *tsep, struct TreeStoreElem *tselem, void *user_data); @@ -190,8 +199,10 @@ TreeElement *outliner_dropzone_find(const struct SpaceOops *soops, const float f void OUTLINER_OT_item_activate(struct wmOperatorType *ot); void OUTLINER_OT_item_openclose(struct wmOperatorType *ot); void OUTLINER_OT_item_rename(struct wmOperatorType *ot); +void OUTLINER_OT_lib_relocate(struct wmOperatorType *ot); +void OUTLINER_OT_lib_reload(struct wmOperatorType *ot); -void OUTLINER_OT_lib_delete(struct wmOperatorType *ot); +void OUTLINER_OT_id_delete(struct wmOperatorType *ot); void OUTLINER_OT_show_one_level(struct wmOperatorType *ot); void OUTLINER_OT_show_active(struct wmOperatorType *ot); @@ -230,6 +241,7 @@ void OUTLINER_OT_object_operation(struct wmOperatorType *ot); void OUTLINER_OT_group_operation(struct wmOperatorType *ot); void OUTLINER_OT_lib_operation(struct wmOperatorType *ot); void OUTLINER_OT_id_operation(struct wmOperatorType *ot); +void OUTLINER_OT_id_remap(struct wmOperatorType *ot); void OUTLINER_OT_data_operation(struct wmOperatorType *ot); void OUTLINER_OT_animdata_operation(struct wmOperatorType *ot); void OUTLINER_OT_action_set(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_outliner/outliner_ops.c b/source/blender/editors/space_outliner/outliner_ops.c index 720cfe12567..776717c8443 100644 --- a/source/blender/editors/space_outliner/outliner_ops.c +++ b/source/blender/editors/space_outliner/outliner_ops.c @@ -47,13 +47,15 @@ void outliner_operatortypes(void) WM_operatortype_append(OUTLINER_OT_select_border); WM_operatortype_append(OUTLINER_OT_item_openclose); WM_operatortype_append(OUTLINER_OT_item_rename); - WM_operatortype_append(OUTLINER_OT_lib_delete); WM_operatortype_append(OUTLINER_OT_operation); WM_operatortype_append(OUTLINER_OT_scene_operation); WM_operatortype_append(OUTLINER_OT_object_operation); WM_operatortype_append(OUTLINER_OT_group_operation); WM_operatortype_append(OUTLINER_OT_lib_operation); + WM_operatortype_append(OUTLINER_OT_lib_relocate); WM_operatortype_append(OUTLINER_OT_id_operation); + WM_operatortype_append(OUTLINER_OT_id_delete); + WM_operatortype_append(OUTLINER_OT_id_remap); WM_operatortype_append(OUTLINER_OT_data_operation); WM_operatortype_append(OUTLINER_OT_animdata_operation); WM_operatortype_append(OUTLINER_OT_action_set); diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index 83677b6bd86..265df4a8def 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -57,6 +57,7 @@ #include "BKE_fcurve.h" #include "BKE_group.h" #include "BKE_library.h" +#include "BKE_library_remap.h" #include "BKE_main.h" #include "BKE_report.h" #include "BKE_scene.h" @@ -230,7 +231,8 @@ static void unlink_group_cb( } else { Main *bmain = CTX_data_main(C); - BKE_group_unlink(bmain, group); + BKE_libblock_unlink(bmain, group, false); + BKE_libblock_free(bmain, group); } } @@ -246,7 +248,7 @@ static void unlink_world_cb(bContext *UNUSED(C), Scene *UNUSED(scene), TreeEleme } static void outliner_do_libdata_operation( - bContext *C, Scene *scene, SpaceOops *soops, ListBase *lb, + bContext *C, Scene *scene, SpaceOops *soops, ListBase *lb, void (*operation_cb)(bContext *C, Scene *scene, TreeElement *, TreeStoreElem *, TreeStoreElem *, void *), void *user_data) { @@ -522,8 +524,7 @@ static void group_instance_cb(bContext *C, Scene *scene, TreeElement *UNUSED(te) */ void outliner_do_object_operation_ex( bContext *C, Scene *scene_act, SpaceOops *soops, ListBase *lb, - void (*operation_cb)(bContext *C, Scene *scene, TreeElement *, - TreeStoreElem *, TreeStoreElem *, void *), + void (*operation_cb)(bContext *, Scene *, TreeElement *, TreeStoreElem *, TreeStoreElem *, void *), bool select_recurse) { TreeElement *te; @@ -565,7 +566,7 @@ void outliner_do_object_operation( static void clear_animdata_cb(int UNUSED(event), TreeElement *UNUSED(te), TreeStoreElem *tselem, void *UNUSED(arg)) { - BKE_animdata_free(tselem->id); + BKE_animdata_free(tselem->id, true); } @@ -849,6 +850,7 @@ enum { OL_OP_SELECT_HIERARCHY, OL_OP_DELETE, OL_OP_DELETE_HIERARCHY, + OL_OP_REMAP, OL_OP_LOCALIZED, /* disabled, see below */ OL_OP_TOGVIS, OL_OP_TOGSEL, @@ -862,6 +864,8 @@ static EnumPropertyItem prop_object_op_types[] = { {OL_OP_SELECT_HIERARCHY, "SELECT_HIERARCHY", 0, "Select Hierarchy", ""}, {OL_OP_DELETE, "DELETE", 0, "Delete", ""}, {OL_OP_DELETE_HIERARCHY, "DELETE_HIERARCHY", 0, "Delete Hierarchy", ""}, + {OL_OP_REMAP, "REMAP", 0, "Remap Users", + "Make all users of selected datablocks to use instead a new chosen one"}, {OL_OP_TOGVIS, "TOGVIS", 0, "Toggle Visible", ""}, {OL_OP_TOGSEL, "TOGSEL", 0, "Toggle Selectable", ""}, {OL_OP_TOGREN, "TOGREN", 0, "Toggle Renderable", ""}, @@ -931,6 +935,10 @@ static int outliner_object_operation_exec(bContext *C, wmOperator *op) str = "Delete Object Hierarchy"; WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); } + else if (event == OL_OP_REMAP) { + outliner_do_libdata_operation(C, scene, soops, &soops->tree, id_remap_cb, NULL); + str = "Remap ID"; + } else if (event == OL_OP_LOCALIZED) { /* disabled, see above enum (ton) */ outliner_do_object_operation(C, scene, soops, &soops->tree, id_local_cb); str = "Localized Objects"; @@ -988,6 +996,8 @@ typedef enum eOutliner_PropGroupOps { OL_GROUPOP_UNLINK = 1, OL_GROUPOP_LOCAL, OL_GROUPOP_LINK, + OL_GROUPOP_DELETE, + OL_GROUPOP_REMAP, OL_GROUPOP_INSTANCE, OL_GROUPOP_TOGVIS, OL_GROUPOP_TOGSEL, @@ -999,6 +1009,9 @@ static EnumPropertyItem prop_group_op_types[] = { {OL_GROUPOP_UNLINK, "UNLINK", 0, "Unlink Group", ""}, {OL_GROUPOP_LOCAL, "LOCAL", 0, "Make Local Group", ""}, {OL_GROUPOP_LINK, "LINK", 0, "Link Group Objects to Scene", ""}, + {OL_GROUPOP_DELETE, "DELETE", 0, "Delete Group", "WARNING: no undo"}, + {OL_GROUPOP_REMAP, "REMAP", 0, "Remap Users", + "Make all users of selected datablocks to use instead current (clicked) one"}, {OL_GROUPOP_INSTANCE, "INSTANCE", 0, "Instance Groups in Scene", ""}, {OL_GROUPOP_TOGVIS, "TOGVIS", 0, "Toggle Visible Group", ""}, {OL_GROUPOP_TOGSEL, "TOGSEL", 0, "Toggle Selectable", ""}, @@ -1031,6 +1044,14 @@ static int outliner_group_operation_exec(bContext *C, wmOperator *op) break; case OL_GROUPOP_INSTANCE: outliner_do_libdata_operation(C, scene, soops, &soops->tree, group_instance_cb, NULL); + /* works without this except if you try render right after, see: 22027 */ + DAG_relations_tag_update(CTX_data_main(C)); + break; + case OL_GROUPOP_DELETE: + WM_operator_name_call(C, "OUTLINER_OT_id_delete", WM_OP_INVOKE_REGION_WIN, NULL); + break; + case OL_GROUPOP_REMAP: + outliner_do_libdata_operation(C, scene, soops, &soops->tree, id_remap_cb, NULL); break; case OL_GROUPOP_TOGVIS: outliner_do_libdata_operation(C, scene, soops, &soops->tree, group_toggle_visibility_cb, NULL); @@ -1048,11 +1069,6 @@ static int outliner_group_operation_exec(bContext *C, wmOperator *op) BLI_assert(0); } - if (event == 3) { /* instance */ - /* works without this except if you try render right after, see: 22027 */ - DAG_relations_tag_update(CTX_data_main(C)); - } - ED_undo_push(C, prop_group_op_types[event - 1].name); WM_event_add_notifier(C, NC_GROUP, NULL); @@ -1085,6 +1101,8 @@ typedef enum eOutlinerIdOpTypes { OUTLINER_IDOP_UNLINK, OUTLINER_IDOP_LOCAL, OUTLINER_IDOP_SINGLE, + OUTLINER_IDOP_DELETE, + OUTLINER_IDOP_REMAP, OUTLINER_IDOP_FAKE_ADD, OUTLINER_IDOP_FAKE_CLEAR, @@ -1098,6 +1116,9 @@ static EnumPropertyItem prop_id_op_types[] = { {OUTLINER_IDOP_UNLINK, "UNLINK", 0, "Unlink", ""}, {OUTLINER_IDOP_LOCAL, "LOCAL", 0, "Make Local", ""}, {OUTLINER_IDOP_SINGLE, "SINGLE", 0, "Make Single User", ""}, + {OUTLINER_IDOP_DELETE, "DELETE", 0, "Delete", "WARNING: no undo"}, + {OUTLINER_IDOP_REMAP, "REMAP", 0, "Remap Users", + "Make all users of selected datablocks to use instead current (clicked) one"}, {OUTLINER_IDOP_FAKE_ADD, "ADD_FAKE", 0, "Add Fake User", "Ensure datablock gets saved even if it isn't in use (e.g. for motion and material libraries)"}, {OUTLINER_IDOP_FAKE_CLEAR, "CLEAR_FAKE", 0, "Clear Fake User", ""}, @@ -1187,6 +1208,20 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) } break; } + case OUTLINER_IDOP_DELETE: + { + if (idlevel > 0) { + outliner_do_libdata_operation(C, scene, soops, &soops->tree, id_delete_cb, NULL); + } + break; + } + case OUTLINER_IDOP_REMAP: + { + if (idlevel > 0) { + outliner_do_libdata_operation(C, scene, soops, &soops->tree, id_remap_cb, NULL); + } + break; + } case OUTLINER_IDOP_FAKE_ADD: { /* set fake user */ @@ -1258,13 +1293,16 @@ typedef enum eOutlinerLibOpTypes { OL_LIB_RENAME, OL_LIB_DELETE, + OL_LIB_RELOCATE, + OL_LIB_RELOAD, } eOutlinerLibOpTypes; static EnumPropertyItem outliner_lib_op_type_items[] = { {OL_LIB_RENAME, "RENAME", 0, "Rename", ""}, - {OL_LIB_DELETE, "DELETE", 0, "Delete", "Delete this library and all its item from Blender (needs a save/reload)"}, + {OL_LIB_DELETE, "DELETE", 0, "Delete", "Delete this library and all its item from Blender - WARNING: no undo"}, + {OL_LIB_RELOCATE, "RELOCATE", 0, "Relocate", "Select a new path for this library, and reload all its data"}, + {OL_LIB_RELOAD, "RELOAD", 0, "Reload", "Reload all data from this library"}, {0, NULL, 0, NULL, NULL} - }; static int outliner_lib_operation_exec(bContext *C, wmOperator *op) @@ -1294,13 +1332,19 @@ static int outliner_lib_operation_exec(bContext *C, wmOperator *op) } case OL_LIB_DELETE: { - /* delete */ - outliner_do_libdata_operation(C, scene, soops, &soops->tree, lib_delete_cb, NULL); - - WM_event_add_notifier(C, NC_ID | NA_EDITED, NULL); - /* Note: no undo possible here really, not 100% sure why... - * Probably because of library optimizations in undo/redo process? */ - /* ED_undo_push(C, "Rename"); */ + outliner_do_libdata_operation(C, scene, soops, &soops->tree, id_delete_cb, NULL); + break; + } + case OL_LIB_RELOCATE: + { + /* rename */ + outliner_do_libdata_operation(C, scene, soops, &soops->tree, lib_relocate_cb, NULL); + break; + } + case OL_LIB_RELOAD: + { + /* rename */ + outliner_do_libdata_operation(C, scene, soops, &soops->tree, lib_reload_cb, NULL); break; } default: diff --git a/source/blender/editors/space_outliner/space_outliner.c b/source/blender/editors/space_outliner/space_outliner.c index 1dd66366e5d..76bf9c701ed 100644 --- a/source/blender/editors/space_outliner/space_outliner.c +++ b/source/blender/editors/space_outliner/space_outliner.c @@ -486,6 +486,39 @@ static SpaceLink *outliner_duplicate(SpaceLink *sl) return (SpaceLink *)soutlinern; } +static void outliner_id_remap(ScrArea *UNUSED(sa), SpaceLink *slink, ID *old_id, ID *new_id) +{ + SpaceOops *so = (SpaceOops *)slink; + + /* Some early out checks. */ + if (!TREESTORE_ID_TYPE(old_id)) { + return; /* ID type is not used by outilner... */ + } + + if (so->search_tse.id == old_id) { + so->search_tse.id = new_id; + } + + if (so->treestore) { + TreeStoreElem *tselem; + BLI_mempool_iter iter; + bool changed = false; + + BLI_mempool_iternew(so->treestore, &iter); + while ((tselem = BLI_mempool_iterstep(&iter))) { + if (tselem->id == old_id) { + tselem->id = new_id; + changed = true; + } + } + if (so->treehash && changed) { + /* rebuild hash table, because it depends on ids too */ + /* postpone a full rebuild because this can be called many times on-free */ + so->storeflag |= SO_TREESTORE_REBUILD; + } + } +} + /* only called once, from space_api/spacetypes.c */ void ED_spacetype_outliner(void) { @@ -502,7 +535,8 @@ void ED_spacetype_outliner(void) st->operatortypes = outliner_operatortypes; st->keymap = outliner_keymap; st->dropboxes = outliner_dropboxes; - + st->id_remap = outliner_id_remap; + /* regions: main window */ art = MEM_callocN(sizeof(ARegionType), "spacetype outliner region"); art->regionid = RGN_TYPE_WINDOW; diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index fce40f8ca59..a2a80297041 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -32,6 +32,7 @@ #include <string.h> #include <stdio.h> +#include "DNA_gpencil_types.h" #include "DNA_scene_types.h" #include "DNA_mask_types.h" @@ -41,6 +42,7 @@ #include "BLI_utildefines.h" #include "BKE_context.h" +#include "BKE_library.h" #include "BKE_screen.h" #include "BKE_sequencer.h" #include "BKE_global.h" @@ -687,6 +689,22 @@ static void sequencer_buttons_region_listener(bScreen *UNUSED(sc), ScrArea *UNUS break; } } + +static void sequencer_id_remap(ScrArea *UNUSED(sa), SpaceLink *slink, ID *old_id, ID *new_id) +{ + SpaceSeq *sseq = (SpaceSeq *)slink; + + if (!ELEM(GS(old_id->name), ID_GD)) { + return; + } + + if ((ID *)sseq->gpd == old_id) { + sseq->gpd = (bGPdata *)new_id; + id_us_min(old_id); + id_us_plus(new_id); + } +} + /* ************************************* */ /* only called once, from space/spacetypes.c */ @@ -708,6 +726,7 @@ void ED_spacetype_sequencer(void) st->dropboxes = sequencer_dropboxes; st->refresh = sequencer_refresh; st->listener = sequencer_listener; + st->id_remap = sequencer_id_remap; /* regions: main window */ art = MEM_callocN(sizeof(ARegionType), "spacetype sequencer region"); diff --git a/source/blender/editors/space_text/space_text.c b/source/blender/editors/space_text/space_text.c index 0a6a9a81e63..0dea59fd68c 100644 --- a/source/blender/editors/space_text/space_text.c +++ b/source/blender/editors/space_text/space_text.c @@ -38,6 +38,7 @@ #include "BLI_blenlib.h" #include "BKE_context.h" +#include "BKE_library.h" #include "BKE_screen.h" #include "BKE_text.h" @@ -562,6 +563,20 @@ static void text_properties_region_draw(const bContext *C, ARegion *ar) } } +static void text_id_remap(ScrArea *UNUSED(sa), SpaceLink *slink, ID *old_id, ID *new_id) +{ + SpaceText *stext = (SpaceText *)slink; + + if (!ELEM(GS(old_id->name), ID_GD)) { + return; + } + + if ((ID *)stext->text == old_id) { + stext->text = (Text *)new_id; + id_us_ensure_real(new_id); + } +} + /********************* registration ********************/ /* only called once, from space/spacetypes.c */ @@ -582,7 +597,8 @@ void ED_spacetype_text(void) st->listener = text_listener; st->context = text_context; st->dropboxes = text_dropboxes; - + st->id_remap = text_id_remap; + /* regions: main window */ art = MEM_callocN(sizeof(ARegionType), "spacetype text region"); art->regionid = RGN_TYPE_WINDOW; diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c index 9ebdfb6f21d..c32de6eafc3 100644 --- a/source/blender/editors/space_view3d/drawobject.c +++ b/source/blender/editors/space_view3d/drawobject.c @@ -4244,12 +4244,15 @@ static bool draw_mesh_object(Scene *scene, ARegion *ar, View3D *v3d, RegionView3 if (ob == obedit || drawlinked) { DerivedMesh *finalDM, *cageDM; - if (obedit != ob) - finalDM = cageDM = editbmesh_get_derived_base(ob, em); - else + if (obedit != ob) { + finalDM = cageDM = editbmesh_get_derived_base( + ob, em, scene->customdata_mask); + } + else { cageDM = editbmesh_get_derived_cage_and_final( scene, ob, em, scene->customdata_mask, &finalDM); + } const bool use_material = ((me->drawflag & ME_DRAWEIGHT) == 0); @@ -7293,7 +7296,7 @@ static void draw_object_mesh_instance(Scene *scene, View3D *v3d, RegionView3D *r DerivedMesh *dm = NULL, *edm = NULL; if (ob->mode & OB_MODE_EDIT) { - edm = editbmesh_get_derived_base(ob, me->edit_btmesh); + edm = editbmesh_get_derived_base(ob, me->edit_btmesh, CD_MASK_BAREMESH); DM_update_materials(edm, ob); } else { diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 43d0a31af71..2ec5b5f3c08 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -1399,6 +1399,66 @@ static int view3d_context(const bContext *C, const char *member, bContextDataRes return -1; /* found but not available */ } +static void view3d_id_remap(ScrArea *sa, SpaceLink *slink, ID *old_id, ID *new_id) +{ + View3D *v3d; + ARegion *ar; + bool is_local = false; + + if (!ELEM(GS(old_id->name), ID_OB, ID_MA, ID_IM, ID_MC)) { + return; + } + + for (v3d = (View3D *)slink; v3d; v3d = v3d->localvd, is_local = true) { + if ((ID *)v3d->camera == old_id) { + v3d->camera = (Object *)new_id; + if (!new_id) { + for (ar = sa->regionbase.first; ar; ar = ar->next) { + if (ar->regiontype == RGN_TYPE_WINDOW) { + RegionView3D *rv3d = is_local ? ((RegionView3D *)ar->regiondata)->localvd : ar->regiondata; + if (rv3d && (rv3d->persp == RV3D_CAMOB)) { + rv3d->persp = RV3D_PERSP; + } + } + } + } + } + if ((ID *)v3d->ob_centre == old_id) { + v3d->ob_centre = (Object *)new_id; + if (new_id == NULL) { /* Otherwise, bonename may remain valid... We could be smart and check this, too? */ + v3d->ob_centre_bone[0] = '\0'; + } + } + + if ((ID *)v3d->defmaterial == old_id) { + v3d->defmaterial = (Material *)new_id; + } +#if 0 /* XXX Deprecated? */ + if ((ID *)v3d->gpd == old_id) { + v3d->gpd = (bGPData *)new_id; + } +#endif + + if (ELEM(GS(old_id->name), ID_IM, ID_MC)) { + for (BGpic *bgpic = v3d->bgpicbase.first; bgpic; bgpic = bgpic->next) { + if ((ID *)bgpic->ima == old_id) { + bgpic->ima = (Image *)new_id; + id_us_min(old_id); + id_us_plus(new_id); + } + if ((ID *)bgpic->clip == old_id) { + bgpic->clip = (MovieClip *)new_id; + id_us_min(old_id); + id_us_plus(new_id); + } + } + } + + if (is_local) { + break; + } + } +} /* only called once, from space/spacetypes.c */ void ED_spacetype_view3d(void) @@ -1418,7 +1478,8 @@ void ED_spacetype_view3d(void) st->keymap = view3d_keymap; st->dropboxes = view3d_dropboxes; st->context = view3d_context; - + st->id_remap = view3d_id_remap; + /* regions: main window */ art = MEM_callocN(sizeof(ARegionType), "spacetype view3d main region"); art->regionid = RGN_TYPE_WINDOW; diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c index 1f4ce926f16..e2f60955c81 100644 --- a/source/blender/editors/util/ed_util.c +++ b/source/blender/editors/util/ed_util.c @@ -57,6 +57,7 @@ #include "BKE_multires.h" #include "BKE_packedFile.h" #include "BKE_paint.h" +#include "BKE_screen.h" #include "ED_armature.h" #include "ED_buttons.h" @@ -326,22 +327,14 @@ void ED_region_draw_mouse_line_cb(const bContext *C, ARegion *ar, void *arg_info /** * Use to free ID references within runtime data (stored outside of DNA) * - * \note Typically notifiers take care of this, - * but there are times we have to free references immediately, see: T44376 + * \param new_id may be NULL to unlink \a old_id. */ -void ED_spacedata_id_unref(struct SpaceLink *sl, const ID *id) +void ED_spacedata_id_remap(struct ScrArea *sa, struct SpaceLink *sl, ID *old_id, ID *new_id) { + SpaceType *st = BKE_spacetype_from_id(sl->spacetype); - switch (sl->spacetype) { - case SPACE_OUTLINER: - ED_outliner_id_unref((SpaceOops *)sl, id); - break; - case SPACE_BUTS: - ED_buttons_id_unref((SpaceButs *)sl, id); - break; - case SPACE_NODE: - ED_node_id_unref((SpaceNode *)sl, id); - break; + if (st && st->id_remap) { + st->id_remap(sa, sl, old_id, new_id); } } diff --git a/source/blender/makesrna/RNA_types.h b/source/blender/makesrna/RNA_types.h index d8bea93bcbc..1d5f46a1814 100644 --- a/source/blender/makesrna/RNA_types.h +++ b/source/blender/makesrna/RNA_types.h @@ -285,6 +285,11 @@ typedef struct CollectionPointerLink { PointerRNA ptr; } CollectionPointerLink; +/* Copy of ListBase for RNA... */ +typedef struct CollectionListBase { + struct CollectionPointerLink *first, *last; +} CollectionListBase; + typedef enum RawPropertyType { PROP_RAW_UNSET = -1, PROP_RAW_INT, // XXX - abused for types that are not set, eg. MFace.verts, needs fixing. diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index 400610677ce..dca52926f00 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -466,7 +466,7 @@ static const char *rna_parameter_type_name(PropertyRNA *parm) } case PROP_COLLECTION: { - return "ListBase"; + return "CollectionListBase"; } default: return "<error, no type specified>"; diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index d8894a0c314..19545336f46 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -86,10 +86,13 @@ EnumPropertyItem rna_enum_id_type_items[] = { #include "DNA_anim_types.h" +#include "BLI_listbase.h" + #include "BKE_font.h" #include "BKE_idprop.h" #include "BKE_library.h" #include "BKE_library_query.h" +#include "BKE_library_remap.h" #include "BKE_animsys.h" #include "BKE_material.h" #include "BKE_depsgraph.h" @@ -321,6 +324,19 @@ static void rna_ID_user_clear(ID *id) id->us = 0; /* don't save */ } +static void rna_ID_delete(ID *id, Main *bmain) +{ + BKE_libblock_delete(bmain, id); +} + +static void rna_ID_user_remap(ID *id, Main *bmain, ID *new_id) +{ + if (GS(id->name) == GS(new_id->name)) { + /* For now, do not allow remapping data in linked data from here... */ + BKE_libblock_remap(bmain, id, new_id, ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_NEVER_NULL_USAGE); + } +} + static AnimData * rna_ID_animation_data_create(ID *id, Main *bmain) { AnimData *adt = BKE_animdata_add_id(id); @@ -330,7 +346,7 @@ static AnimData * rna_ID_animation_data_create(ID *id, Main *bmain) static void rna_ID_animation_data_free(ID *id, Main *bmain) { - BKE_animdata_free(id); + BKE_animdata_free(id, true); DAG_relations_tag_update(bmain); } @@ -961,10 +977,20 @@ static void rna_def_ID(BlenderRNA *brna) parm = RNA_def_pointer(func, "id", "ID", "", "New copy of the ID"); RNA_def_function_return(func, parm); + func = RNA_def_function(srna, "destroy", "rna_ID_delete"); + RNA_def_function_flag(func, FUNC_USE_MAIN); + RNA_def_function_ui_description(func, "Delete this ID from Blender (WARNING: no undo, do not use it after calling this!)"); + func = RNA_def_function(srna, "user_clear", "rna_ID_user_clear"); RNA_def_function_ui_description(func, "Clear the user count of a data-block so its not saved, " "on reload the data will be removed"); + func = RNA_def_function(srna, "user_remap", "rna_ID_user_remap"); + RNA_def_function_ui_description(func, "Replace all usage in the .blend file of this ID by new given one"); + RNA_def_function_flag(func, FUNC_USE_MAIN); + parm = RNA_def_pointer(func, "new_id", "ID", "", "New ID to use"); + RNA_def_property_flag(parm, PROP_NEVER_NULL); + func = RNA_def_function(srna, "user_of_id", "BKE_library_ID_use_ID"); RNA_def_function_ui_description(func, "Count the number of times that ID uses/references given one"); parm = RNA_def_pointer(func, "id", "ID", "", "ID to count usages"); diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c index a1ada20936d..cd379603901 100644 --- a/source/blender/makesrna/intern/rna_main_api.c +++ b/source/blender/makesrna/intern/rna_main_api.c @@ -58,6 +58,7 @@ #include "BKE_armature.h" #include "BKE_lamp.h" #include "BKE_library.h" +#include "BKE_library_remap.h" #include "BKE_object.h" #include "BKE_material.h" #include "BKE_icons.h" @@ -158,7 +159,8 @@ static void rna_Main_scenes_remove(Main *bmain, bContext *C, ReportList *reports } - BKE_scene_unlink(bmain, scene, scene_new); + BKE_libblock_remap(bmain, scene, scene_new, ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_NEVER_NULL_USAGE); + BKE_libblock_free(bmain, scene); RNA_POINTER_INVALIDATE(scene_ptr); } else { @@ -224,7 +226,7 @@ static void rna_Main_objects_remove(Main *bmain, ReportList *reports, PointerRNA { Object *object = object_ptr->data; if (ID_REAL_USERS(object) <= 0) { - BKE_object_unlink(bmain, object); /* needed or ID pointers to this are not cleared */ + BKE_libblock_unlink(bmain, object, false); BKE_libblock_free(bmain, object); RNA_POINTER_INVALIDATE(object_ptr); } @@ -540,7 +542,7 @@ static Group *rna_Main_groups_new(Main *bmain, const char *name) static void rna_Main_groups_remove(Main *bmain, PointerRNA *group_ptr) { Group *group = group_ptr->data; - BKE_group_unlink(bmain, group); + BKE_libblock_unlink(bmain, group, false); BKE_libblock_free(bmain, group); RNA_POINTER_INVALIDATE(group_ptr); } @@ -715,7 +717,6 @@ static Mask *rna_Main_mask_new(Main *bmain, const char *name) static void rna_Main_masks_remove(Main *bmain, PointerRNA *mask_ptr) { Mask *mask = mask_ptr->data; - BKE_mask_free(bmain, mask); BKE_libblock_free(bmain, mask); RNA_POINTER_INVALIDATE(mask_ptr); } diff --git a/source/blender/nodes/shader/node_shader_tree.c b/source/blender/nodes/shader/node_shader_tree.c index 5b65a2bbfd4..8523b7275bf 100644 --- a/source/blender/nodes/shader/node_shader_tree.c +++ b/source/blender/nodes/shader/node_shader_tree.c @@ -488,7 +488,7 @@ void ntreeGPUMaterialNodes(bNodeTree *ntree, GPUMaterial *mat, short compatibili ntreeExecGPUNodes(exec, mat, 1, compatibility); ntreeShaderEndExecTree(exec); - ntreeFreeTree_ex(localtree, false); + ntreeFreeTree(localtree); MEM_freeN(localtree); } diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 49b806347d6..1c6471c2cca 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -5220,7 +5220,7 @@ static PyObject *pyrna_param_to_py(PointerRNA *ptr, PropertyRNA *prop, void *dat } case PROP_COLLECTION: { - ListBase *lb = (ListBase *)data; + CollectionListBase *lb = (CollectionListBase *)data; CollectionPointerLink *link; ret = PyList_New(0); diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c index 2af2449c4ba..d81d77dc161 100644 --- a/source/blender/render/intern/source/pipeline.c +++ b/source/blender/render/intern/source/pipeline.c @@ -66,6 +66,7 @@ #include "BKE_global.h" #include "BKE_image.h" #include "BKE_library.h" +#include "BKE_library_remap.h" #include "BKE_main.h" #include "BKE_modifier.h" #include "BKE_node.h" @@ -2266,7 +2267,8 @@ static void free_all_freestyle_renders(void) if (freestyle_render) { freestyle_scene = freestyle_render->scene; RE_FreeRender(freestyle_render); - BKE_scene_unlink(re1->freestyle_bmain, freestyle_scene, NULL); + BKE_libblock_unlink(re1->freestyle_bmain, freestyle_scene, false); + BKE_libblock_free(re1->freestyle_bmain, freestyle_scene); } } BLI_freelistN(&re1->freestyle_renders); diff --git a/source/blender/render/intern/source/render_texture.c b/source/blender/render/intern/source/render_texture.c index 8f61f4159e6..530ebc084be 100644 --- a/source/blender/render/intern/source/render_texture.c +++ b/source/blender/render/intern/source/render_texture.c @@ -3806,6 +3806,7 @@ void RE_sample_material_free(Material *mat) MTex *mtex= mat->mtex[tex_nr]; if (mtex->tex) { + /* don't update user counts as we are freeing a duplicate */ BKE_texture_free(mtex->tex); MEM_freeN(mtex->tex); mtex->tex = NULL; @@ -3814,7 +3815,7 @@ void RE_sample_material_free(Material *mat) } /* don't update user counts as we are freeing a duplicate */ - BKE_material_free_ex(mat, false); + BKE_material_free(mat); MEM_freeN(mat); } diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 388837af67a..9bb2462a4e9 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -191,7 +191,7 @@ void WM_ndof_deadzone_set(float deadzone); void WM_event_add_notifier(const struct bContext *C, unsigned int type, void *reference); void WM_main_add_notifier(unsigned int type, void *reference); void WM_main_remove_notifier_reference(const void *reference); -void WM_main_remove_editor_id_reference(const struct ID *id); +void WM_main_remap_editor_id_reference(struct ID *old_id, struct ID *new_id); /* reports */ void WM_report_banner_show(void); diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index f632dd9aff4..4515ae92f66 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -241,7 +241,7 @@ void WM_main_remove_notifier_reference(const void *reference) } } -void WM_main_remove_editor_id_reference(const ID *id) +void WM_main_remap_editor_id_reference(ID *old_id, ID *new_id) { Main *bmain = G.main; bScreen *sc; @@ -253,7 +253,7 @@ void WM_main_remove_editor_id_reference(const ID *id) SpaceLink *sl; for (sl = sa->spacedata.first; sl; sl = sl->next) { - ED_spacedata_id_unref(sl, id); + ED_spacedata_id_remap(sa, sl, old_id, new_id); } } } diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index 2e4a4b63b7a..3709e10b366 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -61,6 +61,7 @@ #include "BKE_context.h" #include "BKE_depsgraph.h" #include "BKE_library.h" +#include "BKE_library_remap.h" #include "BKE_global.h" #include "BKE_main.h" #include "BKE_report.h" @@ -211,7 +212,8 @@ static WMLinkAppendDataItem *wm_link_append_data_item_add( } static void wm_link_do( - WMLinkAppendData *lapp_data, ReportList *reports, Main *bmain, Scene *scene, View3D *v3d) + WMLinkAppendData *lapp_data, ReportList *reports, Main *bmain, Scene *scene, View3D *v3d, + const bool use_placeholders, const bool force_indirect) { Main *mainl; BlendHandle *bh; @@ -258,7 +260,9 @@ static void wm_link_do( continue; } - new_id = BLO_library_link_named_part_ex(mainl, &bh, item->idcode, item->name, flag, scene, v3d); + new_id = BLO_library_link_named_part_ex( + mainl, &bh, item->idcode, item->name, flag, scene, v3d, use_placeholders, force_indirect); + if (new_id) { /* If the link is sucessful, clear item's libs 'todo' flags. * This avoids trying to link same item with other libraries to come. */ @@ -401,7 +405,7 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) /* XXX We'd need re-entrant locking on Main for this to work... */ /* BKE_main_lock(bmain); */ - wm_link_do(lapp_data, op->reports, bmain, scene, CTX_wm_view3d(C)); + wm_link_do(lapp_data, op->reports, bmain, scene, CTX_wm_view3d(C), false, false); /* BKE_main_unlock(bmain); */ @@ -518,3 +522,393 @@ void WM_OT_append(wmOperatorType *ot) RNA_def_boolean(ot->srna, "use_recursive", true, "Localize All", "Localize all appended data, including those indirectly linked from other libraries"); } + +/** \name Reload/relocate libraries. + * + * \{ */ + +static int wm_lib_relocate_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + Library *lib; + char lib_name[MAX_NAME]; + + RNA_string_get(op->ptr, "library", lib_name); + lib = (Library *)BKE_libblock_find_name_ex(CTX_data_main(C), ID_LI, lib_name); + + if (lib) { + if (lib->parent) { + BKE_reportf(op->reports, RPT_ERROR_INVALID_INPUT, + "Cannot relocate indirectly linked library '%s'", lib->filepath); + return OPERATOR_CANCELLED; + } + RNA_string_set(op->ptr, "filepath", lib->filepath); + + WM_event_add_fileselect(C, op); + + return OPERATOR_RUNNING_MODAL; + } + + return OPERATOR_CANCELLED; +} + +/* Note that IDs listed in lapp_data items *must* have been removed from bmain by caller. */ +static void lib_relocate_do(Main *bmain, WMLinkAppendData *lapp_data, ReportList *reports, const bool do_reload) +{ + ListBase *lbarray[MAX_LIBARRAY]; + int lba_idx; + + LinkNode *itemlink; + int item_idx; + + BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true); + + /* We do not want any instanciation here! */ + wm_link_do(lapp_data, reports, bmain, NULL, NULL, do_reload, do_reload); + + BKE_main_lock(bmain); + + /* We add back old id to bmain. + * We need to do this in a first, separated loop, otherwise some of those may not be handled by + * ID remapping, which means they would still reference old data to be deleted... */ + for (item_idx = 0, itemlink = lapp_data->items.list; itemlink; item_idx++, itemlink = itemlink->next) { + WMLinkAppendDataItem *item = itemlink->link; + ID *old_id = item->customdata; + + BLI_assert(old_id); + BLI_addtail(which_libbase(bmain, GS(old_id->name)), old_id); + } + + /* Note that in reload case, we also want to replace indirect usages. */ + const short remap_flags = ID_REMAP_SKIP_NEVER_NULL_USAGE | (do_reload ? 0 : ID_REMAP_SKIP_INDIRECT_USAGE); + for (item_idx = 0, itemlink = lapp_data->items.list; itemlink; item_idx++, itemlink = itemlink->next) { + WMLinkAppendDataItem *item = itemlink->link; + ID *old_id = item->customdata; + ID *new_id = item->new_id; + + BLI_assert(old_id); + if (do_reload) { + /* Since we asked for placeholders in case of missing IDs, we expect to always get a valid one. */ + BLI_assert(new_id); + } + if (new_id) { +#ifdef PRINT_DEBUG + printf("before remap, old_id users: %d, new_id users: %d\n", old_id->us, new_id->us); +#endif + BKE_libblock_remap_locked(bmain, old_id, new_id, remap_flags); + + if (old_id->flag & LIB_FAKEUSER) { + id_fake_user_clear(old_id); + id_fake_user_set(new_id); + } + +#ifdef PRINT_DEBUG + printf("after remap, old_id users: %d, new_id users: %d\n", old_id->us, new_id->us); +#endif + + /* In some cases, new_id might become direct link, remove parent of library in this case. */ + if (new_id->lib->parent && (new_id->tag & LIB_TAG_INDIRECT) == 0) { + if (do_reload) { + BLI_assert(0); /* Should not happen in 'pure' reload case... */ + } + new_id->lib->parent = NULL; + } + } + + if (old_id->us > 0 && new_id && old_id->lib == new_id->lib) { + /* Note that this *should* not happen - but better be safe than sorry in this area, at least until we are + * 100% sure this cannot ever happen. + * Also, we can safely assume names were unique so far, so just replacing '.' by '~' should work, + * but this does not totally rules out the possibility of name collision. */ + size_t len = strlen(old_id->name); + size_t dot_pos; + bool has_num = false; + + for (dot_pos = len; dot_pos--;) { + char c = old_id->name[dot_pos]; + if (c == '.') { + break; + } + else if (c < '0' || c > '9') { + has_num = false; + break; + } + has_num = true; + } + + if (has_num) { + old_id->name[dot_pos] = '~'; + } + else { + len = MIN2(len, MAX_ID_NAME - 7); + BLI_strncpy(&old_id->name[len], "~000", 7); + } + + id_sort_by_name(which_libbase(bmain, GS(old_id->name)), old_id); + + BKE_reportf(reports, RPT_WARNING, + "Lib Reload: Replacing all references to old datablock '%s' by reloaded one failed, " + "old one (%d remaining users) had to be kept and was renamed to '%s'", + new_id->name, old_id->us, old_id->name); + } + } + + BKE_main_unlock(bmain); + + for (item_idx = 0, itemlink = lapp_data->items.list; itemlink; item_idx++, itemlink = itemlink->next) { + WMLinkAppendDataItem *item = itemlink->link; + ID *old_id = item->customdata; + + if (old_id->us == 0) { + BKE_libblock_free(bmain, old_id); + } + } + + /* Some datablocks can get reloaded/replaced 'silently' because they are not linkable (shape keys e.g.), + * so we need another loop here to clear old ones if possible. */ + lba_idx = set_listbasepointers(bmain, lbarray); + while (lba_idx--) { + ID *id, *id_next; + for (id = lbarray[lba_idx]->first; id; id = id_next) { + id_next = id->next; + /* XXX That check may be a bit to generic/permissive? */ + if (id->lib && (id->flag & LIB_TAG_PRE_EXISTING) && id->us == 0) { + BKE_libblock_free(bmain, id); + } + } + } + + /* Get rid of no more used libraries... */ + BKE_main_id_tag_idcode(bmain, ID_LI, LIB_TAG_DOIT, true); + lba_idx = set_listbasepointers(bmain, lbarray); + while (lba_idx--) { + ID *id; + for (id = lbarray[lba_idx]->first; id; id = id->next) { + if (id->lib) { + id->lib->id.tag &= ~LIB_TAG_DOIT; + } + } + } + Library *lib, *lib_next; + for (lib = which_libbase(bmain, ID_LI)->first; lib; lib = lib_next) { + lib_next = lib->id.next; + if (lib->id.tag & LIB_TAG_DOIT) { + id_us_clear_real(&lib->id); + if (lib->id.us == 0) { + BKE_libblock_free(bmain, (ID *)lib); + } + } + } +} + +static int wm_lib_relocate_exec_do(bContext *C, wmOperator *op, bool do_reload) +{ + Library *lib; + char lib_name[MAX_NAME]; + + RNA_string_get(op->ptr, "library", lib_name); + lib = (Library *)BKE_libblock_find_name_ex(CTX_data_main(C), ID_LI, lib_name); + + if (lib) { + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + PropertyRNA *prop; + WMLinkAppendData *lapp_data; + + ListBase *lbarray[MAX_LIBARRAY]; + int lba_idx; + + char path[FILE_MAX], root[FILE_MAXDIR], libname[FILE_MAX], relname[FILE_MAX]; + short flag = 0; + + if (RNA_boolean_get(op->ptr, "relative_path")) { + flag |= FILE_RELPATH; + } + + if (lib->parent && !do_reload) { + BKE_reportf(op->reports, RPT_ERROR_INVALID_INPUT, + "Cannot relocate indirectly linked library '%s'", lib->filepath); + return OPERATOR_CANCELLED; + } + + RNA_string_get(op->ptr, "directory", root); + RNA_string_get(op->ptr, "filename", libname); + + if (!BLO_has_bfile_extension(libname)) { + BKE_report(op->reports, RPT_ERROR, "Not a library"); + return OPERATOR_CANCELLED; + } + + BLI_join_dirfile(path, sizeof(path), root, libname); + + if (!BLI_exists(path)) { + BKE_reportf(op->reports, RPT_ERROR_INVALID_INPUT, + "Trying to reload or relocate library '%s' to invalid path '%s'", lib->id.name, path); + return OPERATOR_CANCELLED; + } + + if (BLI_path_cmp(lib->filepath, path) == 0) { +#ifdef PRINT_DEBUG + printf("We are supposed to reload '%s' lib (%d)...\n", lib->filepath, lib->id.us); +#endif + + do_reload = true; + + lapp_data = wm_link_append_data_new(flag); + wm_link_append_data_library_add(lapp_data, path); + } + else { + int totfiles = 0; + +#ifdef PRINT_DEBUG + printf("We are supposed to relocate '%s' lib to new '%s' one...\n", lib->filepath, libname); +#endif + + /* Check if something is indicated for relocate. */ + prop = RNA_struct_find_property(op->ptr, "files"); + if (prop) { + totfiles = RNA_property_collection_length(op->ptr, prop); + if (totfiles == 0) { + if (!libname[0]) { + BKE_report(op->reports, RPT_ERROR, "Nothing indicated"); + return OPERATOR_CANCELLED; + } + } + } + + lapp_data = wm_link_append_data_new(flag); + + if (totfiles) { + RNA_BEGIN (op->ptr, itemptr, "files") + { + RNA_string_get(&itemptr, "name", relname); + + BLI_join_dirfile(path, sizeof(path), root, relname); + + if (BLI_path_cmp(path, lib->filepath) == 0 || !BLO_has_bfile_extension(relname)) { + continue; + } + +#ifdef PRINT_DEBUG + printf("\t candidate new lib to reload datablocks from: %s\n", path); +#endif + wm_link_append_data_library_add(lapp_data, path); + } + RNA_END; + } + else { +#ifdef PRINT_DEBUG + printf("\t candidate new lib to reload datablocks from: %s\n", path); +#endif + wm_link_append_data_library_add(lapp_data, path); + } + } + + lba_idx = set_listbasepointers(bmain, lbarray); + while (lba_idx--) { + ID *id = lbarray[lba_idx]->first; + const short idcode = id ? GS(id->name) : 0; + + if (!id || !BKE_idcode_is_linkable(idcode)) { + /* No need to reload non-linkable datatypes, those will get relinked with their 'users ID'. */ + continue; + } + + for (; id; id = id->next) { + if (id->lib == lib) { + WMLinkAppendDataItem *item; + + /* We remove it from current Main, and add it to items to link... */ + /* Note that non-linkable IDs (like e.g. shapekeys) are also explicitely linked here... */ + BLI_remlink(lbarray[lba_idx], id); + item = wm_link_append_data_item_add(lapp_data, id->name + 2, idcode, id); + BLI_BITMAP_SET_ALL(item->libraries, true, lapp_data->num_libraries); + +#ifdef PRINT_DEBUG + printf("\tdatablock to seek for: %s\n", id->name); +#endif + } + } + } + + lib_relocate_do(bmain, lapp_data, op->reports, do_reload); + + wm_link_append_data_free(lapp_data); + + BKE_main_lib_objects_recalc_all(bmain); + IMB_colormanagement_check_file_config(bmain); + + /* important we unset, otherwise these object wont + * link into other scenes from this blend file */ + BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false); + + /* recreate dependency graph to include new objects */ + DAG_scene_relations_rebuild(bmain, scene); + + /* free gpu materials, some materials depend on existing objects, such as lamps so freeing correctly refreshes */ + GPU_materials_free(); + + /* XXX TODO: align G.lib with other directory storage (like last opened image etc...) */ + BLI_strncpy(G.lib, root, FILE_MAX); + + WM_event_add_notifier(C, NC_WINDOW, NULL); + + return OPERATOR_FINISHED; + } + + return OPERATOR_CANCELLED; +} + +static int wm_lib_relocate_exec(bContext *C, wmOperator *op) +{ + return wm_lib_relocate_exec_do(C, op, false); +} + +void WM_OT_lib_relocate(wmOperatorType *ot) +{ + PropertyRNA *prop; + + ot->name = "Relocate Library"; + ot->idname = "WM_OT_lib_relocate"; + ot->description = "Relocate the given library to one or several others"; + + ot->invoke = wm_lib_relocate_invoke; + ot->exec = wm_lib_relocate_exec; + + ot->flag |= OPTYPE_UNDO; + + prop = RNA_def_string(ot->srna, "library", NULL, MAX_NAME, "Library", "Library to relocate"); + RNA_def_property_flag(prop, PROP_HIDDEN); + + WM_operator_properties_filesel( + ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER, FILE_BLENDER, FILE_OPENFILE, + WM_FILESEL_FILEPATH | WM_FILESEL_DIRECTORY | WM_FILESEL_FILENAME | WM_FILESEL_FILES | WM_FILESEL_RELPATH, + FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); +} + +static int wm_lib_reload_exec(bContext *C, wmOperator *op) +{ + return wm_lib_relocate_exec_do(C, op, true); +} + +void WM_OT_lib_reload(wmOperatorType *ot) +{ + PropertyRNA *prop; + + ot->name = "Reload Library"; + ot->idname = "WM_OT_lib_reload"; + ot->description = "Reload the given library"; + + ot->exec = wm_lib_reload_exec; + + ot->flag |= OPTYPE_UNDO; + + prop = RNA_def_string(ot->srna, "library", NULL, MAX_NAME, "Library", "Library to reload"); + RNA_def_property_flag(prop, PROP_HIDDEN); + + WM_operator_properties_filesel( + ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER, FILE_BLENDER, FILE_OPENFILE, + WM_FILESEL_FILEPATH | WM_FILESEL_DIRECTORY | WM_FILESEL_FILENAME | WM_FILESEL_RELPATH, + FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); +} + +/** \} */ diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 917e2bf5913..a1ca89c6a8c 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -61,6 +61,7 @@ #include "BKE_global.h" #include "BKE_icons.h" #include "BKE_library.h" +#include "BKE_library_remap.h" #include "BKE_main.h" #include "BKE_mball_tessellate.h" #include "BKE_node.h" @@ -160,9 +161,9 @@ void WM_init(bContext *C, int argc, const char **argv) BKE_library_callback_free_window_manager_set(wm_close_and_free); /* library.c */ BKE_library_callback_free_notifier_reference_set(WM_main_remove_notifier_reference); /* library.c */ - BKE_library_callback_free_editor_id_reference_set(WM_main_remove_editor_id_reference); /* library.c */ + BKE_library_callback_remap_editor_id_reference_set(WM_main_remap_editor_id_reference); /* library.c */ BKE_blender_callback_test_break_set(wm_window_testbreak); /* blender.c */ - BKE_spacedata_callback_id_unref_set(ED_spacedata_id_unref); /* screen.c */ + BKE_spacedata_callback_id_remap_set(ED_spacedata_id_remap); /* screen.c */ DAG_editors_update_cb(ED_render_id_flush_update, ED_render_scene_update, ED_render_scene_update_pre); /* depsgraph.c */ diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index 866e332b34f..a51648290db 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -4127,6 +4127,8 @@ void wm_operatortype_init(void) WM_operatortype_append(WM_OT_revert_mainfile); WM_operatortype_append(WM_OT_link); WM_operatortype_append(WM_OT_append); + WM_operatortype_append(WM_OT_lib_relocate); + WM_operatortype_append(WM_OT_lib_reload); WM_operatortype_append(WM_OT_recover_last_session); WM_operatortype_append(WM_OT_recover_auto_save); WM_operatortype_append(WM_OT_save_as_mainfile); diff --git a/source/blender/windowmanager/wm_files.h b/source/blender/windowmanager/wm_files.h index 2eae9cdb012..396907a3f6d 100644 --- a/source/blender/windowmanager/wm_files.h +++ b/source/blender/windowmanager/wm_files.h @@ -59,5 +59,8 @@ void WM_OT_save_mainfile(struct wmOperatorType *ot); void WM_OT_link(struct wmOperatorType *ot); void WM_OT_append(struct wmOperatorType *ot); +void WM_OT_lib_relocate(struct wmOperatorType *ot); +void WM_OT_lib_reload(struct wmOperatorType *ot); + #endif /* __WM_FILES_H__ */ diff --git a/source/gameengine/GamePlayer/ghost/GPG_ghost.cpp b/source/gameengine/GamePlayer/ghost/GPG_ghost.cpp index 30ad5b37777..0ea3a3e62eb 100644 --- a/source/gameengine/GamePlayer/ghost/GPG_ghost.cpp +++ b/source/gameengine/GamePlayer/ghost/GPG_ghost.cpp @@ -74,6 +74,7 @@ extern "C" #include "BKE_node.h" #include "BKE_report.h" #include "BKE_library.h" +#include "BKE_library_remap.h" #include "BKE_modifier.h" #include "BKE_material.h" #include "BKE_text.h" |