Blender V4.5
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
11
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_library.hh"
22#include "BKE_modifier.hh"
23#include "BKE_object.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/* -------------------------------------------------------------------- */
57
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;
133 case OB_CURVES:
134 if (mode & (OB_MODE_EDIT | OB_MODE_SCULPT_CURVES)) {
135 return true;
136 }
137 break;
138 case OB_GREASE_PENCIL:
141 {
142 return true;
143 }
144 break;
145 }
146
147 return false;
148}
149
151{
152 bool ok;
153 if (!ELEM(ob->mode, mode, OB_MODE_OBJECT)) {
154 const char *opstring = object_mode_op_string(eObjectMode(ob->mode));
155
156 WM_operator_name_call(C, opstring, WM_OP_EXEC_REGION_WIN, nullptr, nullptr);
157 ok = ELEM(ob->mode, mode, OB_MODE_OBJECT);
158 if (!ok) {
159 wmOperatorType *ot = WM_operatortype_find(opstring, false);
160 BKE_reportf(reports, RPT_ERROR, "Unable to execute '%s', error changing modes", ot->name);
161 }
162 }
163 else {
164 ok = true;
165 }
166
167 return ok;
168}
169
171
172/* -------------------------------------------------------------------- */
179
180bool mode_set_ex(bContext *C, eObjectMode mode, bool use_undo, ReportList *reports)
181{
183 const Scene *scene = CTX_data_scene(C);
184 ViewLayer *view_layer = CTX_data_view_layer(C);
185
186 BKE_view_layer_synced_ensure(scene, view_layer);
188 if (ob == nullptr) {
189 return (mode == OB_MODE_OBJECT);
190 }
191
192 if (ob->mode == mode) {
193 return true;
194 }
195
196 if (!mode_compat_test(ob, mode)) {
197 return false;
198 }
199
200 const char *opstring = object_mode_op_string((mode == OB_MODE_OBJECT) ? eObjectMode(ob->mode) :
201 mode);
202 wmOperatorType *ot = WM_operatortype_find(opstring, false);
203
204 if (!use_undo) {
205 wm->op_undo_depth++;
206 }
208 if (!use_undo) {
209 wm->op_undo_depth--;
210 }
211
212 if (ob->mode != mode) {
213 BKE_reportf(reports, RPT_ERROR, "Unable to execute '%s', error changing modes", ot->name);
214 return false;
215 }
216
217 return true;
218}
219
221{
222 /* Don't do undo push by default, since this may be called by lower level code. */
223 return mode_set_ex(C, mode, true, nullptr);
224}
225
231 Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob, bool only_test)
232{
233 BLI_assert((bmain == nullptr) == only_test);
234 if (ob->mode & OB_MODE_EDIT) {
236 if (only_test) {
237 return true;
238 }
239 editmode_exit_ex(bmain, scene, ob, EM_FREEDATA);
240 }
241 }
242 else if (ob->mode & OB_MODE_VERTEX_PAINT) {
243 if (ob->sculpt && (ob->sculpt->mode_type == OB_MODE_VERTEX_PAINT)) {
244 if (only_test) {
245 return true;
246 }
248 }
249 }
250 else if (ob->mode & OB_MODE_WEIGHT_PAINT) {
251 if (ob->sculpt && (ob->sculpt->mode_type == OB_MODE_WEIGHT_PAINT)) {
252 if (only_test) {
253 return true;
254 }
256 }
257 }
258 else if (ob->mode & OB_MODE_SCULPT) {
259 if (ob->sculpt && (ob->sculpt->mode_type == OB_MODE_SCULPT)) {
260 if (only_test) {
261 return true;
262 }
264 }
265 }
266 else if (ob->mode & OB_MODE_POSE) {
267 if (ob->pose != nullptr) {
268 if (only_test) {
269 return true;
270 }
272 }
273 }
274 else if (ob->mode & OB_MODE_TEXTURE_PAINT) {
275 if (only_test) {
276 return true;
277 }
278 ED_object_texture_paint_mode_exit_ex(*bmain, *scene, *ob);
279 }
280 else if (ob->mode & OB_MODE_PARTICLE_EDIT) {
281 if (only_test) {
282 return true;
283 }
285 }
286 else if (ob->type == OB_GREASE_PENCIL) {
287 BLI_assert((ob->mode & OB_MODE_OBJECT) == 0);
288 if (only_test) {
289 return true;
290 }
291 ob->restore_mode = ob->mode;
294
295 /* Inform all evaluated versions that we changed the mode. */
297 }
298 else {
299 if (only_test) {
300 return false;
301 }
303 }
304
305 return false;
306}
307
308/* When locked, it's almost impossible to select the pose-object
309 * then the mesh-object to enter weight paint mode.
310 * Even when the object mode is not locked this is inconvenient - so allow in either case.
311 *
312 * In this case move our pose object in/out of pose mode.
313 * This is in fits with the convention of selecting multiple objects and entering a mode.
314 */
316 Main *bmain,
317 Object *ob_arm,
318 const bool is_mode_set)
319{
320 View3D *v3d = CTX_wm_view3d(C);
321 const Scene *scene = CTX_data_scene(C);
322 ViewLayer *view_layer = CTX_data_view_layer(C);
323
324 if (ob_arm != nullptr) {
325 BKE_view_layer_synced_ensure(scene, view_layer);
326 const Base *base_arm = BKE_view_layer_base_find(view_layer, ob_arm);
327 if (base_arm && BASE_VISIBLE(v3d, base_arm)) {
328 if (is_mode_set) {
329 if ((ob_arm->mode & OB_MODE_POSE) != 0) {
330 ED_object_posemode_exit_ex(bmain, ob_arm);
331 }
332 }
333 else {
334 /* Only check selected status when entering weight-paint mode
335 * because we may have multiple armature objects.
336 * Selecting one will de-select the other, which would leave it in pose-mode
337 * when exiting weight paint mode. While usable, this looks like inconsistent
338 * behavior from a user perspective. */
339 if (base_arm->flag & BASE_SELECTED) {
340 if ((ob_arm->mode & OB_MODE_POSE) == 0) {
341 ED_object_posemode_enter_ex(bmain, ob_arm);
342 }
343 }
344 }
345 }
346 }
347}
348
349void posemode_set_for_weight_paint(bContext *C, Main *bmain, Object *ob, const bool is_mode_set)
350{
351 VirtualModifierData virtual_modifier_data;
352 ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob, &virtual_modifier_data);
353 for (; md; md = md->next) {
354 if (md->type == eModifierType_Armature) {
355 ArmatureModifierData *amd = reinterpret_cast<ArmatureModifierData *>(md);
356 Object *ob_arm = amd->object;
357 ed_object_posemode_set_for_weight_paint_ex(C, bmain, ob_arm, is_mode_set);
358 }
361 md);
362 Object *ob_arm = amd->object;
363 ed_object_posemode_set_for_weight_paint_ex(C, bmain, ob_arm, is_mode_set);
364 }
365 }
366}
367
368void mode_generic_exit(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob)
369{
370 ed_object_mode_generic_exit_ex(bmain, depsgraph, scene, ob, false);
371}
372
373bool mode_generic_has_data(Depsgraph *depsgraph, const Object *ob)
374{
375 return ed_object_mode_generic_exit_ex(nullptr, depsgraph, nullptr, (Object *)ob, true);
376}
377
379
380/* -------------------------------------------------------------------- */
386
388{
389 if (!CTX_wm_region_view3d(C)) {
390 return false;
391 }
392 const Object *ob = CTX_data_active_object(C);
393 return ob && (ob->mode != OB_MODE_OBJECT);
394}
395
396/* Update the viewport rotation origin to the mouse cursor. */
398 Scene *scene,
399 const int mval[2])
400{
401 float global_loc[3];
402 if (!ED_view3d_autodist_simple(region, mval, global_loc, 0, nullptr)) {
403 return;
404 }
406 copy_v3_v3(ups->average_stroke_accum, global_loc);
407 ups->average_stroke_counter = 1;
408 ups->last_stroke_valid = true;
409}
410
412{
414 Object *ob_dst_eval = DEG_get_evaluated(depsgraph, ob_dst);
415 ob_dst_eval->runtime->overlay_mode_transfer_start_time = BLI_time_now_seconds();
416}
417
419 wmOperator *op,
420 Scene *scene,
421 Object * /*ob_src*/,
422 Object *ob_dst,
423 const eObjectMode mode_dst)
424{
425 ViewLayer *view_layer = CTX_data_view_layer(C);
426
427 /* Undo is handled manually here, such that the entry in the user-visible undo history is named
428 * from the expected mode toggle operator name, and not the 'Transfer Mode' operator itself.
429 *
430 * The undo grouping is needed to ensure that only one step is visible, even though there may be
431 * two undo steps stored when executed successfully (moving source object to Object mode, and
432 * then target object to the previous mode of source object). */
434
435 const bool mode_transferred = mode_set_ex(C, OB_MODE_OBJECT, true, op->reports);
436 if (mode_transferred) {
437 BKE_view_layer_synced_ensure(scene, view_layer);
438 Base *base_dst = BKE_view_layer_base_find(view_layer, ob_dst);
439 BKE_view_layer_base_deselect_all(scene, view_layer);
440 BKE_view_layer_base_select_and_set_active(view_layer, base_dst);
441
442 /* Not entirely clear why, but this extra undo step (the two calls to #mode_set_ex should
443 * already create their own) is required. Otherwise some mode switching does not work as
444 * expected on undo/redo (see #130420 with Sculpt mode). */
445 ED_undo_push(C, "Change Active");
446
447 mode_set_ex(C, mode_dst, true, op->reports);
448
449 if (RNA_boolean_get(op->ptr, "use_flash_on_transfer")) {
451 }
452 }
453
455
456 return mode_transferred;
457}
458
460 wmOperator *op,
461 const wmEvent *event)
462{
463 Scene *scene = CTX_data_scene(C);
464 ARegion *region = CTX_wm_region(C);
466 const eObjectMode mode_src = eObjectMode(ob_src->mode);
467
468 Base *base_dst = ED_view3d_give_base_under_cursor(C, event->mval);
469 if (!base_dst) {
470 BKE_reportf(op->reports, RPT_ERROR, "No target object to transfer the mode to");
471 return OPERATOR_CANCELLED;
472 }
473
474 Object *ob_dst = base_dst->object;
475
476 if (ob_src == ob_dst) {
477 return OPERATOR_CANCELLED;
478 }
479
480 BLI_assert(ob_dst->id.orig_id == nullptr);
481 if (!ID_IS_EDITABLE(ob_dst) || !ID_IS_EDITABLE(ob_src)) {
483 RPT_ERROR,
484 "Unable to transfer mode, the source and/or target objects are not editable");
485 return OPERATOR_CANCELLED;
486 }
487 if (ID_IS_OVERRIDE_LIBRARY(ob_dst) && !ELEM(mode_src, OB_MODE_OBJECT, OB_MODE_POSE)) {
489 op->reports,
490 RPT_ERROR,
491 "Current mode of source object '%s' is not compatible with target liboverride object '%s'",
492 ob_src->id.name + 2,
493 ob_dst->id.name + 2);
494 return OPERATOR_CANCELLED;
495 }
496 if (!mode_compat_test(ob_dst, mode_src)) {
498 RPT_ERROR,
499 "Current mode of source object '%s' is not compatible with target object '%s'",
500 ob_src->id.name + 2,
501 ob_dst->id.name + 2);
502 return OPERATOR_CANCELLED;
503 }
504
505 const bool mode_transferred = object_transfer_mode_to_base(
506 C, op, scene, ob_src, ob_dst, mode_src);
507 if (!mode_transferred) {
508 /* Error report should have been set by #object_transfer_mode_to_base call here. */
509 return OPERATOR_CANCELLED;
510 }
511
515
517 if (mode_src & OB_MODE_ALL_PAINT) {
519 }
520
521 return OPERATOR_FINISHED;
522}
523
525{
526 /* identifiers */
527 ot->name = "Transfer Mode";
528 ot->idname = "OBJECT_OT_transfer_mode";
529 ot->description =
530 "Switches the active object and assigns the same mode to a new one under the mouse cursor, "
531 "leaving the active mode in the current one";
532
533 /* API callbacks. */
536
537 /* Undo push is handled by the operator, see #object_transfer_mode_to_base for details. */
539
540 ot->cursor_pending = WM_CURSOR_EYEDROPPER;
541
542 RNA_def_boolean(ot->srna,
543 "use_flash_on_transfer",
544 true,
545 "Flash On Transfer",
546 "Flash the target object when transferring the mode");
547}
548
550
551} // 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:46
MINLINE void copy_v3_v3(float r[3], const float a[3])
Platform independent time functions.
double BLI_time_now_seconds(void)
Definition time.cc:65
#define ELEM(...)
void DEG_id_tag_update(ID *id, unsigned int flags)
T * DEG_get_evaluated(const Depsgraph *depsgraph, T *id)
@ ID_RECALC_SELECT
Definition DNA_ID.h:1009
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1026
@ eModifierType_GreasePencilArmature
@ eModifierType_Armature
#define OB_MODE_ALL_PAINT
#define OB_MODE_ALL_MODE_DATA
eObjectMode
@ OB_MODE_VERTEX_GREASE_PENCIL
@ OB_MODE_PARTICLE_EDIT
@ 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_CURVES
#define BASE_SELECTED(v3d, base)
#define BASE_VISIBLE(v3d, base)
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
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_push(bContext *C, const char *str)
Definition ed_undo.cc:99
void ED_undo_group_begin(bContext *C)
Definition ed_undo.cc:87
void ED_undo_group_end(bContext *C)
Definition ed_undo.cc:93
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])
#define C
Definition RandGen.cpp:29
#define ND_OB_SELECT
Definition WM_types.hh:439
#define NC_SCENE
Definition WM_types.hh:375
ReportList * reports
Definition WM_types.hh:1025
@ WM_OP_EXEC_REGION_WIN
Definition WM_types.hh:246
@ OPTYPE_DEPENDS_ON_CURSOR
Definition WM_types.hh:218
@ OPTYPE_REGISTER
Definition WM_types.hh:180
BPy_StructRNA * depsgraph
#define ID_IS_EDITABLE(_id)
#define ID_IS_OVERRIDE_LIBRARY(_id)
DEG_id_tag_update_ex(cb_data->bmain, cb_data->owner_id, ID_RECALC_TAG_FOR_UNDO|ID_RECALC_SYNC_TO_EVAL)
bool mode_generic_has_data(Depsgraph *depsgraph, const Object *ob)
static void object_overlay_mode_transfer_animation_start(bContext *C, Object *ob_dst)
static wmOperatorStatus object_transfer_mode_invoke(bContext *C, wmOperator *op, const wmEvent *event)
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)
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:77
bool ED_object_posemode_exit_ex(Main *bmain, Object *ob)
Definition pose_edit.cc:112
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:415
struct ModifierData * next
struct bPose * pose
ObjectRuntimeHandle * runtime
struct SculptSession * sculpt
struct ToolSettings * toolsettings
eObjectMode mode_type
Definition BKE_paint.hh:511
struct UnifiedPaintSettings unified_paint_settings
int mval[2]
Definition WM_types.hh:760
struct ReportList * reports
struct PointerRNA * ptr
@ WM_CURSOR_EYEDROPPER
Definition wm_cursors.hh:36
wmOperatorStatus WM_operator_name_call_ptr(bContext *C, wmOperatorType *ot, wmOperatorCallContext context, PointerRNA *properties, const wmEvent *event)
wmOperatorStatus WM_operator_name_call(bContext *C, const char *opstring, wmOperatorCallContext context, PointerRNA *properties, const wmEvent *event)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4226
wmOperatorType * WM_operatortype_find(const char *idname, bool quiet)
void WM_toolsystem_update_from_context_view3d(bContext *C)