From eb1ede569316027803e8327af92029f2b9598944 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 1 Apr 2022 16:41:52 +0200 Subject: Assets: Instancing operator option for collection asset dropping Makes it possible to toggle instancing via the "Adjust Last Operation" panel after dropping a collection asset into the viewport. A design task that puts this into more context is pending still, but this is a useful option to have either way. Differential Revision: https://developer.blender.org/D14507 Reviewed by: Bastien Montagne --- source/blender/editors/object/object_add.cc | 199 ++++++++++++++++++--- source/blender/editors/object/object_intern.h | 1 + source/blender/editors/object/object_ops.c | 1 + source/blender/editors/space_view3d/space_view3d.c | 82 ++++++++- 4 files changed, 254 insertions(+), 29 deletions(-) diff --git a/source/blender/editors/object/object_add.cc b/source/blender/editors/object/object_add.cc index f681f49df90..6a7920d4d75 100644 --- a/source/blender/editors/object/object_add.cc +++ b/source/blender/editors/object/object_add.cc @@ -8,6 +8,7 @@ #include #include #include +#include #include "MEM_guardedalloc.h" @@ -1629,66 +1630,100 @@ void OBJECT_OT_light_add(wmOperatorType *ot) /** \name Add Collection Instance Operator * \{ */ -static int collection_instance_add_exec(bContext *C, wmOperator *op) -{ - Main *bmain = CTX_data_main(C); +struct CollectionAddInfo { + /* The collection that is supposed to be added, determined through operator properties. */ Collection *collection; + /* The local-view bits (if any) the object should have set to become visible in current context. + */ ushort local_view_bits; + /* The transform that should be applied to the collection, determined through operator properties + * if set (e.g. to place the collection under the cursor), otherwise through context (e.g. 3D + * cursor location). */ float loc[3], rot[3]; +}; + +static std::optional collection_add_info_get_from_op(bContext *C, + wmOperator *op) +{ + CollectionAddInfo add_info{}; + + Main *bmain = CTX_data_main(C); - PropertyRNA *prop_name = RNA_struct_find_property(op->ptr, "name"); PropertyRNA *prop_location = RNA_struct_find_property(op->ptr, "location"); PropertyRNA *prop_session_uuid = RNA_struct_find_property(op->ptr, "session_uuid"); + PropertyRNA *prop_name = RNA_struct_find_property(op->ptr, "name"); bool update_location_if_necessary = false; - if (RNA_property_is_set(op->ptr, prop_name)) { + if (prop_name && RNA_property_is_set(op->ptr, prop_name)) { char name[MAX_ID_NAME - 2]; RNA_property_string_get(op->ptr, prop_name, name); - collection = (Collection *)BKE_libblock_find_name(bmain, ID_GR, name); + add_info.collection = (Collection *)BKE_libblock_find_name(bmain, ID_GR, name); update_location_if_necessary = true; } else if (RNA_property_is_set(op->ptr, prop_session_uuid)) { const uint32_t session_uuid = (uint32_t)RNA_property_int_get(op->ptr, prop_session_uuid); - collection = (Collection *)BKE_libblock_find_session_uuid(bmain, ID_GR, session_uuid); + add_info.collection = (Collection *)BKE_libblock_find_session_uuid(bmain, ID_GR, session_uuid); update_location_if_necessary = true; } else { - collection = static_cast( + add_info.collection = static_cast( BLI_findlink(&bmain->collections, RNA_enum_get(op->ptr, "collection"))); } if (update_location_if_necessary) { int mval[2]; if (!RNA_property_is_set(op->ptr, prop_location) && object_add_drop_xy_get(C, op, &mval)) { - ED_object_location_from_view(C, loc); - ED_view3d_cursor3d_position(C, mval, false, loc); - RNA_property_float_set_array(op->ptr, prop_location, loc); + ED_object_location_from_view(C, add_info.loc); + ED_view3d_cursor3d_position(C, mval, false, add_info.loc); + RNA_property_float_set_array(op->ptr, prop_location, add_info.loc); } } - if (collection == nullptr) { - return OPERATOR_CANCELLED; + if (add_info.collection == nullptr) { + return std::nullopt; } - if (!ED_object_add_generic_get_opts( - C, op, 'Z', loc, rot, nullptr, nullptr, &local_view_bits, nullptr)) { - return OPERATOR_CANCELLED; + if (!ED_object_add_generic_get_opts(C, + op, + 'Z', + add_info.loc, + add_info.rot, + nullptr, + nullptr, + &add_info.local_view_bits, + nullptr)) { + return std::nullopt; } ViewLayer *view_layer = CTX_data_view_layer(C); /* Avoid dependency cycles. */ LayerCollection *active_lc = BKE_layer_collection_get_active(view_layer); - while (BKE_collection_cycle_find(active_lc->collection, collection)) { + while (BKE_collection_cycle_find(active_lc->collection, add_info.collection)) { active_lc = BKE_layer_collection_activate_parent(view_layer, active_lc); } - Object *ob = ED_object_add_type( - C, OB_EMPTY, collection->id.name + 2, loc, rot, false, local_view_bits); - ob->instance_collection = collection; + return add_info; +} + +static int collection_instance_add_exec(bContext *C, wmOperator *op) +{ + std::optional add_info = collection_add_info_get_from_op(C, op); + if (!add_info) { + return OPERATOR_CANCELLED; + } + + Object *ob = ED_object_add_type(C, + OB_EMPTY, + add_info->collection->id.name + 2, + add_info->loc, + add_info->rot, + false, + add_info->local_view_bits); + ob->instance_collection = add_info->collection; ob->empty_drawsize = U.collection_instance_empty_size; ob->transflag |= OB_DUPLICOLLECTION; - id_us_plus(&collection->id); + id_us_plus(&add_info->collection->id); return OPERATOR_FINISHED; } @@ -1749,6 +1784,128 @@ void OBJECT_OT_collection_instance_add(wmOperatorType *ot) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Collection Drop Operator + * + * Internal operator for collection dropping. + * + * \warning This is tied closely together to the drop-box callbacks, so it shouldn't be used on its + * own. + * + * The drop-box callback imports the collection, links it into the view-layer, selects all imported + * objects (which may include peripheral objects like parents or boolean-objects of an object in + * the collection) and activates one. Only the callback has enough info to do this reliably. Based + * on the instancing operator option, this operator then does one of two things: + * - Instancing enabled: Unlink the collection again, and instead add a collection instance empty + * at the drop position. + * - Instancing disabled: Transform the objects to the drop position, keeping all relative + * transforms of the objects to each other as is. + * + * \{ */ + +static int collection_drop_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + LayerCollection *active_collection = CTX_data_layer_collection(C); + std::optional add_info = collection_add_info_get_from_op(C, op); + if (!add_info) { + return OPERATOR_CANCELLED; + } + + if (RNA_boolean_get(op->ptr, "use_instance")) { + BKE_collection_child_remove(bmain, active_collection->collection, add_info->collection); + DEG_id_tag_update(&active_collection->collection->id, ID_RECALC_COPY_ON_WRITE); + DEG_relations_tag_update(bmain); + + Object *ob = ED_object_add_type(C, + OB_EMPTY, + add_info->collection->id.name + 2, + add_info->loc, + add_info->rot, + false, + add_info->local_view_bits); + ob->instance_collection = add_info->collection; + ob->empty_drawsize = U.collection_instance_empty_size; + ob->transflag |= OB_DUPLICOLLECTION; + id_us_plus(&add_info->collection->id); + } + else { + ViewLayer *view_layer = CTX_data_view_layer(C); + float delta_mat[4][4]; + unit_m4(delta_mat); + + const float scale[3] = {1.0f, 1.0f, 1.0f}; + loc_eul_size_to_mat4(delta_mat, add_info->loc, add_info->rot, scale); + + float offset[3]; + /* Reverse apply the instance offset, so toggling the Instance option doesn't cause the + * collection to jump. */ + negate_v3_v3(offset, add_info->collection->instance_offset); + translate_m4(delta_mat, UNPACK3(offset)); + + ObjectsInViewLayerParams params = {0}; + uint objects_len; + Object **objects = BKE_view_layer_array_selected_objects_params( + view_layer, nullptr, &objects_len, ¶ms); + ED_object_xform_array_m4(objects, objects_len, delta_mat); + + MEM_freeN(objects); + } + + return OPERATOR_FINISHED; +} + +void OBJECT_OT_collection_external_asset_drop(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + /* Name should only be displayed in the drag tooltip. */ + ot->name = "Add Collection"; + ot->description = "Add the dragged collection to the scene"; + ot->idname = "OBJECT_OT_collection_external_asset_drop"; + + /* api callbacks */ + ot->invoke = object_instance_add_invoke; + ot->exec = collection_drop_exec; + ot->poll = ED_operator_objectmode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + + /* properties */ + prop = RNA_def_int(ot->srna, + "session_uuid", + 0, + INT32_MIN, + INT32_MAX, + "Session UUID", + "Session UUID of the collection to add", + INT32_MIN, + INT32_MAX); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_SKIP_SAVE | PROP_HIDDEN)); + + ED_object_add_generic_props(ot, false); + + /* Important: Instancing option. Intentionally remembered across executions (no #PROP_SKIP_SAVE). + */ + RNA_def_boolean(ot->srna, + "use_instance", + true, + "Instance", + "Add the dropped collection as collection instance"); + + object_add_drop_xy_props(ot); + + prop = RNA_def_enum(ot->srna, "collection", DummyRNA_NULL_items, 0, "Collection", ""); + RNA_def_enum_funcs(prop, RNA_collection_itemf); + RNA_def_property_flag(prop, + (PropertyFlag)(PROP_SKIP_SAVE | PROP_HIDDEN | PROP_ENUM_NO_TRANSLATE)); + ot->prop = prop; +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Add Data Instance Operator * diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index 490c495dad5..cb703caa8d1 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -119,6 +119,7 @@ void OBJECT_OT_pointcloud_add(struct wmOperatorType *ot); * Only used as menu. */ void OBJECT_OT_collection_instance_add(struct wmOperatorType *ot); +void OBJECT_OT_collection_external_asset_drop(struct wmOperatorType *ot); void OBJECT_OT_data_instance_add(struct wmOperatorType *ot); void OBJECT_OT_duplicates_make_real(struct wmOperatorType *ot); diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index 35f5ede270d..b390cb286ee 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -98,6 +98,7 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_transform_to_mouse); WM_operatortype_append(OBJECT_OT_effector_add); WM_operatortype_append(OBJECT_OT_collection_instance_add); + WM_operatortype_append(OBJECT_OT_collection_external_asset_drop); WM_operatortype_append(OBJECT_OT_data_instance_add); WM_operatortype_append(OBJECT_OT_metaball_add); WM_operatortype_append(OBJECT_OT_duplicates_make_real); diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index e2a027837a7..2b613d0206e 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -8,6 +8,7 @@ #include #include +#include "DNA_collection_types.h" #include "DNA_defaults.h" #include "DNA_gpencil_types.h" #include "DNA_lightprobe_types.h" @@ -47,6 +48,7 @@ #include "ED_screen.h" #include "ED_space_api.h" #include "ED_transform.h" +#include "ED_undo.h" #include "GPU_matrix.h" @@ -562,6 +564,24 @@ static bool view3d_collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent return view3d_drop_id_in_main_region_poll(C, drag, event, ID_GR); } +static bool view3d_collection_drop_poll_local_id(bContext *C, wmDrag *drag, const wmEvent *event) +{ + if (!view3d_collection_drop_poll(C, drag, event) || (drag->type != WM_DRAG_ID)) { + return false; + } + return true; +} + +static bool view3d_collection_drop_poll_external_asset(bContext *C, + wmDrag *drag, + const wmEvent *event) +{ + if (!view3d_collection_drop_poll(C, drag, event) || (drag->type != WM_DRAG_ASSET)) { + return false; + } + return true; +} + static bool view3d_mat_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { return view3d_drop_id_in_main_region_poll(C, drag, event, ID_MA); @@ -708,6 +728,8 @@ static void view3d_ob_drop_copy_local_id(wmDrag *drag, wmDropBox *drop) RNA_float_set_array(drop->ptr, "matrix", &obmat_final[0][0]); } +/* Mostly the same logic as #view3d_collection_drop_copy_external_asset(), just different enough to + * make sharing code a bit difficult. */ static void view3d_ob_drop_copy_external_asset(wmDrag *drag, wmDropBox *drop) { /* NOTE(@campbellbarton): Selection is handled here, de-selecting objects before append, @@ -749,11 +771,48 @@ static void view3d_ob_drop_copy_external_asset(wmDrag *drag, wmDropBox *drop) } } -static void view3d_collection_drop_copy(wmDrag *drag, wmDropBox *drop) +static void view3d_collection_drop_copy_local_id(wmDrag *drag, wmDropBox *drop) { - ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, ID_GR); + ID *id = WM_drag_get_local_ID(drag, ID_GR); + RNA_int_set(drop->ptr, "session_uuid", (int)id->session_uuid); +} + +/* Mostly the same logic as #view3d_ob_drop_copy_external_asset(), just different enough to make + * sharing code a bit difficult. */ +static void view3d_collection_drop_copy_external_asset(wmDrag *drag, wmDropBox *drop) +{ + BLI_assert(drag->type == WM_DRAG_ASSET); + + wmDragAsset *asset_drag = WM_drag_get_asset_data(drag, 0); + bContext *C = asset_drag->evil_C; + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + + BKE_view_layer_base_deselect_all(view_layer); + + ID *id = WM_drag_asset_id_import(asset_drag, FILE_AUTOSELECT); + Collection *collection = (Collection *)id; + + /* TODO(sergey): Only update relations for the current scene. */ + DEG_relations_tag_update(CTX_data_main(C)); + WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); RNA_int_set(drop->ptr, "session_uuid", (int)id->session_uuid); + + /* Make an object active, just use the first one in the collection. */ + CollectionObject *cobject = collection->gobject.first; + Base *base = cobject ? BKE_view_layer_base_find(view_layer, cobject->ob) : NULL; + if (base) { + BLI_assert((base->flag & BASE_SELECTABLE) && (base->flag & BASE_ENABLED_VIEWPORT)); + BKE_view_layer_base_select_and_set_active(view_layer, base); + WM_main_add_notifier(NC_SCENE | ND_OB_ACTIVE, scene); + } + DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); + ED_outliner_select_sync_from_object_tag(C); + + /* XXX Without an undo push here, there will be a crash when the user modifies operator + * properties. The stuff we do in these drop callbacks just isn't safe over undo/redo. */ + ED_undo_push(C, "Collection_Drop"); } static void view3d_id_drop_copy(wmDrag *drag, wmDropBox *drop) @@ -834,6 +893,19 @@ static void view3d_dropboxes(void) drop->draw_activate = view3d_ob_drop_draw_activate; drop->draw_deactivate = view3d_ob_drop_draw_deactivate; + WM_dropbox_add(lb, + "OBJECT_OT_collection_external_asset_drop", + view3d_collection_drop_poll_external_asset, + view3d_collection_drop_copy_external_asset, + WM_drag_free_imported_drag_ID, + NULL); + WM_dropbox_add(lb, + "OBJECT_OT_collection_instance_add", + view3d_collection_drop_poll_local_id, + view3d_collection_drop_copy_local_id, + WM_drag_free_imported_drag_ID, + NULL); + WM_dropbox_add(lb, "OBJECT_OT_drop_named_material", view3d_mat_drop_poll, @@ -858,12 +930,6 @@ static void view3d_dropboxes(void) view3d_id_path_drop_copy, WM_drag_free_imported_drag_ID, NULL); - WM_dropbox_add(lb, - "OBJECT_OT_collection_instance_add", - view3d_collection_drop_poll, - view3d_collection_drop_copy, - WM_drag_free_imported_drag_ID, - NULL); WM_dropbox_add(lb, "OBJECT_OT_data_instance_add", view3d_object_data_drop_poll, -- cgit v1.2.3