Fix T61536: can't snap vertex to another vertex in edit mode using curves
[blender.git] / source / blender / editors / transform / transform_gizmo_3d.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16
17 /** \file \ingroup edtransform
18  *
19  * \name 3D Transform Gizmo
20  *
21  * Used for 3D View
22  */
23
24 #include <stdlib.h>
25 #include <string.h>
26 #include <math.h>
27 #include <float.h>
28
29 #include "DNA_armature_types.h"
30 #include "DNA_curve_types.h"
31 #include "DNA_gpencil_types.h"
32 #include "DNA_lattice_types.h"
33 #include "DNA_meta_types.h"
34 #include "DNA_screen_types.h"
35 #include "DNA_scene_types.h"
36 #include "DNA_view3d_types.h"
37
38 #include "BLI_array_utils.h"
39 #include "BLI_listbase.h"
40 #include "BLI_math.h"
41 #include "BLI_utildefines.h"
42
43 #include "BKE_action.h"
44 #include "BKE_context.h"
45 #include "BKE_curve.h"
46 #include "BKE_global.h"
47 #include "BKE_layer.h"
48 #include "BKE_particle.h"
49 #include "BKE_pointcache.h"
50 #include "BKE_editmesh.h"
51 #include "BKE_lattice.h"
52 #include "BKE_gpencil.h"
53 #include "BKE_scene.h"
54 #include "BKE_workspace.h"
55 #include "BKE_object.h"
56
57 #include "DEG_depsgraph.h"
58
59 #include "WM_api.h"
60 #include "WM_types.h"
61 #include "WM_message.h"
62 #include "WM_toolsystem.h"
63 #include "wm.h"
64
65 #include "ED_armature.h"
66 #include "ED_curve.h"
67 #include "ED_object.h"
68 #include "ED_particle.h"
69 #include "ED_view3d.h"
70 #include "ED_gpencil.h"
71 #include "ED_screen.h"
72 #include "ED_gizmo_library.h"
73 #include "ED_gizmo_utils.h"
74
75 #include "UI_interface.h"
76 #include "UI_resources.h"
77
78 #include "RNA_access.h"
79 #include "RNA_define.h"
80
81 /* local module include */
82 #include "transform.h"
83
84 #include "MEM_guardedalloc.h"
85
86 #include "GPU_select.h"
87 #include "GPU_state.h"
88 #include "GPU_immediate.h"
89 #include "GPU_matrix.h"
90
91 #include "DEG_depsgraph_query.h"
92
93 /* return codes for select, and drawing flags */
94
95 #define MAN_TRANS_X             (1 << 0)
96 #define MAN_TRANS_Y             (1 << 1)
97 #define MAN_TRANS_Z             (1 << 2)
98 #define MAN_TRANS_C             (MAN_TRANS_X | MAN_TRANS_Y | MAN_TRANS_Z)
99
100 #define MAN_ROT_X               (1 << 3)
101 #define MAN_ROT_Y               (1 << 4)
102 #define MAN_ROT_Z               (1 << 5)
103 #define MAN_ROT_C               (MAN_ROT_X | MAN_ROT_Y | MAN_ROT_Z)
104
105 #define MAN_SCALE_X             (1 << 8)
106 #define MAN_SCALE_Y             (1 << 9)
107 #define MAN_SCALE_Z             (1 << 10)
108 #define MAN_SCALE_C             (MAN_SCALE_X | MAN_SCALE_Y | MAN_SCALE_Z)
109
110 /* threshold for testing view aligned gizmo axis */
111 static struct {
112         float min, max;
113 } g_tw_axis_range[2] = {
114         /* Regular range */
115         {0.02f, 0.1f},
116         /* Use a different range because we flip the dot product,
117          * also the view aligned planes are harder to see so hiding early is preferred. */
118         {0.175f,  0.25f},
119 };
120
121 /* axes as index */
122 enum {
123         MAN_AXIS_TRANS_X = 0,
124         MAN_AXIS_TRANS_Y,
125         MAN_AXIS_TRANS_Z,
126         MAN_AXIS_TRANS_C,
127
128         MAN_AXIS_TRANS_XY,
129         MAN_AXIS_TRANS_YZ,
130         MAN_AXIS_TRANS_ZX,
131 #define MAN_AXIS_RANGE_TRANS_START MAN_AXIS_TRANS_X
132 #define MAN_AXIS_RANGE_TRANS_END (MAN_AXIS_TRANS_ZX + 1)
133
134         MAN_AXIS_ROT_X,
135         MAN_AXIS_ROT_Y,
136         MAN_AXIS_ROT_Z,
137         MAN_AXIS_ROT_C,
138         MAN_AXIS_ROT_T, /* trackball rotation */
139 #define MAN_AXIS_RANGE_ROT_START MAN_AXIS_ROT_X
140 #define MAN_AXIS_RANGE_ROT_END (MAN_AXIS_ROT_T + 1)
141
142         MAN_AXIS_SCALE_X,
143         MAN_AXIS_SCALE_Y,
144         MAN_AXIS_SCALE_Z,
145         MAN_AXIS_SCALE_C,
146         MAN_AXIS_SCALE_XY,
147         MAN_AXIS_SCALE_YZ,
148         MAN_AXIS_SCALE_ZX,
149 #define MAN_AXIS_RANGE_SCALE_START MAN_AXIS_SCALE_X
150 #define MAN_AXIS_RANGE_SCALE_END (MAN_AXIS_SCALE_ZX + 1)
151
152         MAN_AXIS_LAST = MAN_AXIS_SCALE_ZX + 1,
153 };
154
155 /* axis types */
156 enum {
157         MAN_AXES_ALL = 0,
158         MAN_AXES_TRANSLATE,
159         MAN_AXES_ROTATE,
160         MAN_AXES_SCALE,
161 };
162
163 typedef struct GizmoGroup {
164         bool all_hidden;
165         int twtype;
166
167         /* Users may change the twtype, detect changes to re-setup gizmo options. */
168         int twtype_init;
169         int twtype_prev;
170         int use_twtype_refresh;
171
172         /* Only for view orientation. */
173         struct {
174                 float viewinv_m3[3][3];
175         } prev;
176
177         struct wmGizmo *gizmos[MAN_AXIS_LAST];
178 } GizmoGroup;
179
180 /* -------------------------------------------------------------------- */
181 /** \name Utilities
182  * \{ */
183
184 /* loop over axes */
185 #define MAN_ITER_AXES_BEGIN(axis, axis_idx) \
186         { \
187                 wmGizmo *axis; \
188                 int axis_idx; \
189                 for (axis_idx = 0; axis_idx < MAN_AXIS_LAST; axis_idx++) { \
190                         axis = gizmo_get_axis_from_index(ggd, axis_idx);
191
192 #define MAN_ITER_AXES_END \
193                 } \
194         } ((void)0)
195
196 static wmGizmo *gizmo_get_axis_from_index(const GizmoGroup *ggd, const short axis_idx)
197 {
198         BLI_assert(IN_RANGE_INCL(axis_idx, (float)MAN_AXIS_TRANS_X, (float)MAN_AXIS_LAST));
199         return ggd->gizmos[axis_idx];
200 }
201
202 static short gizmo_get_axis_type(const int axis_idx)
203 {
204         if (axis_idx >= MAN_AXIS_RANGE_TRANS_START && axis_idx < MAN_AXIS_RANGE_TRANS_END) {
205                 return MAN_AXES_TRANSLATE;
206         }
207         if (axis_idx >= MAN_AXIS_RANGE_ROT_START && axis_idx < MAN_AXIS_RANGE_ROT_END) {
208                 return MAN_AXES_ROTATE;
209         }
210         if (axis_idx >= MAN_AXIS_RANGE_SCALE_START && axis_idx < MAN_AXIS_RANGE_SCALE_END) {
211                 return MAN_AXES_SCALE;
212         }
213         BLI_assert(0);
214         return -1;
215 }
216
217 static uint gizmo_orientation_axis(const int axis_idx, bool *r_is_plane)
218 {
219         switch (axis_idx) {
220                 case MAN_AXIS_TRANS_YZ:
221                 case MAN_AXIS_SCALE_YZ:
222                         if (r_is_plane) {
223                                 *r_is_plane = true;
224                         }
225                         ATTR_FALLTHROUGH;
226                 case MAN_AXIS_TRANS_X:
227                 case MAN_AXIS_ROT_X:
228                 case MAN_AXIS_SCALE_X:
229                         return 0;
230
231                 case MAN_AXIS_TRANS_ZX:
232                 case MAN_AXIS_SCALE_ZX:
233                         if (r_is_plane) {
234                                 *r_is_plane = true;
235                         }
236                         ATTR_FALLTHROUGH;
237                 case MAN_AXIS_TRANS_Y:
238                 case MAN_AXIS_ROT_Y:
239                 case MAN_AXIS_SCALE_Y:
240                         return 1;
241
242                 case MAN_AXIS_TRANS_XY:
243                 case MAN_AXIS_SCALE_XY:
244                         if (r_is_plane) {
245                                 *r_is_plane = true;
246                         }
247                         ATTR_FALLTHROUGH;
248                 case MAN_AXIS_TRANS_Z:
249                 case MAN_AXIS_ROT_Z:
250                 case MAN_AXIS_SCALE_Z:
251                         return 2;
252         }
253         return 3;
254 }
255
256 static bool gizmo_is_axis_visible(
257         const RegionView3D *rv3d, const int twtype,
258         const float idot[3], const int axis_type, const int axis_idx)
259 {
260         if ((axis_idx >= MAN_AXIS_RANGE_ROT_START && axis_idx < MAN_AXIS_RANGE_ROT_END) == 0) {
261                 bool is_plane = false;
262                 const uint aidx_norm = gizmo_orientation_axis(axis_idx, &is_plane);
263                 /* don't draw axis perpendicular to the view */
264                 if (aidx_norm < 3) {
265                         float idot_axis = idot[aidx_norm];
266                         if (is_plane) {
267                                 idot_axis = 1.0f - idot_axis;
268                         }
269                         if (idot_axis < g_tw_axis_range[is_plane].min) {
270                                 return false;
271                         }
272                 }
273         }
274
275         if ((axis_type == MAN_AXES_TRANSLATE && !(twtype & SCE_GIZMO_SHOW_TRANSLATE)) ||
276             (axis_type == MAN_AXES_ROTATE && !(twtype & SCE_GIZMO_SHOW_ROTATE)) ||
277             (axis_type == MAN_AXES_SCALE && !(twtype & SCE_GIZMO_SHOW_SCALE)))
278         {
279                 return false;
280         }
281
282         switch (axis_idx) {
283                 case MAN_AXIS_TRANS_X:
284                         return (rv3d->twdrawflag & MAN_TRANS_X);
285                 case MAN_AXIS_TRANS_Y:
286                         return (rv3d->twdrawflag & MAN_TRANS_Y);
287                 case MAN_AXIS_TRANS_Z:
288                         return (rv3d->twdrawflag & MAN_TRANS_Z);
289                 case MAN_AXIS_TRANS_C:
290                         return (rv3d->twdrawflag & MAN_TRANS_C);
291                 case MAN_AXIS_ROT_X:
292                         return (rv3d->twdrawflag & MAN_ROT_X);
293                 case MAN_AXIS_ROT_Y:
294                         return (rv3d->twdrawflag & MAN_ROT_Y);
295                 case MAN_AXIS_ROT_Z:
296                         return (rv3d->twdrawflag & MAN_ROT_Z);
297                 case MAN_AXIS_ROT_C:
298                 case MAN_AXIS_ROT_T:
299                         return (rv3d->twdrawflag & MAN_ROT_C);
300                 case MAN_AXIS_SCALE_X:
301                         return (rv3d->twdrawflag & MAN_SCALE_X);
302                 case MAN_AXIS_SCALE_Y:
303                         return (rv3d->twdrawflag & MAN_SCALE_Y);
304                 case MAN_AXIS_SCALE_Z:
305                         return (rv3d->twdrawflag & MAN_SCALE_Z);
306                 case MAN_AXIS_SCALE_C:
307                         return (rv3d->twdrawflag & MAN_SCALE_C && (twtype & SCE_GIZMO_SHOW_TRANSLATE) == 0);
308                 case MAN_AXIS_TRANS_XY:
309                         return (rv3d->twdrawflag & MAN_TRANS_X &&
310                                 rv3d->twdrawflag & MAN_TRANS_Y &&
311                                 (twtype & SCE_GIZMO_SHOW_ROTATE) == 0);
312                 case MAN_AXIS_TRANS_YZ:
313                         return (rv3d->twdrawflag & MAN_TRANS_Y &&
314                                 rv3d->twdrawflag & MAN_TRANS_Z &&
315                                 (twtype & SCE_GIZMO_SHOW_ROTATE) == 0);
316                 case MAN_AXIS_TRANS_ZX:
317                         return (rv3d->twdrawflag & MAN_TRANS_Z &&
318                                 rv3d->twdrawflag & MAN_TRANS_X &&
319                                 (twtype & SCE_GIZMO_SHOW_ROTATE) == 0);
320                 case MAN_AXIS_SCALE_XY:
321                         return (rv3d->twdrawflag & MAN_SCALE_X &&
322                                 rv3d->twdrawflag & MAN_SCALE_Y &&
323                                 (twtype & SCE_GIZMO_SHOW_TRANSLATE) == 0 &&
324                                 (twtype & SCE_GIZMO_SHOW_ROTATE) == 0);
325                 case MAN_AXIS_SCALE_YZ:
326                         return (rv3d->twdrawflag & MAN_SCALE_Y &&
327                                 rv3d->twdrawflag & MAN_SCALE_Z &&
328                                 (twtype & SCE_GIZMO_SHOW_TRANSLATE) == 0 &&
329                                 (twtype & SCE_GIZMO_SHOW_ROTATE) == 0);
330                 case MAN_AXIS_SCALE_ZX:
331                         return (rv3d->twdrawflag & MAN_SCALE_Z &&
332                                 rv3d->twdrawflag & MAN_SCALE_X &&
333                                 (twtype & SCE_GIZMO_SHOW_TRANSLATE) == 0 &&
334                                 (twtype & SCE_GIZMO_SHOW_ROTATE) == 0);
335         }
336         return false;
337 }
338
339 static void gizmo_get_axis_color(
340         const int axis_idx, const float idot[3],
341         float r_col[4], float r_col_hi[4])
342 {
343         /* alpha values for normal/highlighted states */
344         const float alpha = 0.6f;
345         const float alpha_hi = 1.0f;
346         float alpha_fac;
347
348         if (axis_idx >= MAN_AXIS_RANGE_ROT_START && axis_idx < MAN_AXIS_RANGE_ROT_END) {
349                 /* Never fade rotation rings. */
350                 /* trackball rotation axis is a special case, we only draw a slight overlay */
351                 alpha_fac = (axis_idx == MAN_AXIS_ROT_T) ? 0.1f : 1.0f;
352         }
353         else {
354                 bool is_plane = false;
355                 const int axis_idx_norm = gizmo_orientation_axis(axis_idx, &is_plane);
356                 /* get alpha fac based on axis angle, to fade axis out when hiding it because it points towards view */
357                 if (axis_idx_norm < 3) {
358                         const float idot_min = g_tw_axis_range[is_plane].min;
359                         const float idot_max = g_tw_axis_range[is_plane].max;
360                         float idot_axis = idot[axis_idx_norm];
361                         if (is_plane) {
362                                 idot_axis = 1.0f - idot_axis;
363                         }
364                         alpha_fac = (
365                                 (idot_axis > idot_max) ?
366                                 1.0f : (idot_axis < idot_min) ?
367                                 0.0f : ((idot_axis - idot_min) / (idot_max - idot_min)));
368                 }
369                 else {
370                         alpha_fac = 1.0f;
371                 }
372         }
373
374         switch (axis_idx) {
375                 case MAN_AXIS_TRANS_X:
376                 case MAN_AXIS_ROT_X:
377                 case MAN_AXIS_SCALE_X:
378                 case MAN_AXIS_TRANS_YZ:
379                 case MAN_AXIS_SCALE_YZ:
380                         UI_GetThemeColor4fv(TH_AXIS_X, r_col);
381                         break;
382                 case MAN_AXIS_TRANS_Y:
383                 case MAN_AXIS_ROT_Y:
384                 case MAN_AXIS_SCALE_Y:
385                 case MAN_AXIS_TRANS_ZX:
386                 case MAN_AXIS_SCALE_ZX:
387                         UI_GetThemeColor4fv(TH_AXIS_Y, r_col);
388                         break;
389                 case MAN_AXIS_TRANS_Z:
390                 case MAN_AXIS_ROT_Z:
391                 case MAN_AXIS_SCALE_Z:
392                 case MAN_AXIS_TRANS_XY:
393                 case MAN_AXIS_SCALE_XY:
394                         UI_GetThemeColor4fv(TH_AXIS_Z, r_col);
395                         break;
396                 case MAN_AXIS_TRANS_C:
397                 case MAN_AXIS_ROT_C:
398                 case MAN_AXIS_SCALE_C:
399                 case MAN_AXIS_ROT_T:
400                         copy_v4_fl(r_col, 1.0f);
401                         break;
402         }
403
404         copy_v4_v4(r_col_hi, r_col);
405
406         r_col[3] = alpha * alpha_fac;
407         r_col_hi[3] = alpha_hi * alpha_fac;
408 }
409
410 static void gizmo_get_axis_constraint(const int axis_idx, bool r_axis[3])
411 {
412         ARRAY_SET_ITEMS(r_axis, 0, 0, 0);
413
414         switch (axis_idx) {
415                 case MAN_AXIS_TRANS_X:
416                 case MAN_AXIS_ROT_X:
417                 case MAN_AXIS_SCALE_X:
418                         r_axis[0] = 1;
419                         break;
420                 case MAN_AXIS_TRANS_Y:
421                 case MAN_AXIS_ROT_Y:
422                 case MAN_AXIS_SCALE_Y:
423                         r_axis[1] = 1;
424                         break;
425                 case MAN_AXIS_TRANS_Z:
426                 case MAN_AXIS_ROT_Z:
427                 case MAN_AXIS_SCALE_Z:
428                         r_axis[2] = 1;
429                         break;
430                 case MAN_AXIS_TRANS_XY:
431                 case MAN_AXIS_SCALE_XY:
432                         r_axis[0] = r_axis[1] = 1;
433                         break;
434                 case MAN_AXIS_TRANS_YZ:
435                 case MAN_AXIS_SCALE_YZ:
436                         r_axis[1] = r_axis[2] = 1;
437                         break;
438                 case MAN_AXIS_TRANS_ZX:
439                 case MAN_AXIS_SCALE_ZX:
440                         r_axis[2] = r_axis[0] = 1;
441                         break;
442                 default:
443                         break;
444         }
445 }
446
447
448 /* **************** Preparation Stuff **************** */
449
450 static void reset_tw_center(struct TransformBounds *tbounds)
451 {
452         INIT_MINMAX(tbounds->min, tbounds->max);
453         zero_v3(tbounds->center);
454
455         for (int i = 0; i < 3; i++) {
456                 tbounds->axis_min[i] = +FLT_MAX;
457                 tbounds->axis_max[i] = -FLT_MAX;
458         }
459 }
460
461 /* transform widget center calc helper for below */
462 static void calc_tw_center(struct TransformBounds *tbounds, const float co[3])
463 {
464         minmax_v3v3_v3(tbounds->min, tbounds->max, co);
465         add_v3_v3(tbounds->center, co);
466
467         for (int i = 0; i < 3; i++) {
468                 const float d = dot_v3v3(tbounds->axis[i], co);
469                 tbounds->axis_min[i] = min_ff(d, tbounds->axis_min[i]);
470                 tbounds->axis_max[i] = max_ff(d, tbounds->axis_max[i]);
471         }
472 }
473
474 static void calc_tw_center_with_matrix(
475         struct TransformBounds *tbounds, const float co[3],
476         const bool use_matrix, const float matrix[4][4])
477 {
478         float co_world[3];
479         if (use_matrix) {
480                 mul_v3_m4v3(co_world, matrix, co);
481                 co = co_world;
482         }
483         calc_tw_center(tbounds, co);
484 }
485
486 static void protectflag_to_drawflags(short protectflag, short *drawflags)
487 {
488         if (protectflag & OB_LOCK_LOCX)
489                 *drawflags &= ~MAN_TRANS_X;
490         if (protectflag & OB_LOCK_LOCY)
491                 *drawflags &= ~MAN_TRANS_Y;
492         if (protectflag & OB_LOCK_LOCZ)
493                 *drawflags &= ~MAN_TRANS_Z;
494
495         if (protectflag & OB_LOCK_ROTX)
496                 *drawflags &= ~MAN_ROT_X;
497         if (protectflag & OB_LOCK_ROTY)
498                 *drawflags &= ~MAN_ROT_Y;
499         if (protectflag & OB_LOCK_ROTZ)
500                 *drawflags &= ~MAN_ROT_Z;
501
502         if (protectflag & OB_LOCK_SCALEX)
503                 *drawflags &= ~MAN_SCALE_X;
504         if (protectflag & OB_LOCK_SCALEY)
505                 *drawflags &= ~MAN_SCALE_Y;
506         if (protectflag & OB_LOCK_SCALEZ)
507                 *drawflags &= ~MAN_SCALE_Z;
508 }
509
510 /* for pose mode */
511 static void protectflag_to_drawflags_pchan(RegionView3D *rv3d, const bPoseChannel *pchan)
512 {
513         protectflag_to_drawflags(pchan->protectflag, &rv3d->twdrawflag);
514 }
515
516 /* for editmode*/
517 static void protectflag_to_drawflags_ebone(RegionView3D *rv3d, const EditBone *ebo)
518 {
519         if (ebo->flag & BONE_EDITMODE_LOCKED) {
520                 protectflag_to_drawflags(OB_LOCK_LOC | OB_LOCK_ROT | OB_LOCK_SCALE, &rv3d->twdrawflag);
521         }
522 }
523
524 /* could move into BLI_math however this is only useful for display/editing purposes */
525 static void axis_angle_to_gimbal_axis(float gmat[3][3], const float axis[3], const float angle)
526 {
527         /* X/Y are arbitrary axies, most importantly Z is the axis of rotation */
528
529         float cross_vec[3];
530         float quat[4];
531
532         /* this is an un-scientific method to get a vector to cross with
533          * XYZ intentionally YZX */
534         cross_vec[0] = axis[1];
535         cross_vec[1] = axis[2];
536         cross_vec[2] = axis[0];
537
538         /* X-axis */
539         cross_v3_v3v3(gmat[0], cross_vec, axis);
540         normalize_v3(gmat[0]);
541         axis_angle_to_quat(quat, axis, angle);
542         mul_qt_v3(quat, gmat[0]);
543
544         /* Y-axis */
545         axis_angle_to_quat(quat, axis, M_PI_2);
546         copy_v3_v3(gmat[1], gmat[0]);
547         mul_qt_v3(quat, gmat[1]);
548
549         /* Z-axis */
550         copy_v3_v3(gmat[2], axis);
551
552         normalize_m3(gmat);
553 }
554
555
556 static bool test_rotmode_euler(short rotmode)
557 {
558         return (ELEM(rotmode, ROT_MODE_AXISANGLE, ROT_MODE_QUAT)) ? 0 : 1;
559 }
560
561 bool gimbal_axis(Object *ob, float gmat[3][3])
562 {
563         if (ob->mode & OB_MODE_POSE) {
564                 bPoseChannel *pchan = BKE_pose_channel_active(ob);
565
566                 if (pchan) {
567                         float mat[3][3], tmat[3][3], obmat[3][3];
568                         if (test_rotmode_euler(pchan->rotmode)) {
569                                 eulO_to_gimbal_axis(mat, pchan->eul, pchan->rotmode);
570                         }
571                         else if (pchan->rotmode == ROT_MODE_AXISANGLE) {
572                                 axis_angle_to_gimbal_axis(mat, pchan->rotAxis, pchan->rotAngle);
573                         }
574                         else { /* quat */
575                                 return 0;
576                         }
577
578
579                         /* apply bone transformation */
580                         mul_m3_m3m3(tmat, pchan->bone->bone_mat, mat);
581
582                         if (pchan->parent) {
583                                 float parent_mat[3][3];
584
585                                 copy_m3_m4(parent_mat, pchan->parent->pose_mat);
586                                 mul_m3_m3m3(mat, parent_mat, tmat);
587
588                                 /* needed if object transformation isn't identity */
589                                 copy_m3_m4(obmat, ob->obmat);
590                                 mul_m3_m3m3(gmat, obmat, mat);
591                         }
592                         else {
593                                 /* needed if object transformation isn't identity */
594                                 copy_m3_m4(obmat, ob->obmat);
595                                 mul_m3_m3m3(gmat, obmat, tmat);
596                         }
597
598                         normalize_m3(gmat);
599                         return 1;
600                 }
601         }
602         else {
603                 if (test_rotmode_euler(ob->rotmode)) {
604                         eulO_to_gimbal_axis(gmat, ob->rot, ob->rotmode);
605                 }
606                 else if (ob->rotmode == ROT_MODE_AXISANGLE) {
607                         axis_angle_to_gimbal_axis(gmat, ob->rotAxis, ob->rotAngle);
608                 }
609                 else { /* quat */
610                         return 0;
611                 }
612
613                 if (ob->parent) {
614                         float parent_mat[3][3];
615                         copy_m3_m4(parent_mat, ob->parent->obmat);
616                         normalize_m3(parent_mat);
617                         mul_m3_m3m3(gmat, parent_mat, gmat);
618                 }
619                 return 1;
620         }
621
622         return 0;
623 }
624
625 void ED_transform_calc_orientation_from_type(
626         const bContext *C, float r_mat[3][3])
627 {
628         ARegion *ar = CTX_wm_region(C);
629         Scene *scene = CTX_data_scene(C);
630         ViewLayer *view_layer = CTX_data_view_layer(C);
631         Object *obedit = CTX_data_edit_object(C);
632         RegionView3D *rv3d = ar->regiondata;
633         Object *ob = OBACT(view_layer);
634         const short orientation_type = scene->orientation_slots[SCE_ORIENT_DEFAULT].type;
635         const short orientation_index_custom = scene->orientation_slots[SCE_ORIENT_DEFAULT].index_custom;
636         const int pivot_point = scene->toolsettings->transform_pivot_point;
637
638         ED_transform_calc_orientation_from_type_ex(
639                 C, r_mat,
640                 scene, rv3d, ob, obedit, orientation_type, orientation_index_custom, pivot_point);
641 }
642
643 void ED_transform_calc_orientation_from_type_ex(
644         const bContext *C, float r_mat[3][3],
645         /* extra args (can be accessed from context) */
646         Scene *scene, RegionView3D *rv3d, Object *ob, Object *obedit,
647         const short orientation_type, int orientation_index_custom,
648         const int pivot_point)
649 {
650         bool ok = false;
651
652         switch (orientation_type) {
653                 case V3D_ORIENT_GLOBAL:
654                 {
655                         break; /* nothing to do */
656                 }
657                 case V3D_ORIENT_GIMBAL:
658                 {
659                         if (gimbal_axis(ob, r_mat)) {
660                                 ok = true;
661                                 break;
662                         }
663                         /* if not gimbal, fall through to normal */
664                         ATTR_FALLTHROUGH;
665                 }
666                 case V3D_ORIENT_NORMAL:
667                 {
668                         if (obedit || ob->mode & OB_MODE_POSE) {
669                                 ED_getTransformOrientationMatrix(C, r_mat, pivot_point);
670                                 ok = true;
671                                 break;
672                         }
673                         /* no break we define 'normal' as 'local' in Object mode */
674                         ATTR_FALLTHROUGH;
675                 }
676                 case V3D_ORIENT_LOCAL:
677                 {
678                         if (ob->mode & OB_MODE_POSE) {
679                                 /* each bone moves on its own local axis, but  to avoid confusion,
680                                  * use the active pones axis for display [#33575], this works as expected on a single bone
681                                  * and users who select many bones will understand what's going on and what local means
682                                  * when they start transforming */
683                                 ED_getTransformOrientationMatrix(C, r_mat, pivot_point);
684                                 ok = true;
685                                 break;
686                         }
687                         copy_m3_m4(r_mat, ob->obmat);
688                         normalize_m3(r_mat);
689                         ok = true;
690                         break;
691                 }
692                 case V3D_ORIENT_VIEW:
693                 {
694                         if (rv3d != NULL) {
695                                 copy_m3_m4(r_mat, rv3d->viewinv);
696                                 normalize_m3(r_mat);
697                                 ok = true;
698                         }
699                         break;
700                 }
701                 case V3D_ORIENT_CURSOR:
702                 {
703                         ED_view3d_cursor3d_calc_mat3(scene, r_mat);
704                         ok = true;
705                         break;
706                 }
707                 case V3D_ORIENT_CUSTOM:
708                 {
709                         TransformOrientation *custom_orientation = BKE_scene_transform_orientation_find(
710                                 scene, orientation_index_custom);
711                         if (applyTransformOrientation(custom_orientation, r_mat, NULL)) {
712                                 ok = true;
713                         }
714                         break;
715                 }
716         }
717
718         if (!ok) {
719                 unit_m3(r_mat);
720         }
721 }
722
723 /* centroid, boundbox, of selection */
724 /* returns total items selected */
725 int ED_transform_calc_gizmo_stats(
726         const bContext *C,
727         const struct TransformCalcParams *params,
728         struct TransformBounds *tbounds)
729 {
730         ScrArea *sa = CTX_wm_area(C);
731         ARegion *ar = CTX_wm_region(C);
732         Scene *scene = CTX_data_scene(C);
733         Depsgraph *depsgraph = CTX_data_depsgraph(C);
734         ViewLayer *view_layer = CTX_data_view_layer(C);
735         View3D *v3d = sa->spacedata.first;
736         Object *obedit = CTX_data_edit_object(C);
737         RegionView3D *rv3d = ar->regiondata;
738         Base *base;
739         Object *ob = OBACT(view_layer);
740         bGPdata *gpd = CTX_data_gpencil_data(C);
741         const bool is_gp_edit = GPENCIL_ANY_MODE(gpd);
742         int a, totsel = 0;
743         const int pivot_point = scene->toolsettings->transform_pivot_point;
744
745         /* transform widget matrix */
746         unit_m4(rv3d->twmat);
747
748         unit_m3(rv3d->tw_axis_matrix);
749         zero_v3(rv3d->tw_axis_min);
750         zero_v3(rv3d->tw_axis_max);
751
752         rv3d->twdrawflag = 0xFFFF;
753
754         /* global, local or normal orientation?
755          * if we could check 'totsel' now, this should be skipped with no selection. */
756         if (ob) {
757                 const short orientation_type = params->orientation_type ?
758                         (params->orientation_type - 1) : scene->orientation_slots[SCE_ORIENT_DEFAULT].type;
759                 const short orientation_index_custom = params->orientation_type ?
760                         params->orientation_index_custom : scene->orientation_slots[SCE_ORIENT_DEFAULT].index_custom;
761                 float mat[3][3];
762                 ED_transform_calc_orientation_from_type_ex(
763                         C, mat,
764                         scene, rv3d, ob, obedit, orientation_type, orientation_index_custom, pivot_point);
765                 copy_m4_m3(rv3d->twmat, mat);
766         }
767
768         /* transform widget centroid/center */
769         reset_tw_center(tbounds);
770
771         copy_m3_m4(tbounds->axis, rv3d->twmat);
772         if (params->use_local_axis && (ob && ob->mode & OB_MODE_EDIT)) {
773                 float diff_mat[3][3];
774                 copy_m3_m4(diff_mat, ob->obmat);
775                 normalize_m3(diff_mat);
776                 invert_m3(diff_mat);
777                 mul_m3_m3m3(tbounds->axis, tbounds->axis, diff_mat);
778                 normalize_m3(tbounds->axis);
779         }
780
781         if (is_gp_edit) {
782                 float diff_mat[4][4];
783                 const bool use_mat_local = true;
784                 for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
785                         /* only editable and visible layers are considered */
786
787                         if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
788
789                                 /* calculate difference matrix */
790                                 ED_gpencil_parent_location(depsgraph, ob, gpd, gpl, diff_mat);
791
792                                 for (bGPDstroke *gps = gpl->actframe->strokes.first; gps; gps = gps->next) {
793                                         /* skip strokes that are invalid for current view */
794                                         if (ED_gpencil_stroke_can_use(C, gps) == false) {
795                                                 continue;
796                                         }
797
798                                         /* we're only interested in selected points here... */
799                                         if (gps->flag & GP_STROKE_SELECT) {
800                                                 bGPDspoint *pt;
801                                                 int i;
802
803                                                 /* Change selection status of all points, then make the stroke match */
804                                                 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
805                                                         if (pt->flag & GP_SPOINT_SELECT) {
806                                                                 calc_tw_center_with_matrix(tbounds, &pt->x, use_mat_local, diff_mat);
807                                                                 totsel++;
808                                                         }
809                                                 }
810                                         }
811                                 }
812                         }
813                 }
814
815
816                 /* selection center */
817                 if (totsel) {
818                         mul_v3_fl(tbounds->center, 1.0f / (float)totsel);   /* centroid! */
819                 }
820         }
821         else if (obedit) {
822
823 #define FOREACH_EDIT_OBJECT_BEGIN(ob_iter, use_mat_local) \
824                 { \
825                         invert_m4_m4(obedit->imat, obedit->obmat); \
826                         uint objects_len = 0; \
827                         Object **objects = BKE_view_layer_array_from_objects_in_edit_mode(view_layer, CTX_wm_view3d(C), &objects_len); \
828                         for (uint ob_index = 0; ob_index < objects_len; ob_index++) { \
829                                 Object *ob_iter = objects[ob_index]; \
830                                 const bool use_mat_local = (ob_iter != obedit);
831
832 #define FOREACH_EDIT_OBJECT_END() \
833                         } \
834                         MEM_freeN(objects); \
835                 } ((void)0)
836
837                 ob = obedit;
838                 if (obedit->type == OB_MESH) {
839                         FOREACH_EDIT_OBJECT_BEGIN(ob_iter, use_mat_local) {
840                                 BMEditMesh *em_iter = BKE_editmesh_from_object(ob_iter);
841                                 BMesh *bm = em_iter->bm;
842
843                                 if (bm->totvertsel == 0) {
844                                         continue;
845                                 }
846
847                                 BMVert *eve;
848                                 BMIter iter;
849
850                                 float mat_local[4][4];
851                                 if (use_mat_local) {
852                                         mul_m4_m4m4(mat_local, obedit->imat, ob_iter->obmat);
853                                 }
854
855                                 BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
856                                         if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
857                                                 if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
858                                                         calc_tw_center_with_matrix(tbounds, eve->co, use_mat_local, mat_local);
859                                                         totsel++;
860                                                 }
861                                         }
862                                 }
863                         } FOREACH_EDIT_OBJECT_END();
864                 } /* end editmesh */
865                 else if (obedit->type == OB_ARMATURE) {
866                         FOREACH_EDIT_OBJECT_BEGIN(ob_iter, use_mat_local) {
867                                 bArmature *arm = ob_iter->data;
868
869                                 float mat_local[4][4];
870                                 if (use_mat_local) {
871                                         mul_m4_m4m4(mat_local, obedit->imat, ob_iter->obmat);
872                                 }
873                                 for (EditBone *ebo = arm->edbo->first; ebo; ebo = ebo->next) {
874                                         if (EBONE_VISIBLE(arm, ebo)) {
875                                                 if (ebo->flag & BONE_TIPSEL) {
876                                                         calc_tw_center_with_matrix(tbounds, ebo->tail, use_mat_local, mat_local);
877                                                         totsel++;
878                                                 }
879                                                 if ((ebo->flag & BONE_ROOTSEL) &&
880                                                     /* don't include same point multiple times */
881                                                     ((ebo->flag & BONE_CONNECTED) &&
882                                                      (ebo->parent != NULL) &&
883                                                      (ebo->parent->flag & BONE_TIPSEL) &&
884                                                      EBONE_VISIBLE(arm, ebo->parent)) == 0)
885                                                 {
886                                                         calc_tw_center_with_matrix(tbounds, ebo->head, use_mat_local, mat_local);
887                                                         totsel++;
888                                                 }
889                                                 if (ebo->flag & BONE_SELECTED) {
890                                                         protectflag_to_drawflags_ebone(rv3d, ebo);
891                                                 }
892                                         }
893                                 }
894                         } FOREACH_EDIT_OBJECT_END();
895                 }
896                 else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) {
897                         FOREACH_EDIT_OBJECT_BEGIN(ob_iter, use_mat_local) {
898                                 Curve *cu = ob_iter->data;
899                                 Nurb *nu;
900                                 BezTriple *bezt;
901                                 BPoint *bp;
902                                 ListBase *nurbs = BKE_curve_editNurbs_get(cu);
903
904                                 float mat_local[4][4];
905                                 if (use_mat_local) {
906                                         mul_m4_m4m4(mat_local, obedit->imat, ob_iter->obmat);
907                                 }
908
909                                 nu = nurbs->first;
910                                 while (nu) {
911                                         if (nu->type == CU_BEZIER) {
912                                                 bezt = nu->bezt;
913                                                 a = nu->pntsu;
914                                                 while (a--) {
915                                                         /* exceptions
916                                                          * if handles are hidden then only check the center points.
917                                                          * If the center knot is selected then only use this as the center point.
918                                                          */
919                                                         if ((v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_CU_HANDLES) == 0) {
920                                                                 if (bezt->f2 & SELECT) {
921                                                                         calc_tw_center_with_matrix(tbounds, bezt->vec[1], use_mat_local, mat_local);
922                                                                         totsel++;
923                                                                 }
924                                                         }
925                                                         else if (bezt->f2 & SELECT) {
926                                                                 calc_tw_center_with_matrix(tbounds, bezt->vec[1], use_mat_local, mat_local);
927                                                                 totsel++;
928                                                         }
929                                                         else {
930                                                                 if (bezt->f1 & SELECT) {
931                                                                         const float *co = bezt->vec[(pivot_point == V3D_AROUND_LOCAL_ORIGINS) ? 1 : 0];
932                                                                         calc_tw_center_with_matrix(tbounds, co, use_mat_local, mat_local);
933                                                                         totsel++;
934                                                                 }
935                                                                 if (bezt->f3 & SELECT) {
936                                                                         const float *co = bezt->vec[(pivot_point == V3D_AROUND_LOCAL_ORIGINS) ? 1 : 2];
937                                                                         calc_tw_center_with_matrix(tbounds, co, use_mat_local, mat_local);
938                                                                         totsel++;
939                                                                 }
940                                                         }
941                                                         bezt++;
942                                                 }
943                                         }
944                                         else {
945                                                 bp = nu->bp;
946                                                 a = nu->pntsu * nu->pntsv;
947                                                 while (a--) {
948                                                         if (bp->f1 & SELECT) {
949                                                                 calc_tw_center_with_matrix(tbounds, bp->vec, use_mat_local, mat_local);
950                                                                 totsel++;
951                                                         }
952                                                         bp++;
953                                                 }
954                                         }
955                                         nu = nu->next;
956                                 }
957                         } FOREACH_EDIT_OBJECT_END();
958                 }
959                 else if (obedit->type == OB_MBALL) {
960                         FOREACH_EDIT_OBJECT_BEGIN(ob_iter, use_mat_local) {
961                                 MetaBall *mb = (MetaBall *)ob_iter->data;
962
963                                 float mat_local[4][4];
964                                 if (use_mat_local) {
965                                         mul_m4_m4m4(mat_local, obedit->imat, ob_iter->obmat);
966                                 }
967
968                                 for (MetaElem *ml = mb->editelems->first; ml; ml = ml->next) {
969                                         if (ml->flag & SELECT) {
970                                                 calc_tw_center_with_matrix(tbounds, &ml->x, use_mat_local, mat_local);
971                                                 totsel++;
972                                         }
973                                 }
974                         } FOREACH_EDIT_OBJECT_END();
975                 }
976                 else if (obedit->type == OB_LATTICE) {
977                         FOREACH_EDIT_OBJECT_BEGIN(ob_iter, use_mat_local) {
978                                 Lattice *lt = ((Lattice *)ob_iter->data)->editlatt->latt;
979                                 BPoint *bp = lt->def;
980                                 a = lt->pntsu * lt->pntsv * lt->pntsw;
981
982                                 float mat_local[4][4];
983                                 if (use_mat_local) {
984                                         mul_m4_m4m4(mat_local, obedit->imat, ob_iter->obmat);
985                                 }
986
987                                 while (a--) {
988                                         if (bp->f1 & SELECT) {
989                                                 calc_tw_center_with_matrix(tbounds, bp->vec, use_mat_local, mat_local);
990                                                 totsel++;
991                                         }
992                                         bp++;
993                                 }
994                         } FOREACH_EDIT_OBJECT_END();
995                 }
996
997 #undef FOREACH_EDIT_OBJECT_BEGIN
998 #undef FOREACH_EDIT_OBJECT_END
999
1000                 /* selection center */
1001                 if (totsel) {
1002                         mul_v3_fl(tbounds->center, 1.0f / (float)totsel);   // centroid!
1003                         mul_m4_v3(obedit->obmat, tbounds->center);
1004                         mul_m4_v3(obedit->obmat, tbounds->min);
1005                         mul_m4_v3(obedit->obmat, tbounds->max);
1006                 }
1007         }
1008         else if (ob && (ob->mode & OB_MODE_POSE)) {
1009                 invert_m4_m4(ob->imat, ob->obmat);
1010                 uint objects_len = 0;
1011                 Object **objects = BKE_view_layer_array_from_objects_in_mode(
1012                         view_layer, v3d, &objects_len, {.object_mode = OB_MODE_POSE});
1013                 for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
1014                         Object *ob_iter = objects[ob_index];
1015                         const bool use_mat_local = (ob_iter != ob);
1016                         bPoseChannel *pchan;
1017
1018                         /* mislead counting bones... bah. We don't know the gizmo mode, could be mixed */
1019                         const int mode = TFM_ROTATION;
1020
1021                         const int totsel_iter = count_set_pose_transflags(ob_iter, mode, V3D_AROUND_CENTER_BOUNDS, NULL);
1022
1023                         if (totsel_iter) {
1024                                 float mat_local[4][4];
1025                                 if (use_mat_local) {
1026                                         mul_m4_m4m4(mat_local, ob->imat, ob_iter->obmat);
1027                                 }
1028
1029                                 /* use channels to get stats */
1030                                 for (pchan = ob_iter->pose->chanbase.first; pchan; pchan = pchan->next) {
1031                                         Bone *bone = pchan->bone;
1032                                         if (bone && (bone->flag & BONE_TRANSFORM)) {
1033                                                 calc_tw_center_with_matrix(tbounds, pchan->pose_head, use_mat_local, mat_local);
1034                                                 protectflag_to_drawflags_pchan(rv3d, pchan);
1035                                         }
1036                                 }
1037                                 totsel += totsel_iter;
1038                         }
1039                 }
1040                 MEM_freeN(objects);
1041
1042                 if (totsel) {
1043                         mul_v3_fl(tbounds->center, 1.0f / (float)totsel);   // centroid!
1044                         mul_m4_v3(ob->obmat, tbounds->center);
1045                         mul_m4_v3(ob->obmat, tbounds->min);
1046                         mul_m4_v3(ob->obmat, tbounds->max);
1047                 }
1048         }
1049         else if (ob && (ob->mode & OB_MODE_ALL_PAINT)) {
1050                 /* pass */
1051         }
1052         else if (ob && ob->mode & OB_MODE_PARTICLE_EDIT) {
1053                 PTCacheEdit *edit = PE_get_current(scene, ob);
1054                 PTCacheEditPoint *point;
1055                 PTCacheEditKey *ek;
1056                 int k;
1057
1058                 if (edit) {
1059                         point = edit->points;
1060                         for (a = 0; a < edit->totpoint; a++, point++) {
1061                                 if (point->flag & PEP_HIDE) continue;
1062
1063                                 for (k = 0, ek = point->keys; k < point->totkey; k++, ek++) {
1064                                         if (ek->flag & PEK_SELECT) {
1065                                                 calc_tw_center(tbounds, (ek->flag & PEK_USE_WCO) ? ek->world_co : ek->co);
1066                                                 totsel++;
1067                                         }
1068                                 }
1069                         }
1070
1071                         /* selection center */
1072                         if (totsel)
1073                                 mul_v3_fl(tbounds->center, 1.0f / (float)totsel);  // centroid!
1074                 }
1075         }
1076         else {
1077
1078                 /* we need the one selected object, if its not active */
1079                 base = BASACT(view_layer);
1080                 ob = OBACT(view_layer);
1081                 if (base && ((base->flag & BASE_SELECTED) == 0)) ob = NULL;
1082
1083                 for (base = view_layer->object_bases.first; base; base = base->next) {
1084                         if (!BASE_SELECTED_EDITABLE(v3d, base)) {
1085                                 continue;
1086                         }
1087                         if (ob == NULL) {
1088                                 ob = base->object;
1089                         }
1090
1091                         /* Get the boundbox out of the evaluated object. */
1092                         const BoundBox *bb = NULL;
1093                         if (params->use_only_center == false) {
1094                                 bb = BKE_object_boundbox_get(base->object);
1095                         }
1096
1097                         if (params->use_only_center || (bb == NULL)) {
1098                                 calc_tw_center(tbounds, base->object->obmat[3]);
1099                         }
1100                         else {
1101                                 for (uint j = 0; j < 8; j++) {
1102                                         float co[3];
1103                                         mul_v3_m4v3(co, base->object->obmat, bb->vec[j]);
1104                                         calc_tw_center(tbounds, co);
1105                                 }
1106                         }
1107                         protectflag_to_drawflags(base->object->protectflag, &rv3d->twdrawflag);
1108                         totsel++;
1109                 }
1110
1111                 /* selection center */
1112                 if (totsel) {
1113                         mul_v3_fl(tbounds->center, 1.0f / (float)totsel);   // centroid!
1114                 }
1115         }
1116
1117         if (totsel == 0) {
1118                 unit_m4(rv3d->twmat);
1119         }
1120         else {
1121                 copy_v3_v3(rv3d->tw_axis_min, tbounds->axis_min);
1122                 copy_v3_v3(rv3d->tw_axis_max, tbounds->axis_max);
1123                 copy_m3_m3(rv3d->tw_axis_matrix, tbounds->axis);
1124         }
1125
1126         return totsel;
1127 }
1128
1129 static void gizmo_get_idot(RegionView3D *rv3d, float r_idot[3])
1130 {
1131         float view_vec[3], axis_vec[3];
1132         ED_view3d_global_to_vector(rv3d, rv3d->twmat[3], view_vec);
1133         for (int i = 0; i < 3; i++) {
1134                 normalize_v3_v3(axis_vec, rv3d->twmat[i]);
1135                 r_idot[i] = 1.0f - fabsf(dot_v3v3(view_vec, axis_vec));
1136         }
1137 }
1138
1139 static void gizmo_prepare_mat(
1140         const bContext *C, RegionView3D *rv3d, const struct TransformBounds *tbounds)
1141 {
1142         Scene *scene = CTX_data_scene(C);
1143         ViewLayer *view_layer = CTX_data_view_layer(C);
1144
1145         switch (scene->toolsettings->transform_pivot_point) {
1146                 case V3D_AROUND_CENTER_BOUNDS:
1147                 case V3D_AROUND_ACTIVE:
1148                 {
1149                         mid_v3_v3v3(rv3d->twmat[3], tbounds->min, tbounds->max);
1150
1151                         if (scene->toolsettings->transform_pivot_point == V3D_AROUND_ACTIVE) {
1152                                 bGPdata *gpd = CTX_data_gpencil_data(C);
1153                                 Object *ob = OBACT(view_layer);
1154                                 if (gpd && (gpd->flag & GP_DATA_STROKE_EDITMODE)) {
1155                                         /* pass */
1156                                 }
1157                                 else if (ob != NULL) {
1158                                         ED_object_calc_active_center(ob, false, rv3d->twmat[3]);
1159                                 }
1160                         }
1161                         break;
1162                 }
1163                 case V3D_AROUND_LOCAL_ORIGINS:
1164                 case V3D_AROUND_CENTER_MEDIAN:
1165                         copy_v3_v3(rv3d->twmat[3], tbounds->center);
1166                         break;
1167                 case V3D_AROUND_CURSOR:
1168                         copy_v3_v3(rv3d->twmat[3], scene->cursor.location);
1169                         break;
1170         }
1171 }
1172
1173 /**
1174  * Sets up \a r_start and \a r_len to define arrow line range.
1175  * Needed to adjust line drawing for combined gizmo axis types.
1176  */
1177 static void gizmo_line_range(const int twtype, const short axis_type, float *r_start, float *r_len)
1178 {
1179         const float ofs = 0.2f;
1180
1181         *r_start = 0.2f;
1182         *r_len = 1.0f;
1183
1184         switch (axis_type) {
1185                 case MAN_AXES_TRANSLATE:
1186                         if (twtype & SCE_GIZMO_SHOW_SCALE) {
1187                                 *r_start = *r_len - ofs + 0.075f;
1188                         }
1189                         if (twtype & SCE_GIZMO_SHOW_ROTATE) {
1190                                 *r_len += ofs;
1191                         }
1192                         break;
1193                 case MAN_AXES_SCALE:
1194                         if (twtype & (SCE_GIZMO_SHOW_TRANSLATE | SCE_GIZMO_SHOW_ROTATE)) {
1195                                 *r_len -= ofs + 0.025f;
1196                         }
1197                         break;
1198         }
1199
1200         *r_len -= *r_start;
1201 }
1202
1203 static void gizmo_xform_message_subscribe(
1204         wmGizmoGroup *gzgroup, struct wmMsgBus *mbus,
1205         Scene *scene, bScreen *UNUSED(screen), ScrArea *UNUSED(sa), ARegion *ar, const void *type_fn)
1206 {
1207         /* Subscribe to view properties */
1208         wmMsgSubscribeValue msg_sub_value_gz_tag_refresh = {
1209                 .owner = ar,
1210                 .user_data = gzgroup->parent_gzmap,
1211                 .notify = WM_gizmo_do_msg_notify_tag_refresh,
1212         };
1213
1214         int orient_flag = 0;
1215         if (type_fn == TRANSFORM_GGT_gizmo) {
1216                 GizmoGroup *ggd = gzgroup->customdata;
1217                 orient_flag = ggd->twtype_init;
1218         }
1219         else if (type_fn == VIEW3D_GGT_xform_cage) {
1220                 orient_flag = SCE_GIZMO_SHOW_SCALE;
1221                 /* pass */
1222         }
1223         else if (type_fn == VIEW3D_GGT_xform_shear) {
1224                 orient_flag = SCE_GIZMO_SHOW_ROTATE;
1225         }
1226         TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get(scene, orient_flag);
1227         PointerRNA orient_ref_ptr;
1228         RNA_pointer_create(&scene->id, &RNA_TransformOrientationSlot, orient_slot, &orient_ref_ptr);
1229
1230         PointerRNA scene_ptr;
1231         RNA_id_pointer_create(&scene->id, &scene_ptr);
1232         {
1233                 const ToolSettings *ts = scene->toolsettings;
1234                 extern PropertyRNA rna_Scene_transform_orientation_slots;
1235                 extern PropertyRNA rna_Scene_cursor_location;
1236                 const PropertyRNA *props[] = {
1237                         &rna_Scene_transform_orientation_slots,
1238                         ((ts->transform_pivot_point == V3D_AROUND_CURSOR) || (orient_slot->type == V3D_ORIENT_CURSOR)) ?
1239                         &rna_Scene_cursor_location : NULL,
1240                 };
1241                 for (int i = 0; i < ARRAY_SIZE(props); i++) {
1242                         if (props[i]) {
1243                                 WM_msg_subscribe_rna(mbus, &scene_ptr, props[i], &msg_sub_value_gz_tag_refresh, __func__);
1244                         }
1245                 }
1246         }
1247
1248         {
1249                 extern PropertyRNA rna_TransformOrientationSlot_type;
1250                 extern PropertyRNA rna_TransformOrientationSlot_use;
1251                 const PropertyRNA *props[] = {
1252                         &rna_TransformOrientationSlot_type,
1253                         &rna_TransformOrientationSlot_use,
1254                 };
1255                 for (int i = 0; i < ARRAY_SIZE(props); i++) {
1256                         if (props[i]) {
1257                                 WM_msg_subscribe_rna(mbus, &orient_ref_ptr, props[i], &msg_sub_value_gz_tag_refresh, __func__);
1258                         }
1259                 }
1260         }
1261
1262         PointerRNA toolsettings_ptr;
1263         RNA_pointer_create(&scene->id, &RNA_ToolSettings, scene->toolsettings, &toolsettings_ptr);
1264
1265         if (type_fn == TRANSFORM_GGT_gizmo) {
1266                 GizmoGroup *ggd = gzgroup->customdata;
1267                 extern PropertyRNA rna_ToolSettings_transform_pivot_point;
1268                 extern PropertyRNA rna_ToolSettings_use_gizmo_mode;
1269                 const PropertyRNA *props[] = {
1270                         &rna_ToolSettings_transform_pivot_point,
1271                         ggd->use_twtype_refresh ? &rna_ToolSettings_use_gizmo_mode : NULL,
1272                 };
1273                 for (int i = 0; i < ARRAY_SIZE(props); i++) {
1274                         WM_msg_subscribe_rna(mbus, &toolsettings_ptr, props[i], &msg_sub_value_gz_tag_refresh, __func__);
1275                 }
1276         }
1277         else if (type_fn == VIEW3D_GGT_xform_cage) {
1278                 /* pass */
1279         }
1280         else if (type_fn == VIEW3D_GGT_xform_shear) {
1281                 /* pass */
1282         }
1283         else {
1284                 BLI_assert(0);
1285         }
1286
1287         WM_msg_subscribe_rna_anon_prop(mbus, Window, view_layer, &msg_sub_value_gz_tag_refresh);
1288 }
1289
1290
1291 void drawDial3d(const TransInfo *t)
1292 {
1293         if (t->mode == TFM_ROTATION && t->spacetype == SPACE_VIEW3D) {
1294                 wmGizmo *gz = wm_gizmomap_highlight_get(t->ar->gizmo_map);
1295                 if (gz == NULL) {
1296                         /* We only draw Dial3d if the operator has been called by a gizmo. */
1297                         return;
1298                 }
1299
1300                 float mat_basis[4][4];
1301                 float mat_final[4][4];
1302                 float color[4];
1303                 float increment;
1304                 float line_with = GIZMO_AXIS_LINE_WIDTH + 1.0f;
1305                 float scale = UI_DPI_FAC * U.gizmo_size;
1306
1307                 int axis_idx;
1308
1309                 const TransCon *tc = &(t->con);
1310                 if (tc->mode & CON_APPLY) {
1311                         if (tc->mode & CON_AXIS0) {
1312                                 axis_idx = MAN_AXIS_ROT_X;
1313                                 negate_v3_v3(mat_basis[2], tc->mtx[0]);
1314                         }
1315                         else if (tc->mode &  CON_AXIS1) {
1316                                 axis_idx = MAN_AXIS_ROT_Y;
1317                                 negate_v3_v3(mat_basis[2], tc->mtx[1]);
1318                         }
1319                         else {
1320                                 BLI_assert((tc->mode & CON_AXIS2) != 0);
1321                                 axis_idx = MAN_AXIS_ROT_Z;
1322                                 negate_v3_v3(mat_basis[2], tc->mtx[2]);
1323                         }
1324                 }
1325                 else {
1326                         axis_idx = MAN_AXIS_ROT_C;
1327                         negate_v3_v3(mat_basis[2], t->axis);
1328                         scale *= 1.2f;
1329                         line_with -= 1.0f;
1330                 }
1331
1332                 copy_v3_v3(mat_basis[3], t->center_global);
1333                 mat_basis[2][3] = -dot_v3v3(mat_basis[2], mat_basis[3]);
1334
1335                 if (ED_view3d_win_to_3d_on_plane(
1336                             t->ar, mat_basis[2], (float[2]){UNPACK2(t->mouse.imval)},
1337                             false, mat_basis[1]))
1338                 {
1339                         sub_v3_v3(mat_basis[1], mat_basis[3]);
1340                         normalize_v3(mat_basis[1]);
1341                         cross_v3_v3v3(mat_basis[0], mat_basis[1], mat_basis[2]);
1342                 }
1343                 else {
1344                         /* The plane and the mouse direction are parallel.
1345                          * Calculate a matrix orthogonal to the axis. */
1346                         ortho_basis_v3v3_v3(mat_basis[0], mat_basis[1], mat_basis[2]);
1347                 }
1348
1349                 mat_basis[0][3] = 0.0f;
1350                 mat_basis[1][3] = 0.0f;
1351                 mat_basis[2][3] = 0.0f;
1352                 mat_basis[3][3] = 1.0f;
1353
1354                 copy_m4_m4(mat_final, mat_basis);
1355                 scale *= ED_view3d_pixel_size_no_ui_scale(t->ar->regiondata, mat_final[3]);
1356                 mul_mat3_m4_fl(mat_final, scale);
1357
1358                 if ((t->tsnap.mode & (SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID)) &&
1359                     activeSnap(t))
1360                 {
1361                         increment = (t->modifiers & MOD_PRECISION) ? t->snap[2] : t->snap[1];
1362                 }
1363                 else {
1364                         increment = t->snap[0];
1365                 }
1366
1367                 BLI_assert(axis_idx >= MAN_AXIS_RANGE_ROT_START && axis_idx < MAN_AXIS_RANGE_ROT_END);
1368                 gizmo_get_axis_color(axis_idx, NULL, color, color);
1369
1370                 GPU_depth_test(false);
1371                 GPU_blend(true);
1372                 GPU_line_smooth(true);
1373
1374                 ED_gizmotypes_dial_3d_draw_util(
1375                         mat_basis, mat_final, line_with, color,
1376                         &(struct Dial3dParams){
1377                             .draw_options = ED_GIZMO_DIAL_DRAW_FLAG_ANGLE_VALUE,
1378                             .angle_delta = t->values[0],
1379                             .angle_increment = increment,
1380                         });
1381
1382                 GPU_line_smooth(false);
1383                 GPU_depth_test(true);
1384                 GPU_blend(false);
1385         }
1386 }
1387
1388 /** \} */
1389
1390
1391 /* -------------------------------------------------------------------- */
1392 /** \name Transform Gizmo
1393  * \{ */
1394
1395 static GizmoGroup *gizmogroup_init(wmGizmoGroup *gzgroup)
1396 {
1397         GizmoGroup *ggd;
1398
1399         ggd = MEM_callocN(sizeof(GizmoGroup), "gizmo_data");
1400
1401         const wmGizmoType *gzt_arrow = WM_gizmotype_find("GIZMO_GT_arrow_3d", true);
1402         const wmGizmoType *gzt_dial = WM_gizmotype_find("GIZMO_GT_dial_3d", true);
1403         const wmGizmoType *gzt_prim = WM_gizmotype_find("GIZMO_GT_primitive_3d", true);
1404
1405 #define GIZMO_NEW_ARROW(v, draw_style) { \
1406         ggd->gizmos[v] = WM_gizmo_new_ptr(gzt_arrow, gzgroup, NULL); \
1407         RNA_enum_set(ggd->gizmos[v]->ptr, "draw_style", draw_style); \
1408 } ((void)0)
1409 #define GIZMO_NEW_DIAL(v, draw_options) { \
1410         ggd->gizmos[v] = WM_gizmo_new_ptr(gzt_dial, gzgroup, NULL); \
1411         RNA_enum_set(ggd->gizmos[v]->ptr, "draw_options", draw_options); \
1412 } ((void)0)
1413 #define GIZMO_NEW_PRIM(v, draw_style) { \
1414         ggd->gizmos[v] = WM_gizmo_new_ptr(gzt_prim, gzgroup, NULL); \
1415         RNA_enum_set(ggd->gizmos[v]->ptr, "draw_style", draw_style); \
1416 } ((void)0)
1417
1418         /* add/init widgets - order matters! */
1419         GIZMO_NEW_DIAL(MAN_AXIS_ROT_T, ED_GIZMO_DIAL_DRAW_FLAG_FILL);
1420
1421         GIZMO_NEW_DIAL(MAN_AXIS_SCALE_C, ED_GIZMO_DIAL_DRAW_FLAG_NOP);
1422
1423         GIZMO_NEW_ARROW(MAN_AXIS_SCALE_X, ED_GIZMO_ARROW_STYLE_BOX);
1424         GIZMO_NEW_ARROW(MAN_AXIS_SCALE_Y, ED_GIZMO_ARROW_STYLE_BOX);
1425         GIZMO_NEW_ARROW(MAN_AXIS_SCALE_Z, ED_GIZMO_ARROW_STYLE_BOX);
1426
1427         GIZMO_NEW_PRIM(MAN_AXIS_SCALE_XY, ED_GIZMO_PRIMITIVE_STYLE_PLANE);
1428         GIZMO_NEW_PRIM(MAN_AXIS_SCALE_YZ, ED_GIZMO_PRIMITIVE_STYLE_PLANE);
1429         GIZMO_NEW_PRIM(MAN_AXIS_SCALE_ZX, ED_GIZMO_PRIMITIVE_STYLE_PLANE);
1430
1431         GIZMO_NEW_DIAL(MAN_AXIS_ROT_X, ED_GIZMO_DIAL_DRAW_FLAG_CLIP);
1432         GIZMO_NEW_DIAL(MAN_AXIS_ROT_Y, ED_GIZMO_DIAL_DRAW_FLAG_CLIP);
1433         GIZMO_NEW_DIAL(MAN_AXIS_ROT_Z, ED_GIZMO_DIAL_DRAW_FLAG_CLIP);
1434
1435         /* init screen aligned widget last here, looks better, behaves better */
1436         GIZMO_NEW_DIAL(MAN_AXIS_ROT_C, ED_GIZMO_DIAL_DRAW_FLAG_NOP);
1437
1438         GIZMO_NEW_DIAL(MAN_AXIS_TRANS_C, ED_GIZMO_DIAL_DRAW_FLAG_NOP);
1439
1440         GIZMO_NEW_ARROW(MAN_AXIS_TRANS_X, ED_GIZMO_ARROW_STYLE_NORMAL);
1441         GIZMO_NEW_ARROW(MAN_AXIS_TRANS_Y, ED_GIZMO_ARROW_STYLE_NORMAL);
1442         GIZMO_NEW_ARROW(MAN_AXIS_TRANS_Z, ED_GIZMO_ARROW_STYLE_NORMAL);
1443
1444         GIZMO_NEW_PRIM(MAN_AXIS_TRANS_XY, ED_GIZMO_PRIMITIVE_STYLE_PLANE);
1445         GIZMO_NEW_PRIM(MAN_AXIS_TRANS_YZ, ED_GIZMO_PRIMITIVE_STYLE_PLANE);
1446         GIZMO_NEW_PRIM(MAN_AXIS_TRANS_ZX, ED_GIZMO_PRIMITIVE_STYLE_PLANE);
1447
1448         ggd->gizmos[MAN_AXIS_ROT_T]->flag |= WM_GIZMO_SELECT_BACKGROUND;
1449
1450         return ggd;
1451 }
1452
1453 /**
1454  * Custom handler for gizmo widgets
1455  */
1456 static int gizmo_modal(
1457         bContext *C, wmGizmo *widget, const wmEvent *event,
1458         eWM_GizmoFlagTweak UNUSED(tweak_flag))
1459 {
1460         /* Avoid unnecessary updates, partially address: T55458. */
1461         if (ELEM(event->type, TIMER, INBETWEEN_MOUSEMOVE)) {
1462                 return OPERATOR_RUNNING_MODAL;
1463         }
1464
1465         ARegion *ar = CTX_wm_region(C);
1466         RegionView3D *rv3d = ar->regiondata;
1467         struct TransformBounds tbounds;
1468
1469
1470         if (ED_transform_calc_gizmo_stats(
1471                     C, &(struct TransformCalcParams){
1472                         .use_only_center = true,
1473                     }, &tbounds))
1474         {
1475                 gizmo_prepare_mat(C, rv3d, &tbounds);
1476                 WM_gizmo_set_matrix_location(widget, rv3d->twmat[3]);
1477         }
1478
1479         ED_region_tag_redraw(ar);
1480
1481         return OPERATOR_RUNNING_MODAL;
1482 }
1483
1484 static void gizmogroup_init_properties_from_twtype(wmGizmoGroup *gzgroup)
1485 {
1486         struct {
1487                 wmOperatorType *translate, *rotate, *trackball, *resize;
1488         } ot_store = {NULL};
1489         GizmoGroup *ggd = gzgroup->customdata;
1490
1491         MAN_ITER_AXES_BEGIN(axis, axis_idx)
1492         {
1493                 const short axis_type = gizmo_get_axis_type(axis_idx);
1494                 bool constraint_axis[3] = {1, 0, 0};
1495                 PointerRNA *ptr = NULL;
1496
1497                 gizmo_get_axis_constraint(axis_idx, constraint_axis);
1498
1499                 /* custom handler! */
1500                 WM_gizmo_set_fn_custom_modal(axis, gizmo_modal);
1501
1502                 switch (axis_idx) {
1503                         case MAN_AXIS_TRANS_X:
1504                         case MAN_AXIS_TRANS_Y:
1505                         case MAN_AXIS_TRANS_Z:
1506                         case MAN_AXIS_SCALE_X:
1507                         case MAN_AXIS_SCALE_Y:
1508                         case MAN_AXIS_SCALE_Z:
1509                                 if (axis_idx >= MAN_AXIS_RANGE_TRANS_START && axis_idx < MAN_AXIS_RANGE_TRANS_END) {
1510                                         int draw_options = 0;
1511                                         if ((ggd->twtype & (SCE_GIZMO_SHOW_ROTATE | SCE_GIZMO_SHOW_SCALE)) == 0) {
1512                                                 draw_options |= ED_GIZMO_ARROW_DRAW_FLAG_STEM;
1513                                         }
1514                                         RNA_enum_set(axis->ptr, "draw_options", draw_options);
1515                                 }
1516
1517                                 WM_gizmo_set_line_width(axis, GIZMO_AXIS_LINE_WIDTH);
1518                                 break;
1519                         case MAN_AXIS_ROT_X:
1520                         case MAN_AXIS_ROT_Y:
1521                         case MAN_AXIS_ROT_Z:
1522                                 /* increased line width for better display */
1523                                 WM_gizmo_set_line_width(axis, GIZMO_AXIS_LINE_WIDTH + 1.0f);
1524                                 WM_gizmo_set_flag(axis, WM_GIZMO_DRAW_VALUE, true);
1525                                 break;
1526                         case MAN_AXIS_TRANS_XY:
1527                         case MAN_AXIS_TRANS_YZ:
1528                         case MAN_AXIS_TRANS_ZX:
1529                         case MAN_AXIS_SCALE_XY:
1530                         case MAN_AXIS_SCALE_YZ:
1531                         case MAN_AXIS_SCALE_ZX:
1532                         {
1533                                 const float ofs_ax = 7.0f;
1534                                 const float ofs[3] = {ofs_ax, ofs_ax, 0.0f};
1535                                 WM_gizmo_set_scale(axis, 0.07f);
1536                                 WM_gizmo_set_matrix_offset_location(axis, ofs);
1537                                 WM_gizmo_set_flag(axis, WM_GIZMO_DRAW_OFFSET_SCALE, true);
1538                                 break;
1539                         }
1540                         case MAN_AXIS_TRANS_C:
1541                         case MAN_AXIS_ROT_C:
1542                         case MAN_AXIS_SCALE_C:
1543                         case MAN_AXIS_ROT_T:
1544                                 WM_gizmo_set_line_width(axis, GIZMO_AXIS_LINE_WIDTH);
1545                                 if (axis_idx == MAN_AXIS_ROT_T) {
1546                                         WM_gizmo_set_flag(axis, WM_GIZMO_DRAW_HOVER, true);
1547                                 }
1548                                 else if (axis_idx == MAN_AXIS_ROT_C) {
1549                                         WM_gizmo_set_flag(axis, WM_GIZMO_DRAW_VALUE, true);
1550                                         WM_gizmo_set_scale(axis, 1.2f);
1551                                 }
1552                                 else {
1553                                         WM_gizmo_set_scale(axis, 0.2f);
1554                                 }
1555                                 break;
1556                 }
1557
1558                 switch (axis_type) {
1559                         case MAN_AXES_TRANSLATE:
1560                                 if (ot_store.translate == NULL) {
1561                                         ot_store.translate = WM_operatortype_find("TRANSFORM_OT_translate", true);
1562                                 }
1563                                 ptr = WM_gizmo_operator_set(axis, 0, ot_store.translate, NULL);
1564                                 break;
1565                         case MAN_AXES_ROTATE:
1566                         {
1567                                 wmOperatorType *ot_rotate;
1568                                 if (axis_idx == MAN_AXIS_ROT_T) {
1569                                         if (ot_store.trackball == NULL) {
1570                                                 ot_store.trackball = WM_operatortype_find("TRANSFORM_OT_trackball", true);
1571                                         }
1572                                         ot_rotate = ot_store.trackball;
1573                                 }
1574                                 else {
1575                                         if (ot_store.rotate == NULL) {
1576                                                 ot_store.rotate = WM_operatortype_find("TRANSFORM_OT_rotate", true);
1577                                         }
1578                                         ot_rotate = ot_store.rotate;
1579                                 }
1580                                 ptr = WM_gizmo_operator_set(axis, 0, ot_rotate, NULL);
1581                                 break;
1582                         }
1583                         case MAN_AXES_SCALE:
1584                         {
1585                                 if (ot_store.resize == NULL) {
1586                                         ot_store.resize = WM_operatortype_find("TRANSFORM_OT_resize", true);
1587                                 }
1588                                 ptr = WM_gizmo_operator_set(axis, 0, ot_store.resize, NULL);
1589                                 break;
1590                         }
1591                 }
1592
1593                 if (ptr) {
1594                         PropertyRNA *prop;
1595                         if (ELEM(true, UNPACK3(constraint_axis))) {
1596                                 if ((prop = RNA_struct_find_property(ptr, "constraint_axis"))) {
1597                                         RNA_property_boolean_set_array(ptr, prop, constraint_axis);
1598                                 }
1599                         }
1600
1601                         RNA_boolean_set(ptr, "release_confirm", 1);
1602                 }
1603         }
1604         MAN_ITER_AXES_END;
1605 }
1606
1607 static void WIDGETGROUP_gizmo_setup(const bContext *C, wmGizmoGroup *gzgroup)
1608 {
1609         GizmoGroup *ggd = gizmogroup_init(gzgroup);
1610
1611         gzgroup->customdata = ggd;
1612
1613         {
1614                 ggd->twtype = 0;
1615                 ScrArea *sa = CTX_wm_area(C);
1616                 const bToolRef *tref = sa->runtime.tool;
1617
1618                 if (tref == NULL || STREQ(tref->idname, "Transform")) {
1619                         /* Setup all gizmos, they can be toggled via 'ToolSettings.gizmo_flag' */
1620                         ggd->twtype = SCE_GIZMO_SHOW_TRANSLATE | SCE_GIZMO_SHOW_ROTATE | SCE_GIZMO_SHOW_SCALE;
1621                         ggd->use_twtype_refresh = true;
1622                 }
1623                 else if (STREQ(tref->idname, "Move")) {
1624                         ggd->twtype |= SCE_GIZMO_SHOW_TRANSLATE;
1625                 }
1626                 else if (STREQ(tref->idname, "Rotate")) {
1627                         ggd->twtype |= SCE_GIZMO_SHOW_ROTATE;
1628                 }
1629                 else if (STREQ(tref->idname, "Scale")) {
1630                         ggd->twtype |= SCE_GIZMO_SHOW_SCALE;
1631                 }
1632                 BLI_assert(ggd->twtype != 0);
1633                 ggd->twtype_init = ggd->twtype;
1634         }
1635
1636         /* *** set properties for axes *** */
1637         gizmogroup_init_properties_from_twtype(gzgroup);
1638 }
1639
1640 static void WIDGETGROUP_gizmo_refresh(const bContext *C, wmGizmoGroup *gzgroup)
1641 {
1642         GizmoGroup *ggd = gzgroup->customdata;
1643         Scene *scene = CTX_data_scene(C);
1644         ARegion *ar = CTX_wm_region(C);
1645         RegionView3D *rv3d = ar->regiondata;
1646         struct TransformBounds tbounds;
1647
1648         if (ggd->use_twtype_refresh) {
1649                 ggd->twtype = scene->toolsettings->gizmo_flag & ggd->twtype_init;
1650                 if (ggd->twtype != ggd->twtype_prev) {
1651                         ggd->twtype_prev = ggd->twtype;
1652                         gizmogroup_init_properties_from_twtype(gzgroup);
1653                 }
1654         }
1655
1656         const TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get(scene, ggd->twtype_init);
1657
1658         /* skip, we don't draw anything anyway */
1659         if ((ggd->all_hidden =
1660              (ED_transform_calc_gizmo_stats(
1661                      C, &(struct TransformCalcParams){
1662                          .use_only_center = true,
1663                          .orientation_type = orient_slot->type + 1,
1664                          .orientation_index_custom = orient_slot->index_custom,
1665                      }, &tbounds) == 0)))
1666         {
1667                 return;
1668         }
1669
1670         gizmo_prepare_mat(C, rv3d, &tbounds);
1671
1672         /* *** set properties for axes *** */
1673
1674         MAN_ITER_AXES_BEGIN(axis, axis_idx)
1675         {
1676                 const short axis_type = gizmo_get_axis_type(axis_idx);
1677                 const int aidx_norm = gizmo_orientation_axis(axis_idx, NULL);
1678
1679                 WM_gizmo_set_matrix_location(axis, rv3d->twmat[3]);
1680
1681                 switch (axis_idx) {
1682                         case MAN_AXIS_TRANS_X:
1683                         case MAN_AXIS_TRANS_Y:
1684                         case MAN_AXIS_TRANS_Z:
1685                         case MAN_AXIS_SCALE_X:
1686                         case MAN_AXIS_SCALE_Y:
1687                         case MAN_AXIS_SCALE_Z:
1688                         {
1689                                 float start_co[3] = {0.0f, 0.0f, 0.0f};
1690                                 float len;
1691
1692                                 gizmo_line_range(ggd->twtype, axis_type, &start_co[2], &len);
1693
1694                                 WM_gizmo_set_matrix_rotation_from_z_axis(axis, rv3d->twmat[aidx_norm]);
1695                                 RNA_float_set(axis->ptr, "length", len);
1696
1697                                 if (axis_idx >= MAN_AXIS_RANGE_TRANS_START && axis_idx < MAN_AXIS_RANGE_TRANS_END) {
1698                                         if (ggd->twtype & SCE_GIZMO_SHOW_ROTATE) {
1699                                                 /* Avoid rotate and translate arrows overlap. */
1700                                                 start_co[2] += 0.215f;
1701                                         }
1702                                 }
1703                                 WM_gizmo_set_matrix_offset_location(axis, start_co);
1704                                 WM_gizmo_set_flag(axis, WM_GIZMO_DRAW_OFFSET_SCALE, true);
1705                                 break;
1706                         }
1707                         case MAN_AXIS_ROT_X:
1708                         case MAN_AXIS_ROT_Y:
1709                         case MAN_AXIS_ROT_Z:
1710                                 WM_gizmo_set_matrix_rotation_from_z_axis(axis, rv3d->twmat[aidx_norm]);
1711                                 break;
1712                         case MAN_AXIS_TRANS_XY:
1713                         case MAN_AXIS_TRANS_YZ:
1714                         case MAN_AXIS_TRANS_ZX:
1715                         case MAN_AXIS_SCALE_XY:
1716                         case MAN_AXIS_SCALE_YZ:
1717                         case MAN_AXIS_SCALE_ZX:
1718                         {
1719                                 const float *y_axis = rv3d->twmat[aidx_norm - 1 < 0 ? 2 : aidx_norm - 1];
1720                                 const float *z_axis = rv3d->twmat[aidx_norm];
1721                                 WM_gizmo_set_matrix_rotation_from_yz_axis(axis, y_axis, z_axis);
1722                                 break;
1723                         }
1724                 }
1725         }
1726         MAN_ITER_AXES_END;
1727 }
1728
1729 static void WIDGETGROUP_gizmo_message_subscribe(
1730         const bContext *C, wmGizmoGroup *gzgroup, struct wmMsgBus *mbus)
1731 {
1732         Scene *scene = CTX_data_scene(C);
1733         bScreen *screen = CTX_wm_screen(C);
1734         ScrArea *sa = CTX_wm_area(C);
1735         ARegion *ar = CTX_wm_region(C);
1736         gizmo_xform_message_subscribe(gzgroup, mbus, scene, screen, sa, ar, TRANSFORM_GGT_gizmo);
1737 }
1738
1739 static void WIDGETGROUP_gizmo_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup)
1740 {
1741         GizmoGroup *ggd = gzgroup->customdata;
1742         // ScrArea *sa = CTX_wm_area(C);
1743         ARegion *ar = CTX_wm_region(C);
1744         // View3D *v3d = sa->spacedata.first;
1745         RegionView3D *rv3d = ar->regiondata;
1746         float viewinv_m3[3][3];
1747         copy_m3_m4(viewinv_m3, rv3d->viewinv);
1748         float idot[3];
1749
1750         /* when looking through a selected camera, the gizmo can be at the
1751          * exact same position as the view, skip so we don't break selection */
1752         if (ggd->all_hidden || fabsf(ED_view3d_pixel_size(rv3d, rv3d->twmat[3])) < 1e-6f) {
1753                 MAN_ITER_AXES_BEGIN(axis, axis_idx)
1754                 {
1755                         WM_gizmo_set_flag(axis, WM_GIZMO_HIDDEN, true);
1756                 }
1757                 MAN_ITER_AXES_END;
1758                 return;
1759         }
1760         gizmo_get_idot(rv3d, idot);
1761
1762         /* *** set properties for axes *** */
1763         MAN_ITER_AXES_BEGIN(axis, axis_idx) {
1764                 const short axis_type = gizmo_get_axis_type(axis_idx);
1765                 /* XXX maybe unset _HIDDEN flag on redraw? */
1766
1767                 if (gizmo_is_axis_visible(rv3d, ggd->twtype, idot, axis_type, axis_idx)) {
1768                         WM_gizmo_set_flag(axis, WM_GIZMO_HIDDEN, false);
1769                 }
1770                 else {
1771                         WM_gizmo_set_flag(axis, WM_GIZMO_HIDDEN, true);
1772                         continue;
1773                 }
1774
1775                 float color[4], color_hi[4];
1776                 gizmo_get_axis_color(axis_idx, idot, color, color_hi);
1777                 WM_gizmo_set_color(axis, color);
1778                 WM_gizmo_set_color_highlight(axis, color_hi);
1779
1780                 switch (axis_idx) {
1781                         case MAN_AXIS_TRANS_C:
1782                         case MAN_AXIS_ROT_C:
1783                         case MAN_AXIS_SCALE_C:
1784                         case MAN_AXIS_ROT_T:
1785                                 WM_gizmo_set_matrix_rotation_from_z_axis(axis, rv3d->viewinv[2]);
1786                                 break;
1787                 }
1788         } MAN_ITER_AXES_END;
1789
1790         /* Refresh handled above when using view orientation. */
1791         if (!equals_m3m3(viewinv_m3, ggd->prev.viewinv_m3)) {
1792                 {
1793                         Scene *scene = CTX_data_scene(C);
1794                         const TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get(scene, ggd->twtype_init);
1795                         switch (orient_slot->type) {
1796                                 case V3D_ORIENT_VIEW:
1797                                 {
1798                                         WIDGETGROUP_gizmo_refresh(C, gzgroup);
1799                                         break;
1800                                 }
1801                         }
1802                 }
1803                 copy_m3_m4(ggd->prev.viewinv_m3, rv3d->viewinv);
1804         }
1805
1806 }
1807
1808 static void WIDGETGROUP_gizmo_invoke_prepare(
1809         const bContext *C, wmGizmoGroup *gzgroup, wmGizmo *gz)
1810 {
1811
1812         GizmoGroup *ggd = gzgroup->customdata;
1813
1814         /* Support gizmo spesific orientation. */
1815         if (gz != ggd->gizmos[MAN_AXIS_ROT_T]) {
1816                 Scene *scene = CTX_data_scene(C);
1817                 wmGizmoOpElem *gzop = WM_gizmo_operator_get(gz, 0);
1818                 PointerRNA *ptr = &gzop->ptr;
1819                 PropertyRNA *prop_constraint_orientation = RNA_struct_find_property(ptr, "constraint_orientation");
1820                 const TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get(scene, ggd->twtype_init);
1821                 if (orient_slot == &scene->orientation_slots[SCE_ORIENT_DEFAULT]) {
1822                         RNA_property_unset(ptr, prop_constraint_orientation);
1823                 }
1824                 else {
1825                         /* TODO: APIfunction */
1826                         int index = BKE_scene_orientation_slot_get_index(orient_slot);
1827                         RNA_property_enum_set(ptr, prop_constraint_orientation, index);
1828                 }
1829         }
1830
1831         /* Support shift click to constrain axis. */
1832         const int axis_idx = BLI_array_findindex(ggd->gizmos, ARRAY_SIZE(ggd->gizmos), &gz);
1833         int axis = -1;
1834         switch (axis_idx) {
1835                 case MAN_AXIS_TRANS_X:
1836                 case MAN_AXIS_TRANS_Y:
1837                 case MAN_AXIS_TRANS_Z:
1838                         axis = axis_idx - MAN_AXIS_TRANS_X; break;
1839                 case MAN_AXIS_SCALE_X:
1840                 case MAN_AXIS_SCALE_Y:
1841                 case MAN_AXIS_SCALE_Z:
1842                         axis = axis_idx - MAN_AXIS_SCALE_X; break;
1843         }
1844
1845         if (axis != -1) {
1846                 wmWindow *win = CTX_wm_window(C);
1847                 /* Swap single axis for two-axis constraint. */
1848                 bool flip = win->eventstate->shift;
1849                 BLI_assert(axis_idx != -1);
1850                 const short axis_type = gizmo_get_axis_type(axis_idx);
1851                 if (axis_type != MAN_AXES_ROTATE) {
1852                         wmGizmoOpElem *gzop = WM_gizmo_operator_get(gz, 0);
1853                         PointerRNA *ptr = &gzop->ptr;
1854                         PropertyRNA *prop_constraint_axis = RNA_struct_find_property(ptr, "constraint_axis");
1855                         if (prop_constraint_axis) {
1856                                 bool constraint[3] = {false};
1857                                 constraint[axis] = true;
1858                                 if (flip) {
1859                                         for (int i = 0; i < ARRAY_SIZE(constraint); i++) {
1860                                                 constraint[i] = !constraint[i];
1861                                         }
1862                                 }
1863                                 RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint);
1864                         }
1865                 }
1866         }
1867 }
1868
1869 static bool WIDGETGROUP_gizmo_poll(const struct bContext *C, struct wmGizmoGroupType *gzgt)
1870 {
1871         if (!ED_gizmo_poll_or_unlink_delayed_from_tool(C, gzgt)) {
1872                 return false;
1873         }
1874         View3D *v3d = CTX_wm_view3d(C);
1875         if (v3d->gizmo_flag & (V3D_GIZMO_HIDE | V3D_GIZMO_HIDE_TOOL)) {
1876                 return false;
1877         }
1878         if (G.moving & (G_TRANSFORM_OBJ | G_TRANSFORM_EDIT)) {
1879                 return false;
1880         }
1881         return true;
1882 }
1883
1884 void TRANSFORM_GGT_gizmo(wmGizmoGroupType *gzgt)
1885 {
1886         gzgt->name = "Transform Gizmo";
1887         gzgt->idname = "TRANSFORM_GGT_gizmo";
1888
1889         gzgt->flag |= WM_GIZMOGROUPTYPE_3D;
1890
1891         gzgt->gzmap_params.spaceid = SPACE_VIEW3D;
1892         gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW;
1893
1894         gzgt->poll = WIDGETGROUP_gizmo_poll;
1895         gzgt->setup = WIDGETGROUP_gizmo_setup;
1896         gzgt->refresh = WIDGETGROUP_gizmo_refresh;
1897         gzgt->message_subscribe = WIDGETGROUP_gizmo_message_subscribe;
1898         gzgt->draw_prepare = WIDGETGROUP_gizmo_draw_prepare;
1899         gzgt->invoke_prepare = WIDGETGROUP_gizmo_invoke_prepare;
1900
1901         static const EnumPropertyItem rna_enum_gizmo_items[] = {
1902                 {SCE_GIZMO_SHOW_TRANSLATE, "TRANSLATE", 0, "Move", ""},
1903                 {SCE_GIZMO_SHOW_ROTATE, "ROTATE", 0, "Rotate", ""},
1904                 {SCE_GIZMO_SHOW_SCALE, "SCALE", 0, "Scale", ""},
1905                 {0, "NONE", 0, "None", ""},
1906                 {0, NULL, 0, NULL, NULL},
1907         };
1908         RNA_def_enum(gzgt->srna, "drag_action", rna_enum_gizmo_items, SCE_GIZMO_SHOW_TRANSLATE, "Drag Action", "");
1909 }
1910
1911 /** \} */
1912
1913
1914 /* -------------------------------------------------------------------- */
1915 /** \name Scale Cage Gizmo
1916  * \{ */
1917
1918 struct XFormCageWidgetGroup {
1919         wmGizmo *gizmo;
1920         /* Only for view orientation. */
1921         struct {
1922                 float viewinv_m3[3][3];
1923         } prev;
1924 };
1925
1926 static bool WIDGETGROUP_xform_cage_poll(const bContext *C, wmGizmoGroupType *gzgt)
1927 {
1928         if (!ED_gizmo_poll_or_unlink_delayed_from_tool(C, gzgt)) {
1929                 return false;
1930         }
1931         View3D *v3d = CTX_wm_view3d(C);
1932         if (v3d->gizmo_flag & (V3D_GIZMO_HIDE | V3D_GIZMO_HIDE_TOOL)) {
1933                 return false;
1934         }
1935         if (G.moving & (G_TRANSFORM_OBJ | G_TRANSFORM_EDIT)) {
1936                 return false;
1937         }
1938         return true;
1939 }
1940
1941 static void WIDGETGROUP_xform_cage_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup)
1942 {
1943         struct XFormCageWidgetGroup *xgzgroup = MEM_mallocN(sizeof(struct XFormCageWidgetGroup), __func__);
1944         const wmGizmoType *gzt_cage = WM_gizmotype_find("GIZMO_GT_cage_3d", true);
1945         xgzgroup->gizmo = WM_gizmo_new_ptr(gzt_cage, gzgroup, NULL);
1946         wmGizmo *gz = xgzgroup->gizmo;
1947
1948         RNA_enum_set(gz->ptr, "transform",
1949                      ED_GIZMO_CAGE2D_XFORM_FLAG_SCALE |
1950                      ED_GIZMO_CAGE2D_XFORM_FLAG_TRANSLATE);
1951
1952         gz->color[0] = 1;
1953         gz->color_hi[0] = 1;
1954
1955         gzgroup->customdata = xgzgroup;
1956
1957         {
1958                 wmOperatorType *ot_resize = WM_operatortype_find("TRANSFORM_OT_resize", true);
1959                 PointerRNA *ptr;
1960
1961                 /* assign operator */
1962                 PropertyRNA *prop_release_confirm = NULL;
1963                 PropertyRNA *prop_constraint_axis = NULL;
1964
1965                 int i = ED_GIZMO_CAGE3D_PART_SCALE_MIN_X_MIN_Y_MIN_Z;
1966                 for (int x = 0; x < 3; x++) {
1967                         for (int y = 0; y < 3; y++) {
1968                                 for (int z = 0; z < 3; z++) {
1969                                         bool constraint[3] = {x != 1, y != 1, z != 1};
1970                                         ptr = WM_gizmo_operator_set(gz, i, ot_resize, NULL);
1971                                         if (prop_release_confirm == NULL) {
1972                                                 prop_release_confirm = RNA_struct_find_property(ptr, "release_confirm");
1973                                                 prop_constraint_axis = RNA_struct_find_property(ptr, "constraint_axis");
1974                                         }
1975                                         RNA_property_boolean_set(ptr, prop_release_confirm, true);
1976                                         RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint);
1977                                         i++;
1978                                 }
1979                         }
1980                 }
1981         }
1982 }
1983
1984 static void WIDGETGROUP_xform_cage_refresh(const bContext *C, wmGizmoGroup *gzgroup)
1985 {
1986         ARegion *ar = CTX_wm_region(C);
1987         RegionView3D *rv3d = ar->regiondata;
1988         Scene *scene = CTX_data_scene(C);
1989
1990         struct XFormCageWidgetGroup *xgzgroup = gzgroup->customdata;
1991         wmGizmo *gz = xgzgroup->gizmo;
1992
1993         struct TransformBounds tbounds;
1994
1995         const TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get(scene, SCE_GIZMO_SHOW_SCALE);
1996
1997         if ((ED_transform_calc_gizmo_stats(
1998                      C, &(struct TransformCalcParams) {
1999                          .use_local_axis = true,
2000                          .orientation_type = orient_slot->type + 1,
2001                          .orientation_index_custom = orient_slot->index_custom,
2002                      }, &tbounds) == 0) ||
2003             equals_v3v3(rv3d->tw_axis_min, rv3d->tw_axis_max))
2004         {
2005                 WM_gizmo_set_flag(gz, WM_GIZMO_HIDDEN, true);
2006         }
2007         else {
2008                 gizmo_prepare_mat(C, rv3d, &tbounds);
2009
2010                 WM_gizmo_set_flag(gz, WM_GIZMO_HIDDEN, false);
2011                 WM_gizmo_set_flag(gz, WM_GIZMO_MOVE_CURSOR, true);
2012
2013                 float dims[3];
2014                 sub_v3_v3v3(dims, rv3d->tw_axis_max, rv3d->tw_axis_min);
2015                 RNA_float_set_array(gz->ptr, "dimensions", dims);
2016                 mul_v3_fl(dims, 0.5f);
2017
2018                 copy_m4_m3(gz->matrix_offset, rv3d->tw_axis_matrix);
2019                 mid_v3_v3v3(gz->matrix_offset[3], rv3d->tw_axis_max, rv3d->tw_axis_min);
2020                 mul_m3_v3(rv3d->tw_axis_matrix, gz->matrix_offset[3]);
2021
2022                 float matrix_offset_global[4][4];
2023                 mul_m4_m4m4(matrix_offset_global, gz->matrix_space, gz->matrix_offset);
2024
2025                 PropertyRNA *prop_center_override = NULL;
2026                 float center[3];
2027                 float center_global[3];
2028                 int i = ED_GIZMO_CAGE3D_PART_SCALE_MIN_X_MIN_Y_MIN_Z;
2029                 for (int x = 0; x < 3; x++) {
2030                         center[0] = (float)(1 - x) * dims[0];
2031                         for (int y = 0; y < 3; y++) {
2032                                 center[1] = (float)(1 - y) * dims[1];
2033                                 for (int z = 0; z < 3; z++) {
2034                                         center[2] = (float)(1 - z) * dims[2];
2035                                         struct wmGizmoOpElem *gzop = WM_gizmo_operator_get(gz, i);
2036                                         if (prop_center_override == NULL) {
2037                                                 prop_center_override = RNA_struct_find_property(&gzop->ptr, "center_override");
2038                                         }
2039                                         mul_v3_m4v3(center_global, matrix_offset_global, center);
2040                                         RNA_property_float_set_array(&gzop->ptr, prop_center_override, center_global);
2041                                         i++;
2042                                 }
2043                         }
2044                 }
2045         }
2046
2047         /* Needed to test view orientation changes. */
2048         copy_m3_m4(xgzgroup->prev.viewinv_m3, rv3d->viewinv);
2049 }
2050
2051 static void WIDGETGROUP_xform_cage_message_subscribe(
2052         const bContext *C, wmGizmoGroup *gzgroup, struct wmMsgBus *mbus)
2053 {
2054         Scene *scene = CTX_data_scene(C);
2055         bScreen *screen = CTX_wm_screen(C);
2056         ScrArea *sa = CTX_wm_area(C);
2057         ARegion *ar = CTX_wm_region(C);
2058         gizmo_xform_message_subscribe(gzgroup, mbus, scene, screen, sa, ar, VIEW3D_GGT_xform_cage);
2059 }
2060
2061 static void WIDGETGROUP_xform_cage_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup)
2062 {
2063         struct XFormCageWidgetGroup *xgzgroup = gzgroup->customdata;
2064         wmGizmo *gz = xgzgroup->gizmo;
2065         ViewLayer *view_layer = CTX_data_view_layer(C);
2066         Object *ob = OBACT(view_layer);
2067         if (ob && ob->mode & OB_MODE_EDIT) {
2068                 copy_m4_m4(gz->matrix_space, ob->obmat);
2069         }
2070         else {
2071                 unit_m4(gz->matrix_space);
2072         }
2073
2074         RegionView3D *rv3d = CTX_wm_region_view3d(C);
2075         {
2076                 Scene *scene = CTX_data_scene(C);
2077                 const TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get(scene, SCE_GIZMO_SHOW_SCALE);
2078                 switch (orient_slot->type) {
2079                         case V3D_ORIENT_VIEW:
2080                         {
2081                                 float viewinv_m3[3][3];
2082                                 copy_m3_m4(viewinv_m3, rv3d->viewinv);
2083                                 if (!equals_m3m3(viewinv_m3, xgzgroup->prev.viewinv_m3)) {
2084                                         /* Take care calling refresh from draw_prepare,
2085                                          * this should be OK because it's only adjusting the cage orientation. */
2086                                         WIDGETGROUP_xform_cage_refresh(C, gzgroup);
2087                                 }
2088                                 break;
2089                         }
2090                 }
2091         }
2092 }
2093
2094 void VIEW3D_GGT_xform_cage(wmGizmoGroupType *gzgt)
2095 {
2096         gzgt->name = "Transform Cage";
2097         gzgt->idname = "VIEW3D_GGT_xform_cage";
2098
2099         gzgt->flag |= WM_GIZMOGROUPTYPE_3D;
2100
2101         gzgt->gzmap_params.spaceid = SPACE_VIEW3D;
2102         gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW;
2103
2104         gzgt->poll = WIDGETGROUP_xform_cage_poll;
2105         gzgt->setup = WIDGETGROUP_xform_cage_setup;
2106         gzgt->refresh = WIDGETGROUP_xform_cage_refresh;
2107         gzgt->message_subscribe = WIDGETGROUP_xform_cage_message_subscribe;
2108         gzgt->draw_prepare = WIDGETGROUP_xform_cage_draw_prepare;
2109 }
2110
2111 /** \} */
2112
2113 /* -------------------------------------------------------------------- */
2114 /** \name Transform Shear Gizmo
2115  * \{ */
2116
2117 struct XFormShearWidgetGroup {
2118         wmGizmo *gizmo[3][2];
2119         /* Only for view orientation. */
2120         struct {
2121                 float viewinv_m3[3][3];
2122         } prev;
2123 };
2124
2125 static bool WIDGETGROUP_xform_shear_poll(const bContext *C, wmGizmoGroupType *gzgt)
2126 {
2127         if (!ED_gizmo_poll_or_unlink_delayed_from_tool(C, gzgt)) {
2128                 return false;
2129         }
2130         View3D *v3d = CTX_wm_view3d(C);
2131         if (v3d->gizmo_flag & (V3D_GIZMO_HIDE | V3D_GIZMO_HIDE_TOOL)) {
2132                 return false;
2133         }
2134         return true;
2135 }
2136
2137 static void WIDGETGROUP_xform_shear_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup)
2138 {
2139         struct XFormShearWidgetGroup *xgzgroup = MEM_mallocN(sizeof(struct XFormShearWidgetGroup), __func__);
2140         const wmGizmoType *gzt_arrow = WM_gizmotype_find("GIZMO_GT_arrow_3d", true);
2141         wmOperatorType *ot_shear = WM_operatortype_find("TRANSFORM_OT_shear", true);
2142
2143         float axis_color[3][3];
2144         for (int i = 0; i < 3; i++) {
2145                 UI_GetThemeColor3fv(TH_AXIS_X + i, axis_color[i]);
2146         }
2147
2148         for (int i = 0; i < 3; i++) {
2149                 for (int j = 0; j < 2; j++) {
2150                         wmGizmo *gz = WM_gizmo_new_ptr(gzt_arrow, gzgroup, NULL);
2151                         RNA_enum_set(gz->ptr, "draw_style", ED_GIZMO_ARROW_STYLE_BOX);
2152                         const int i_ortho_a = (i + j + 1) % 3;
2153                         const int i_ortho_b = (i + (1 - j) + 1) % 3;
2154                         interp_v3_v3v3(gz->color, axis_color[i_ortho_a], axis_color[i_ortho_b], 0.75f);
2155                         gz->color[3] = 0.5f;
2156                         PointerRNA *ptr = WM_gizmo_operator_set(gz, 0, ot_shear, NULL);
2157                         RNA_enum_set(ptr, "shear_axis", 0);
2158                         RNA_boolean_set(ptr, "release_confirm", 1);
2159                         xgzgroup->gizmo[i][j] = gz;
2160                 }
2161         }
2162
2163         gzgroup->customdata = xgzgroup;
2164 }
2165
2166 static void WIDGETGROUP_xform_shear_refresh(const bContext *C, wmGizmoGroup *gzgroup)
2167 {
2168         Scene *scene = CTX_data_scene(C);
2169         ARegion *ar = CTX_wm_region(C);
2170         RegionView3D *rv3d = ar->regiondata;
2171
2172         struct XFormShearWidgetGroup *xgzgroup = gzgroup->customdata;
2173         struct TransformBounds tbounds;
2174
2175         const TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get(scene, SCE_GIZMO_SHOW_ROTATE);
2176
2177         if (ED_transform_calc_gizmo_stats(
2178                     C, &(struct TransformCalcParams) {
2179                         .use_local_axis = false,
2180                         .orientation_type = orient_slot->type + 1,
2181                         .orientation_index_custom = orient_slot->index_custom,
2182                     }, &tbounds) == 0)
2183         {
2184                 for (int i = 0; i < 3; i++) {
2185                         for (int j = 0; j < 2; j++) {
2186                                 wmGizmo *gz = xgzgroup->gizmo[i][j];
2187                                 WM_gizmo_set_flag(gz, WM_GIZMO_HIDDEN, true);
2188                         }
2189                 }
2190         }
2191         else {
2192                 gizmo_prepare_mat(C, rv3d, &tbounds);
2193                 for (int i = 0; i < 3; i++) {
2194                         for (int j = 0; j < 2; j++) {
2195                                 wmGizmo *gz = xgzgroup->gizmo[i][j];
2196                                 WM_gizmo_set_flag(gz, WM_GIZMO_HIDDEN, false);
2197                                 WM_gizmo_set_flag(gz, WM_GIZMO_MOVE_CURSOR, true);
2198
2199                                 wmGizmoOpElem *gzop = WM_gizmo_operator_get(gz, 0);
2200                                 const int i_ortho_a = (i + j + 1) % 3;
2201                                 const int i_ortho_b = (i + (1 - j) + 1) % 3;
2202                                 WM_gizmo_set_matrix_rotation_from_yz_axis(gz, rv3d->twmat[i_ortho_a], rv3d->twmat[i]);
2203                                 WM_gizmo_set_matrix_location(gz, rv3d->twmat[3]);
2204
2205                                 float axis[3];
2206                                 if (j == 0) {
2207                                         copy_v3_v3(axis, tbounds.axis[i_ortho_b]);
2208                                 }
2209                                 else {
2210                                         negate_v3_v3(axis, tbounds.axis[i_ortho_b]);
2211                                 }
2212                                 RNA_float_set_array(&gzop->ptr, "axis", axis);
2213                                 RNA_float_set_array(&gzop->ptr, "axis_ortho", tbounds.axis[i_ortho_a]);
2214                                 mul_v3_fl(gz->matrix_basis[0], 0.5f);
2215                                 mul_v3_fl(gz->matrix_basis[1], 6.0f);
2216                         }
2217                 }
2218         }
2219
2220         /* Needed to test view orientation changes. */
2221         copy_m3_m4(xgzgroup->prev.viewinv_m3, rv3d->viewinv);
2222 }
2223
2224 static void WIDGETGROUP_xform_shear_message_subscribe(
2225         const bContext *C, wmGizmoGroup *gzgroup, struct wmMsgBus *mbus)
2226 {
2227         Scene *scene = CTX_data_scene(C);
2228         bScreen *screen = CTX_wm_screen(C);
2229         ScrArea *sa = CTX_wm_area(C);
2230         ARegion *ar = CTX_wm_region(C);
2231         gizmo_xform_message_subscribe(gzgroup, mbus, scene, screen, sa, ar, VIEW3D_GGT_xform_shear);
2232 }
2233
2234 static void WIDGETGROUP_xform_shear_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup)
2235 {
2236         struct XFormShearWidgetGroup *xgzgroup = gzgroup->customdata;
2237         RegionView3D *rv3d = CTX_wm_region_view3d(C);
2238         {
2239                 Scene *scene = CTX_data_scene(C);
2240                 /* Shear is like rotate, use the rotate setting. */
2241                 const TransformOrientationSlot *orient_slot = BKE_scene_orientation_slot_get(scene, SCE_GIZMO_SHOW_ROTATE);
2242                 switch (orient_slot->type) {
2243                         case V3D_ORIENT_VIEW:
2244                         {
2245                                 float viewinv_m3[3][3];
2246                                 copy_m3_m4(viewinv_m3, rv3d->viewinv);
2247                                 if (!equals_m3m3(viewinv_m3, xgzgroup->prev.viewinv_m3)) {
2248                                         /* Take care calling refresh from draw_prepare,
2249                                          * this should be OK because it's only adjusting the cage orientation. */
2250                                         WIDGETGROUP_xform_shear_refresh(C, gzgroup);
2251                                 }
2252                                 break;
2253                         }
2254                 }
2255         }
2256
2257         /* Basic ordering for drawing only. */
2258         {
2259                 LISTBASE_FOREACH (wmGizmo *, gz, &gzgroup->gizmos) {
2260                         /* Since we have two pairs of each axis,
2261                          * bias the values so gizmos that are orthogonal to the view get priority.
2262                          * This means we never default to shearing along
2263                          * the view axis in the case of an overlap. */
2264                         float axis_order[3], axis_bias[3];
2265                         copy_v3_v3(axis_order, gz->matrix_basis[2]);
2266                         copy_v3_v3(axis_bias, gz->matrix_basis[1]);
2267                         if (dot_v3v3(axis_bias, rv3d->viewinv[2]) < 0.0f) {
2268                                 negate_v3(axis_bias);
2269                         }
2270                         madd_v3_v3fl(axis_order, axis_bias, 0.01f);
2271                         gz->temp.f = dot_v3v3(rv3d->viewinv[2], axis_order);
2272                 }
2273                 BLI_listbase_sort(&gzgroup->gizmos, WM_gizmo_cmp_temp_fl_reverse);
2274         }
2275 }
2276
2277 void VIEW3D_GGT_xform_shear(wmGizmoGroupType *gzgt)
2278 {
2279         gzgt->name = "Transform Shear";
2280         gzgt->idname = "VIEW3D_GGT_xform_shear";
2281
2282         gzgt->flag |= WM_GIZMOGROUPTYPE_3D;
2283
2284         gzgt->gzmap_params.spaceid = SPACE_VIEW3D;
2285         gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW;
2286
2287         gzgt->poll = WIDGETGROUP_xform_shear_poll;
2288         gzgt->setup = WIDGETGROUP_xform_shear_setup;
2289         gzgt->refresh = WIDGETGROUP_xform_shear_refresh;
2290         gzgt->message_subscribe = WIDGETGROUP_xform_shear_message_subscribe;
2291         gzgt->draw_prepare = WIDGETGROUP_xform_shear_draw_prepare;
2292 }
2293
2294 /** \} */