From 19b63b932d2b799c7904ebaf59911aef4926dcbb Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Thu, 26 Jan 2023 07:54:04 -0300 Subject: [PATCH] Fix transform gizmo not updating according to state Whenever a transform operation is activated by gizmo, the gizmo modal is maintained, but its drawing remains the same even if the transform mode or constrain is changed. So update the gizmo according to the mode or constrain set. NOTE: Currently only 3D view gizmo is affected --- source/blender/editors/transform/transform.c | 5 + source/blender/editors/transform/transform.h | 24 + .../editors/transform/transform_generics.c | 2 + .../editors/transform/transform_gizmo_3d.c | 788 ++++++++++++------ .../editors/transform/transform_mode.c | 2 + .../windowmanager/gizmo/WM_gizmo_api.h | 10 + .../windowmanager/gizmo/intern/wm_gizmo.c | 20 + 7 files changed, 582 insertions(+), 269 deletions(-) diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 3cff4c11cad..7f16a5796ba 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -862,6 +862,9 @@ static bool transform_event_modal_constraint(TransInfo *t, short modal_type) else { setUserConstraint(t, constraint_new, msg_3d); } + + /* Take the opportunity to update the gizmo. */ + transform_gizmo_3d_model_from_constraint_and_mode_set(t); } t->redraw |= TREDRAW_HARD; return true; @@ -2057,6 +2060,8 @@ int transformEnd(bContext *C, TransInfo *t) viewRedrawPost(C, t); viewRedrawForce(C, t); + + transform_gizmo_3d_model_from_constraint_and_mode_restore(t); } t->context = NULL; diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index 82ab9039db5..08f560d16e9 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -138,6 +138,9 @@ typedef enum { /** No cursor wrapping on region bounds */ T_NO_CURSOR_WRAP = 1 << 23, + + /** Do not display Xform gizmo even though it is available. */ + T_NO_GIZMO = 1 << 24, } eTFlag; ENUM_OPERATORS(eTFlag, T_NO_CURSOR_WRAP); @@ -734,6 +737,27 @@ void transform_final_value_get(const TransInfo *t, float *value, int value_num); bool gimbal_axis_pose(struct Object *ob, const struct bPoseChannel *pchan, float gmat[3][3]); bool gimbal_axis_object(struct Object *ob, float gmat[3][3]); +/** + * Set the #T_NO_GIZMO flag. + * + * \note This maintains the conventional behavior of not displaying the gizmo if the operator has + * been triggered by shortcuts. + */ +void transform_gizmo_3d_model_from_constraint_and_mode_init(TransInfo *t); + +/** + * Change the gizmo and its orientation to match the transform state. + * + * \note This used while the modal operator is running so changes to the constraint or mode show + * the gizmo associated with that state, as if it had been the initial gizmo dragged. + */ +void transform_gizmo_3d_model_from_constraint_and_mode_set(TransInfo *t); + +/** + * Restores the non-modal state of the gizmo. + */ +void transform_gizmo_3d_model_from_constraint_and_mode_restore(TransInfo *t); + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index 53e346ee86a..d1f210ca772 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -640,6 +640,8 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve setTransformViewMatrices(t); initNumInput(&t->num); + + transform_gizmo_3d_model_from_constraint_and_mode_init(t); } static void freeTransCustomData(TransInfo *t, TransDataContainer *tc, TransCustomData *custom_data) diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c index 28b39786bbc..306502ab3bc 100644 --- a/source/blender/editors/transform/transform_gizmo_3d.c +++ b/source/blender/editors/transform/transform_gizmo_3d.c @@ -70,9 +70,13 @@ #include "GPU_state.h" -static void gizmo_refresh_from_matrix(wmGizmoGroup *gzgroup, - const float twmat[4][4], - const float scale[3]); +static wmGizmoGroupType *g_GGT_xform_gizmo = NULL; +static wmGizmoGroupType *g_GGT_xform_gizmo_context = NULL; + +static void gizmogroup_refresh_from_matrix(wmGizmoGroup *gzgroup, + const float twmat[4][4], + const float scale[3], + const bool ignore_hidden); /* return codes for select, and drawing flags */ @@ -1135,30 +1139,36 @@ static void gizmo_prepare_mat(const bContext *C, * Sets up \a r_start and \a r_len to define arrow line range. * Needed to adjust line drawing for combined gizmo axis types. */ -static void gizmo_line_range(const int twtype, const short axis_type, float *r_start, float *r_len) +static void gizmo_line_range(const int twtype, const short axis_type, float *r_start, float *r_end) { - const float ofs = 0.2f; - - *r_start = 0.2f; - *r_len = 1.0f; + float start = 0.2f; + float end = 1.0f; switch (axis_type) { case MAN_AXES_TRANSLATE: if (twtype & V3D_GIZMO_SHOW_OBJECT_SCALE) { - *r_start = *r_len - ofs + 0.075f; + start = end - 0.125f; } if (twtype & V3D_GIZMO_SHOW_OBJECT_ROTATE) { - *r_len += ofs; + /* Avoid rotate and translate gizmos overlap. */ + const float rotate_offset = 0.215f; + start += rotate_offset; + end += rotate_offset + 0.2f; } break; case MAN_AXES_SCALE: if (twtype & (V3D_GIZMO_SHOW_OBJECT_TRANSLATE | V3D_GIZMO_SHOW_OBJECT_ROTATE)) { - *r_len -= ofs + 0.025f; + end -= 0.225f; } break; } - *r_len -= *r_start; + if (r_start) { + *r_start = start; + } + if (r_end) { + *r_end = end; + } } static void gizmo_xform_message_subscribe(wmGizmoGroup *gzgroup, @@ -1280,9 +1290,9 @@ static void gizmo_xform_message_subscribe(wmGizmoGroup *gzgroup, } static void gizmo_3d_dial_matrixbasis_calc(const ARegion *region, - float axis[3], - float center_global[3], - float mval_init[2], + const float axis[3], + const float center_global[3], + const float mval_init[2], float r_mat_basis[4][4]) { copy_v3_v3(r_mat_basis[2], axis); @@ -1329,6 +1339,169 @@ static void rotation_set_fn(const wmGizmo *UNUSED(gz), wmGizmoProperty *gz_prop, ggd->rotation = *(const float *)value; } +static void gizmo_3d_setup_draw_default(wmGizmo *axis, const int axis_idx) +{ + switch (axis_idx) { + /* Arrow. */ + case MAN_AXIS_TRANS_X: + case MAN_AXIS_TRANS_Y: + case MAN_AXIS_TRANS_Z: + RNA_enum_set(axis->ptr, "draw_style", ED_GIZMO_ARROW_STYLE_NORMAL); + break; + case MAN_AXIS_SCALE_X: + case MAN_AXIS_SCALE_Y: + case MAN_AXIS_SCALE_Z: + RNA_enum_set(axis->ptr, "draw_style", ED_GIZMO_ARROW_STYLE_BOX); + RNA_enum_set(axis->ptr, "draw_options", ED_GIZMO_ARROW_DRAW_FLAG_STEM); + break; + + /* Primitive. */ + case MAN_AXIS_TRANS_XY: + case MAN_AXIS_TRANS_YZ: + case MAN_AXIS_TRANS_ZX: + case MAN_AXIS_SCALE_XY: + case MAN_AXIS_SCALE_YZ: + case MAN_AXIS_SCALE_ZX: { + RNA_enum_set(axis->ptr, "draw_style", ED_GIZMO_PRIMITIVE_STYLE_PLANE); + + const float ofs[3] = {MAN_AXIS_SCALE_PLANE_OFFSET, MAN_AXIS_SCALE_PLANE_OFFSET, 0.0f}; + WM_gizmo_set_scale(axis, MAN_AXIS_SCALE_PLANE_SCALE); + WM_gizmo_set_matrix_offset_location(axis, ofs); + WM_gizmo_set_flag(axis, WM_GIZMO_DRAW_OFFSET_SCALE, true); + break; + } + + /* Dial. */ + case MAN_AXIS_TRANS_C: + RNA_enum_set(axis->ptr, "draw_options", ED_GIZMO_DIAL_DRAW_FLAG_FILL_SELECT); + WM_gizmo_set_flag(axis, WM_GIZMO_DRAW_MODAL, true); + WM_gizmo_set_scale(axis, 0.2f); + + /* Prevent axis gizmos overlapping the center point, see: T63744. */ + axis->select_bias = 2.0f; + break; + case MAN_AXIS_SCALE_C: + RNA_enum_set(axis->ptr, "draw_options", ED_GIZMO_DIAL_DRAW_FLAG_FILL_SELECT); + + /* Use 1/6 since this is '0.2' if the main scale is 1.2. */ + RNA_float_set(axis->ptr, "arc_inner_factor", 1.0 / 6.0); + WM_gizmo_set_scale(axis, 1.2f); + + /* Prevent axis gizmos overlapping the center point, see: T63744. */ + axis->select_bias = -2.0f; + break; + + case MAN_AXIS_ROT_X: + case MAN_AXIS_ROT_Y: + case MAN_AXIS_ROT_Z: + RNA_enum_set(axis->ptr, "draw_options", ED_GIZMO_DIAL_DRAW_FLAG_CLIP); + WM_gizmo_set_flag(axis, WM_GIZMO_DRAW_VALUE, true); + break; + case MAN_AXIS_ROT_C: + RNA_enum_set(axis->ptr, "draw_options", ED_GIZMO_DIAL_DRAW_FLAG_NOP); + WM_gizmo_set_flag(axis, WM_GIZMO_DRAW_VALUE, true); + WM_gizmo_set_scale(axis, 1.2f); + break; + case MAN_AXIS_ROT_T: + RNA_enum_set(axis->ptr, "draw_options", ED_GIZMO_DIAL_DRAW_FLAG_FILL); + WM_gizmo_set_flag(axis, WM_GIZMO_SELECT_BACKGROUND, true); + WM_gizmo_set_flag(axis, WM_GIZMO_DRAW_HOVER, true); + break; + } + + switch (axis_idx) { + case MAN_AXIS_ROT_X: + case MAN_AXIS_ROT_Y: + case MAN_AXIS_ROT_Z: + /* Increased line width for better display. */ + WM_gizmo_set_line_width(axis, GIZMO_AXIS_LINE_WIDTH + 1.0f); + break; + case MAN_AXIS_TRANS_XY: + case MAN_AXIS_TRANS_YZ: + case MAN_AXIS_TRANS_ZX: + case MAN_AXIS_SCALE_XY: + case MAN_AXIS_SCALE_YZ: + case MAN_AXIS_SCALE_ZX: + WM_gizmo_set_line_width(axis, 1.0f); + break; + default: + WM_gizmo_set_line_width(axis, GIZMO_AXIS_LINE_WIDTH); + break; + } + + const short axis_type = gizmo_get_axis_type(axis_idx); + switch (axis_type) { + case MAN_AXES_ROTATE: { + RNA_float_set(axis->ptr, "incremental_angle", 0.0f); + axis->select_bias = 0.0f; + break; + } + default: + break; + } +} + +static void gizmo_3d_setup_draw_from_twtype(wmGizmo *axis, const int axis_idx, const int twtype) +{ + switch (axis_idx) { + case MAN_AXIS_TRANS_X: + case MAN_AXIS_TRANS_Y: + case MAN_AXIS_TRANS_Z: + RNA_enum_set(axis->ptr, + "draw_options", + (twtype & (V3D_GIZMO_SHOW_OBJECT_ROTATE | V3D_GIZMO_SHOW_OBJECT_SCALE)) ? + 0 : + ED_GIZMO_ARROW_DRAW_FLAG_STEM); + break; + default: + break; + } + + const short axis_type = gizmo_get_axis_type(axis_idx); + switch (axis_idx) { + case MAN_AXIS_TRANS_X: + case MAN_AXIS_TRANS_Y: + case MAN_AXIS_TRANS_Z: + case MAN_AXIS_SCALE_X: + case MAN_AXIS_SCALE_Y: + case MAN_AXIS_SCALE_Z: { + float start_co[3] = {0.0f, 0.0f, 0.0f}; + float end; + gizmo_line_range(twtype, axis_type, &start_co[2], &end); + + WM_gizmo_set_matrix_offset_location(axis, start_co); + RNA_float_set(axis->ptr, "length", end - start_co[2]); + WM_gizmo_set_flag(axis, WM_GIZMO_DRAW_OFFSET_SCALE, true); + break; + } + default: + break; + } + + switch (axis_type) { + case MAN_AXES_ROTATE: { + if ((twtype & V3D_GIZMO_SHOW_OBJECT_SCALE) && twtype & V3D_GIZMO_SHOW_OBJECT_ROTATE) { + axis->select_bias = -2.0f; + } + } + } +} + +static void gizmo_3d_setup_draw_modal(wmGizmo *axis, const int axis_idx) +{ + const short axis_type = gizmo_get_axis_type(axis_idx); + switch (axis_type) { + case MAN_AXES_ROTATE: { + PropertyRNA *prop = RNA_struct_find_property(axis->ptr, "draw_options"); + const int dial_flag = RNA_property_enum_get(axis->ptr, prop); + RNA_property_enum_set(axis->ptr, prop, dial_flag | ED_GIZMO_DIAL_DRAW_FLAG_ANGLE_VALUE); + break; + } + default: + break; + } +} + static GizmoGroup *gizmogroup_init(wmGizmoGroup *gzgroup) { GizmoGroup *ggd; @@ -1342,65 +1515,57 @@ static GizmoGroup *gizmogroup_init(wmGizmoGroup *gzgroup) wmGizmoPropertyFnParams params = { .value_get_fn = rotation_get_fn, .value_set_fn = rotation_set_fn, .user_data = ggd}; -#define GIZMO_NEW_ARROW(v, draw_style) \ +#define GIZMO_NEW_ARROW(v) \ { \ ggd->gizmos[v] = WM_gizmo_new_ptr(gzt_arrow, gzgroup, NULL); \ - RNA_enum_set(ggd->gizmos[v]->ptr, "draw_style", draw_style); \ } \ ((void)0) -#define GIZMO_NEW_DIAL(v, draw_options) \ +#define GIZMO_NEW_DIAL(v) \ { \ ggd->gizmos[v] = WM_gizmo_new_ptr(gzt_dial, gzgroup, NULL); \ - RNA_enum_set(ggd->gizmos[v]->ptr, "draw_options", draw_options); \ WM_gizmo_target_property_def_func(ggd->gizmos[v], "offset", ¶ms); \ } \ ((void)0) -#define GIZMO_NEW_PRIM(v, draw_style) \ +#define GIZMO_NEW_PRIM(v) \ { \ ggd->gizmos[v] = WM_gizmo_new_ptr(gzt_prim, gzgroup, NULL); \ - RNA_enum_set(ggd->gizmos[v]->ptr, "draw_style", draw_style); \ } \ ((void)0) /* add/init widgets - order matters! */ - GIZMO_NEW_DIAL(MAN_AXIS_ROT_T, ED_GIZMO_DIAL_DRAW_FLAG_FILL); + GIZMO_NEW_DIAL(MAN_AXIS_ROT_T); - GIZMO_NEW_DIAL(MAN_AXIS_SCALE_C, ED_GIZMO_DIAL_DRAW_FLAG_FILL_SELECT); + GIZMO_NEW_DIAL(MAN_AXIS_SCALE_C); - GIZMO_NEW_ARROW(MAN_AXIS_SCALE_X, ED_GIZMO_ARROW_STYLE_BOX); - GIZMO_NEW_ARROW(MAN_AXIS_SCALE_Y, ED_GIZMO_ARROW_STYLE_BOX); - GIZMO_NEW_ARROW(MAN_AXIS_SCALE_Z, ED_GIZMO_ARROW_STYLE_BOX); + GIZMO_NEW_ARROW(MAN_AXIS_SCALE_X); + GIZMO_NEW_ARROW(MAN_AXIS_SCALE_Y); + GIZMO_NEW_ARROW(MAN_AXIS_SCALE_Z); - GIZMO_NEW_PRIM(MAN_AXIS_SCALE_XY, ED_GIZMO_PRIMITIVE_STYLE_PLANE); - GIZMO_NEW_PRIM(MAN_AXIS_SCALE_YZ, ED_GIZMO_PRIMITIVE_STYLE_PLANE); - GIZMO_NEW_PRIM(MAN_AXIS_SCALE_ZX, ED_GIZMO_PRIMITIVE_STYLE_PLANE); + GIZMO_NEW_PRIM(MAN_AXIS_SCALE_XY); + GIZMO_NEW_PRIM(MAN_AXIS_SCALE_YZ); + GIZMO_NEW_PRIM(MAN_AXIS_SCALE_ZX); - GIZMO_NEW_DIAL(MAN_AXIS_ROT_X, ED_GIZMO_DIAL_DRAW_FLAG_CLIP); - GIZMO_NEW_DIAL(MAN_AXIS_ROT_Y, ED_GIZMO_DIAL_DRAW_FLAG_CLIP); - GIZMO_NEW_DIAL(MAN_AXIS_ROT_Z, ED_GIZMO_DIAL_DRAW_FLAG_CLIP); + GIZMO_NEW_DIAL(MAN_AXIS_ROT_X); + GIZMO_NEW_DIAL(MAN_AXIS_ROT_Y); + GIZMO_NEW_DIAL(MAN_AXIS_ROT_Z); /* init screen aligned widget last here, looks better, behaves better */ - GIZMO_NEW_DIAL(MAN_AXIS_ROT_C, ED_GIZMO_DIAL_DRAW_FLAG_NOP); + GIZMO_NEW_DIAL(MAN_AXIS_ROT_C); - GIZMO_NEW_DIAL(MAN_AXIS_TRANS_C, ED_GIZMO_DIAL_DRAW_FLAG_FILL_SELECT); + GIZMO_NEW_DIAL(MAN_AXIS_TRANS_C); - GIZMO_NEW_ARROW(MAN_AXIS_TRANS_X, ED_GIZMO_ARROW_STYLE_NORMAL); - GIZMO_NEW_ARROW(MAN_AXIS_TRANS_Y, ED_GIZMO_ARROW_STYLE_NORMAL); - GIZMO_NEW_ARROW(MAN_AXIS_TRANS_Z, ED_GIZMO_ARROW_STYLE_NORMAL); + GIZMO_NEW_ARROW(MAN_AXIS_TRANS_X); + GIZMO_NEW_ARROW(MAN_AXIS_TRANS_Y); + GIZMO_NEW_ARROW(MAN_AXIS_TRANS_Z); - GIZMO_NEW_PRIM(MAN_AXIS_TRANS_XY, ED_GIZMO_PRIMITIVE_STYLE_PLANE); - GIZMO_NEW_PRIM(MAN_AXIS_TRANS_YZ, ED_GIZMO_PRIMITIVE_STYLE_PLANE); - GIZMO_NEW_PRIM(MAN_AXIS_TRANS_ZX, ED_GIZMO_PRIMITIVE_STYLE_PLANE); + GIZMO_NEW_PRIM(MAN_AXIS_TRANS_XY); + GIZMO_NEW_PRIM(MAN_AXIS_TRANS_YZ); + GIZMO_NEW_PRIM(MAN_AXIS_TRANS_ZX); - ggd->gizmos[MAN_AXIS_ROT_T]->flag |= WM_GIZMO_SELECT_BACKGROUND; - - /* Prevent axis gizmos overlapping the center point, see: T63744. */ - ggd->gizmos[MAN_AXIS_TRANS_C]->select_bias = 2.0f; - - ggd->gizmos[MAN_AXIS_SCALE_C]->select_bias = -2.0f; - - /* Use 1/6 since this is '0.2' if the main scale is 1.2. */ - RNA_float_set(ggd->gizmos[MAN_AXIS_SCALE_C]->ptr, "arc_inner_factor", 1.0 / 6.0); + MAN_ITER_AXES_BEGIN (axis, axis_idx) { + gizmo_3d_setup_draw_default(axis, axis_idx); + } + MAN_ITER_AXES_END; return ggd; } @@ -1445,39 +1610,6 @@ static int gizmo_modal(bContext *C, } } else { - GizmoGroup *ggd = gzgroup->customdata; - - short axis_type = 0; - MAN_ITER_AXES_BEGIN (axis, axis_idx) { - if (axis == widget) { - axis_type = gizmo_get_axis_type(axis_idx); - break; - } - } - MAN_ITER_AXES_END; - - /* Showing axes which aren't being manipulated doesn't always work so well. - * - * For rotate: global axis will reset after finish. - * Also, gimbal axis isn't properly recalculated while transforming. - */ - if (axis_type == MAN_AXES_ROTATE) { - MAN_ITER_AXES_BEGIN (axis, axis_idx) { - if (axis == widget) { - continue; - } - - bool is_plane_dummy; - const uint aidx_norm = gizmo_orientation_axis(axis_idx, &is_plane_dummy); - /* Always show the axis-aligned handle as it's distracting when it's disabled. */ - if (aidx_norm == 3) { - continue; - } - WM_gizmo_set_flag(axis, WM_GIZMO_HIDDEN, true); - } - MAN_ITER_AXES_END; - } - wmWindow *win = CTX_wm_window(C); wmOperator *op = NULL; for (int i = 0; i < widget->op_data_len; i++) { @@ -1489,6 +1621,10 @@ static int gizmo_modal(bContext *C, } if (op != NULL) { + GizmoGroup *ggd = gzgroup->customdata; + const int axis_idx = BLI_array_findindex(ggd->gizmos, ARRAY_SIZE(ggd->gizmos), &widget); + const short axis_type = gizmo_get_axis_type(axis_idx); + float twmat[4][4]; float scale_buf[3]; float *scale = NULL; @@ -1513,7 +1649,7 @@ static int gizmo_modal(bContext *C, } if (update) { - gizmo_refresh_from_matrix(gzgroup, twmat, scale); + gizmogroup_refresh_from_matrix(gzgroup, twmat, scale, true); ED_region_tag_redraw_editor_overlays(region); } } @@ -1539,62 +1675,7 @@ static void gizmogroup_init_properties_from_twtype(wmGizmoGroup *gzgroup) /* custom handler! */ WM_gizmo_set_fn_custom_modal(axis, gizmo_modal); - switch (axis_idx) { - case MAN_AXIS_TRANS_X: - case MAN_AXIS_TRANS_Y: - case MAN_AXIS_TRANS_Z: - case MAN_AXIS_SCALE_X: - case MAN_AXIS_SCALE_Y: - case MAN_AXIS_SCALE_Z: - if (axis_idx >= MAN_AXIS_RANGE_TRANS_START && axis_idx < MAN_AXIS_RANGE_TRANS_END) { - int draw_options = 0; - if ((ggd->twtype & (V3D_GIZMO_SHOW_OBJECT_ROTATE | V3D_GIZMO_SHOW_OBJECT_SCALE)) == 0) { - draw_options |= ED_GIZMO_ARROW_DRAW_FLAG_STEM; - } - RNA_enum_set(axis->ptr, "draw_options", draw_options); - } - - WM_gizmo_set_line_width(axis, GIZMO_AXIS_LINE_WIDTH); - break; - case MAN_AXIS_ROT_X: - case MAN_AXIS_ROT_Y: - case MAN_AXIS_ROT_Z: - /* increased line width for better display */ - WM_gizmo_set_line_width(axis, GIZMO_AXIS_LINE_WIDTH + 1.0f); - WM_gizmo_set_flag(axis, WM_GIZMO_DRAW_VALUE, true); - break; - case MAN_AXIS_TRANS_XY: - case MAN_AXIS_TRANS_YZ: - case MAN_AXIS_TRANS_ZX: - case MAN_AXIS_SCALE_XY: - case MAN_AXIS_SCALE_YZ: - case MAN_AXIS_SCALE_ZX: { - const float ofs[3] = {MAN_AXIS_SCALE_PLANE_OFFSET, MAN_AXIS_SCALE_PLANE_OFFSET, 0.0f}; - WM_gizmo_set_scale(axis, MAN_AXIS_SCALE_PLANE_SCALE); - WM_gizmo_set_matrix_offset_location(axis, ofs); - WM_gizmo_set_flag(axis, WM_GIZMO_DRAW_OFFSET_SCALE, true); - break; - } - case MAN_AXIS_TRANS_C: - case MAN_AXIS_ROT_C: - case MAN_AXIS_SCALE_C: - case MAN_AXIS_ROT_T: - WM_gizmo_set_line_width(axis, GIZMO_AXIS_LINE_WIDTH); - if (axis_idx == MAN_AXIS_ROT_T) { - WM_gizmo_set_flag(axis, WM_GIZMO_DRAW_HOVER, true); - } - else if (axis_idx == MAN_AXIS_ROT_C) { - WM_gizmo_set_flag(axis, WM_GIZMO_DRAW_VALUE, true); - WM_gizmo_set_scale(axis, 1.2f); - } - else if (axis_idx == MAN_AXIS_SCALE_C) { - WM_gizmo_set_scale(axis, 1.2f); - } - else { - WM_gizmo_set_scale(axis, 0.2f); - } - break; - } + gizmo_3d_setup_draw_from_twtype(axis, axis_idx, ggd->twtype); switch (axis_type) { case MAN_AXES_TRANSLATE: @@ -1688,111 +1769,87 @@ static void WIDGETGROUP_gizmo_setup(const bContext *C, wmGizmoGroup *gzgroup) * \param twmat: The transform matrix (typically #RegionView3D.twmat). * \param scale: Optional scale, to show scale while modally dragging the scale handles. */ -static void gizmo_refresh_from_matrix(wmGizmoGroup *gzgroup, +static void gizmo_refresh_from_matrix(wmGizmo *axis, + const int axis_idx, + const int twtype, const float twmat[4][4], const float scale[3]) +{ + const short axis_type = gizmo_get_axis_type(axis_idx); + const int aidx_norm = gizmo_orientation_axis(axis_idx, NULL); + + WM_gizmo_set_matrix_location(axis, twmat[3]); + switch (axis_idx) { + case MAN_AXIS_TRANS_X: + case MAN_AXIS_TRANS_Y: + case MAN_AXIS_TRANS_Z: + case MAN_AXIS_SCALE_X: + case MAN_AXIS_SCALE_Y: + case MAN_AXIS_SCALE_Z: { + const float *z_axis = twmat[aidx_norm]; + if (axis_type == MAN_AXES_SCALE) { + /* Scale handles are cubes that don't look right when not aligned with other axes. + * This is noticeable when the axis is rotated to something besides the global-axis. */ + const int aidx_norm_y = (aidx_norm + 2) % 3; + const float *y_axis = twmat[aidx_norm_y]; + WM_gizmo_set_matrix_rotation_from_yz_axis(axis, y_axis, z_axis); + + if (scale) { + float start, end; + gizmo_line_range(twtype, axis_type, &start, &end); + RNA_float_set(axis->ptr, "length", (end * scale[aidx_norm]) - start); + } + } + else { + WM_gizmo_set_matrix_rotation_from_z_axis(axis, z_axis); + } + + break; + } + case MAN_AXIS_ROT_X: + case MAN_AXIS_ROT_Y: + case MAN_AXIS_ROT_Z: + WM_gizmo_set_matrix_rotation_from_z_axis(axis, twmat[aidx_norm]); + break; + case MAN_AXIS_TRANS_XY: + case MAN_AXIS_TRANS_YZ: + case MAN_AXIS_TRANS_ZX: + case MAN_AXIS_SCALE_XY: + case MAN_AXIS_SCALE_YZ: + case MAN_AXIS_SCALE_ZX: { + const int aidx_norm_x = (aidx_norm + 1) % 3; + const int aidx_norm_y = (aidx_norm + 2) % 3; + const float *y_axis = twmat[aidx_norm_y]; + const float *z_axis = twmat[aidx_norm]; + WM_gizmo_set_matrix_rotation_from_yz_axis(axis, y_axis, z_axis); + + if (axis_type == MAN_AXES_SCALE) { + float ofs[3] = {MAN_AXIS_SCALE_PLANE_OFFSET, MAN_AXIS_SCALE_PLANE_OFFSET, 0.0f}; + if (scale) { + ofs[0] *= scale[aidx_norm_x]; + ofs[1] *= scale[aidx_norm_y]; + } + WM_gizmo_set_matrix_offset_location(axis, ofs); + } + break; + } + } +} + +static void gizmogroup_refresh_from_matrix(wmGizmoGroup *gzgroup, + const float twmat[4][4], + const float scale[3], + const bool ignore_hidden) { GizmoGroup *ggd = gzgroup->customdata; MAN_ITER_AXES_BEGIN (axis, axis_idx) { - const short axis_type = gizmo_get_axis_type(axis_idx); - const int aidx_norm = gizmo_orientation_axis(axis_idx, NULL); - - WM_gizmo_set_matrix_location(axis, twmat[3]); - switch (axis_idx) { - case MAN_AXIS_TRANS_X: - case MAN_AXIS_TRANS_Y: - case MAN_AXIS_TRANS_Z: - case MAN_AXIS_SCALE_X: - case MAN_AXIS_SCALE_Y: - case MAN_AXIS_SCALE_Z: { - float start_co[3] = {0.0f, 0.0f, 0.0f}; - float len; - - gizmo_line_range(ggd->twtype, axis_type, &start_co[2], &len); - - const float *z_axis = twmat[aidx_norm]; - if (axis_type == MAN_AXES_SCALE) { - /* Scale handles are cubes that don't look right when not aligned with other axes. - * This is noticeable when the axis is rotated to something besides the global-axis. */ - const int aidx_norm_y = (aidx_norm + 2) % 3; - const float *y_axis = twmat[aidx_norm_y]; - WM_gizmo_set_matrix_rotation_from_yz_axis(axis, y_axis, z_axis); - } - else { - WM_gizmo_set_matrix_rotation_from_z_axis(axis, z_axis); - } - - if (axis_idx >= MAN_AXIS_RANGE_TRANS_START && axis_idx < MAN_AXIS_RANGE_TRANS_END) { - if (ggd->twtype & V3D_GIZMO_SHOW_OBJECT_ROTATE) { - /* Avoid rotate and translate arrows overlap. */ - start_co[2] += 0.215f; - } - } - - if (scale) { - if (axis_type == MAN_AXES_SCALE) { - len = ((start_co[2] + len) * scale[aidx_norm]) - start_co[2]; - } - } - - RNA_float_set(axis->ptr, "length", len); - - WM_gizmo_set_matrix_offset_location(axis, start_co); - - WM_gizmo_set_flag(axis, WM_GIZMO_DRAW_OFFSET_SCALE, true); - - break; - } - case MAN_AXIS_ROT_X: - case MAN_AXIS_ROT_Y: - case MAN_AXIS_ROT_Z: - case MAN_AXIS_ROT_C: { - if (axis_idx != MAN_AXIS_ROT_C) { - WM_gizmo_set_matrix_rotation_from_z_axis(axis, twmat[aidx_norm]); - } - - /* Remove #ED_GIZMO_DIAL_DRAW_FLAG_ANGLE_VALUE. It is used only for modal drawing. */ - PropertyRNA *prop = RNA_struct_find_property(axis->ptr, "draw_options"); - RNA_property_enum_set(axis->ptr, - prop, - RNA_property_enum_get(axis->ptr, prop) & - ~ED_GIZMO_DIAL_DRAW_FLAG_ANGLE_VALUE); - } break; - case MAN_AXIS_TRANS_XY: - case MAN_AXIS_TRANS_YZ: - case MAN_AXIS_TRANS_ZX: - case MAN_AXIS_SCALE_XY: - case MAN_AXIS_SCALE_YZ: - case MAN_AXIS_SCALE_ZX: { - const int aidx_norm_x = (aidx_norm + 1) % 3; - const int aidx_norm_y = (aidx_norm + 2) % 3; - const float *y_axis = twmat[aidx_norm_y]; - const float *z_axis = twmat[aidx_norm]; - WM_gizmo_set_matrix_rotation_from_yz_axis(axis, y_axis, z_axis); - - if (axis_type == MAN_AXES_SCALE) { - float ofs[3] = {MAN_AXIS_SCALE_PLANE_OFFSET, MAN_AXIS_SCALE_PLANE_OFFSET, 0.0f}; - if (scale) { - ofs[0] *= scale[aidx_norm_x]; - ofs[1] *= scale[aidx_norm_y]; - } - WM_gizmo_set_matrix_offset_location(axis, ofs); - } - break; - } + if (ignore_hidden && axis->flag & WM_GIZMO_HIDDEN) { + continue; } + gizmo_refresh_from_matrix(axis, axis_idx, ggd->twtype, twmat, scale); } MAN_ITER_AXES_END; - - /* Ensure rotate disks don't overlap scale arrows, especially in ortho view. */ - float rotate_select_bias = 0.0f; - if ((ggd->twtype & V3D_GIZMO_SHOW_OBJECT_SCALE) && ggd->twtype & V3D_GIZMO_SHOW_OBJECT_ROTATE) { - rotate_select_bias = -2.0f; - } - for (int i = MAN_AXIS_RANGE_ROT_START; i < MAN_AXIS_RANGE_ROT_END; i++) { - ggd->gizmos[i]->select_bias = rotate_select_bias; - } } static void WIDGETGROUP_gizmo_refresh(const bContext *C, wmGizmoGroup *gzgroup) @@ -1835,7 +1892,7 @@ static void WIDGETGROUP_gizmo_refresh(const bContext *C, wmGizmoGroup *gzgroup) gizmo_prepare_mat(C, rv3d, &tbounds); - gizmo_refresh_from_matrix(gzgroup, rv3d->twmat, NULL); + gizmogroup_refresh_from_matrix(gzgroup, rv3d->twmat, NULL, false); } static void WIDGETGROUP_gizmo_message_subscribe(const bContext *C, @@ -1850,6 +1907,14 @@ static void WIDGETGROUP_gizmo_message_subscribe(const bContext *C, gzgroup, mbus, scene, screen, area, region, VIEW3D_GGT_xform_gizmo); } +static void gizmogroup_hide_all(GizmoGroup *ggd) +{ + MAN_ITER_AXES_BEGIN (axis, axis_idx) { + WM_gizmo_set_flag(axis, WM_GIZMO_HIDDEN, true); + } + MAN_ITER_AXES_END; +} + static void WIDGETGROUP_gizmo_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup) { GizmoGroup *ggd = gzgroup->customdata; @@ -1873,38 +1938,32 @@ static void WIDGETGROUP_gizmo_draw_prepare(const bContext *C, wmGizmoGroup *gzgr /* when looking through a selected camera, the gizmo can be at the * exact same position as the view, skip so we don't break selection */ if (ggd->all_hidden || fabsf(ED_view3d_pixel_size(rv3d, rv3d->twmat[3])) < 5e-7f) { - MAN_ITER_AXES_BEGIN (axis, axis_idx) { - if (!is_modal) { - WM_gizmo_set_flag(axis, WM_GIZMO_HIDDEN, true); - } + if (!is_modal) { + gizmogroup_hide_all(ggd); } - MAN_ITER_AXES_END; return; } gizmo_get_idot(rv3d, idot); /* *** set properties for axes *** */ MAN_ITER_AXES_BEGIN (axis, axis_idx) { - const short axis_type = gizmo_get_axis_type(axis_idx); - /* XXX maybe unset _HIDDEN flag on redraw? */ - if (gizmo_is_axis_visible(rv3d, ggd->twtype, idot, axis_type, axis_idx)) { - if (!is_modal) { - WM_gizmo_set_flag(axis, WM_GIZMO_HIDDEN, false); + if (is_modal) { + if (axis->flag & WM_GIZMO_HIDDEN) { + continue; } } else { - if (!is_modal) { - WM_gizmo_set_flag(axis, WM_GIZMO_HIDDEN, true); + const short axis_type = gizmo_get_axis_type(axis_idx); + if (gizmo_is_axis_visible(rv3d, ggd->twtype, idot, axis_type, axis_idx)) { + /* XXX maybe unset _HIDDEN flag on redraw? */ + WM_gizmo_set_flag(axis, WM_GIZMO_HIDDEN, false); + } + else { + WM_gizmo_set_flag(axis, WM_GIZMO_HIDDEN, true); + continue; } - continue; - } - float color[4], color_hi[4]; - gizmo_get_axis_color(axis_idx, idot, color, color_hi); - WM_gizmo_set_color(axis, color); - WM_gizmo_set_color_highlight(axis, color_hi); - - if (!is_modal) { + /* Align to view. */ switch (axis_idx) { case MAN_AXIS_TRANS_C: case MAN_AXIS_ROT_C: @@ -1914,6 +1973,11 @@ static void WIDGETGROUP_gizmo_draw_prepare(const bContext *C, wmGizmoGroup *gzgr break; } } + + float color[4], color_hi[4]; + gizmo_get_axis_color(axis_idx, idot, color, color_hi); + WM_gizmo_set_color(axis, color); + WM_gizmo_set_color_highlight(axis, color_hi); } MAN_ITER_AXES_END; @@ -1934,13 +1998,59 @@ static void WIDGETGROUP_gizmo_draw_prepare(const bContext *C, wmGizmoGroup *gzgr } } +static void gizmo_3d_draw_invoke(wmGizmoGroup *gzgroup, + const ARegion *region, + const int axis_idx_active, + const float mval[2]) +{ + GizmoGroup *ggd = gzgroup->customdata; + const RegionView3D *rv3d = region->regiondata; + + struct wmGizmo *axis_active = ggd->gizmos[axis_idx_active]; + + const short axis_active_type = gizmo_get_axis_type(axis_idx_active); + if (axis_active_type == MAN_AXES_ROTATE) { + /* Hide other gizmos except the active one and #MAN_AXIS_ROT_C. + * #MAN_AXIS_ROT_C was displayed before and remains visible by convention. */ + gizmogroup_hide_all(ggd); + WM_gizmo_set_flag(axis_active, WM_GIZMO_HIDDEN, false); + WM_gizmo_set_flag(ggd->gizmos[MAN_AXIS_ROT_C], WM_GIZMO_HIDDEN, false); + } + + MAN_ITER_AXES_BEGIN (axis, axis_idx) { + if (axis->flag & WM_GIZMO_HIDDEN) { + continue; + } + gizmo_refresh_from_matrix(axis, axis_idx, ggd->twtype, rv3d->twmat, NULL); + + if (ELEM(axis_idx, MAN_AXIS_TRANS_C, MAN_AXIS_SCALE_C, MAN_AXIS_ROT_C, MAN_AXIS_ROT_T)) { + WM_gizmo_set_matrix_rotation_from_z_axis(axis, rv3d->viewinv[2]); + } + + if (axis == axis_active) { + if (axis_active_type == MAN_AXES_ROTATE) { + gizmo_3d_dial_matrixbasis_calc(region, + axis_active->matrix_basis[2], + axis_active->matrix_basis[3], + mval, + axis_active->matrix_basis); + } + + gizmo_3d_setup_draw_modal(axis_active, axis_idx); + } + } + MAN_ITER_AXES_END; +} + static void WIDGETGROUP_gizmo_invoke_prepare(const bContext *C, wmGizmoGroup *gzgroup, wmGizmo *gz, const wmEvent *event) { - GizmoGroup *ggd = gzgroup->customdata; + const int axis_idx = BLI_array_findindex(ggd->gizmos, ARRAY_SIZE(ggd->gizmos), &gz); + + gizmo_3d_draw_invoke(gzgroup, CTX_wm_region(C), axis_idx, (float[2]){UNPACK2(event->mval)}); /* Support gizmo specific orientation. */ if (gz != ggd->gizmos[MAN_AXIS_ROT_T]) { @@ -1964,7 +2074,6 @@ static void WIDGETGROUP_gizmo_invoke_prepare(const bContext *C, } /* Support shift click to constrain axis. */ - const int axis_idx = BLI_array_findindex(ggd->gizmos, ARRAY_SIZE(ggd->gizmos), &gz); int axis = -1; switch (axis_idx) { case MAN_AXIS_TRANS_X: @@ -2000,17 +2109,6 @@ static void WIDGETGROUP_gizmo_invoke_prepare(const bContext *C, } } } - else if (ELEM(axis_idx, MAN_AXIS_ROT_X, MAN_AXIS_ROT_Y, MAN_AXIS_ROT_Z, MAN_AXIS_ROT_C)) { - gizmo_3d_dial_matrixbasis_calc(CTX_wm_region(C), - gz->matrix_basis[2], - gz->matrix_basis[3], - (float[2]){UNPACK2(event->mval)}, - gz->matrix_basis); - PropertyRNA *prop = RNA_struct_find_property(gz->ptr, "draw_options"); - RNA_property_enum_set( - gz->ptr, prop, RNA_property_enum_get(gz->ptr, prop) | ED_GIZMO_DIAL_DRAW_FLAG_ANGLE_VALUE); - RNA_float_set(gz->ptr, "incremental_angle", 0.0f); - } } static bool WIDGETGROUP_gizmo_poll_generic(View3D *v3d) @@ -2103,6 +2201,8 @@ void VIEW3D_GGT_xform_gizmo(wmGizmoGroupType *gzgt) V3D_GIZMO_SHOW_OBJECT_TRANSLATE, "Drag Action", ""); + + g_GGT_xform_gizmo = gzgt; } void VIEW3D_GGT_xform_gizmo_context(wmGizmoGroupType *gzgt) @@ -2120,6 +2220,8 @@ void VIEW3D_GGT_xform_gizmo_context(wmGizmoGroupType *gzgt) gzgt->message_subscribe = WIDGETGROUP_gizmo_message_subscribe; gzgt->draw_prepare = WIDGETGROUP_gizmo_draw_prepare; gzgt->invoke_prepare = WIDGETGROUP_gizmo_invoke_prepare; + + g_GGT_xform_gizmo_context = gzgt; } /** \} */ @@ -2558,3 +2660,151 @@ void VIEW3D_GGT_xform_shear(wmGizmoGroupType *gzgt) } /** \} */ + +static wmGizmoGroup *gizmogroup_xform_find(TransInfo *t) +{ + wmGizmo *gizmo_modal_current = WM_gizmomap_get_modal(t->region->gizmo_map); + if (gizmo_modal_current) { + wmGizmoGroup *gzgroup = gizmo_modal_current->parent_gzgroup; + /* Check #wmGizmoGroup::customdata to make sure the GizmoGroup has been initialized. */ + if (gzgroup->customdata && ELEM(gzgroup->type, g_GGT_xform_gizmo, g_GGT_xform_gizmo_context)) { + return gzgroup; + } + } + else { + /* See #WM_gizmomap_group_find_ptr. */ + LISTBASE_FOREACH (wmGizmoGroup *, gzgroup, WM_gizmomap_group_list(t->region->gizmo_map)) { + if (ELEM(gzgroup->type, g_GGT_xform_gizmo, g_GGT_xform_gizmo_context)) { + /* Choose the one that has been initialized. */ + if (gzgroup->customdata) { + return gzgroup; + } + } + } + } + + return NULL; +} + +void transform_gizmo_3d_model_from_constraint_and_mode_init(TransInfo *t) +{ + wmGizmo *gizmo_modal_current = WM_gizmomap_get_modal(t->region->gizmo_map); + if (!gizmo_modal_current || !ELEM(gizmo_modal_current->parent_gzgroup->type, + g_GGT_xform_gizmo, + g_GGT_xform_gizmo_context)) { + t->flag |= T_NO_GIZMO; + } +} + +void transform_gizmo_3d_model_from_constraint_and_mode_set(TransInfo *t) +{ + if (t->flag & T_NO_GIZMO) { + return; + } + + wmGizmoGroup *gzgroup_xform = gizmogroup_xform_find(t); + if (gzgroup_xform == NULL) { + return; + } + + int axis_idx = -1; + if (t->mode == TFM_TRACKBALL) { + axis_idx = MAN_AXIS_ROT_T; + } + else if (ELEM(t->mode, TFM_TRANSLATION, TFM_ROTATION, TFM_RESIZE)) { + const int axis_map[3][7] = { + {MAN_AXIS_TRANS_X, + MAN_AXIS_TRANS_Y, + MAN_AXIS_TRANS_XY, + MAN_AXIS_TRANS_Z, + MAN_AXIS_TRANS_ZX, + MAN_AXIS_TRANS_YZ, + MAN_AXIS_TRANS_C}, + {MAN_AXIS_ROT_X, + MAN_AXIS_ROT_Y, + MAN_AXIS_ROT_Z, + MAN_AXIS_ROT_Z, + MAN_AXIS_ROT_Y, + MAN_AXIS_ROT_X, + MAN_AXIS_ROT_C}, + {MAN_AXIS_SCALE_X, + MAN_AXIS_SCALE_Y, + MAN_AXIS_SCALE_XY, + MAN_AXIS_SCALE_Z, + MAN_AXIS_SCALE_ZX, + MAN_AXIS_SCALE_YZ, + MAN_AXIS_SCALE_C}, + }; + + BLI_STATIC_ASSERT( + /* Assert mode values. */ + ((TFM_ROTATION == TFM_TRANSLATION + 1) && (TFM_RESIZE == TFM_TRANSLATION + 2) && + /* Assert constrain values. */ + (CON_AXIS0 == (1 << 1)) && (CON_AXIS1 == (1 << 2)) && (CON_AXIS2 == (1 << 3))), + ""); + + const int trans_mode = t->mode - TFM_TRANSLATION; + int con_mode = ((CON_AXIS0 | CON_AXIS1 | CON_AXIS2) >> 1) - 1; + if (t->con.mode & CON_APPLY) { + con_mode = ((t->con.mode & (CON_AXIS0 | CON_AXIS1 | CON_AXIS2)) >> 1) - 1; + } + + axis_idx = axis_map[trans_mode][con_mode]; + } + + wmGizmo *gizmo_modal_current = WM_gizmomap_get_modal(t->region->gizmo_map); + if (axis_idx != -1) { + RegionView3D *rv3d = t->region->regiondata; + bool update_orientation = !(equals_v3v3(rv3d->twmat[0], t->spacemtx[0]) && + equals_v3v3(rv3d->twmat[1], t->spacemtx[1]) && + equals_v3v3(rv3d->twmat[2], t->spacemtx[2])); + + GizmoGroup *ggd = gzgroup_xform->customdata; + wmGizmo *gizmo_expected = ggd->gizmos[axis_idx]; + if (update_orientation || gizmo_modal_current != gizmo_expected) { + if (update_orientation) { + copy_m4_m3(rv3d->twmat, t->spacemtx); + copy_v3_v3(rv3d->twmat[3], t->center_global); + } + + wmEvent event = {NULL}; + + /* Set the initial mouse value. Used for rotation gizmos. */ + copy_v2_v2_int(event.mval, t->mouse.imval); + + /* We need to update the position of the gizmo before invoking otherwise + * #wmGizmo::scale_final could be calculated wrong. */ + gizmo_refresh_from_matrix(gizmo_expected, axis_idx, ggd->twtype, rv3d->twmat, NULL); + + BLI_assert_msg(!gizmo_modal_current || gizmo_modal_current->highlight_part == 0, + "Avoid changing the highlight part"); + gizmo_expected->highlight_part = 0; + WM_gizmo_modal_set_while_modal(t->region->gizmo_map, t->context, gizmo_expected, &event); + WM_gizmo_highlight_set(t->region->gizmo_map, gizmo_expected); + } + } + else if (gizmo_modal_current) { + WM_gizmo_modal_set_while_modal(t->region->gizmo_map, t->context, NULL, NULL); + } +} + +void transform_gizmo_3d_model_from_constraint_and_mode_restore(TransInfo *t) +{ + if (t->flag & T_NO_GIZMO) { + return; + } + + wmGizmoGroup *gzgroup_xform = gizmogroup_xform_find(t); + if (gzgroup_xform == NULL) { + return; + } + + GizmoGroup *ggd = gzgroup_xform->customdata; + + /* #wmGizmoGroup::draw_prepare will handle the rest. */ + MAN_ITER_AXES_BEGIN (axis, axis_idx) { + gizmo_3d_setup_draw_default(axis, axis_idx); + gizmo_3d_setup_draw_from_twtype(axis, axis_idx, ggd->twtype); + } + MAN_ITER_AXES_END; +} diff --git a/source/blender/editors/transform/transform_mode.c b/source/blender/editors/transform/transform_mode.c index 95d2b4d6439..d76dba63262 100644 --- a/source/blender/editors/transform/transform_mode.c +++ b/source/blender/editors/transform/transform_mode.c @@ -1215,6 +1215,8 @@ void transform_mode_init(TransInfo *t, wmOperator *op, const int mode) transform_convert_mesh_customdatacorrect_init(t); } + transform_gizmo_3d_model_from_constraint_and_mode_set(t); + /* TODO(@germano): Some of these operations change the `t->mode`. * This can be bad for Redo. */ // BLI_assert(t->mode == mode); diff --git a/source/blender/windowmanager/gizmo/WM_gizmo_api.h b/source/blender/windowmanager/gizmo/WM_gizmo_api.h index 663c8a0baed..608b87b0bf6 100644 --- a/source/blender/windowmanager/gizmo/WM_gizmo_api.h +++ b/source/blender/windowmanager/gizmo/WM_gizmo_api.h @@ -87,6 +87,16 @@ void WM_gizmo_modal_set_from_setup(struct wmGizmoMap *gzmap, int part_index, const struct wmEvent *event); +/** + * Replaces the current gizmo modal. + * The substitute gizmo start out interactive. + * It is similar to #WM_gizmo_modal_set_from_setup but without operator initialization. + */ +void WM_gizmo_modal_set_while_modal(struct wmGizmoMap *gzmap, + struct bContext *C, + struct wmGizmo *gz, + const struct wmEvent *event); + struct wmGizmoOpElem *WM_gizmo_operator_get(struct wmGizmo *gz, int part_index); struct PointerRNA *WM_gizmo_operator_set(struct wmGizmo *gz, int part_index, diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo.c index 7853c7096dd..9d212798f39 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo.c @@ -430,6 +430,26 @@ void WM_gizmo_modal_set_from_setup(struct wmGizmoMap *gzmap, } } +void WM_gizmo_modal_set_while_modal(struct wmGizmoMap *gzmap, + struct bContext *C, + struct wmGizmo *gz, + const wmEvent *event) +{ + if (gzmap->gzmap_context.modal) { + wm_gizmomap_modal_set(gzmap, C, gzmap->gzmap_context.modal, event, false); + } + + if (gz) { + wm_gizmo_calculate_scale(gz, C); + + /* Set `highlight_part` to -1 to skip operator invocation. */ + const int highlight_part = gz->highlight_part; + gz->highlight_part = -1; + wm_gizmomap_modal_set(gzmap, C, gz, event, true); + gz->highlight_part = highlight_part; + } +} + void wm_gizmo_calculate_scale(wmGizmo *gz, const bContext *C) { const RegionView3D *rv3d = CTX_wm_region_view3d(C);