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