From a062d86230a07caf022b105f395a6b47dc604040 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 27 Oct 2021 04:19:45 +1100 Subject: Drop object assets and associated objects at the cursor location When dropping asset objects, place them under the mouse-cursor along with any other objects they link in. Ref D12935 Reviewed By: Severin --- source/blender/editors/include/ED_object.h | 2 + source/blender/editors/object/object_add.c | 168 +++++++++++++++------ source/blender/editors/object/object_intern.h | 1 + source/blender/editors/object/object_ops.c | 1 + source/blender/editors/object/object_utils.c | 68 +++++++++ source/blender/editors/space_view3d/space_view3d.c | 104 +++++++++++-- source/blender/windowmanager/WM_api.h | 1 + source/blender/windowmanager/intern/wm_dragdrop.c | 27 ++-- 8 files changed, 296 insertions(+), 76 deletions(-) (limited to 'source/blender') diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h index 458ce57ab86..2a557f1abd3 100644 --- a/source/blender/editors/include/ED_object.h +++ b/source/blender/editors/include/ED_object.h @@ -122,6 +122,8 @@ void ED_object_xform_skip_child_container_item_ensure(struct XFormObjectSkipChil struct Object *ob_parent_recurse, int mode); +void ED_object_xform_array_m4(struct Object **objects, uint objects_len, const float matrix[4][4]); + /* object_ops.c */ void ED_operatortypes_object(void); void ED_operatormacros_object(void); diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 917df0da868..f5a304a718b 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -3481,19 +3481,6 @@ void OBJECT_OT_duplicate(wmOperatorType *ot) * Use for drag & drop. * \{ */ -static Base *object_add_ensure_in_view_layer(Main *bmain, ViewLayer *view_layer, Object *ob) -{ - Base *base = BKE_view_layer_base_find(view_layer, ob); - - if (!base) { - LayerCollection *layer_collection = BKE_layer_collection_get_active(view_layer); - BKE_collection_object_add(bmain, layer_collection->collection, ob); - base = BKE_view_layer_base_find(view_layer, ob); - } - - return base; -} - static int object_add_named_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); @@ -3501,8 +3488,7 @@ static int object_add_named_exec(bContext *C, wmOperator *op) ViewLayer *view_layer = CTX_data_view_layer(C); Base *basen; Object *ob; - const bool duplicate = RNA_boolean_get(op->ptr, "duplicate"); - const bool linked = duplicate && RNA_boolean_get(op->ptr, "linked"); + const bool linked = RNA_boolean_get(op->ptr, "linked"); const eDupli_ID_Flags dupflag = (linked) ? 0 : (eDupli_ID_Flags)U.dupflag; char name[MAX_ID_NAME - 2]; @@ -3516,30 +3502,21 @@ static int object_add_named_exec(bContext *C, wmOperator *op) } /* prepare dupli */ - if (duplicate) { - basen = object_add_duplicate_internal( - bmain, - scene, - view_layer, - ob, - dupflag, - /* Sub-process flag because the new-ID remapping (#BKE_libblock_relink_to_newid()) in this - * function will only work if the object is already linked in the view layer, which is not - * the case here. So we have to do the new-ID relinking ourselves - * (#copy_object_set_idnew()). - */ - LIB_ID_DUPLICATE_IS_SUBPROCESS | LIB_ID_DUPLICATE_IS_ROOT_ID); - } - else { - /* basen is actually not a new base in this case. */ - basen = object_add_ensure_in_view_layer(bmain, view_layer, ob); - } + basen = object_add_duplicate_internal( + bmain, + scene, + view_layer, + ob, + dupflag, + /* Sub-process flag because the new-ID remapping (#BKE_libblock_relink_to_newid()) in this + * function will only work if the object is already linked in the view layer, which is not + * the case here. So we have to do the new-ID relinking ourselves + * (#copy_object_set_idnew()). + */ + LIB_ID_DUPLICATE_IS_SUBPROCESS | LIB_ID_DUPLICATE_IS_ROOT_ID); if (basen == NULL) { - BKE_report(op->reports, - RPT_ERROR, - duplicate ? "Object could not be duplicated" : - "Object could not be linked to the view layer"); + BKE_report(op->reports, RPT_ERROR, "Object could not be duplicated"); return OPERATOR_CANCELLED; } @@ -3597,22 +3574,11 @@ void OBJECT_OT_add_named(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; PropertyRNA *prop; - - prop = RNA_def_boolean( - ot->srna, - "duplicate", - true, - "Duplicate", - "Create a duplicate of the object. If not set, only ensures the object is linked into the " - "active view layer, positions and selects/activates it (deselecting others)"); - RNA_def_property_flag(prop, PROP_HIDDEN); - RNA_def_boolean(ot->srna, "linked", false, "Linked", - "Duplicate object but not object data, linking to the original data (ignored if " - "'duplicate' is false)"); + "Duplicate object but not object data, linking to the original data"); RNA_def_string(ot->srna, "name", NULL, MAX_ID_NAME - 2, "Name", "Object name to add"); @@ -3625,6 +3591,110 @@ void OBJECT_OT_add_named(wmOperatorType *ot) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Transform Object to Mouse Operator + * \{ */ + +/** + * Alternate behavior for dropping an asset that positions the appended object(s). + */ +static int object_transform_to_mouse_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob; + + if (RNA_struct_property_is_set(op->ptr, "name")) { + char name[MAX_ID_NAME - 2]; + RNA_string_get(op->ptr, "name", name); + ob = (Object *)BKE_libblock_find_name(bmain, ID_OB, name); + } + else { + ob = OBACT(view_layer); + } + + if (ob == NULL) { + BKE_report(op->reports, RPT_ERROR, "Object not found"); + return OPERATOR_CANCELLED; + } + /* Ensure the locations are updated so snap reads the evaluated active location. */ + CTX_data_ensure_evaluated_depsgraph(C); + + PropertyRNA *prop_matrix = RNA_struct_find_property(op->ptr, "matrix"); + if (RNA_property_is_set(op->ptr, prop_matrix)) { + uint objects_len; + Object **objects = BKE_view_layer_array_selected_objects(view_layer, NULL, &objects_len, {}); + + float matrix[4][4]; + RNA_property_float_get_array(op->ptr, prop_matrix, &matrix[0][0]); + + float mat_src_unit[4][4]; + float mat_dst_unit[4][4]; + float final_delta[4][4]; + + normalize_m4_m4(mat_src_unit, ob->obmat); + normalize_m4_m4(mat_dst_unit, matrix); + invert_m4(mat_src_unit); + mul_m4_m4m4(final_delta, mat_dst_unit, mat_src_unit); + + ED_object_xform_array_m4(objects, objects_len, final_delta); + + MEM_freeN(objects); + } + else { + int mval[2]; + if (object_add_drop_xy_get(C, op, &mval)) { + float cursor[3]; + ED_object_location_from_view(C, cursor); + ED_view3d_cursor3d_position(C, mval, false, cursor); + + /* Use the active objects location since this is the ID which the user selected to drop. + * + * This transforms all selected objects, so that dropping a single object which links in + * other objects will have their relative transformation preserved. + * For example a child/parent relationship or other objects used with a boolean modifier. + * + * The caller is responsible for ensuring the selection state gives useful results. + * Link/append does this using #FILE_AUTOSELECT. */ + ED_view3d_snap_selected_to_location(C, cursor, V3D_AROUND_ACTIVE); + } + } + + return OPERATOR_FINISHED; +} + +void OBJECT_OT_transform_to_mouse(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Place Object Under Mouse"; + ot->description = "Snap selected item(s) to the mouse location"; + ot->idname = "OBJECT_OT_transform_to_mouse"; + + /* api callbacks */ + ot->invoke = object_add_drop_xy_generic_invoke; + ot->exec = object_transform_to_mouse_exec; + ot->poll = ED_operator_objectmode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + PropertyRNA *prop; + RNA_def_string(ot->srna, + "name", + NULL, + MAX_ID_NAME - 2, + "Name", + "Object name to place (when unset use the active object)"); + + prop = RNA_def_float_matrix( + ot->srna, "matrix", 4, 4, NULL, 0.0f, 0.0f, "Matrix", "", 0.0f, 0.0f); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + + object_add_drop_xy_props(ot); +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Join Object Operator * \{ */ diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index ea9a2de090b..fe07ecef438 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -106,6 +106,7 @@ void OBJECT_OT_select_same_collection(struct wmOperatorType *ot); /* object_add.c */ void OBJECT_OT_add(struct wmOperatorType *ot); void OBJECT_OT_add_named(struct wmOperatorType *ot); +void OBJECT_OT_transform_to_mouse(struct wmOperatorType *ot); void OBJECT_OT_metaball_add(struct wmOperatorType *ot); void OBJECT_OT_text_add(struct wmOperatorType *ot); void OBJECT_OT_armature_add(struct wmOperatorType *ot); diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index b3bf2c64a91..b171da42522 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -112,6 +112,7 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_volume_import); WM_operatortype_append(OBJECT_OT_add); WM_operatortype_append(OBJECT_OT_add_named); + 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_data_instance_add); diff --git a/source/blender/editors/object/object_utils.c b/source/blender/editors/object/object_utils.c index 66390f6f165..c7dfe911ce7 100644 --- a/source/blender/editors/object/object_utils.c +++ b/source/blender/editors/object/object_utils.c @@ -36,6 +36,7 @@ #include "BKE_armature.h" #include "BKE_editmesh.h" #include "BKE_lattice.h" +#include "BKE_object.h" #include "BKE_scene.h" #include "DEG_depsgraph_query.h" @@ -430,3 +431,70 @@ void ED_object_data_xform_container_destroy(struct XFormObjectData_Container *xd } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Transform Object Array + * + * Low level object transform function, transforming objects by `matrix`. + * Simple alternative to full transform logic. + * \{ */ + +static bool object_parent_in_set(GSet *objects_set, Object *ob) +{ + for (Object *parent = ob->parent; parent; parent = parent->parent) { + if (BLI_gset_lookup(objects_set, parent)) { + return true; + } + } + return false; +} + +void ED_object_xform_array_m4(Object **objects, uint objects_len, const float matrix[4][4]) +{ + /* Filter out objects that have parents in `objects_set`. */ + { + GSet *objects_set = BLI_gset_ptr_new_ex(__func__, objects_len); + for (uint i = 0; i < objects_len; i++) { + BLI_gset_add(objects_set, objects[i]); + } + for (uint i = 0; i < objects_len;) { + if (object_parent_in_set(objects_set, objects[i])) { + objects[i] = objects[--objects_len]; + } + else { + i++; + } + } + BLI_gset_free(objects_set, NULL); + } + + /* Detect translation only matrix, prevent rotation/scale channels from being touched at all. */ + bool is_translation_only; + { + float test_m4_a[4][4], test_m4_b[4][4]; + unit_m4(test_m4_a); + copy_m4_m4(test_m4_b, matrix); + zero_v3(test_m4_b[3]); + is_translation_only = equals_m4m4(test_m4_a, test_m4_b); + } + + if (is_translation_only) { + for (uint i = 0; i < objects_len; i++) { + Object *ob = objects[i]; + add_v3_v3(ob->loc, matrix[3]); + DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); + } + } + else { + for (uint i = 0; i < objects_len; i++) { + float m4[4][4]; + Object *ob = objects[i]; + BKE_object_to_mat4(ob, m4); + mul_m4_m4m4(m4, matrix, m4); + BKE_object_apply_mat4(ob, m4, true, true); + DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); + } + } +} + +/** \} */ diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 10fd7670996..0ab39d59aff 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -47,6 +47,7 @@ #include "BKE_icons.h" #include "BKE_idprop.h" #include "BKE_lattice.h" +#include "BKE_layer.h" #include "BKE_main.h" #include "BKE_mball.h" #include "BKE_mesh.h" @@ -56,6 +57,7 @@ #include "BKE_workspace.h" #include "ED_object.h" +#include "ED_outliner.h" #include "ED_render.h" #include "ED_screen.h" #include "ED_space_api.h" @@ -557,6 +559,25 @@ static bool view3d_ob_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { return view3d_drop_id_in_main_region_poll(C, drag, event, ID_OB); } +static bool view3d_ob_drop_poll_external_asset(bContext *C, wmDrag *drag, const wmEvent *event) +{ + if (!view3d_ob_drop_poll(C, drag, event) || (drag->type != WM_DRAG_ASSET)) { + return false; + } + return true; +} + +/** + * \note the term local here refers to not being an external asset, + * poll will succeed for linked library objects. + */ +static bool view3d_ob_drop_poll_local_id(bContext *C, wmDrag *drag, const wmEvent *event) +{ + if (!view3d_ob_drop_poll(C, drag, event) || (drag->type != WM_DRAG_ID)) { + return false; + } + return true; +} static bool view3d_collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { @@ -669,22 +690,13 @@ static bool view3d_volume_drop_poll(bContext *UNUSED(C), return (drag->type == WM_DRAG_PATH) && (drag->icon == ICON_FILE_VOLUME); } -static void view3d_ob_drop_copy(wmDrag *drag, wmDropBox *drop) +static void view3d_ob_drop_matrix_from_snap(V3DSnapCursorState *snap_state, + Object *ob, + float obmat_final[4][4]) { - ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, ID_OB); - - RNA_string_set(drop->ptr, "name", id->name + 2); - /* Don't duplicate ID's which were just imported. Only do that for existing, local IDs. */ - const bool is_imported_id = drag->type == WM_DRAG_ASSET; - RNA_boolean_set(drop->ptr, "duplicate", !is_imported_id); - - V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); - BLI_assert(snap_state->draw_box || snap_state->draw_plane); - Object *ob = (Object *)id; - float obmat_final[4][4]; - V3DSnapCursorData *snap_data; snap_data = ED_view3d_cursor_snap_data_get(snap_state, NULL, 0, 0); + BLI_assert(snap_state->draw_box || snap_state->draw_plane); copy_m4_m3(obmat_final, snap_data->plane_omat); copy_v3_v3(obmat_final[3], snap_data->loc); @@ -700,6 +712,56 @@ static void view3d_ob_drop_copy(wmDrag *drag, wmDropBox *drop) mul_mat3_m4_v3(obmat_final, offset); sub_v3_v3(obmat_final[3], offset); } +} + +static void view3d_ob_drop_copy_local_id(wmDrag *drag, wmDropBox *drop) +{ + ID *id = WM_drag_get_local_ID(drag, ID_OB); + + RNA_string_set(drop->ptr, "name", id->name + 2); + /* Don't duplicate ID's which were just imported. Only do that for existing, local IDs. */ + BLI_assert(drag->type != WM_DRAG_ASSET); + + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + float obmat_final[4][4]; + + view3d_ob_drop_matrix_from_snap(snap_state, (Object *)id, obmat_final); + + RNA_float_set_array(drop->ptr, "matrix", &obmat_final[0][0]); +} + +static void view3d_ob_drop_copy_external_asset(wmDrag *drag, wmDropBox *drop) +{ + /* NOTE(@campbellbarton): Selection is handled here, de-selecting objects before append, + * using auto-select to ensure the new objects are selected. + * This is done so #OBJECT_OT_transform_to_mouse (which runs after this drop handler) + * can use the context setup here to place the objects. */ + 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); + + RNA_string_set(drop->ptr, "name", id->name + 2); + + Base *base = BKE_view_layer_base_find(view_layer, (Object *)id); + if (base != NULL) { + 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); + + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + float obmat_final[4][4]; + + view3d_ob_drop_matrix_from_snap(snap_state, (Object *)id, obmat_final); + RNA_float_set_array(drop->ptr, "matrix", &obmat_final[0][0]); } @@ -768,8 +830,8 @@ static void view3d_dropboxes(void) struct wmDropBox *drop; drop = WM_dropbox_add(lb, "OBJECT_OT_add_named", - view3d_ob_drop_poll, - view3d_ob_drop_copy, + view3d_ob_drop_poll_local_id, + view3d_ob_drop_copy_local_id, WM_drag_free_imported_drag_ID, NULL); @@ -778,6 +840,18 @@ static void view3d_dropboxes(void) drop->draw_deactivate = view3d_ob_drop_draw_deactivate; drop->opcontext = WM_OP_EXEC_DEFAULT; /* Not really needed. */ + drop = WM_dropbox_add(lb, + "OBJECT_OT_transform_to_mouse", + view3d_ob_drop_poll_external_asset, + view3d_ob_drop_copy_external_asset, + WM_drag_free_imported_drag_ID, + NULL); + + drop->draw = WM_drag_draw_item_name_fn; + drop->draw_activate = view3d_ob_drop_draw_activate; + drop->draw_deactivate = view3d_ob_drop_draw_deactivate; + drop->opcontext = WM_OP_INVOKE_DEFAULT; + WM_dropbox_add(lb, "OBJECT_OT_drop_named_material", view3d_mat_drop_poll, diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 2d5100eec7c..f9bc309af9f 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -751,6 +751,7 @@ void WM_drag_draw_default_fn(struct bContext *C, ListBase *WM_dropboxmap_find(const char *idname, int spaceid, int regionid); /* ID drag and drop */ +ID *WM_drag_asset_id_import(wmDragAsset *asset_drag, int flag_extra); void WM_drag_add_local_ID(struct wmDrag *drag, struct ID *id, struct ID *from_parent); struct ID *WM_drag_get_local_ID(const struct wmDrag *drag, short idcode); struct ID *WM_drag_get_local_ID_from_event(const struct wmEvent *event, short idcode); diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index 3206d0c044e..21f5925b037 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -526,8 +526,15 @@ struct AssetMetaData *WM_drag_get_asset_meta_data(const wmDrag *drag, int idcode return NULL; } -static ID *wm_drag_asset_id_import(wmDragAsset *asset_drag) +/** + * \param flag_extra: Additional linking flags (from #eFileSel_Params_Flag). + */ +ID *WM_drag_asset_id_import(wmDragAsset *asset_drag, const int flag_extra) { + /* Only support passing in limited flags. */ + BLI_assert(flag_extra == (flag_extra & FILE_AUTOSELECT)); + eFileSel_Params_Flag flag = flag_extra | FILE_ACTIVE_COLLECTION; + const char *name = asset_drag->name; ID_Type idtype = asset_drag->id_type; @@ -541,14 +548,8 @@ static ID *wm_drag_asset_id_import(wmDragAsset *asset_drag) switch ((eFileAssetImportType)asset_drag->import_type) { case FILE_ASSET_IMPORT_LINK: - return WM_file_link_datablock(bmain, - scene, - view_layer, - view3d, - asset_drag->path, - idtype, - name, - FILE_ACTIVE_COLLECTION); + return WM_file_link_datablock( + bmain, scene, view_layer, view3d, asset_drag->path, idtype, name, flag); case FILE_ASSET_IMPORT_APPEND: return WM_file_append_datablock(bmain, scene, @@ -557,7 +558,7 @@ static ID *wm_drag_asset_id_import(wmDragAsset *asset_drag) asset_drag->path, idtype, name, - BLO_LIBLINK_APPEND_RECURSIVE | FILE_ACTIVE_COLLECTION | + flag | BLO_LIBLINK_APPEND_RECURSIVE | BLO_LIBLINK_APPEND_ASSET_DATA_CLEAR); case FILE_ASSET_IMPORT_APPEND_REUSE: return WM_file_append_datablock(G_MAIN, @@ -567,7 +568,7 @@ static ID *wm_drag_asset_id_import(wmDragAsset *asset_drag) asset_drag->path, idtype, name, - BLO_LIBLINK_APPEND_RECURSIVE | FILE_ACTIVE_COLLECTION | + flag | BLO_LIBLINK_APPEND_RECURSIVE | BLO_LIBLINK_APPEND_ASSET_DATA_CLEAR | BLO_LIBLINK_APPEND_LOCAL_ID_REUSE); } @@ -582,6 +583,8 @@ static ID *wm_drag_asset_id_import(wmDragAsset *asset_drag) * * Use #WM_drag_free_imported_drag_ID() as cancel callback of the drop-box, so that the asset * import is rolled back if the drop operator fails. + * + * \param flag: #eFileSel_Params_Flag passed to linking code. */ ID *WM_drag_get_local_ID_or_import_from_asset(const wmDrag *drag, int idcode) { @@ -599,7 +602,7 @@ ID *WM_drag_get_local_ID_or_import_from_asset(const wmDrag *drag, int idcode) } /* Link/append the asset. */ - return wm_drag_asset_id_import(asset_drag); + return WM_drag_asset_id_import(asset_drag, 0); } /** -- cgit v1.2.3