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
This commit is contained in:
Campbell Barton 2021-10-27 04:19:45 +11:00
parent eaed38cbd3
commit a062d86230
8 changed files with 296 additions and 76 deletions

View File

@ -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);

View File

@ -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
* \{ */

View File

@ -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);

View File

@ -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);

View File

@ -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);
}
}
}
/** \} */

View File

@ -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,

View File

@ -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);

View File

@ -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);
}
/**