Blender V4.3
object_modes.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
12#include "DNA_object_enums.h"
13#include "DNA_object_types.h"
14#include "DNA_scene_types.h"
15
16#include "BLI_time.h"
17#include "BLI_utildefines.h"
18
19#include "BKE_context.hh"
20#include "BKE_layer.hh"
21#include "BKE_modifier.hh"
22#include "BKE_object.hh"
23#include "BKE_object_types.hh"
24#include "BKE_paint.hh"
25#include "BKE_report.hh"
26
27#include "BLI_math_vector.h"
28
29#include "WM_api.hh"
30#include "WM_types.hh"
31
32#include "RNA_access.hh"
33#include "RNA_define.hh"
34
35#include "DEG_depsgraph.hh"
37
38#include "ED_armature.hh"
39#include "ED_gpencil_legacy.hh"
40#include "ED_outliner.hh"
41#include "ED_paint.hh"
42#include "ED_physics.hh"
43#include "ED_sculpt.hh"
44#include "ED_undo.hh"
45#include "ED_view3d.hh"
46
47#include "WM_toolsystem.hh"
48
49#include "ED_object.hh" /* own include */
50#include "object_intern.hh"
51
52namespace blender::ed::object {
53
54/* -------------------------------------------------------------------- */
58static const char *object_mode_op_string(eObjectMode mode)
59{
60 if (mode & OB_MODE_EDIT) {
61 return "OBJECT_OT_editmode_toggle";
62 }
63 if (mode == OB_MODE_SCULPT) {
64 return "SCULPT_OT_sculptmode_toggle";
65 }
66 if (mode == OB_MODE_VERTEX_PAINT) {
67 return "PAINT_OT_vertex_paint_toggle";
68 }
69 if (mode == OB_MODE_WEIGHT_PAINT) {
70 return "PAINT_OT_weight_paint_toggle";
71 }
72 if (mode == OB_MODE_TEXTURE_PAINT) {
73 return "PAINT_OT_texture_paint_toggle";
74 }
75 if (mode == OB_MODE_PARTICLE_EDIT) {
76 return "PARTICLE_OT_particle_edit_toggle";
77 }
78 if (mode == OB_MODE_POSE) {
79 return "OBJECT_OT_posemode_toggle";
80 }
81 if (mode == OB_MODE_PAINT_GREASE_PENCIL) {
82 return "GREASE_PENCIL_OT_paintmode_toggle";
83 }
84 if (mode == OB_MODE_SCULPT_GREASE_PENCIL) {
85 return "GREASE_PENCIL_OT_sculptmode_toggle";
86 }
87 if (mode == OB_MODE_WEIGHT_GREASE_PENCIL) {
88 return "GREASE_PENCIL_OT_weightmode_toggle";
89 }
90 if (mode == OB_MODE_VERTEX_GREASE_PENCIL) {
91 return "GREASE_PENCIL_OT_vertexmode_toggle";
92 }
93 if (mode == OB_MODE_SCULPT_CURVES) {
94 return "CURVES_OT_sculptmode_toggle";
95 }
96 return nullptr;
97}
98
99bool mode_compat_test(const Object *ob, eObjectMode mode)
100{
101 if (mode == OB_MODE_OBJECT) {
102 return true;
103 }
104
105 switch (ob->type) {
106 case OB_MESH:
109 {
110 return true;
111 }
112 if (mode & OB_MODE_PARTICLE_EDIT) {
114 return true;
115 }
116 }
117 break;
118 case OB_CURVES_LEGACY:
119 case OB_SURF:
120 case OB_FONT:
121 case OB_MBALL:
122 case OB_POINTCLOUD:
123 case OB_LATTICE:
124 if (mode & OB_MODE_EDIT) {
125 return true;
126 }
127 break;
128 case OB_ARMATURE:
129 if (mode & (OB_MODE_EDIT | OB_MODE_POSE)) {
130 return true;
131 }
132 break;
135 return true;
136 }
137 break;
138 case OB_CURVES:
139 if (mode & (OB_MODE_EDIT | OB_MODE_SCULPT_CURVES)) {
140 return true;
141 }
142 break;
143 case OB_GREASE_PENCIL:
146 {
147 return true;
148 }
149 break;
150 }
151
152 return false;
153}
154
156{
157 bool ok;
158 if (!ELEM(ob->mode, mode, OB_MODE_OBJECT)) {
159 const char *opstring = object_mode_op_string(eObjectMode(ob->mode));
160
161 WM_operator_name_call(C, opstring, WM_OP_EXEC_REGION_WIN, nullptr, nullptr);
162 ok = ELEM(ob->mode, mode, OB_MODE_OBJECT);
163 if (!ok) {
164 wmOperatorType *ot = WM_operatortype_find(opstring, false);
165 BKE_reportf(reports, RPT_ERROR, "Unable to execute '%s', error changing modes", ot->name);
166 }
167 }
168 else {
169 ok = true;
170 }
171
172 return ok;
173}
174
177/* -------------------------------------------------------------------- */
185bool mode_set_ex(bContext *C, eObjectMode mode, bool use_undo, ReportList *reports)
186{
188 const Scene *scene = CTX_data_scene(C);
189 ViewLayer *view_layer = CTX_data_view_layer(C);
190
191 BKE_view_layer_synced_ensure(scene, view_layer);
193 if (ob == nullptr) {
194 return (mode == OB_MODE_OBJECT);
195 }
196
197 if ((ob->type == OB_GPENCIL_LEGACY) && (mode == OB_MODE_EDIT)) {
199 }
200
201 if (ob->mode == mode) {
202 return true;
203 }
204
205 if (!mode_compat_test(ob, mode)) {
206 return false;
207 }
208
209 const char *opstring = object_mode_op_string((mode == OB_MODE_OBJECT) ? eObjectMode(ob->mode) :
210 mode);
211 wmOperatorType *ot = WM_operatortype_find(opstring, false);
212
213 if (!use_undo) {
214 wm->op_undo_depth++;
215 }
217 if (!use_undo) {
218 wm->op_undo_depth--;
219 }
220
221 if (ob->mode != mode) {
222 BKE_reportf(reports, RPT_ERROR, "Unable to execute '%s', error changing modes", ot->name);
223 return false;
224 }
225
226 return true;
227}
228
230{
231 /* Don't do undo push by default, since this may be called by lower level code. */
232 return mode_set_ex(C, mode, true, nullptr);
233}
234
240 Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob, bool only_test)
241{
242 BLI_assert((bmain == nullptr) == only_test);
243 if (ob->mode & OB_MODE_EDIT) {
245 if (only_test) {
246 return true;
247 }
248 editmode_exit_ex(bmain, scene, ob, EM_FREEDATA);
249 }
250 }
251 else if (ob->mode & OB_MODE_VERTEX_PAINT) {
252 if (ob->sculpt && (ob->sculpt->mode_type == OB_MODE_VERTEX_PAINT)) {
253 if (only_test) {
254 return true;
255 }
257 }
258 }
259 else if (ob->mode & OB_MODE_WEIGHT_PAINT) {
260 if (ob->sculpt && (ob->sculpt->mode_type == OB_MODE_WEIGHT_PAINT)) {
261 if (only_test) {
262 return true;
263 }
265 }
266 }
267 else if (ob->mode & OB_MODE_SCULPT) {
268 if (ob->sculpt && (ob->sculpt->mode_type == OB_MODE_SCULPT)) {
269 if (only_test) {
270 return true;
271 }
273 }
274 }
275 else if (ob->mode & OB_MODE_POSE) {
276 if (ob->pose != nullptr) {
277 if (only_test) {
278 return true;
279 }
281 }
282 }
283 else if (ob->mode & OB_MODE_TEXTURE_PAINT) {
284 if (only_test) {
285 return true;
286 }
287 ED_object_texture_paint_mode_exit_ex(*bmain, *scene, *ob);
288 }
289 else if (ob->mode & OB_MODE_PARTICLE_EDIT) {
290 if (only_test) {
291 return true;
292 }
294 }
295 else if (ob->type == OB_GREASE_PENCIL) {
296 BLI_assert((ob->mode & OB_MODE_OBJECT) == 0);
297 if (only_test) {
298 return true;
299 }
300 ob->restore_mode = ob->mode;
303
304 /* Inform all evaluated versions that we changed the mode. */
306 }
307 else {
308 if (only_test) {
309 return false;
310 }
312 }
313
314 return false;
315}
316
317/* When locked, it's almost impossible to select the pose-object
318 * then the mesh-object to enter weight paint mode.
319 * Even when the object mode is not locked this is inconvenient - so allow in either case.
320 *
321 * In this case move our pose object in/out of pose mode.
322 * This is in fits with the convention of selecting multiple objects and entering a mode.
323 */
325 Main *bmain,
326 Object *ob_arm,
327 const bool is_mode_set)
328{
329 View3D *v3d = CTX_wm_view3d(C);
330 const Scene *scene = CTX_data_scene(C);
331 ViewLayer *view_layer = CTX_data_view_layer(C);
332
333 if (ob_arm != nullptr) {
334 BKE_view_layer_synced_ensure(scene, view_layer);
335 const Base *base_arm = BKE_view_layer_base_find(view_layer, ob_arm);
336 if (base_arm && BASE_VISIBLE(v3d, base_arm)) {
337 if (is_mode_set) {
338 if ((ob_arm->mode & OB_MODE_POSE) != 0) {
339 ED_object_posemode_exit_ex(bmain, ob_arm);
340 }
341 }
342 else {
343 /* Only check selected status when entering weight-paint mode
344 * because we may have multiple armature objects.
345 * Selecting one will de-select the other, which would leave it in pose-mode
346 * when exiting weight paint mode. While usable, this looks like inconsistent
347 * behavior from a user perspective. */
348 if (base_arm->flag & BASE_SELECTED) {
349 if ((ob_arm->mode & OB_MODE_POSE) == 0) {
350 ED_object_posemode_enter_ex(bmain, ob_arm);
351 }
352 }
353 }
354 }
355 }
356}
357
358void posemode_set_for_weight_paint(bContext *C, Main *bmain, Object *ob, const bool is_mode_set)
359{
360 VirtualModifierData virtual_modifier_data;
361 ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob, &virtual_modifier_data);
362 for (; md; md = md->next) {
363 if (md->type == eModifierType_Armature) {
365 Object *ob_arm = amd->object;
366 ed_object_posemode_set_for_weight_paint_ex(C, bmain, ob_arm, is_mode_set);
367 }
368 }
369}
370
371void mode_generic_exit(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob)
372{
373 ed_object_mode_generic_exit_ex(bmain, depsgraph, scene, ob, false);
374}
375
376bool mode_generic_has_data(Depsgraph *depsgraph, const Object *ob)
377{
378 return ed_object_mode_generic_exit_ex(nullptr, depsgraph, nullptr, (Object *)ob, true);
379}
380
383/* -------------------------------------------------------------------- */
391{
392 if (!CTX_wm_region_view3d(C)) {
393 return false;
394 }
395 const Object *ob = CTX_data_active_object(C);
396 return ob && (ob->mode != OB_MODE_OBJECT);
397}
398
399/* Update the viewport rotation origin to the mouse cursor. */
401 Scene *scene,
402 const int mval[2])
403{
404 float global_loc[3];
405 if (!ED_view3d_autodist_simple(region, mval, global_loc, 0, nullptr)) {
406 return;
407 }
408 UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
409 copy_v3_v3(ups->average_stroke_accum, global_loc);
410 ups->average_stroke_counter = 1;
411 ups->last_stroke_valid = true;
412}
413
415{
417 Object *ob_dst_eval = DEG_get_evaluated_object(depsgraph, ob_dst);
418 ob_dst_eval->runtime->overlay_mode_transfer_start_time = BLI_time_now_seconds();
419}
420
422 wmOperator *op,
423 Scene *scene,
424 Object * /*ob_src*/,
425 Object *ob_dst,
426 const eObjectMode mode_dst)
427{
428 ViewLayer *view_layer = CTX_data_view_layer(C);
429
430 /* Undo is handled manually here, such that the entry in the user-visible undo history is named
431 * from the expected mode toggle operator name, and not the 'Transfer Mode' operator itself.
432 *
433 * The undo grouping is needed to ensure that only one step is visible, even though there may be
434 * two undo steps stored when executed successfully (moving source object to Object mode, and
435 * then target object to the previous mode of source object). */
437
438 const bool mode_transferred = mode_set_ex(C, OB_MODE_OBJECT, true, op->reports);
439 if (mode_transferred) {
440 BKE_view_layer_synced_ensure(scene, view_layer);
441 Base *base_dst = BKE_view_layer_base_find(view_layer, ob_dst);
442 BKE_view_layer_base_deselect_all(scene, view_layer);
443 BKE_view_layer_base_select_and_set_active(view_layer, base_dst);
444
445 mode_set_ex(C, mode_dst, true, op->reports);
446
447 if (RNA_boolean_get(op->ptr, "use_flash_on_transfer")) {
449 }
450 }
451
453
454 return mode_transferred;
455}
456
458{
459 Scene *scene = CTX_data_scene(C);
460 ARegion *region = CTX_wm_region(C);
461 Object *ob_src = CTX_data_active_object(C);
462 const eObjectMode mode_src = eObjectMode(ob_src->mode);
463
464 Base *base_dst = ED_view3d_give_base_under_cursor(C, event->mval);
465 if (!base_dst) {
466 BKE_reportf(op->reports, RPT_ERROR, "No target object to transfer the mode to");
467 return OPERATOR_CANCELLED;
468 }
469
470 Object *ob_dst = base_dst->object;
471
472 if (ob_src == ob_dst) {
473 return OPERATOR_CANCELLED;
474 }
475
476 BLI_assert(ob_dst->id.orig_id == nullptr);
477 if (!ID_IS_EDITABLE(ob_dst) || !ID_IS_EDITABLE(ob_src)) {
479 RPT_ERROR,
480 "Unable to transfer mode, the source and/or target objects are not editable");
481 return OPERATOR_CANCELLED;
482 }
483 if (ID_IS_OVERRIDE_LIBRARY(ob_dst) && !ELEM(mode_src, OB_MODE_OBJECT, OB_MODE_POSE)) {
485 op->reports,
486 RPT_ERROR,
487 "Current mode of source object '%s' is not compatible with target liboverride object '%s'",
488 ob_src->id.name + 2,
489 ob_dst->id.name + 2);
490 return OPERATOR_CANCELLED;
491 }
492 if (!mode_compat_test(ob_dst, mode_src)) {
494 RPT_ERROR,
495 "Current mode of source object '%s' is not compatible with target object '%s'",
496 ob_src->id.name + 2,
497 ob_dst->id.name + 2);
498 return OPERATOR_CANCELLED;
499 }
500
501 const bool mode_transferred = object_transfer_mode_to_base(
502 C, op, scene, ob_src, ob_dst, mode_src);
503 if (!mode_transferred) {
504 /* Error report should have been set by #object_transfer_mode_to_base call here. */
505 return OPERATOR_CANCELLED;
506 }
507
511
513 if (mode_src & OB_MODE_ALL_PAINT) {
515 }
516
517 return OPERATOR_FINISHED;
518}
519
521{
522 /* identifiers */
523 ot->name = "Transfer Mode";
524 ot->idname = "OBJECT_OT_transfer_mode";
525 ot->description =
526 "Switches the active object and assigns the same mode to a new one under the mouse cursor, "
527 "leaving the active mode in the current one";
528
529 /* api callbacks */
532
533 /* Undo push is handled by the operator, see #object_transfer_mode_to_base for details. */
535
537
539 "use_flash_on_transfer",
540 true,
541 "Flash On Transfer",
542 "Flash the target object when transferring the mode");
543}
544
547} // namespace blender::ed::object
Depsgraph * CTX_data_depsgraph_pointer(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
RegionView3D * CTX_wm_region_view3d(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
View3D * CTX_wm_view3d(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
void BKE_view_layer_base_deselect_all(const Scene *scene, ViewLayer *view_layer)
Object * BKE_view_layer_active_object_get(const ViewLayer *view_layer)
Base * BKE_view_layer_base_find(ViewLayer *view_layer, Object *ob)
void BKE_view_layer_base_select_and_set_active(ViewLayer *view_layer, Base *selbase)
ModifierData * BKE_modifiers_get_virtual_modifierlist(const Object *ob, VirtualModifierData *data)
General operations, lookup, etc. for blender objects.
bool BKE_object_is_in_editmode(const Object *ob)
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
#define BLI_assert(a)
Definition BLI_assert.h:50
MINLINE void copy_v3_v3(float r[3], const float a[3])
Platform independent time functions.
double BLI_time_now_seconds(void)
Definition time.c:65
#define ELEM(...)
void DEG_id_tag_update(ID *id, unsigned int flags)
void DEG_id_tag_update_ex(Main *bmain, ID *id, unsigned int flags)
Object * DEG_get_evaluated_object(const Depsgraph *depsgraph, Object *object)
@ ID_RECALC_SELECT
Definition DNA_ID.h:1068
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1085
#define ID_IS_EDITABLE(_id)
Definition DNA_ID.h:658
#define ID_IS_OVERRIDE_LIBRARY(_id)
Definition DNA_ID.h:683
@ eModifierType_Armature
#define OB_MODE_ALL_PAINT
#define OB_MODE_ALL_MODE_DATA
#define OB_MODE_ALL_PAINT_GPENCIL
eObjectMode
@ OB_MODE_VERTEX_GREASE_PENCIL
@ OB_MODE_PARTICLE_EDIT
@ OB_MODE_EDIT_GPENCIL_LEGACY
@ OB_MODE_EDIT
@ OB_MODE_WEIGHT_PAINT
@ OB_MODE_SCULPT
@ OB_MODE_SCULPT_CURVES
@ OB_MODE_PAINT_GREASE_PENCIL
@ OB_MODE_SCULPT_GREASE_PENCIL
@ OB_MODE_POSE
@ OB_MODE_TEXTURE_PAINT
@ OB_MODE_OBJECT
@ OB_MODE_WEIGHT_GREASE_PENCIL
@ OB_MODE_VERTEX_PAINT
Object is a sort of wrapper for general info.
@ OB_LATTICE
@ OB_MBALL
@ OB_SURF
@ OB_FONT
@ OB_GREASE_PENCIL
@ OB_ARMATURE
@ OB_MESH
@ OB_POINTCLOUD
@ OB_CURVES_LEGACY
@ OB_GPENCIL_LEGACY
@ OB_CURVES
#define BASE_SELECTED(v3d, base)
#define BASE_VISIBLE(v3d, base)
void ED_outliner_select_sync_from_object_tag(bContext *C)
void ED_object_wpaintmode_exit_ex(Object &ob)
void ED_object_vpaintmode_exit_ex(Object &ob)
void ED_object_texture_paint_mode_exit_ex(Main &bmain, Scene &scene, Object &ob)
bool ED_object_particle_edit_mode_supported(const Object *ob)
void ED_object_particle_edit_mode_exit_ex(Scene *scene, Object *ob)
void ED_undo_group_begin(bContext *C)
Definition ed_undo.cc:92
void ED_undo_group_end(bContext *C)
Definition ed_undo.cc:98
bool ED_view3d_autodist_simple(ARegion *region, const int mval[2], float mouse_worldloc[3], int margin, const float *force_depth)
Base * ED_view3d_give_base_under_cursor(bContext *C, const int mval[2])
@ OPTYPE_DEPENDS_ON_CURSOR
Definition WM_types.hh:198
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define ND_OB_SELECT
Definition WM_types.hh:409
#define NC_SCENE
Definition WM_types.hh:345
@ WM_OP_EXEC_REGION_WIN
Definition WM_types.hh:226
const Depsgraph * depsgraph
bool mode_generic_has_data(Depsgraph *depsgraph, const Object *ob)
static void object_overlay_mode_transfer_animation_start(bContext *C, Object *ob_dst)
static bool ed_object_mode_generic_exit_ex(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob, bool only_test)
bool mode_set_ex(bContext *C, eObjectMode mode, bool use_undo, ReportList *reports)
static int object_transfer_mode_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void mode_generic_exit(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob)
static bool object_transfer_mode_to_base(bContext *C, wmOperator *op, Scene *scene, Object *, Object *ob_dst, const eObjectMode mode_dst)
bool mode_compat_test(const Object *ob, eObjectMode mode)
static void ed_object_posemode_set_for_weight_paint_ex(bContext *C, Main *bmain, Object *ob_arm, const bool is_mode_set)
void posemode_set_for_weight_paint(bContext *C, Main *bmain, Object *ob, bool is_mode_set)
bool mode_compat_set(bContext *C, Object *ob, eObjectMode mode, ReportList *reports)
static const char * object_mode_op_string(eObjectMode mode)
static bool object_transfer_mode_poll(bContext *C)
bool mode_set(bContext *C, eObjectMode mode)
bool editmode_exit_ex(Main *bmain, Scene *scene, Object *obedit, int flag)
static void object_transfer_mode_reposition_view_pivot(ARegion *region, Scene *scene, const int mval[2])
void OBJECT_OT_transfer_mode(wmOperatorType *ot)
void object_sculpt_mode_exit(Main &bmain, Depsgraph &depsgraph, Scene &scene, Object &ob)
bool ED_object_posemode_enter_ex(Main *bmain, Object *ob)
Definition pose_edit.cc:78
bool ED_object_posemode_exit_ex(Main *bmain, Object *ob)
Definition pose_edit.cc:113
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
short flag
struct Object * object
struct ID * orig_id
Definition DNA_ID.h:466
char name[66]
Definition DNA_ID.h:425
struct ModifierData * next
struct bPose * pose
ObjectRuntimeHandle * runtime
struct SculptSession * sculpt
eObjectMode mode_type
Definition BKE_paint.hh:487
int mval[2]
Definition WM_types.hh:728
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
const char * description
Definition WM_types.hh:996
StructRNA * srna
Definition WM_types.hh:1080
struct ReportList * reports
struct PointerRNA * ptr
@ WM_CURSOR_EYEDROPPER
Definition wm_cursors.hh:35
int WM_operator_name_call_ptr(bContext *C, wmOperatorType *ot, wmOperatorCallContext context, PointerRNA *properties, const wmEvent *event)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
int WM_operator_name_call(bContext *C, const char *opstring, wmOperatorCallContext context, PointerRNA *properties, const wmEvent *event)
wmOperatorType * ot
Definition wm_files.cc:4125
wmOperatorType * WM_operatortype_find(const char *idname, bool quiet)
void WM_toolsystem_update_from_context_view3d(bContext *C)