Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/transform/transform_gizmo_loc_rot_scale_3d.c
- This file was added.
| /* | |||||
| * This program is free software; you can redistribute it and/or | |||||
| * modify it under the terms of the GNU General Public License | |||||
| * as published by the Free Software Foundation; either version 2 | |||||
| * of the License, or (at your option) any later version. | |||||
| * | |||||
| * This program is distributed in the hope that it will be useful, | |||||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
| * GNU General Public License for more details. | |||||
| * | |||||
| * You should have received a copy of the GNU General Public License | |||||
| * along with this program; if not, write to the Free Software Foundation, | |||||
| * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||||
| */ | |||||
| /** \file | |||||
| * \ingroup edtransform | |||||
| * | |||||
| * \name 3D Transform Gizmo | |||||
| * | |||||
| * Used for 3D View | |||||
| */ | |||||
| #include <float.h> | |||||
| #include "BLI_array_utils.h" | |||||
| #include "BLI_math.h" | |||||
| #include "BKE_context.h" | |||||
| #include "BKE_global.h" | |||||
| #include "BKE_scene.h" | |||||
| #include "WM_api.h" | |||||
| #include "ED_gizmo_library.h" | |||||
| #include "ED_gizmo_utils.h" | |||||
| #include "ED_screen.h" | |||||
| #include "RNA_access.h" | |||||
| #include "RNA_define.h" | |||||
| #include "MEM_guardedalloc.h" | |||||
| /* local module include */ | |||||
| #include "transform.h" | |||||
| #include "transform_gizmo.h" | |||||
| typedef struct GizmoGroup { | |||||
| bool all_hidden; | |||||
| int twtype; | |||||
| /* Users may change the twtype, detect changes to re-setup gizmo options. */ | |||||
| int twtype_init; | |||||
| int twtype_prev; | |||||
| int use_twtype_refresh; | |||||
| /* Only for view orientation. */ | |||||
| struct { | |||||
| float viewinv_m3[3][3]; | |||||
| } prev; | |||||
| struct wmGizmo *gizmos[MAN_AXIS_LAST]; | |||||
| } GizmoGroup; | |||||
| /* -------------------------------------------------------------------- */ | |||||
| /** \name Utilities | |||||
| * \{ */ | |||||
| /* loop over axes */ | |||||
| #define MAN_ITER_AXES_BEGIN(axis, axis_idx) \ | |||||
| { \ | |||||
| wmGizmo *axis; \ | |||||
| int axis_idx; \ | |||||
| for (axis_idx = 0; axis_idx < MAN_AXIS_LAST; axis_idx++) { \ | |||||
| axis = gizmo_get_axis_from_index(ggd, axis_idx); | |||||
| #define MAN_ITER_AXES_END \ | |||||
| } \ | |||||
| } \ | |||||
| ((void)0) | |||||
| static wmGizmo *gizmo_get_axis_from_index(const GizmoGroup *ggd, const short axis_idx) | |||||
| { | |||||
| BLI_assert(IN_RANGE_INCL(axis_idx, (float)MAN_AXIS_TRANS_X, (float)MAN_AXIS_LAST)); | |||||
| return ggd->gizmos[axis_idx]; | |||||
| } | |||||
| static short gizmo_get_axis_type(const int axis_idx) | |||||
| { | |||||
| if (axis_idx >= MAN_AXIS_RANGE_TRANS_START && axis_idx < MAN_AXIS_RANGE_TRANS_END) { | |||||
| return MAN_AXES_TRANSLATE; | |||||
| } | |||||
| if (axis_idx >= MAN_AXIS_RANGE_ROT_START && axis_idx < MAN_AXIS_RANGE_ROT_END) { | |||||
| return MAN_AXES_ROTATE; | |||||
| } | |||||
| if (axis_idx >= MAN_AXIS_RANGE_SCALE_START && axis_idx < MAN_AXIS_RANGE_SCALE_END) { | |||||
| return MAN_AXES_SCALE; | |||||
| } | |||||
| BLI_assert(0); | |||||
| return -1; | |||||
| } | |||||
| static void gizmo_get_axis_constraint(const int axis_idx, bool r_axis[3]) | |||||
| { | |||||
| ARRAY_SET_ITEMS(r_axis, 0, 0, 0); | |||||
| switch (axis_idx) { | |||||
| case MAN_AXIS_TRANS_X: | |||||
| case MAN_AXIS_ROT_X: | |||||
| case MAN_AXIS_SCALE_X: | |||||
| r_axis[0] = 1; | |||||
| break; | |||||
| case MAN_AXIS_TRANS_Y: | |||||
| case MAN_AXIS_ROT_Y: | |||||
| case MAN_AXIS_SCALE_Y: | |||||
| r_axis[1] = 1; | |||||
| break; | |||||
| case MAN_AXIS_TRANS_Z: | |||||
| case MAN_AXIS_ROT_Z: | |||||
| case MAN_AXIS_SCALE_Z: | |||||
| r_axis[2] = 1; | |||||
| break; | |||||
| case MAN_AXIS_TRANS_XY: | |||||
| case MAN_AXIS_SCALE_XY: | |||||
| r_axis[0] = r_axis[1] = 1; | |||||
| break; | |||||
| case MAN_AXIS_TRANS_YZ: | |||||
| case MAN_AXIS_SCALE_YZ: | |||||
| r_axis[1] = r_axis[2] = 1; | |||||
| break; | |||||
| case MAN_AXIS_TRANS_ZX: | |||||
| case MAN_AXIS_SCALE_ZX: | |||||
| r_axis[2] = r_axis[0] = 1; | |||||
| break; | |||||
| default: | |||||
| break; | |||||
| } | |||||
| } | |||||
| static void gizmo_get_idot(RegionView3D *rv3d, float r_idot[3]) | |||||
| { | |||||
| float view_vec[3], axis_vec[3]; | |||||
| ED_view3d_global_to_vector(rv3d, rv3d->twmat[3], view_vec); | |||||
| for (int i = 0; i < 3; i++) { | |||||
| normalize_v3_v3(axis_vec, rv3d->twmat[i]); | |||||
| r_idot[i] = 1.0f - fabsf(dot_v3v3(view_vec, axis_vec)); | |||||
| } | |||||
| } | |||||
| /** | |||||
| * 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) | |||||
| { | |||||
| const float ofs = 0.2f; | |||||
| *r_start = 0.2f; | |||||
| *r_len = 1.0f; | |||||
| switch (axis_type) { | |||||
| case MAN_AXES_TRANSLATE: | |||||
| if (twtype & V3D_GIZMO_SHOW_OBJECT_SCALE) { | |||||
| *r_start = *r_len - ofs + 0.075f; | |||||
| } | |||||
| if (twtype & V3D_GIZMO_SHOW_OBJECT_ROTATE) { | |||||
| *r_len += ofs; | |||||
| } | |||||
| break; | |||||
| case MAN_AXES_SCALE: | |||||
| if (twtype & (V3D_GIZMO_SHOW_OBJECT_TRANSLATE | V3D_GIZMO_SHOW_OBJECT_ROTATE)) { | |||||
| *r_len -= ofs + 0.025f; | |||||
| } | |||||
| break; | |||||
| } | |||||
| *r_len -= *r_start; | |||||
| } | |||||
| /** \} */ | |||||
| /* -------------------------------------------------------------------- */ | |||||
| /** \name Transform Gizmo | |||||
| * \{ */ | |||||
| static GizmoGroup *gizmogroup_init(wmGizmoGroup *gzgroup) | |||||
| { | |||||
| GizmoGroup *ggd; | |||||
| ggd = MEM_callocN(sizeof(GizmoGroup), "gizmo_data"); | |||||
| const wmGizmoType *gzt_arrow = WM_gizmotype_find("GIZMO_GT_arrow_3d", true); | |||||
| const wmGizmoType *gzt_dial = WM_gizmotype_find("GIZMO_GT_dial_3d", true); | |||||
| const wmGizmoType *gzt_prim = WM_gizmotype_find("GIZMO_GT_primitive_3d", true); | |||||
| #define GIZMO_NEW_ARROW(v, draw_style) \ | |||||
| { \ | |||||
| 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) \ | |||||
| { \ | |||||
| ggd->gizmos[v] = WM_gizmo_new_ptr(gzt_dial, gzgroup, NULL); \ | |||||
| RNA_enum_set(ggd->gizmos[v]->ptr, "draw_options", draw_options); \ | |||||
| } \ | |||||
| ((void)0) | |||||
| #define GIZMO_NEW_PRIM(v, draw_style) \ | |||||
| { \ | |||||
| 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_SCALE_C, ED_GIZMO_DIAL_DRAW_FLAG_FILL_SELECT); | |||||
| 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_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_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); | |||||
| /* 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_TRANS_C, ED_GIZMO_DIAL_DRAW_FLAG_FILL_SELECT); | |||||
| 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_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); | |||||
| 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); | |||||
| return ggd; | |||||
| } | |||||
| /** | |||||
| * Custom handler for gizmo widgets | |||||
| */ | |||||
| static int gizmo_modal(bContext *C, | |||||
| wmGizmo *widget, | |||||
| const wmEvent *event, | |||||
| eWM_GizmoFlagTweak UNUSED(tweak_flag)) | |||||
| { | |||||
| /* Avoid unnecessary updates, partially address: T55458. */ | |||||
| if (ELEM(event->type, TIMER, INBETWEEN_MOUSEMOVE)) { | |||||
| return OPERATOR_RUNNING_MODAL; | |||||
| } | |||||
| ARegion *region = CTX_wm_region(C); | |||||
| RegionView3D *rv3d = region->regiondata; | |||||
| struct TransformBounds tbounds; | |||||
| if (ED_transform_calc_gizmo_stats(C, | |||||
| &(struct TransformCalcParams){ | |||||
| .use_only_center = true, | |||||
| }, | |||||
| &tbounds)) { | |||||
| gizmo_prepare_mat(C, rv3d, &tbounds); | |||||
| WM_gizmo_set_matrix_location(widget, rv3d->twmat[3]); | |||||
| } | |||||
| ED_region_tag_redraw_editor_overlays(region); | |||||
| return OPERATOR_RUNNING_MODAL; | |||||
| } | |||||
| static void gizmogroup_init_properties_from_twtype(wmGizmoGroup *gzgroup) | |||||
| { | |||||
| struct { | |||||
| wmOperatorType *translate, *rotate, *trackball, *resize; | |||||
| } ot_store = {NULL}; | |||||
| GizmoGroup *ggd = gzgroup->customdata; | |||||
| MAN_ITER_AXES_BEGIN (axis, axis_idx) { | |||||
| const short axis_type = gizmo_get_axis_type(axis_idx); | |||||
| bool constraint_axis[3] = {1, 0, 0}; | |||||
| PointerRNA *ptr = NULL; | |||||
| gizmo_get_axis_constraint(axis_idx, constraint_axis); | |||||
| /* 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_ax = 7.0f; | |||||
| const float ofs[3] = {ofs_ax, ofs_ax, 0.0f}; | |||||
| WM_gizmo_set_scale(axis, 0.07f); | |||||
| 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; | |||||
| } | |||||
| switch (axis_type) { | |||||
| case MAN_AXES_TRANSLATE: | |||||
| if (ot_store.translate == NULL) { | |||||
| ot_store.translate = WM_operatortype_find("TRANSFORM_OT_translate", true); | |||||
| } | |||||
| ptr = WM_gizmo_operator_set(axis, 0, ot_store.translate, NULL); | |||||
| break; | |||||
| case MAN_AXES_ROTATE: { | |||||
| wmOperatorType *ot_rotate; | |||||
| if (axis_idx == MAN_AXIS_ROT_T) { | |||||
| if (ot_store.trackball == NULL) { | |||||
| ot_store.trackball = WM_operatortype_find("TRANSFORM_OT_trackball", true); | |||||
| } | |||||
| ot_rotate = ot_store.trackball; | |||||
| } | |||||
| else { | |||||
| if (ot_store.rotate == NULL) { | |||||
| ot_store.rotate = WM_operatortype_find("TRANSFORM_OT_rotate", true); | |||||
| } | |||||
| ot_rotate = ot_store.rotate; | |||||
| } | |||||
| ptr = WM_gizmo_operator_set(axis, 0, ot_rotate, NULL); | |||||
| break; | |||||
| } | |||||
| case MAN_AXES_SCALE: { | |||||
| if (ot_store.resize == NULL) { | |||||
| ot_store.resize = WM_operatortype_find("TRANSFORM_OT_resize", true); | |||||
| } | |||||
| ptr = WM_gizmo_operator_set(axis, 0, ot_store.resize, NULL); | |||||
| break; | |||||
| } | |||||
| } | |||||
| if (ptr) { | |||||
| PropertyRNA *prop; | |||||
| if (ELEM(true, UNPACK3(constraint_axis))) { | |||||
| if ((prop = RNA_struct_find_property(ptr, "constraint_axis"))) { | |||||
| RNA_property_boolean_set_array(ptr, prop, constraint_axis); | |||||
| } | |||||
| } | |||||
| RNA_boolean_set(ptr, "release_confirm", 1); | |||||
| } | |||||
| } | |||||
| MAN_ITER_AXES_END; | |||||
| } | |||||
| static void WIDGETGROUP_gizmo_setup(const bContext *C, wmGizmoGroup *gzgroup) | |||||
| { | |||||
| GizmoGroup *ggd = gizmogroup_init(gzgroup); | |||||
| gzgroup->customdata = ggd; | |||||
| { | |||||
| ScrArea *area = CTX_wm_area(C); | |||||
| const bToolRef *tref = area->runtime.tool; | |||||
| ggd->twtype = 0; | |||||
| if (tref && STREQ(tref->idname, "builtin.move")) { | |||||
| ggd->twtype |= V3D_GIZMO_SHOW_OBJECT_TRANSLATE; | |||||
| } | |||||
| else if (tref && STREQ(tref->idname, "builtin.rotate")) { | |||||
| ggd->twtype |= V3D_GIZMO_SHOW_OBJECT_ROTATE; | |||||
| } | |||||
| else if (tref && STREQ(tref->idname, "builtin.scale")) { | |||||
| ggd->twtype |= V3D_GIZMO_SHOW_OBJECT_SCALE; | |||||
| } | |||||
| else if (tref && STREQ(tref->idname, "builtin.transform")) { | |||||
| ggd->twtype = V3D_GIZMO_SHOW_OBJECT_TRANSLATE | V3D_GIZMO_SHOW_OBJECT_ROTATE | | |||||
| V3D_GIZMO_SHOW_OBJECT_SCALE; | |||||
| } | |||||
| else { | |||||
| /* This is also correct logic for 'builtin.transform', no special check needed. */ | |||||
| /* Setup all gizmos, they can be toggled via 'ToolSettings.gizmo_flag' */ | |||||
| ggd->twtype = V3D_GIZMO_SHOW_OBJECT_TRANSLATE | V3D_GIZMO_SHOW_OBJECT_ROTATE | | |||||
| V3D_GIZMO_SHOW_OBJECT_SCALE; | |||||
| ggd->use_twtype_refresh = true; | |||||
| } | |||||
| BLI_assert(ggd->twtype != 0); | |||||
| ggd->twtype_init = ggd->twtype; | |||||
| } | |||||
| /* *** set properties for axes *** */ | |||||
| gizmogroup_init_properties_from_twtype(gzgroup); | |||||
| } | |||||
| static void WIDGETGROUP_gizmo_refresh(const bContext *C, wmGizmoGroup *gzgroup) | |||||
| { | |||||
| GizmoGroup *ggd = gzgroup->customdata; | |||||
| Scene *scene = CTX_data_scene(C); | |||||
| ScrArea *area = CTX_wm_area(C); | |||||
| View3D *v3d = area->spacedata.first; | |||||
| ARegion *region = CTX_wm_region(C); | |||||
| RegionView3D *rv3d = region->regiondata; | |||||
| struct TransformBounds tbounds; | |||||
| if (ggd->use_twtype_refresh) { | |||||
| ggd->twtype = v3d->gizmo_show_object & ggd->twtype_init; | |||||
| if (ggd->twtype != ggd->twtype_prev) { | |||||
| ggd->twtype_prev = ggd->twtype; | |||||
| gizmogroup_init_properties_from_twtype(gzgroup); | |||||
| } | |||||
| } | |||||
| const int orient_index = BKE_scene_orientation_get_index_from_flag(scene, ggd->twtype_init); | |||||
| /* skip, we don't draw anything anyway */ | |||||
| if ((ggd->all_hidden = (ED_transform_calc_gizmo_stats(C, | |||||
| &(struct TransformCalcParams){ | |||||
| .use_only_center = true, | |||||
| .orientation_index = orient_index + 1, | |||||
| }, | |||||
| &tbounds) == 0))) { | |||||
| return; | |||||
| } | |||||
| gizmo_prepare_mat(C, rv3d, &tbounds); | |||||
| /* *** set properties for axes *** */ | |||||
| 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, rv3d->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); | |||||
| WM_gizmo_set_matrix_rotation_from_z_axis(axis, rv3d->twmat[aidx_norm]); | |||||
| RNA_float_set(axis->ptr, "length", len); | |||||
| 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; | |||||
| } | |||||
| } | |||||
| 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: | |||||
| WM_gizmo_set_matrix_rotation_from_z_axis(axis, rv3d->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 float *y_axis = rv3d->twmat[aidx_norm - 1 < 0 ? 2 : aidx_norm - 1]; | |||||
| const float *z_axis = rv3d->twmat[aidx_norm]; | |||||
| WM_gizmo_set_matrix_rotation_from_yz_axis(axis, y_axis, z_axis); | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| 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_message_subscribe(const bContext *C, | |||||
| wmGizmoGroup *gzgroup, | |||||
| struct wmMsgBus *mbus) | |||||
| { | |||||
| Scene *scene = CTX_data_scene(C); | |||||
| bScreen *screen = CTX_wm_screen(C); | |||||
| ScrArea *area = CTX_wm_area(C); | |||||
| ARegion *region = CTX_wm_region(C); | |||||
| GizmoGroup *ggd = gzgroup->customdata; | |||||
| gizmo_xform_message_subscribe(gzgroup, | |||||
| mbus, | |||||
| scene, | |||||
| screen, | |||||
| area, | |||||
| region, | |||||
| ggd->twtype_init, | |||||
| ggd->use_twtype_refresh, | |||||
| VIEW3D_GGT_xform_gizmo); | |||||
| } | |||||
| static void WIDGETGROUP_gizmo_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup) | |||||
| { | |||||
| GizmoGroup *ggd = gzgroup->customdata; | |||||
| // ScrArea *area = CTX_wm_area(C); | |||||
| ARegion *region = CTX_wm_region(C); | |||||
| // View3D *v3d = area->spacedata.first; | |||||
| RegionView3D *rv3d = region->regiondata; | |||||
| float viewinv_m3[3][3]; | |||||
| copy_m3_m4(viewinv_m3, rv3d->viewinv); | |||||
| float idot[3]; | |||||
| /* 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) { | |||||
| WM_gizmo_set_flag(axis, WM_GIZMO_HIDDEN, true); | |||||
| } | |||||
| 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)) { | |||||
| WM_gizmo_set_flag(axis, WM_GIZMO_HIDDEN, false); | |||||
| } | |||||
| else { | |||||
| WM_gizmo_set_flag(axis, WM_GIZMO_HIDDEN, true); | |||||
| 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); | |||||
| switch (axis_idx) { | |||||
| case MAN_AXIS_TRANS_C: | |||||
| case MAN_AXIS_ROT_C: | |||||
| case MAN_AXIS_SCALE_C: | |||||
| case MAN_AXIS_ROT_T: | |||||
| WM_gizmo_set_matrix_rotation_from_z_axis(axis, rv3d->viewinv[2]); | |||||
| break; | |||||
| } | |||||
| } | |||||
| MAN_ITER_AXES_END; | |||||
| /* Refresh handled above when using view orientation. */ | |||||
| if (!equals_m3m3(viewinv_m3, ggd->prev.viewinv_m3)) { | |||||
| { | |||||
| Scene *scene = CTX_data_scene(C); | |||||
| const TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get_from_flag( | |||||
| scene, ggd->twtype_init); | |||||
| switch (orient_slot->type) { | |||||
| case V3D_ORIENT_VIEW: { | |||||
| WIDGETGROUP_gizmo_refresh(C, gzgroup); | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| copy_m3_m4(ggd->prev.viewinv_m3, rv3d->viewinv); | |||||
| } | |||||
| } | |||||
| static void WIDGETGROUP_gizmo_invoke_prepare(const bContext *C, | |||||
| wmGizmoGroup *gzgroup, | |||||
| wmGizmo *gz, | |||||
| const wmEvent *UNUSED(event)) | |||||
| { | |||||
| GizmoGroup *ggd = gzgroup->customdata; | |||||
| /* Support gizmo specific orientation. */ | |||||
| if (gz != ggd->gizmos[MAN_AXIS_ROT_T]) { | |||||
| Scene *scene = CTX_data_scene(C); | |||||
| wmGizmoOpElem *gzop = WM_gizmo_operator_get(gz, 0); | |||||
| PointerRNA *ptr = &gzop->ptr; | |||||
| PropertyRNA *prop_orient_type = RNA_struct_find_property(ptr, "orient_type"); | |||||
| const TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get_from_flag( | |||||
| scene, ggd->twtype_init); | |||||
| if ((gz == ggd->gizmos[MAN_AXIS_ROT_C]) || | |||||
| (orient_slot == &scene->orientation_slots[SCE_ORIENT_DEFAULT])) { | |||||
| /* #MAN_AXIS_ROT_C always uses the #V3D_ORIENT_VIEW orientation, | |||||
| * optionally we could set this orientation instead of unset the property. */ | |||||
| RNA_property_unset(ptr, prop_orient_type); | |||||
| } | |||||
| else { | |||||
| /* TODO: APIfunction */ | |||||
| int index = BKE_scene_orientation_slot_get_index(orient_slot); | |||||
| RNA_property_enum_set(ptr, prop_orient_type, index); | |||||
| } | |||||
| } | |||||
| /* 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: | |||||
| case MAN_AXIS_TRANS_Y: | |||||
| case MAN_AXIS_TRANS_Z: | |||||
| axis = axis_idx - MAN_AXIS_TRANS_X; | |||||
| break; | |||||
| case MAN_AXIS_SCALE_X: | |||||
| case MAN_AXIS_SCALE_Y: | |||||
| case MAN_AXIS_SCALE_Z: | |||||
| axis = axis_idx - MAN_AXIS_SCALE_X; | |||||
| break; | |||||
| } | |||||
| if (axis != -1) { | |||||
| wmWindow *win = CTX_wm_window(C); | |||||
| /* Swap single axis for two-axis constraint. */ | |||||
| bool flip = win->eventstate->shift; | |||||
| BLI_assert(axis_idx != -1); | |||||
| const short axis_type = gizmo_get_axis_type(axis_idx); | |||||
| if (axis_type != MAN_AXES_ROTATE) { | |||||
| wmGizmoOpElem *gzop = WM_gizmo_operator_get(gz, 0); | |||||
| PointerRNA *ptr = &gzop->ptr; | |||||
| PropertyRNA *prop_constraint_axis = RNA_struct_find_property(ptr, "constraint_axis"); | |||||
| if (prop_constraint_axis) { | |||||
| bool constraint[3] = {false}; | |||||
| constraint[axis] = true; | |||||
| if (flip) { | |||||
| for (int i = 0; i < ARRAY_SIZE(constraint); i++) { | |||||
| constraint[i] = !constraint[i]; | |||||
| } | |||||
| } | |||||
| RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| static bool WIDGETGROUP_gizmo_poll_generic(View3D *v3d) | |||||
| { | |||||
| if (v3d->gizmo_flag & V3D_GIZMO_HIDE) { | |||||
| return false; | |||||
| } | |||||
| if (G.moving & (G_TRANSFORM_OBJ | G_TRANSFORM_EDIT)) { | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| static bool WIDGETGROUP_gizmo_poll_context(const struct bContext *C, | |||||
| struct wmGizmoGroupType *UNUSED(gzgt)) | |||||
| { | |||||
| ScrArea *area = CTX_wm_area(C); | |||||
| View3D *v3d = area->spacedata.first; | |||||
| if (!WIDGETGROUP_gizmo_poll_generic(v3d)) { | |||||
| return false; | |||||
| } | |||||
| const bToolRef *tref = area->runtime.tool; | |||||
| if (v3d->gizmo_flag & V3D_GIZMO_HIDE_CONTEXT) { | |||||
| return false; | |||||
| } | |||||
| if ((v3d->gizmo_show_object & (V3D_GIZMO_SHOW_OBJECT_TRANSLATE | V3D_GIZMO_SHOW_OBJECT_ROTATE | | |||||
| V3D_GIZMO_SHOW_OBJECT_SCALE)) == 0) { | |||||
| return false; | |||||
| } | |||||
| /* Don't show if the tool has a gizmo. */ | |||||
| if (tref && tref->runtime && tref->runtime->gizmo_group[0]) { | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| static bool WIDGETGROUP_gizmo_poll_tool(const struct bContext *C, struct wmGizmoGroupType *gzgt) | |||||
| { | |||||
| if (!ED_gizmo_poll_or_unlink_delayed_from_tool(C, gzgt)) { | |||||
| return false; | |||||
| } | |||||
| ScrArea *area = CTX_wm_area(C); | |||||
| View3D *v3d = area->spacedata.first; | |||||
| if (!WIDGETGROUP_gizmo_poll_generic(v3d)) { | |||||
| return false; | |||||
| } | |||||
| if (v3d->gizmo_flag & V3D_GIZMO_HIDE_TOOL) { | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| /* Expose as multiple gizmos so tools use one, persistent context another. | |||||
| * Needed because they use different options which isn't so simple to dynamically update. */ | |||||
| void VIEW3D_GGT_xform_gizmo(wmGizmoGroupType *gzgt) | |||||
| { | |||||
| gzgt->name = "3D View: Transform Gizmo"; | |||||
| gzgt->idname = "VIEW3D_GGT_xform_gizmo"; | |||||
| gzgt->flag = WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_DRAW_MODAL_EXCLUDE | | |||||
| WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK; | |||||
| gzgt->gzmap_params.spaceid = SPACE_VIEW3D; | |||||
| gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW; | |||||
| gzgt->poll = WIDGETGROUP_gizmo_poll_tool; | |||||
| gzgt->setup = WIDGETGROUP_gizmo_setup; | |||||
| gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag; | |||||
| gzgt->refresh = WIDGETGROUP_gizmo_refresh; | |||||
| gzgt->message_subscribe = WIDGETGROUP_gizmo_message_subscribe; | |||||
| gzgt->draw_prepare = WIDGETGROUP_gizmo_draw_prepare; | |||||
| gzgt->invoke_prepare = WIDGETGROUP_gizmo_invoke_prepare; | |||||
| static const EnumPropertyItem rna_enum_gizmo_items[] = { | |||||
| {V3D_GIZMO_SHOW_OBJECT_TRANSLATE, "TRANSLATE", 0, "Move", ""}, | |||||
| {V3D_GIZMO_SHOW_OBJECT_ROTATE, "ROTATE", 0, "Rotate", ""}, | |||||
| {V3D_GIZMO_SHOW_OBJECT_SCALE, "SCALE", 0, "Scale", ""}, | |||||
| {0, "NONE", 0, "None", ""}, | |||||
| {0, NULL, 0, NULL, NULL}, | |||||
| }; | |||||
| RNA_def_enum(gzgt->srna, | |||||
| "drag_action", | |||||
| rna_enum_gizmo_items, | |||||
| V3D_GIZMO_SHOW_OBJECT_TRANSLATE, | |||||
| "Drag Action", | |||||
| ""); | |||||
| } | |||||
| /** Only poll, flag & gzmap_params differ. */ | |||||
| void VIEW3D_GGT_xform_gizmo_context(wmGizmoGroupType *gzgt) | |||||
| { | |||||
| gzgt->name = "3D View: Transform Gizmo Context"; | |||||
| gzgt->idname = "VIEW3D_GGT_xform_gizmo_context"; | |||||
| gzgt->flag = WM_GIZMOGROUPTYPE_3D | WM_GIZMOGROUPTYPE_PERSISTENT | | |||||
| WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK; | |||||
| gzgt->poll = WIDGETGROUP_gizmo_poll_context; | |||||
| gzgt->setup = WIDGETGROUP_gizmo_setup; | |||||
| gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag; | |||||
| gzgt->refresh = WIDGETGROUP_gizmo_refresh; | |||||
| gzgt->message_subscribe = WIDGETGROUP_gizmo_message_subscribe; | |||||
| gzgt->draw_prepare = WIDGETGROUP_gizmo_draw_prepare; | |||||
| gzgt->invoke_prepare = WIDGETGROUP_gizmo_invoke_prepare; | |||||
| } | |||||
| /** \} */ | |||||