Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/object/object_relations.c
| Show First 20 Lines • Show All 64 Lines • ▼ Show 20 Lines | |||||
| #include "BKE_camera.h" | #include "BKE_camera.h" | ||||
| #include "BKE_collection.h" | #include "BKE_collection.h" | ||||
| #include "BKE_context.h" | #include "BKE_context.h" | ||||
| #include "BKE_constraint.h" | #include "BKE_constraint.h" | ||||
| #include "BKE_curve.h" | #include "BKE_curve.h" | ||||
| #include "BKE_DerivedMesh.h" | #include "BKE_DerivedMesh.h" | ||||
| #include "BKE_displist.h" | #include "BKE_displist.h" | ||||
| #include "BKE_global.h" | #include "BKE_global.h" | ||||
| #include "BKE_group.h" | |||||
| #include "BKE_fcurve.h" | #include "BKE_fcurve.h" | ||||
| #include "BKE_idprop.h" | #include "BKE_idprop.h" | ||||
| #include "BKE_lamp.h" | #include "BKE_lamp.h" | ||||
| #include "BKE_lattice.h" | #include "BKE_lattice.h" | ||||
| #include "BKE_layer.h" | #include "BKE_layer.h" | ||||
| #include "BKE_library.h" | #include "BKE_library.h" | ||||
| #include "BKE_library_override.h" | #include "BKE_library_override.h" | ||||
| #include "BKE_library_query.h" | #include "BKE_library_query.h" | ||||
| ▲ Show 20 Lines • Show All 242 Lines • ▼ Show 20 Lines | else if (ID_IS_LINKED(ob)) { | ||||
| /* present the menu and be done... */ | /* present the menu and be done... */ | ||||
| UI_popup_menu_end(C, pup); | UI_popup_menu_end(C, pup); | ||||
| /* this invoke just calls another instance of this operator... */ | /* this invoke just calls another instance of this operator... */ | ||||
| return OPERATOR_INTERFACE; | return OPERATOR_INTERFACE; | ||||
| } | } | ||||
| else { | else { | ||||
| /* error.. cannot continue */ | /* error.. cannot continue */ | ||||
| BKE_report(op->reports, RPT_ERROR, "Can only make proxy for a referenced object or group"); | BKE_report(op->reports, RPT_ERROR, "Can only make proxy for a referenced object or collection"); | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| } | } | ||||
| static int make_proxy_exec(bContext *C, wmOperator *op) | static int make_proxy_exec(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| Main *bmain = CTX_data_main(C); | Main *bmain = CTX_data_main(C); | ||||
| Object *ob, *gob = ED_object_active_context(C); | Object *ob, *gob = ED_object_active_context(C); | ||||
| Scene *scene = CTX_data_scene(C); | Scene *scene = CTX_data_scene(C); | ||||
| ViewLayer *view_layer = CTX_data_view_layer(C); | ViewLayer *view_layer = CTX_data_view_layer(C); | ||||
| if (gob->dup_group != NULL) { | if (gob->dup_group != NULL) { | ||||
| Base *base = BLI_findlink(&gob->dup_group->view_layer->object_bases, RNA_enum_get(op->ptr, "object")); | const ListBase dup_group_objects = BKE_collection_object_cache_get(gob->dup_group); | ||||
| Base *base = BLI_findlink(&dup_group_objects, RNA_enum_get(op->ptr, "object")); | |||||
| ob = base->object; | ob = base->object; | ||||
| } | } | ||||
| else { | else { | ||||
| ob = gob; | ob = gob; | ||||
| } | } | ||||
| if (ob) { | if (ob) { | ||||
| Object *newob; | Object *newob; | ||||
| Show All 25 Lines | else { | ||||
| BKE_report(op->reports, RPT_ERROR, "No object to make proxy for"); | BKE_report(op->reports, RPT_ERROR, "No object to make proxy for"); | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| /* Generic itemf's for operators that take library args */ | /* Generic itemf's for operators that take library args */ | ||||
| static const EnumPropertyItem *proxy_group_object_itemf(bContext *C, PointerRNA *UNUSED(ptr), | static const EnumPropertyItem *proxy_collection_object_itemf(bContext *C, PointerRNA *UNUSED(ptr), | ||||
| PropertyRNA *UNUSED(prop), bool *r_free) | PropertyRNA *UNUSED(prop), bool *r_free) | ||||
| { | { | ||||
| EnumPropertyItem item_tmp = {0}, *item = NULL; | EnumPropertyItem item_tmp = {0}, *item = NULL; | ||||
| int totitem = 0; | int totitem = 0; | ||||
| int i = 0; | int i = 0; | ||||
| Object *ob = ED_object_active_context(C); | Object *ob = ED_object_active_context(C); | ||||
| if (!ob || !ob->dup_group) | if (!ob || !ob->dup_group) | ||||
| return DummyRNA_DEFAULT_items; | return DummyRNA_DEFAULT_items; | ||||
| /* find the object to affect */ | /* find the object to affect */ | ||||
| FOREACH_GROUP_OBJECT_BEGIN(ob->dup_group, object) | FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN(ob->dup_group, object) | ||||
| { | { | ||||
| item_tmp.identifier = item_tmp.name = object->id.name + 2; | item_tmp.identifier = item_tmp.name = object->id.name + 2; | ||||
| item_tmp.value = i++; | item_tmp.value = i++; | ||||
| RNA_enum_item_add(&item, &totitem, &item_tmp); | RNA_enum_item_add(&item, &totitem, &item_tmp); | ||||
| } | } | ||||
| FOREACH_GROUP_OBJECT_END; | FOREACH_COLLECTION_OBJECT_RECURSIVE_END; | ||||
| RNA_enum_item_end(&item, &totitem); | RNA_enum_item_end(&item, &totitem); | ||||
| *r_free = true; | *r_free = true; | ||||
| return item; | return item; | ||||
| } | } | ||||
| void OBJECT_OT_proxy_make(wmOperatorType *ot) | void OBJECT_OT_proxy_make(wmOperatorType *ot) | ||||
| Show All 11 Lines | void OBJECT_OT_proxy_make(wmOperatorType *ot) | ||||
| ot->poll = ED_operator_object_active; | ot->poll = ED_operator_object_active; | ||||
| /* flags */ | /* flags */ | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||
| /* properties */ | /* properties */ | ||||
| /* XXX, relies on hard coded ID at the moment */ | /* XXX, relies on hard coded ID at the moment */ | ||||
| prop = RNA_def_enum(ot->srna, "object", DummyRNA_DEFAULT_items, 0, "Proxy Object", | prop = RNA_def_enum(ot->srna, "object", DummyRNA_DEFAULT_items, 0, "Proxy Object", | ||||
| "Name of lib-linked/grouped object to make a proxy for"); | "Name of lib-linked/collection object to make a proxy for"); | ||||
| RNA_def_enum_funcs(prop, proxy_group_object_itemf); | RNA_def_enum_funcs(prop, proxy_collection_object_itemf); | ||||
| RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); | RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); | ||||
| ot->prop = prop; | ot->prop = prop; | ||||
| } | } | ||||
| /********************** Clear Parent Operator ******************* */ | /********************** Clear Parent Operator ******************* */ | ||||
| typedef enum eObClearParentTypes { | typedef enum eObClearParentTypes { | ||||
| CLEAR_PARENT_ALL = 0, | CLEAR_PARENT_ALL = 0, | ||||
| ▲ Show 20 Lines • Show All 894 Lines • ▼ Show 20 Lines | if (TESTBASE(v3d, base)) { | ||||
| id_us_plus((ID *)base->object); | id_us_plus((ID *)base->object); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| #endif | #endif | ||||
| static int make_links_scene_exec(bContext *C, wmOperator *op) | static int make_links_scene_exec(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| Scene *scene_to = BLI_findlink(&CTX_data_main(C)->scene, RNA_enum_get(op->ptr, "scene")); | Main *bmain = CTX_data_main(C); | ||||
| Scene *scene_to = BLI_findlink(&bmain->scene, RNA_enum_get(op->ptr, "scene")); | |||||
| if (scene_to == NULL) { | if (scene_to == NULL) { | ||||
| BKE_report(op->reports, RPT_ERROR, "Could not find scene"); | BKE_report(op->reports, RPT_ERROR, "Could not find scene"); | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| if (scene_to == CTX_data_scene(C)) { | if (scene_to == CTX_data_scene(C)) { | ||||
| BKE_report(op->reports, RPT_ERROR, "Cannot link objects into the same scene"); | BKE_report(op->reports, RPT_ERROR, "Cannot link objects into the same scene"); | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| if (ID_IS_LINKED(scene_to)) { | if (ID_IS_LINKED(scene_to)) { | ||||
| BKE_report(op->reports, RPT_ERROR, "Cannot link objects into a linked scene"); | BKE_report(op->reports, RPT_ERROR, "Cannot link objects into a linked scene"); | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| SceneCollection *sc_to = BKE_collection_master(&scene_to->id); | Collection *collection_to = BKE_collection_master(scene_to); | ||||
| CTX_DATA_BEGIN (C, Base *, base, selected_bases) | CTX_DATA_BEGIN (C, Base *, base, selected_bases) | ||||
| { | { | ||||
| BKE_collection_object_add(&scene_to->id, sc_to, base->object); | BKE_collection_object_add(bmain, collection_to, base->object); | ||||
| } | } | ||||
| CTX_DATA_END; | CTX_DATA_END; | ||||
| /* redraw the 3D view because the object center points are colored differently */ | /* redraw the 3D view because the object center points are colored differently */ | ||||
| WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, NULL); | WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, NULL); | ||||
| /* one day multiple scenes will be visible, then we should have some update function for them */ | /* one day multiple scenes will be visible, then we should have some update function for them */ | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| ▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | |||||
| static int make_links_data_exec(bContext *C, wmOperator *op) | static int make_links_data_exec(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| Main *bmain = CTX_data_main(C); | Main *bmain = CTX_data_main(C); | ||||
| const int type = RNA_enum_get(op->ptr, "type"); | const int type = RNA_enum_get(op->ptr, "type"); | ||||
| Object *ob_src; | Object *ob_src; | ||||
| ID *obdata_id; | ID *obdata_id; | ||||
| int a; | int a; | ||||
| /* group */ | /* collection */ | ||||
| LinkNode *ob_groups = NULL; | LinkNode *ob_collections = NULL; | ||||
| bool is_cycle = false; | bool is_cycle = false; | ||||
| bool is_lib = false; | bool is_lib = false; | ||||
| ob_src = ED_object_active_context(C); | ob_src = ED_object_active_context(C); | ||||
| /* avoid searching all groups in source object each time */ | /* avoid searching all collections in source object each time */ | ||||
| if (type == MAKE_LINKS_GROUP) { | if (type == MAKE_LINKS_GROUP) { | ||||
| ob_groups = BKE_object_groups(ob_src); | ob_collections = BKE_object_groups(bmain, ob_src); | ||||
| } | } | ||||
| CTX_DATA_BEGIN (C, Base *, base_dst, selected_editable_bases) | CTX_DATA_BEGIN (C, Base *, base_dst, selected_editable_bases) | ||||
| { | { | ||||
| Object *ob_dst = base_dst->object; | Object *ob_dst = base_dst->object; | ||||
| if (ob_src != ob_dst) { | if (ob_src != ob_dst) { | ||||
| if (allow_make_links_data(type, ob_src, ob_dst)) { | if (allow_make_links_data(type, ob_src, ob_dst)) { | ||||
| Show All 28 Lines | if (ob_src != ob_dst) { | ||||
| break; | break; | ||||
| } | } | ||||
| BKE_animdata_copy_id(bmain, (ID *)ob_dst->data, (ID *)ob_src->data, false, true); | BKE_animdata_copy_id(bmain, (ID *)ob_dst->data, (ID *)ob_src->data, false, true); | ||||
| } | } | ||||
| DEG_id_tag_update(&ob_dst->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); | DEG_id_tag_update(&ob_dst->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); | ||||
| break; | break; | ||||
| case MAKE_LINKS_GROUP: | case MAKE_LINKS_GROUP: | ||||
| { | { | ||||
| LinkNode *group_node; | LinkNode *collection_node; | ||||
| /* first clear groups */ | /* first clear collections */ | ||||
| BKE_object_groups_clear(ob_dst); | BKE_object_groups_clear(bmain, ob_dst); | ||||
| /* now add in the groups from the link nodes */ | /* now add in the collections from the link nodes */ | ||||
| for (group_node = ob_groups; group_node; group_node = group_node->next) { | for (collection_node = ob_collections; collection_node; collection_node = collection_node->next) { | ||||
| if (ob_dst->dup_group != group_node->link) { | if (ob_dst->dup_group != collection_node->link) { | ||||
| BKE_group_object_add(group_node->link, ob_dst); | BKE_collection_object_add(bmain, collection_node->link, ob_dst); | ||||
| } | } | ||||
| else { | else { | ||||
| is_cycle = true; | is_cycle = true; | ||||
| } | } | ||||
| } | } | ||||
| break; | break; | ||||
| } | } | ||||
| case MAKE_LINKS_DUPLIGROUP: | case MAKE_LINKS_DUPLIGROUP: | ||||
| Show All 39 Lines | if (ob_src != ob_dst) { | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| CTX_DATA_END; | CTX_DATA_END; | ||||
| if (type == MAKE_LINKS_GROUP) { | if (type == MAKE_LINKS_GROUP) { | ||||
| if (ob_groups) { | if (ob_collections) { | ||||
| BLI_linklist_free(ob_groups, NULL); | BLI_linklist_free(ob_collections, NULL); | ||||
| } | } | ||||
| if (is_cycle) { | if (is_cycle) { | ||||
| BKE_report(op->reports, RPT_WARNING, "Skipped some groups because of cycle detected"); | BKE_report(op->reports, RPT_WARNING, "Skipped some collections because of cycle detected"); | ||||
| } | } | ||||
| } | } | ||||
| if (is_lib) { | if (is_lib) { | ||||
| BKE_report(op->reports, RPT_WARNING, "Skipped editing library object data"); | BKE_report(op->reports, RPT_WARNING, "Skipped editing library object data"); | ||||
| } | } | ||||
| DEG_relations_tag_update(bmain); | DEG_relations_tag_update(bmain); | ||||
| ▲ Show 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | void OBJECT_OT_make_links_data(wmOperatorType *ot) | ||||
| /* properties */ | /* properties */ | ||||
| ot->prop = RNA_def_enum(ot->srna, "type", make_links_items, 0, "Type", ""); | ot->prop = RNA_def_enum(ot->srna, "type", make_links_items, 0, "Type", ""); | ||||
| } | } | ||||
| /**************************** Make Single User ********************************/ | /**************************** Make Single User ********************************/ | ||||
| static Object *single_object_users_object(Main *bmain, Scene *scene, Object *ob, const bool copy_groups) | static Object *single_object_users_object(Main *bmain, Scene *scene, Object *ob) | ||||
| { | { | ||||
| /* base gets copy of object */ | /* base gets copy of object */ | ||||
| Object *obn = ID_NEW_SET(ob, BKE_object_copy(bmain, ob)); | Object *obn = ID_NEW_SET(ob, BKE_object_copy(bmain, ob)); | ||||
| if (copy_groups) { | |||||
| if (ob->flag & OB_FROMGROUP) { | |||||
| obn->flag |= OB_FROMGROUP; | |||||
| } | |||||
| } | |||||
| else { | |||||
| /* copy already clears */ | |||||
| } | |||||
| /* remap gpencil parenting */ | /* remap gpencil parenting */ | ||||
| if (scene->gpd) { | if (scene->gpd) { | ||||
| bGPdata *gpd = scene->gpd; | bGPdata *gpd = scene->gpd; | ||||
| for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { | for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { | ||||
| if (gpl->parent == ob) { | if (gpl->parent == ob) { | ||||
| gpl->parent = obn; | gpl->parent = obn; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| id_us_min(&ob->id); | id_us_min(&ob->id); | ||||
| return obn; | return obn; | ||||
| } | } | ||||
| static void libblock_relink_scene_collection(SceneCollection *sc) | static void libblock_relink_collection(Collection *collection) | ||||
| { | { | ||||
| for (LinkData *link = sc->objects.first; link; link = link->next) { | for (CollectionObject *cob = collection->gobject.first; cob; cob = cob->next) { | ||||
| BKE_libblock_relink_to_newid(link->data); | BKE_libblock_relink_to_newid(&cob->ob->id); | ||||
| } | } | ||||
| for (SceneCollection *nsc = sc->scene_collections.first; nsc; nsc = nsc->next) { | for (CollectionChild *child = collection->children.first; child; child = child->next) { | ||||
| libblock_relink_scene_collection(nsc); | libblock_relink_collection(child->collection); | ||||
| } | } | ||||
| } | } | ||||
| static void single_object_users_scene_collection(Main *bmain, Scene *scene, SceneCollection *sc, const int flag, const bool copy_groups) | static void single_object_users_collection(Main *bmain, Scene *scene, Collection *collection, const int flag, const bool copy_collections) | ||||
| { | { | ||||
| for (LinkData *link = sc->objects.first; link; link = link->next) { | for (CollectionObject *cob = collection->gobject.first; cob; cob = cob->next) { | ||||
| Object *ob = link->data; | Object *ob = cob->ob; | ||||
| /* an object may be in more than one collection */ | /* an object may be in more than one collection */ | ||||
| if ((ob->id.newid == NULL) && ((ob->flag & flag) == flag)) { | if ((ob->id.newid == NULL) && ((ob->flag & flag) == flag)) { | ||||
| if (!ID_IS_LINKED(ob) && ob->id.us > 1) { | if (!ID_IS_LINKED(ob) && ob->id.us > 1) { | ||||
| link->data = single_object_users_object(bmain, scene, link->data, copy_groups); | cob->ob = single_object_users_object(bmain, scene, cob->ob); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| for (SceneCollection *nsc = sc->scene_collections.first; nsc; nsc = nsc->next) { | for (CollectionChild *child = collection->children.first; child; child = child->next) { | ||||
| single_object_users_scene_collection(bmain, scene, nsc, flag, copy_groups); | single_object_users_collection(bmain, scene, child->collection, flag, copy_collections); | ||||
| } | } | ||||
| } | } | ||||
| /* Warning, sets ID->newid pointers of objects and groups, but does not clear them. */ | /* Warning, sets ID->newid pointers of objects and collections, but does not clear them. */ | ||||
| static void single_object_users(Main *bmain, Scene *scene, View3D *v3d, const int flag, const bool copy_groups) | static void single_object_users(Main *bmain, Scene *scene, View3D *v3d, const int flag, const bool copy_collections) | ||||
| { | { | ||||
| Group *group, *groupn; | Collection *collection, *collectionn; | ||||
| /* duplicate all the objects of the scene */ | /* duplicate all the objects of the scene */ | ||||
| SceneCollection *msc = BKE_collection_master(&scene->id); | Collection *master_collection = BKE_collection_master(scene); | ||||
| single_object_users_scene_collection(bmain, scene, msc, flag, copy_groups); | single_object_users_collection(bmain, scene, master_collection, flag, copy_collections); | ||||
| /* loop over ViewLayers and assign the pointers accordingly */ | /* loop over ViewLayers and assign the pointers accordingly */ | ||||
| for (ViewLayer *view_layer = scene->view_layers.first; view_layer; view_layer = view_layer->next) { | for (ViewLayer *view_layer = scene->view_layers.first; view_layer; view_layer = view_layer->next) { | ||||
| for (Base *base = view_layer->object_bases.first; base; base = base->next) { | for (Base *base = view_layer->object_bases.first; base; base = base->next) { | ||||
| ID_NEW_REMAP(base->object); | ID_NEW_REMAP(base->object); | ||||
| } | } | ||||
| } | } | ||||
| /* duplicate groups that consist entirely of duplicated objects */ | /* duplicate collections that consist entirely of duplicated objects */ | ||||
| for (group = bmain->group.first; group; group = group->id.next) { | for (collection = bmain->collection.first; collection; collection = collection->id.next) { | ||||
| if (copy_groups && group->view_layer->object_bases.first) { | if (copy_collections) { | ||||
| bool all_duplicated = true; | bool all_duplicated = true; | ||||
| bool any_duplicated = false; | |||||
| FOREACH_GROUP_OBJECT_BEGIN(group, object) | for (CollectionObject *cob = collection->gobject.first; cob; cob = cob->next) { | ||||
| { | any_duplicated = true; | ||||
| if (object->id.newid == NULL) { | if (cob->ob->id.newid == NULL) { | ||||
| all_duplicated = false; | all_duplicated = false; | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| FOREACH_GROUP_OBJECT_END; | |||||
| if (all_duplicated) { | if (any_duplicated && all_duplicated) { | ||||
| groupn = ID_NEW_SET(group, BKE_group_copy(bmain, group)); | // TODO: test if this works, with child collections .. | ||||
| collectionn = ID_NEW_SET(collection, BKE_collection_copy(bmain, NULL, collection)); | |||||
| FOREACH_GROUP_BASE_BEGIN(groupn, base) | for (CollectionObject *cob = collectionn->gobject.first; cob; cob = cob->next) { | ||||
| { | cob->ob = (Object *)cob->ob->id.newid; | ||||
| base->object = (Object *)base->object->id.newid; | |||||
| } | } | ||||
| FOREACH_GROUP_BASE_END | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* group pointers in scene */ | /* collection pointers in scene */ | ||||
| BKE_scene_groups_relink(scene); | BKE_scene_groups_relink(scene); | ||||
| ID_NEW_REMAP(scene->camera); | ID_NEW_REMAP(scene->camera); | ||||
| if (v3d) ID_NEW_REMAP(v3d->camera); | if (v3d) ID_NEW_REMAP(v3d->camera); | ||||
| /* object and group pointers */ | /* object and collection pointers */ | ||||
| libblock_relink_scene_collection(msc); | libblock_relink_collection(master_collection); | ||||
| } | } | ||||
| /* not an especially efficient function, only added so the single user | /* not an especially efficient function, only added so the single user | ||||
| * button can be functional.*/ | * button can be functional.*/ | ||||
| void ED_object_single_user(Main *bmain, Scene *scene, Object *ob) | void ED_object_single_user(Main *bmain, Scene *scene, Object *ob) | ||||
| { | { | ||||
| FOREACH_SCENE_OBJECT_BEGIN(scene, ob_iter) | FOREACH_SCENE_OBJECT_BEGIN(scene, ob_iter) | ||||
| { | { | ||||
| ▲ Show 20 Lines • Show All 176 Lines • ▼ Show 20 Lines | if (cu->id.tag & LIB_TAG_NEW) | ||||
| new_id_matar(bmain, cu->mat, cu->totcol); | new_id_matar(bmain, cu->mat, cu->totcol); | ||||
| for (mb = bmain->mball.first; mb; mb = mb->id.next) | for (mb = bmain->mball.first; mb; mb = mb->id.next) | ||||
| if (mb->id.tag & LIB_TAG_NEW) | if (mb->id.tag & LIB_TAG_NEW) | ||||
| new_id_matar(bmain, mb->mat, mb->totcol); | new_id_matar(bmain, mb->mat, mb->totcol); | ||||
| } | } | ||||
| /* used for copying scenes */ | /* used for copying scenes */ | ||||
| void ED_object_single_users(Main *bmain, Scene *scene, const bool full, const bool copy_groups) | void ED_object_single_users(Main *bmain, Scene *scene, const bool full, const bool copy_collections) | ||||
| { | { | ||||
| single_object_users(bmain, scene, NULL, 0, copy_groups); | single_object_users(bmain, scene, NULL, 0, copy_collections); | ||||
| if (full) { | if (full) { | ||||
| single_obdata_users(bmain, scene, NULL, 0); | single_obdata_users(bmain, scene, NULL, 0); | ||||
| single_object_action_users(scene, NULL, 0); | single_object_action_users(scene, NULL, 0); | ||||
| single_mat_users_expand(bmain); | single_mat_users_expand(bmain); | ||||
| } | } | ||||
| /* Relink nodetrees' pointers that have been duplicated. */ | /* Relink nodetrees' pointers that have been duplicated. */ | ||||
| ▲ Show 20 Lines • Show All 102 Lines • ▼ Show 20 Lines | static void tag_localizable_objects(bContext *C, const int mode) | ||||
| /* TODO(sergey): Drivers targets? */ | /* TODO(sergey): Drivers targets? */ | ||||
| } | } | ||||
| /** | /** | ||||
| * Instance indirectly referenced zero user objects, | * Instance indirectly referenced zero user objects, | ||||
| * otherwise they're lost on reload, see T40595. | * otherwise they're lost on reload, see T40595. | ||||
| */ | */ | ||||
| static bool make_local_all__instance_indirect_unused(Main *bmain, Scene *scene, ViewLayer *view_layer, SceneCollection *sc) | static bool make_local_all__instance_indirect_unused(Main *bmain, ViewLayer *view_layer, Collection *collection) | ||||
| { | { | ||||
| Object *ob; | Object *ob; | ||||
| bool changed = false; | bool changed = false; | ||||
| for (ob = bmain->object.first; ob; ob = ob->id.next) { | for (ob = bmain->object.first; ob; ob = ob->id.next) { | ||||
| if (ID_IS_LINKED(ob) && (ob->id.us == 0)) { | if (ID_IS_LINKED(ob) && (ob->id.us == 0)) { | ||||
| Base *base; | Base *base; | ||||
| id_us_plus(&ob->id); | id_us_plus(&ob->id); | ||||
| BKE_collection_object_add(&scene->id, sc, ob); | BKE_collection_object_add(bmain, collection, ob); | ||||
| base = BKE_view_layer_base_find(view_layer, ob); | base = BKE_view_layer_base_find(view_layer, ob); | ||||
| base->flag |= BASE_SELECTED; | base->flag |= BASE_SELECTED; | ||||
| BKE_scene_object_base_flag_sync_from_base(base); | BKE_scene_object_base_flag_sync_from_base(base); | ||||
| DEG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); | DEG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); | ||||
| changed = true; | changed = true; | ||||
| } | } | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 51 Lines • ▼ Show 20 Lines | if (ma) { | ||||
| /* About nodetrees: root one is made local together with material, others we keep linked for now... */ | /* About nodetrees: root one is made local together with material, others we keep linked for now... */ | ||||
| } | } | ||||
| } | } | ||||
| static int make_local_exec(bContext *C, wmOperator *op) | static int make_local_exec(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| Main *bmain = CTX_data_main(C); | Main *bmain = CTX_data_main(C); | ||||
| Scene *scene = CTX_data_scene(C); | |||||
| ParticleSystem *psys; | ParticleSystem *psys; | ||||
| Material *ma, ***matarar; | Material *ma, ***matarar; | ||||
| const int mode = RNA_enum_get(op->ptr, "type"); | const int mode = RNA_enum_get(op->ptr, "type"); | ||||
| int a; | int a; | ||||
| /* Note: we (ab)use LIB_TAG_PRE_EXISTING to cherry pick which ID to make local... */ | /* Note: we (ab)use LIB_TAG_PRE_EXISTING to cherry pick which ID to make local... */ | ||||
| if (mode == MAKE_LOCAL_ALL) { | if (mode == MAKE_LOCAL_ALL) { | ||||
| ViewLayer *view_layer = CTX_data_view_layer(C); | ViewLayer *view_layer = CTX_data_view_layer(C); | ||||
| SceneCollection *scene_collection = CTX_data_scene_collection(C); | Collection *collection = CTX_data_collection(C); | ||||
| BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false); | BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false); | ||||
| /* De-select so the user can differentiate newly instanced from existing objects. */ | /* De-select so the user can differentiate newly instanced from existing objects. */ | ||||
| BKE_view_layer_base_deselect_all(view_layer); | BKE_view_layer_base_deselect_all(view_layer); | ||||
| if (make_local_all__instance_indirect_unused(bmain, scene, view_layer, scene_collection)) { | if (make_local_all__instance_indirect_unused(bmain, view_layer, collection)) { | ||||
| BKE_report(op->reports, RPT_INFO, "Orphan library objects added to the current scene to avoid loss"); | BKE_report(op->reports, RPT_INFO, "Orphan library objects added to the current scene to avoid loss"); | ||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true); | BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true); | ||||
| tag_localizable_objects(C, mode); | tag_localizable_objects(C, mode); | ||||
| CTX_DATA_BEGIN (C, Object *, ob, selected_objects) | CTX_DATA_BEGIN (C, Object *, ob, selected_objects) | ||||
| ▲ Show 20 Lines • Show All 132 Lines • ▼ Show 20 Lines | else if (ID_IS_LINKED(obact)) { | ||||
| /* Present the menu and be done... */ | /* Present the menu and be done... */ | ||||
| UI_popup_menu_end(C, pup); | UI_popup_menu_end(C, pup); | ||||
| /* This invoke just calls another instance of this operator... */ | /* This invoke just calls another instance of this operator... */ | ||||
| return OPERATOR_INTERFACE; | return OPERATOR_INTERFACE; | ||||
| } | } | ||||
| else { | else { | ||||
| /* Error.. cannot continue. */ | /* Error.. cannot continue. */ | ||||
| BKE_report(op->reports, RPT_ERROR, "Can only make static override for a referenced object or group"); | BKE_report(op->reports, RPT_ERROR, "Can only make static override for a referenced object or collection"); | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| } | } | ||||
| static int make_override_static_exec(bContext *C, wmOperator *op) | static int make_override_static_exec(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| Main *bmain = CTX_data_main(C); | Main *bmain = CTX_data_main(C); | ||||
| Object *obact = CTX_data_active_object(C); | Object *obact = CTX_data_active_object(C); | ||||
| bool success = false; | bool success = false; | ||||
| if (!ID_IS_LINKED(obact) && obact->dup_group != NULL && ID_IS_LINKED(obact->dup_group)) { | if (!ID_IS_LINKED(obact) && obact->dup_group != NULL && ID_IS_LINKED(obact->dup_group)) { | ||||
| Base *base = BLI_findlink(&obact->dup_group->view_layer->object_bases, RNA_enum_get(op->ptr, "object")); | // TODO: support making the group itself override and putting | ||||
| Object *obgroup = obact; | // it directly into the scene collections | ||||
| const ListBase dup_collection_objects = BKE_collection_object_cache_get(obact->dup_group); | |||||
| Base *base = BLI_findlink(&dup_collection_objects, RNA_enum_get(op->ptr, "object")); | |||||
| Object *obcollection = obact; | |||||
| obact = base->object; | obact = base->object; | ||||
| Group *group = obgroup->dup_group; | Collection *collection = obcollection->dup_group; | ||||
| /* First, we make a static override of the linked group itself. */ | /* First, we make a static override of the linked collection itself. */ | ||||
| group->id.tag |= LIB_TAG_DOIT; | collection->id.tag |= LIB_TAG_DOIT; | ||||
| /* Then, we make static override of the whole set of objects in the group. */ | /* Then, we make static override of the whole set of objects in the Collection. */ | ||||
| FOREACH_GROUP_OBJECT_BEGIN(group, ob) | FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN(collection, ob) | ||||
| { | { | ||||
| ob->id.tag |= LIB_TAG_DOIT; | ob->id.tag |= LIB_TAG_DOIT; | ||||
| } | } | ||||
| FOREACH_GROUP_OBJECT_END; | FOREACH_COLLECTION_OBJECT_RECURSIVE_END; | ||||
| success = BKE_override_static_create_from_tag(bmain); | success = BKE_override_static_create_from_tag(bmain); | ||||
| /* Intantiate our newly overridden objects in scene, if not yet done. */ | /* Intantiate our newly overridden objects in scene, if not yet done. */ | ||||
| Scene *scene = CTX_data_scene(C); | Scene *scene = CTX_data_scene(C); | ||||
| ViewLayer *view_layer = CTX_data_view_layer(C); | ViewLayer *view_layer = CTX_data_view_layer(C); | ||||
| Group *new_group = (Group *)group->id.newid; | Collection *new_collection = (Collection *)collection->id.newid; | ||||
| FOREACH_GROUP_OBJECT_BEGIN(new_group, new_ob) | FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN(new_collection, new_ob) | ||||
| { | { | ||||
| if (new_ob != NULL && (base = BKE_view_layer_base_find(view_layer, new_ob)) == NULL) { | if (new_ob != NULL && (base = BKE_view_layer_base_find(view_layer, new_ob)) == NULL) { | ||||
| BKE_collection_object_add_from(scene, obgroup, new_ob); | BKE_collection_object_add_from(bmain, scene, obcollection, new_ob); | ||||
| DEG_id_tag_update_ex(bmain, &new_ob->id, OB_RECALC_OB | DEG_TAG_BASE_FLAGS_UPDATE); | DEG_id_tag_update_ex(bmain, &new_ob->id, OB_RECALC_OB | DEG_TAG_BASE_FLAGS_UPDATE); | ||||
| /* parent to 'group' empty */ | /* parent to 'collection' empty */ | ||||
| if (new_ob->parent == NULL) { | if (new_ob->parent == NULL) { | ||||
| new_ob->parent = obgroup; | new_ob->parent = obcollection; | ||||
| } | } | ||||
| if (new_ob == (Object *)obact->id.newid) { | if (new_ob == (Object *)obact->id.newid) { | ||||
| base = BKE_view_layer_base_find(view_layer, new_ob); | base = BKE_view_layer_base_find(view_layer, new_ob); | ||||
| BKE_view_layer_base_select(view_layer, base); | BKE_view_layer_base_select(view_layer, base); | ||||
| } | } | ||||
| else { | else { | ||||
| /* Disable auto-override tags for non-active objects, will help with performaces... */ | /* Disable auto-override tags for non-active objects, will help with performaces... */ | ||||
| new_ob->id.override_static->flag &= ~STATICOVERRIDE_AUTO; | new_ob->id.override_static->flag &= ~STATICOVERRIDE_AUTO; | ||||
| } | } | ||||
| /* We still want to store all objects' current override status (i.e. change of parent). */ | /* We still want to store all objects' current override status (i.e. change of parent). */ | ||||
| BKE_override_static_operations_create(&new_ob->id, true); | BKE_override_static_operations_create(&new_ob->id, true); | ||||
| } | } | ||||
| } | } | ||||
| FOREACH_GROUP_OBJECT_END; | FOREACH_COLLECTION_OBJECT_RECURSIVE_END; | ||||
| /* obgroup is no more dupligroup-ing, it merely parents whole group of overriding instantiated objects. */ | /* obcollection is no more duplicollection-ing, it merely parents whole collection of overriding instantiated objects. */ | ||||
| obgroup->dup_group = NULL; | obcollection->dup_group = NULL; | ||||
| /* Also, we'd likely want to lock by default things like transformations of implicitly overriden objects? */ | /* Also, we'd likely want to lock by default things like transformations of implicitly overriden objects? */ | ||||
| DEG_id_tag_update(&scene->id, 0); | DEG_id_tag_update(&scene->id, 0); | ||||
| /* Cleanup. */ | /* Cleanup. */ | ||||
| BKE_main_id_clear_newpoins(bmain); | BKE_main_id_clear_newpoins(bmain); | ||||
| BKE_main_id_tag_listbase(&bmain->object, LIB_TAG_DOIT, false); | BKE_main_id_tag_listbase(&bmain->object, LIB_TAG_DOIT, false); | ||||
| ▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | void OBJECT_OT_make_override_static(wmOperatorType *ot) | ||||
| ot->poll = make_override_static_poll; | ot->poll = make_override_static_poll; | ||||
| /* flags */ | /* flags */ | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||
| /* properties */ | /* properties */ | ||||
| PropertyRNA *prop; | PropertyRNA *prop; | ||||
| prop = RNA_def_enum(ot->srna, "object", DummyRNA_DEFAULT_items, 0, "Override Object", | prop = RNA_def_enum(ot->srna, "object", DummyRNA_DEFAULT_items, 0, "Override Object", | ||||
| "Name of lib-linked/group object to make an override from"); | "Name of lib-linked/collection object to make an override from"); | ||||
| RNA_def_enum_funcs(prop, proxy_group_object_itemf); | RNA_def_enum_funcs(prop, proxy_collection_object_itemf); | ||||
| RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); | RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); | ||||
| ot->prop = prop; | ot->prop = prop; | ||||
| } | } | ||||
| enum { | enum { | ||||
| MAKE_SINGLE_USER_ALL = 1, | MAKE_SINGLE_USER_ALL = 1, | ||||
| MAKE_SINGLE_USER_SELECTED = 2, | MAKE_SINGLE_USER_SELECTED = 2, | ||||
| }; | }; | ||||
| static int make_single_user_exec(bContext *C, wmOperator *op) | static int make_single_user_exec(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| Main *bmain = CTX_data_main(C); | Main *bmain = CTX_data_main(C); | ||||
| Scene *scene = CTX_data_scene(C); | Scene *scene = CTX_data_scene(C); | ||||
| ViewLayer *view_layer = CTX_data_view_layer(C); | ViewLayer *view_layer = CTX_data_view_layer(C); | ||||
| View3D *v3d = CTX_wm_view3d(C); /* ok if this is NULL */ | View3D *v3d = CTX_wm_view3d(C); /* ok if this is NULL */ | ||||
| const int flag = (RNA_enum_get(op->ptr, "type") == MAKE_SINGLE_USER_SELECTED) ? SELECT : 0; | const int flag = (RNA_enum_get(op->ptr, "type") == MAKE_SINGLE_USER_SELECTED) ? SELECT : 0; | ||||
| const bool copy_groups = false; | const bool copy_collections = false; | ||||
| bool update_deps = false; | bool update_deps = false; | ||||
| if (RNA_boolean_get(op->ptr, "object")) { | if (RNA_boolean_get(op->ptr, "object")) { | ||||
| if (flag == SELECT) { | if (flag == SELECT) { | ||||
| BKE_view_layer_selected_objects_tag(view_layer, OB_DONE); | BKE_view_layer_selected_objects_tag(view_layer, OB_DONE); | ||||
| single_object_users(bmain, scene, v3d, OB_DONE, copy_groups); | single_object_users(bmain, scene, v3d, OB_DONE, copy_collections); | ||||
| } | } | ||||
| else { | else { | ||||
| single_object_users(bmain, scene, v3d, 0, copy_groups); | single_object_users(bmain, scene, v3d, 0, copy_collections); | ||||
| } | } | ||||
| /* needed since object relationships may have changed */ | /* needed since object relationships may have changed */ | ||||
| update_deps = true; | update_deps = true; | ||||
| } | } | ||||
| if (RNA_boolean_get(op->ptr, "obdata")) { | if (RNA_boolean_get(op->ptr, "obdata")) { | ||||
| single_obdata_users(bmain, scene, view_layer, flag); | single_obdata_users(bmain, scene, view_layer, flag); | ||||
| ▲ Show 20 Lines • Show All 143 Lines • Show Last 20 Lines | |||||