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
This commit is contained in:
parent
1de051a7a9
commit
eb1ede5693
|
@ -8,6 +8,7 @@
|
|||
#include <cctype>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <optional>
|
||||
|
||||
#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<CollectionAddInfo> 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<Collection *>(
|
||||
add_info.collection = static_cast<Collection *>(
|
||||
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<CollectionAddInfo> 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<CollectionAddInfo> 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
|
||||
*
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#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,
|
||||
|
|
Loading…
Reference in New Issue