Multi-Object Editing

This adds initial multi-object editing support.

- Selected objects are used when entering edit & pose modes.
- Selection & tools work on all objects however many tools need porting
  See: T54641 for remaining tasks.

Indentation will be done separately.

See patch: D3101
This commit is contained in:
2018-04-16 16:27:55 +02:00
parent 80bb4254c6
commit bfc9d426bb
58 changed files with 3786 additions and 1486 deletions

View File

@@ -89,6 +89,7 @@
#include "BKE_report.h"
#include "BKE_object.h"
#include "BKE_workspace.h"
#include "BKE_layer.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
@@ -277,9 +278,6 @@ bool ED_object_editmode_load(Object *obedit)
void ED_object_editmode_exit_ex(bContext *C, Scene *scene, Object *obedit, int flag)
{
BLI_assert(C || !(flag & EM_DO_UNDO));
/* Note! only in exceptional cases should 'EM_DO_UNDO' NOT be in the flag */
/* Note! if 'EM_FREEDATA' isn't in the flag, use ED_object_editmode_load directly */
ViewLayer *view_layer = CTX_data_view_layer(C);
const bool freedata = (flag & EM_FREEDATA) != 0;
if (flag & EM_WAITCURSOR) waitcursor(1);
@@ -287,8 +285,8 @@ void ED_object_editmode_exit_ex(bContext *C, Scene *scene, Object *obedit, int f
if (ED_object_editmode_load_ex(G.main, obedit, freedata) == false) {
/* in rare cases (background mode) its possible active object
* is flagged for editmode, without 'obedit' being set [#35489] */
if (UNLIKELY(view_layer->basact && (view_layer->basact->object->mode & OB_MODE_EDIT))) {
view_layer->basact->object->mode &= ~OB_MODE_EDIT;
if (UNLIKELY(obedit && obedit->mode & OB_MODE_EDIT)) {
obedit->mode &= ~OB_MODE_EDIT;
}
if (flag & EM_WAITCURSOR) waitcursor(0);
return;
@@ -315,15 +313,18 @@ void ED_object_editmode_exit_ex(bContext *C, Scene *scene, Object *obedit, int f
if (flag & EM_DO_UNDO)
ED_undo_push(C, "Editmode");
WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_OBJECT, scene);
if (C != NULL) {
WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_OBJECT, scene);
}
else {
WM_main_add_notifier(NC_SCENE | ND_MODE | NS_MODE_OBJECT, scene);
}
obedit->mode &= ~OB_MODE_EDIT;
}
if (flag & EM_WAITCURSOR) waitcursor(0);
/* This way we ensure scene's obedit is copied into all CoW scenes. */
DEG_id_tag_update(&scene->id, 0);
}
void ED_object_editmode_exit(bContext *C, int flag)
@@ -333,25 +334,12 @@ void ED_object_editmode_exit(bContext *C, int flag)
ED_object_editmode_exit_ex(C, scene, obedit, flag);
}
void ED_object_editmode_enter(bContext *C, int flag)
void ED_object_editmode_enter_ex(Scene *scene, Object *ob, int flag)
{
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
Object *ob;
bool ok = false;
if (ID_IS_LINKED(scene)) return;
if ((flag & EM_IGNORE_LAYER) == 0) {
ob = CTX_data_active_object(C); /* active layer checked here for view3d */
if (ob == NULL) return;
}
else {
ob = view_layer->basact->object;
}
if (ELEM(NULL, ob, ob->data)) return;
if (ID_IS_LINKED(ob)) return;
/* this checks actual object->data, for cases when other scenes have it in editmode context */
if (BKE_object_is_in_editmode(ob))
@@ -366,11 +354,6 @@ void ED_object_editmode_enter(bContext *C, int flag)
ob->restore_mode = ob->mode;
/* note, when switching scenes the object can have editmode data but
* not be scene->obedit: bug 22954, this avoids calling self eternally */
if ((ob->restore_mode & OB_MODE_EDIT) == 0)
ED_object_mode_toggle(C, ob->mode);
ob->mode = OB_MODE_EDIT;
if (ob->type == OB_MESH) {
@@ -387,7 +370,7 @@ void ED_object_editmode_enter(bContext *C, int flag)
BKE_editmesh_tessface_calc(em);
}
WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_EDITMODE_MESH, scene);
WM_main_add_notifier(NC_SCENE | ND_MODE | NS_EDITMODE_MESH, NULL);
}
else if (ob->type == OB_ARMATURE) {
bArmature *arm = ob->data;
@@ -409,45 +392,64 @@ void ED_object_editmode_enter(bContext *C, int flag)
/* to ensure all goes in restposition and without striding */
DEG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); /* XXX: should this be OB_RECALC_DATA? */
WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_EDITMODE_ARMATURE, scene);
WM_main_add_notifier(NC_SCENE | ND_MODE | NS_EDITMODE_ARMATURE, scene);
}
else if (ob->type == OB_FONT) {
ok = 1;
ED_curve_editfont_make(ob);
WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_EDITMODE_TEXT, scene);
WM_main_add_notifier(NC_SCENE | ND_MODE | NS_EDITMODE_TEXT, scene);
}
else if (ob->type == OB_MBALL) {
ok = 1;
ED_mball_editmball_make(ob);
WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_EDITMODE_MBALL, scene);
WM_main_add_notifier(NC_SCENE | ND_MODE | NS_EDITMODE_MBALL, scene);
}
else if (ob->type == OB_LATTICE) {
ok = 1;
BKE_editlattice_make(ob);
WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_EDITMODE_LATTICE, scene);
WM_main_add_notifier(NC_SCENE | ND_MODE | NS_EDITMODE_LATTICE, scene);
}
else if (ob->type == OB_SURF || ob->type == OB_CURVE) {
ok = 1;
ED_curve_editnurb_make(ob);
WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_EDITMODE_CURVE, scene);
WM_main_add_notifier(NC_SCENE | ND_MODE | NS_EDITMODE_CURVE, scene);
}
if (ok) {
DEG_id_tag_update(&ob->id, OB_RECALC_DATA);
/* This way we ensure scene's obedit is copied into all CoW scenes. */
DEG_id_tag_update(&scene->id, 0);
}
else {
ob->mode &= ~OB_MODE_EDIT;
WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_OBJECT, scene);
if ((flag & EM_NO_CONTEXT) == 0) {
ob->mode &= ~OB_MODE_EDIT;
}
WM_main_add_notifier(NC_SCENE | ND_MODE | NS_MODE_OBJECT, scene);
}
if (flag & EM_DO_UNDO) ED_undo_push(C, "Enter Editmode");
if (flag & EM_WAITCURSOR) waitcursor(0);
BLI_assert((flag & EM_DO_UNDO) == 0);
}
void ED_object_editmode_enter(bContext *C, int flag)
{
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
Object *ob;
if ((flag & EM_IGNORE_LAYER) == 0) {
ob = CTX_data_active_object(C); /* active layer checked here for view3d */
}
else {
ob = view_layer->basact->object;
}
if (ob == NULL) return;
if (ID_IS_LINKED(ob)) return;
ED_object_editmode_enter_ex(scene, ob, flag & ~EM_DO_UNDO);
if (flag & EM_DO_UNDO) ED_undo_push(C, "Enter Editmode");
}
static int editmode_toggle_exec(bContext *C, wmOperator *op)
@@ -455,18 +457,43 @@ static int editmode_toggle_exec(bContext *C, wmOperator *op)
const int mode_flag = OB_MODE_EDIT;
const bool is_mode_set = (CTX_data_edit_object(C) != NULL);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
Object *obact = OBACT(view_layer);
if (!is_mode_set) {
Object *ob = CTX_data_active_object(C);
if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) {
if (!ED_object_mode_compat_set(C, obact, mode_flag, op->reports)) {
return OPERATOR_CANCELLED;
}
}
if (!is_mode_set)
if (!is_mode_set) {
ED_object_editmode_enter(C, EM_WAITCURSOR);
else
if (obact->mode & mode_flag) {
FOREACH_SELECTED_OBJECT_BEGIN(view_layer, ob)
{
if ((ob != obact) && (ob->type == obact->type)) {
if (ob->flag & SELECT) {
ED_object_editmode_enter_ex(scene, ob, EM_WAITCURSOR | EM_NO_CONTEXT);
}
}
}
FOREACH_SELECTED_OBJECT_END;
}
}
else {
ED_object_editmode_exit(C, EM_FREEDATA | EM_WAITCURSOR); /* had EM_DO_UNDO but op flag calls undo too [#24685] */
if ((obact->mode & mode_flag) == 0) {
FOREACH_SELECTED_OBJECT_BEGIN(view_layer, ob)
{
if ((ob != obact) && (ob->type == obact->type)) {
if (ob->flag & SELECT) {
ED_object_editmode_exit_ex(NULL, scene, ob, EM_FREEDATA | EM_WAITCURSOR);
}
}
}
FOREACH_SELECTED_OBJECT_END;
}
}
ED_space_image_uv_sculpt_update(CTX_wm_manager(C), scene);
@@ -510,27 +537,60 @@ void OBJECT_OT_editmode_toggle(wmOperatorType *ot)
static int posemode_exec(bContext *C, wmOperator *op)
{
Base *base = CTX_data_active_base(C);
Object *ob = base->object;
Object *obact = base->object;
const int mode_flag = OB_MODE_POSE;
bool is_mode_set = (ob->mode & mode_flag) != 0;
bool is_mode_set = (obact->mode & mode_flag) != 0;
if (!is_mode_set) {
if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) {
if (!ED_object_mode_compat_set(C, obact, mode_flag, op->reports)) {
return OPERATOR_CANCELLED;
}
}
if (ob->type == OB_ARMATURE) {
if (ob == CTX_data_edit_object(C)) {
if (obact->type == OB_ARMATURE) {
if (obact == CTX_data_edit_object(C)) {
ED_object_editmode_exit(C, EM_FREEDATA | EM_DO_UNDO);
is_mode_set = false;
}
if (is_mode_set) {
ED_object_posemode_exit(C, ob);
bool ok = ED_object_posemode_exit(C, obact);
if (ok) {
struct Main *bmain = CTX_data_main(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
FOREACH_SELECTED_OBJECT_BEGIN(view_layer, ob)
{
if ((ob != obact) &&
(ob->type == OB_ARMATURE) &&
(ob->mode & mode_flag))
{
if (ob->flag & SELECT) {
ED_object_posemode_exit_ex(bmain, ob);
}
}
}
FOREACH_SELECTED_OBJECT_END;
}
}
else {
ED_object_posemode_enter(C, ob);
bool ok = ED_object_posemode_enter(C, obact);
if (ok) {
struct Main *bmain = CTX_data_main(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
FOREACH_SELECTED_OBJECT_BEGIN(view_layer, ob)
{
if ((ob != obact) &&
(ob->type == OB_ARMATURE) &&
(ob->mode == OB_MODE_OBJECT) &&
(!ID_IS_LINKED(ob)))
{
if (ob->flag & SELECT) {
ED_object_posemode_enter_ex(bmain, ob);
}
}
}
FOREACH_SELECTED_OBJECT_END;
}
}
return OPERATOR_FINISHED;
}

View File

@@ -177,6 +177,9 @@ bool ED_object_mode_generic_enter(
struct bContext *C, eObjectMode object_mode)
{
Object *ob = CTX_data_active_object(C);
if (ob == NULL) {
return (object_mode == OB_MODE_OBJECT);
}
if (ob->mode == object_mode) {
return true;
}