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