049fd143a26220e11777d2c94d15f00abca763a7
[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_listbase.h"
44 #include "BLI_math.h"
45 #include "BLI_utildefines.h"
46
47 #include "RNA_access.h"
48
49 #include "BKE_action.h"
50 #include "BKE_context.h"
51 #include "BKE_curve.h"
52 #include "BKE_global.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
61 #include "BIF_gl.h"
62
63 #include "DEG_depsgraph.h"
64
65 #include "WM_api.h"
66 #include "WM_types.h"
67 #include "WM_message.h"
68 #include "WM_toolsystem.h"
69
70 #include "ED_armature.h"
71 #include "ED_curve.h"
72 #include "ED_object.h"
73 #include "ED_particle.h"
74 #include "ED_view3d.h"
75 #include "ED_gpencil.h"
76 #include "ED_screen.h"
77 #include "ED_gizmo_library.h"
78
79 #include "UI_resources.h"
80
81 /* local module include */
82 #include "transform.h"
83
84 #include "MEM_guardedalloc.h"
85
86 #include "GPU_select.h"
87 #include "GPU_immediate.h"
88 #include "GPU_matrix.h"
89
90 #include "DEG_depsgraph_query.h"
91
92 /* return codes for select, and drawing flags */
93
94 #define MAN_TRANS_X             (1 << 0)
95 #define MAN_TRANS_Y             (1 << 1)
96 #define MAN_TRANS_Z             (1 << 2)
97 #define MAN_TRANS_C             (MAN_TRANS_X | MAN_TRANS_Y | MAN_TRANS_Z)
98
99 #define MAN_ROT_X               (1 << 3)
100 #define MAN_ROT_Y               (1 << 4)
101 #define MAN_ROT_Z               (1 << 5)
102 #define MAN_ROT_C               (MAN_ROT_X | MAN_ROT_Y | MAN_ROT_Z)
103
104 #define MAN_SCALE_X             (1 << 8)
105 #define MAN_SCALE_Y             (1 << 9)
106 #define MAN_SCALE_Z             (1 << 10)
107 #define MAN_SCALE_C             (MAN_SCALE_X | MAN_SCALE_Y | MAN_SCALE_Z)
108
109 /* threshold for testing view aligned gizmo axis */
110 static struct {
111         float min, max;
112 } g_tw_axis_range[2] = {
113         /* Regular range */
114         {0.02f, 0.1f},
115         /* Use a different range because we flip the dot product,
116          * also the view aligned planes are harder to see so hiding early is preferred. */
117         {0.175f,  0.25f},
118 };
119
120 /* axes as index */
121 enum {
122         MAN_AXIS_TRANS_X = 0,
123         MAN_AXIS_TRANS_Y,
124         MAN_AXIS_TRANS_Z,
125         MAN_AXIS_TRANS_C,
126
127         MAN_AXIS_TRANS_XY,
128         MAN_AXIS_TRANS_YZ,
129         MAN_AXIS_TRANS_ZX,
130 #define MAN_AXIS_RANGE_TRANS_START MAN_AXIS_TRANS_X
131 #define MAN_AXIS_RANGE_TRANS_END (MAN_AXIS_TRANS_ZX + 1)
132
133         MAN_AXIS_ROT_X,
134         MAN_AXIS_ROT_Y,
135         MAN_AXIS_ROT_Z,
136         MAN_AXIS_ROT_C,
137         MAN_AXIS_ROT_T, /* trackball rotation */
138 #define MAN_AXIS_RANGE_ROT_START MAN_AXIS_ROT_X
139 #define MAN_AXIS_RANGE_ROT_END (MAN_AXIS_ROT_T + 1)
140
141         MAN_AXIS_SCALE_X,
142         MAN_AXIS_SCALE_Y,
143         MAN_AXIS_SCALE_Z,
144         MAN_AXIS_SCALE_C,
145         MAN_AXIS_SCALE_XY,
146         MAN_AXIS_SCALE_YZ,
147         MAN_AXIS_SCALE_ZX,
148 #define MAN_AXIS_RANGE_SCALE_START MAN_AXIS_SCALE_X
149 #define MAN_AXIS_RANGE_SCALE_END (MAN_AXIS_SCALE_ZX + 1)
150
151         MAN_AXIS_APRON_C,
152
153         MAN_AXIS_LAST = MAN_AXIS_APRON_C + 1,
154 };
155
156 /* axis types */
157 enum {
158         MAN_AXES_ALL = 0,
159         MAN_AXES_TRANSLATE,
160         MAN_AXES_ROTATE,
161         MAN_AXES_SCALE,
162 };
163
164 typedef struct GizmoGroup {
165         bool all_hidden;
166         int twtype;
167         int axis_type_default;
168
169         /* Users may change the twtype, detect changes to re-setup gizmo options. */
170         int twtype_init;
171         int twtype_prev;
172         int use_twtype_refresh;
173
174         struct wmGizmo *gizmos[MAN_AXIS_LAST];
175 } GizmoGroup;
176
177 /* -------------------------------------------------------------------- */
178 /** \name Utilities
179  * \{ */
180
181 /* loop over axes */
182 #define MAN_ITER_AXES_BEGIN(axis, axis_idx) \
183         { \
184                 wmGizmo *axis; \
185                 int axis_idx; \
186                 for (axis_idx = 0; axis_idx < MAN_AXIS_LAST; axis_idx++) { \
187                         axis = gizmo_get_axis_from_index(man, axis_idx);
188
189 #define MAN_ITER_AXES_END \
190                 } \
191         } ((void)0)
192
193 static wmGizmo *gizmo_get_axis_from_index(const GizmoGroup *man, const short axis_idx)
194 {
195         BLI_assert(IN_RANGE_INCL(axis_idx, (float)MAN_AXIS_TRANS_X, (float)MAN_AXIS_LAST));
196         return man->gizmos[axis_idx];
197 }
198
199 static short gizmo_get_axis_type(const int axis_idx, const int axis_type_default)
200 {
201         if (axis_idx >= MAN_AXIS_RANGE_TRANS_START && axis_idx < MAN_AXIS_RANGE_TRANS_END) {
202                 return MAN_AXES_TRANSLATE;
203         }
204         if (axis_idx >= MAN_AXIS_RANGE_ROT_START && axis_idx < MAN_AXIS_RANGE_ROT_END) {
205                 return MAN_AXES_ROTATE;
206         }
207         if (axis_idx >= MAN_AXIS_RANGE_SCALE_START && axis_idx < MAN_AXIS_RANGE_SCALE_END) {
208                 return MAN_AXES_SCALE;
209         }
210         if (axis_idx == MAN_AXIS_APRON_C) {
211                 return axis_type_default;
212         }
213         BLI_assert(0);
214         return -1;
215 }
216
217 static uint gizmo_orientation_axis(const int axis_idx, bool *r_is_plane)
218 {
219         switch (axis_idx) {
220                 case MAN_AXIS_TRANS_YZ:
221                 case MAN_AXIS_SCALE_YZ:
222                         if (r_is_plane) {
223                                 *r_is_plane = true;
224                         }
225                         ATTR_FALLTHROUGH;
226                 case MAN_AXIS_TRANS_X:
227                 case MAN_AXIS_ROT_X:
228                 case MAN_AXIS_SCALE_X:
229                         return 0;
230
231                 case MAN_AXIS_TRANS_ZX:
232                 case MAN_AXIS_SCALE_ZX:
233                         if (r_is_plane) {
234                                 *r_is_plane = true;
235                         }
236                         ATTR_FALLTHROUGH;
237                 case MAN_AXIS_TRANS_Y:
238                 case MAN_AXIS_ROT_Y:
239                 case MAN_AXIS_SCALE_Y:
240                         return 1;
241
242                 case MAN_AXIS_TRANS_XY:
243                 case MAN_AXIS_SCALE_XY:
244                         if (r_is_plane) {
245                                 *r_is_plane = true;
246                         }
247                         ATTR_FALLTHROUGH;
248                 case MAN_AXIS_TRANS_Z:
249                 case MAN_AXIS_ROT_Z:
250                 case MAN_AXIS_SCALE_Z:
251                         return 2;
252         }
253         return 3;
254 }
255
256 static bool gizmo_is_axis_visible(
257         const RegionView3D *rv3d, const int twtype,
258         const float idot[3], const int axis_type, const int axis_idx)
259 {
260         if ((axis_idx >= MAN_AXIS_RANGE_ROT_START && axis_idx < MAN_AXIS_RANGE_ROT_END) == 0) {
261                 bool is_plane = false;
262                 const uint aidx_norm = gizmo_orientation_axis(axis_idx, &is_plane);
263                 /* don't draw axis perpendicular to the view */
264                 if (aidx_norm < 3) {
265                         float idot_axis = idot[aidx_norm];
266                         if (is_plane) {
267                                 idot_axis = 1.0f - idot_axis;
268                         }
269                         if (idot_axis < g_tw_axis_range[is_plane].min) {
270                                 return false;
271                         }
272                 }
273         }
274
275         if ((axis_type == MAN_AXES_TRANSLATE && !(twtype & SCE_MANIP_TRANSLATE)) ||
276             (axis_type == MAN_AXES_ROTATE && !(twtype & SCE_MANIP_ROTATE)) ||
277             (axis_type == MAN_AXES_SCALE && !(twtype & SCE_MANIP_SCALE)))
278         {
279                 return false;
280         }
281
282         switch (axis_idx) {
283                 case MAN_AXIS_TRANS_X:
284                         return (rv3d->twdrawflag & MAN_TRANS_X);
285                 case MAN_AXIS_TRANS_Y:
286                         return (rv3d->twdrawflag & MAN_TRANS_Y);
287                 case MAN_AXIS_TRANS_Z:
288                         return (rv3d->twdrawflag & MAN_TRANS_Z);
289                 case MAN_AXIS_TRANS_C:
290                         return (rv3d->twdrawflag & MAN_TRANS_C);
291                 case MAN_AXIS_ROT_X:
292                         return (rv3d->twdrawflag & MAN_ROT_X);
293                 case MAN_AXIS_ROT_Y:
294                         return (rv3d->twdrawflag & MAN_ROT_Y);
295                 case MAN_AXIS_ROT_Z:
296                         return (rv3d->twdrawflag & MAN_ROT_Z);
297                 case MAN_AXIS_ROT_C:
298                 case MAN_AXIS_ROT_T:
299                         return (rv3d->twdrawflag & MAN_ROT_C);
300                 case MAN_AXIS_SCALE_X:
301                         return (rv3d->twdrawflag & MAN_SCALE_X);
302                 case MAN_AXIS_SCALE_Y:
303                         return (rv3d->twdrawflag & MAN_SCALE_Y);
304                 case MAN_AXIS_SCALE_Z:
305                         return (rv3d->twdrawflag & MAN_SCALE_Z);
306                 case MAN_AXIS_SCALE_C:
307                         return (rv3d->twdrawflag & MAN_SCALE_C && (twtype & SCE_MANIP_TRANSLATE) == 0);
308                 case MAN_AXIS_TRANS_XY:
309                         return (rv3d->twdrawflag & MAN_TRANS_X &&
310                                 rv3d->twdrawflag & MAN_TRANS_Y &&
311                                 (twtype & SCE_MANIP_ROTATE) == 0);
312                 case MAN_AXIS_TRANS_YZ:
313                         return (rv3d->twdrawflag & MAN_TRANS_Y &&
314                                 rv3d->twdrawflag & MAN_TRANS_Z &&
315                                 (twtype & SCE_MANIP_ROTATE) == 0);
316                 case MAN_AXIS_TRANS_ZX:
317                         return (rv3d->twdrawflag & MAN_TRANS_Z &&
318                                 rv3d->twdrawflag & MAN_TRANS_X &&
319                                 (twtype & SCE_MANIP_ROTATE) == 0);
320                 case MAN_AXIS_SCALE_XY:
321                         return (rv3d->twdrawflag & MAN_SCALE_X &&
322                                 rv3d->twdrawflag & MAN_SCALE_Y &&
323                                 (twtype & SCE_MANIP_TRANSLATE) == 0 &&
324                                 (twtype & SCE_MANIP_ROTATE) == 0);
325                 case MAN_AXIS_SCALE_YZ:
326                         return (rv3d->twdrawflag & MAN_SCALE_Y &&
327                                 rv3d->twdrawflag & MAN_SCALE_Z &&
328                                 (twtype & SCE_MANIP_TRANSLATE) == 0 &&
329                                 (twtype & SCE_MANIP_ROTATE) == 0);
330                 case MAN_AXIS_SCALE_ZX:
331                         return (rv3d->twdrawflag & MAN_SCALE_Z &&
332                                 rv3d->twdrawflag & MAN_SCALE_X &&
333                                 (twtype & SCE_MANIP_TRANSLATE) == 0 &&
334                                 (twtype & SCE_MANIP_ROTATE) == 0);
335                 case MAN_AXIS_APRON_C:
336                         return true;
337         }
338         return false;
339 }
340
341 static void gizmo_get_axis_color(
342         const int axis_idx, const float idot[3],
343         float r_col[4], float r_col_hi[4])
344 {
345         /* alpha values for normal/highlighted states */
346         const float alpha = 0.6f;
347         const float alpha_hi = 1.0f;
348         float alpha_fac;
349
350         if (axis_idx >= MAN_AXIS_RANGE_ROT_START && axis_idx < MAN_AXIS_RANGE_ROT_END) {
351                 /* Never fade rotation rings. */
352                 /* trackball rotation axis is a special case, we only draw a slight overlay */
353                 alpha_fac = (axis_idx == MAN_AXIS_ROT_T) ? 0.1f : 1.0f;
354         }
355         else {
356                 bool is_plane = false;
357                 const int axis_idx_norm = gizmo_orientation_axis(axis_idx, &is_plane);
358                 /* get alpha fac based on axis angle, to fade axis out when hiding it because it points towards view */
359                 if (axis_idx_norm < 3) {
360                         const float idot_min = g_tw_axis_range[is_plane].min;
361                         const float idot_max = g_tw_axis_range[is_plane].max;
362                         float idot_axis = idot[axis_idx_norm];
363                         if (is_plane) {
364                                 idot_axis = 1.0f - idot_axis;
365                         }
366                         alpha_fac = (
367                                 (idot_axis > idot_max) ?
368                                 1.0f : (idot_axis < idot_min) ?
369                                 0.0f : ((idot_axis - idot_min) / (idot_max - idot_min)));
370                 }
371                 else {
372                         alpha_fac = 1.0f;
373                 }
374         }
375
376         switch (axis_idx) {
377                 case MAN_AXIS_TRANS_X:
378                 case MAN_AXIS_ROT_X:
379                 case MAN_AXIS_SCALE_X:
380                 case MAN_AXIS_TRANS_YZ:
381                 case MAN_AXIS_SCALE_YZ:
382                         UI_GetThemeColor4fv(TH_AXIS_X, r_col);
383                         break;
384                 case MAN_AXIS_TRANS_Y:
385                 case MAN_AXIS_ROT_Y:
386                 case MAN_AXIS_SCALE_Y:
387                 case MAN_AXIS_TRANS_ZX:
388                 case MAN_AXIS_SCALE_ZX:
389                         UI_GetThemeColor4fv(TH_AXIS_Y, r_col);
390                         break;
391                 case MAN_AXIS_TRANS_Z:
392                 case MAN_AXIS_ROT_Z:
393                 case MAN_AXIS_SCALE_Z:
394                 case MAN_AXIS_TRANS_XY:
395                 case MAN_AXIS_SCALE_XY:
396                         UI_GetThemeColor4fv(TH_AXIS_Z, r_col);
397                         break;
398                 case MAN_AXIS_TRANS_C:
399                 case MAN_AXIS_ROT_C:
400                 case MAN_AXIS_SCALE_C:
401                 case MAN_AXIS_ROT_T:
402                         copy_v4_fl(r_col, 1.0f);
403                         break;
404         }
405
406         copy_v4_v4(r_col_hi, r_col);
407
408         r_col[3] = alpha * alpha_fac;
409         r_col_hi[3] = alpha_hi * alpha_fac;
410 }
411
412 static void gizmo_get_axis_constraint(const int axis_idx, bool r_axis[3])
413 {
414         ARRAY_SET_ITEMS(r_axis, 0, 0, 0);
415
416         switch (axis_idx) {
417                 case MAN_AXIS_TRANS_X:
418                 case MAN_AXIS_ROT_X:
419                 case MAN_AXIS_SCALE_X:
420                         r_axis[0] = 1;
421                         break;
422                 case MAN_AXIS_TRANS_Y:
423                 case MAN_AXIS_ROT_Y:
424                 case MAN_AXIS_SCALE_Y:
425                         r_axis[1] = 1;
426                         break;
427                 case MAN_AXIS_TRANS_Z:
428                 case MAN_AXIS_ROT_Z:
429                 case MAN_AXIS_SCALE_Z:
430                         r_axis[2] = 1;
431                         break;
432                 case MAN_AXIS_TRANS_XY:
433                 case MAN_AXIS_SCALE_XY:
434                         r_axis[0] = r_axis[1] = 1;
435                         break;
436                 case MAN_AXIS_TRANS_YZ:
437                 case MAN_AXIS_SCALE_YZ:
438                         r_axis[1] = r_axis[2] = 1;
439                         break;
440                 case MAN_AXIS_TRANS_ZX:
441                 case MAN_AXIS_SCALE_ZX:
442                         r_axis[2] = r_axis[0] = 1;
443                         break;
444                 default:
445                         break;
446         }
447 }
448
449
450 /* **************** Preparation Stuff **************** */
451
452 /* transform widget center calc helper for below */
453 static void calc_tw_center(struct TransformBounds *tbounds, const float co[3])
454 {
455         minmax_v3v3_v3(tbounds->min, tbounds->max, co);
456         add_v3_v3(tbounds->center, co);
457
458         for (int i = 0; i < 3; i++) {
459                 const float d = dot_v3v3(tbounds->axis[i], co);
460                 tbounds->axis_min[i] = min_ff(d, tbounds->axis_min[i]);
461                 tbounds->axis_max[i] = max_ff(d, tbounds->axis_max[i]);
462         }
463 }
464
465 static void protectflag_to_drawflags(short protectflag, short *drawflags)
466 {
467         if (protectflag & OB_LOCK_LOCX)
468                 *drawflags &= ~MAN_TRANS_X;
469         if (protectflag & OB_LOCK_LOCY)
470                 *drawflags &= ~MAN_TRANS_Y;
471         if (protectflag & OB_LOCK_LOCZ)
472                 *drawflags &= ~MAN_TRANS_Z;
473
474         if (protectflag & OB_LOCK_ROTX)
475                 *drawflags &= ~MAN_ROT_X;
476         if (protectflag & OB_LOCK_ROTY)
477                 *drawflags &= ~MAN_ROT_Y;
478         if (protectflag & OB_LOCK_ROTZ)
479                 *drawflags &= ~MAN_ROT_Z;
480
481         if (protectflag & OB_LOCK_SCALEX)
482                 *drawflags &= ~MAN_SCALE_X;
483         if (protectflag & OB_LOCK_SCALEY)
484                 *drawflags &= ~MAN_SCALE_Y;
485         if (protectflag & OB_LOCK_SCALEZ)
486                 *drawflags &= ~MAN_SCALE_Z;
487 }
488
489 /* for pose mode */
490 static void protectflag_to_drawflags_pchan(RegionView3D *rv3d, const bPoseChannel *pchan)
491 {
492         protectflag_to_drawflags(pchan->protectflag, &rv3d->twdrawflag);
493 }
494
495 /* for editmode*/
496 static void protectflag_to_drawflags_ebone(RegionView3D *rv3d, const EditBone *ebo)
497 {
498         if (ebo->flag & BONE_EDITMODE_LOCKED) {
499                 protectflag_to_drawflags(OB_LOCK_LOC | OB_LOCK_ROT | OB_LOCK_SCALE, &rv3d->twdrawflag);
500         }
501 }
502
503 /* could move into BLI_math however this is only useful for display/editing purposes */
504 static void axis_angle_to_gimbal_axis(float gmat[3][3], const float axis[3], const float angle)
505 {
506         /* X/Y are arbitrary axies, most importantly Z is the axis of rotation */
507
508         float cross_vec[3];
509         float quat[4];
510
511         /* this is an un-scientific method to get a vector to cross with
512          * XYZ intentionally YZX */
513         cross_vec[0] = axis[1];
514         cross_vec[1] = axis[2];
515         cross_vec[2] = axis[0];
516
517         /* X-axis */
518         cross_v3_v3v3(gmat[0], cross_vec, axis);
519         normalize_v3(gmat[0]);
520         axis_angle_to_quat(quat, axis, angle);
521         mul_qt_v3(quat, gmat[0]);
522
523         /* Y-axis */
524         axis_angle_to_quat(quat, axis, M_PI_2);
525         copy_v3_v3(gmat[1], gmat[0]);
526         mul_qt_v3(quat, gmat[1]);
527
528         /* Z-axis */
529         copy_v3_v3(gmat[2], axis);
530
531         normalize_m3(gmat);
532 }
533
534
535 static bool test_rotmode_euler(short rotmode)
536 {
537         return (ELEM(rotmode, ROT_MODE_AXISANGLE, ROT_MODE_QUAT)) ? 0 : 1;
538 }
539
540 bool gimbal_axis(Object *ob, float gmat[3][3])
541 {
542         if (ob->mode & OB_MODE_POSE) {
543                 bPoseChannel *pchan = BKE_pose_channel_active(ob);
544
545                 if (pchan) {
546                         float mat[3][3], tmat[3][3], obmat[3][3];
547                         if (test_rotmode_euler(pchan->rotmode)) {
548                                 eulO_to_gimbal_axis(mat, pchan->eul, pchan->rotmode);
549                         }
550                         else if (pchan->rotmode == ROT_MODE_AXISANGLE) {
551                                 axis_angle_to_gimbal_axis(mat, pchan->rotAxis, pchan->rotAngle);
552                         }
553                         else { /* quat */
554                                 return 0;
555                         }
556
557
558                         /* apply bone transformation */
559                         mul_m3_m3m3(tmat, pchan->bone->bone_mat, mat);
560
561                         if (pchan->parent) {
562                                 float parent_mat[3][3];
563
564                                 copy_m3_m4(parent_mat, pchan->parent->pose_mat);
565                                 mul_m3_m3m3(mat, parent_mat, tmat);
566
567                                 /* needed if object transformation isn't identity */
568                                 copy_m3_m4(obmat, ob->obmat);
569                                 mul_m3_m3m3(gmat, obmat, mat);
570                         }
571                         else {
572                                 /* needed if object transformation isn't identity */
573                                 copy_m3_m4(obmat, ob->obmat);
574                                 mul_m3_m3m3(gmat, obmat, tmat);
575                         }
576
577                         normalize_m3(gmat);
578                         return 1;
579                 }
580         }
581         else {
582                 if (test_rotmode_euler(ob->rotmode)) {
583                         eulO_to_gimbal_axis(gmat, ob->rot, ob->rotmode);
584                 }
585                 else if (ob->rotmode == ROT_MODE_AXISANGLE) {
586                         axis_angle_to_gimbal_axis(gmat, ob->rotAxis, ob->rotAngle);
587                 }
588                 else { /* quat */
589                         return 0;
590                 }
591
592                 if (ob->parent) {
593                         float parent_mat[3][3];
594                         copy_m3_m4(parent_mat, ob->parent->obmat);
595                         normalize_m3(parent_mat);
596                         mul_m3_m3m3(gmat, parent_mat, gmat);
597                 }
598                 return 1;
599         }
600
601         return 0;
602 }
603
604 void ED_transform_calc_orientation_from_type(
605         const bContext *C, float r_mat[3][3])
606 {
607         ScrArea *sa = CTX_wm_area(C);
608         ARegion *ar = CTX_wm_region(C);
609         Scene *scene = CTX_data_scene(C);
610         ViewLayer *view_layer = CTX_data_view_layer(C);
611         Object *obedit = CTX_data_edit_object(C);
612         View3D *v3d = sa->spacedata.first;
613         RegionView3D *rv3d = ar->regiondata;
614         Object *ob = OBACT(view_layer);
615         const short orientation_type = scene->orientation_type;
616         const int pivot_point = scene->toolsettings->transform_pivot_point;
617
618         ED_transform_calc_orientation_from_type_ex(
619                 C, r_mat,
620                 scene, v3d, rv3d, ob, obedit, orientation_type, pivot_point);
621 }
622
623 void ED_transform_calc_orientation_from_type_ex(
624         const bContext *C, float r_mat[3][3],
625         /* extra args (can be accessed from context) */
626         Scene *scene, View3D *v3d, RegionView3D *rv3d, Object *ob, Object *obedit,
627         const short orientation_type, const int pivot_point)
628 {
629         bool ok = false;
630
631         switch (orientation_type) {
632                 case V3D_MANIP_GLOBAL:
633                 {
634                         break; /* nothing to do */
635                 }
636                 case V3D_MANIP_GIMBAL:
637                 {
638                         if (gimbal_axis(ob, r_mat)) {
639                                 ok = true;
640                                 break;
641                         }
642                         /* if not gimbal, fall through to normal */
643                         ATTR_FALLTHROUGH;
644                 }
645                 case V3D_MANIP_NORMAL:
646                 {
647                         if (obedit || ob->mode & OB_MODE_POSE) {
648                                 ED_getTransformOrientationMatrix(C, r_mat, pivot_point);
649                                 ok = true;
650                                 break;
651                         }
652                         /* no break we define 'normal' as 'local' in Object mode */
653                         ATTR_FALLTHROUGH;
654                 }
655                 case V3D_MANIP_LOCAL:
656                 {
657                         if (ob->mode & OB_MODE_POSE) {
658                                 /* each bone moves on its own local axis, but  to avoid confusion,
659                                  * use the active pones axis for display [#33575], this works as expected on a single bone
660                                  * and users who select many bones will understand whats going on and what local means
661                                  * when they start transforming */
662                                 ED_getTransformOrientationMatrix(C, r_mat, pivot_point);
663                                 ok = true;
664                                 break;
665                         }
666                         copy_m3_m4(r_mat, ob->obmat);
667                         normalize_m3(r_mat);
668                         ok = true;
669                         break;
670                 }
671                 case V3D_MANIP_VIEW:
672                 {
673                         if (rv3d != NULL) {
674                                 copy_m3_m4(r_mat, rv3d->viewinv);
675                                 normalize_m3(r_mat);
676                                 ok = true;
677                         }
678                         break;
679                 }
680                 case V3D_MANIP_CURSOR:
681                 {
682                         ED_view3d_cursor3d_calc_mat3(scene, v3d, r_mat);
683                         ok = true;
684                         break;
685                 }
686                 case V3D_MANIP_CUSTOM:
687                 {
688                         TransformOrientation *custom_orientation = BKE_scene_transform_orientation_find(
689                                 scene, scene->orientation_index_custom);
690                         if (applyTransformOrientation(custom_orientation, r_mat, NULL)) {
691                                 ok = true;
692                         }
693                         break;
694                 }
695         }
696
697         if (!ok) {
698                 unit_m3(r_mat);
699         }
700 }
701
702 /* centroid, boundbox, of selection */
703 /* returns total items selected */
704 int ED_transform_calc_gizmo_stats(
705         const bContext *C,
706         const struct TransformCalcParams *params,
707         struct TransformBounds *tbounds)
708 {
709         ScrArea *sa = CTX_wm_area(C);
710         ARegion *ar = CTX_wm_region(C);
711         Scene *scene = CTX_data_scene(C);
712         Depsgraph *depsgraph = CTX_data_depsgraph(C);
713         ViewLayer *view_layer = CTX_data_view_layer(C);
714         Object *obedit = CTX_data_edit_object(C);
715         View3D *v3d = sa->spacedata.first;
716         RegionView3D *rv3d = ar->regiondata;
717         Base *base;
718         Object *ob = OBACT(view_layer);
719         bGPdata *gpd = CTX_data_gpencil_data(C);
720         const bool is_gp_edit = GPENCIL_ANY_MODE(gpd);
721         int a, totsel = 0;
722         const int pivot_point = scene->toolsettings->transform_pivot_point;
723
724         /* transform widget matrix */
725         unit_m4(rv3d->twmat);
726
727         unit_m3(rv3d->tw_axis_matrix);
728         zero_v3(rv3d->tw_axis_min);
729         zero_v3(rv3d->tw_axis_max);
730
731         rv3d->twdrawflag = 0xFFFF;
732
733         /* global, local or normal orientation?
734          * if we could check 'totsel' now, this should be skipped with no selection. */
735         if (ob && !is_gp_edit) {
736                 const short orientation_type = params->orientation_type ? (params->orientation_type - 1) : scene->orientation_type;
737                 float mat[3][3];
738                 ED_transform_calc_orientation_from_type_ex(
739                         C, mat,
740                         scene, v3d, rv3d, ob, obedit, orientation_type, pivot_point);
741                 copy_m4_m3(rv3d->twmat, mat);
742         }
743
744         /* transform widget centroid/center */
745         INIT_MINMAX(tbounds->min, tbounds->max);
746         zero_v3(tbounds->center);
747
748         copy_m3_m4(tbounds->axis, rv3d->twmat);
749         if (params->use_local_axis && (ob && ob->mode & OB_MODE_EDIT)) {
750                 float diff_mat[3][3];
751                 copy_m3_m4(diff_mat, ob->obmat);
752                 normalize_m3(diff_mat);
753                 invert_m3(diff_mat);
754                 mul_m3_m3m3(tbounds->axis, tbounds->axis, diff_mat);
755                 normalize_m3(tbounds->axis);
756         }
757
758         for (int i = 0; i < 3; i++) {
759                 tbounds->axis_min[i] = +FLT_MAX;
760                 tbounds->axis_max[i] = -FLT_MAX;
761         }
762
763         if (is_gp_edit) {
764                 float diff_mat[4][4];
765                 float fpt[3];
766
767                 for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
768                         /* only editable and visible layers are considered */
769                         if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
770
771                                 /* calculate difference matrix */
772                                 ED_gpencil_parent_location(depsgraph, ob, gpd, gpl, diff_mat);
773
774                                 for (bGPDstroke *gps = gpl->actframe->strokes.first; gps; gps = gps->next) {
775                                         /* skip strokes that are invalid for current view */
776                                         if (ED_gpencil_stroke_can_use(C, gps) == false) {
777                                                 continue;
778                                         }
779
780                                         /* we're only interested in selected points here... */
781                                         if (gps->flag & GP_STROKE_SELECT) {
782                                                 bGPDspoint *pt;
783                                                 int i;
784
785                                                 /* Change selection status of all points, then make the stroke match */
786                                                 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
787                                                         if (pt->flag & GP_SPOINT_SELECT) {
788                                                                 if (gpl->parent == NULL) {
789                                                                         calc_tw_center(tbounds, &pt->x);
790                                                                         totsel++;
791                                                                 }
792                                                                 else {
793                                                                         mul_v3_m4v3(fpt, diff_mat, &pt->x);
794                                                                         calc_tw_center(tbounds, fpt);
795                                                                         totsel++;
796                                                                 }
797                                                         }
798                                                 }
799                                         }
800                                 }
801                         }
802                 }
803
804
805                 /* selection center */
806                 if (totsel) {
807                         mul_v3_fl(tbounds->center, 1.0f / (float)totsel);   /* centroid! */
808                 }
809         }
810         else if (obedit) {
811                 ob = obedit;
812                 if (obedit->type == OB_MESH) {
813                         BMEditMesh *em = BKE_editmesh_from_object(obedit);
814                         BMEditSelection ese;
815                         float vec[3] = {0, 0, 0};
816
817                         /* USE LAST SELECTE WITH ACTIVE */
818                         if ((pivot_point == V3D_AROUND_ACTIVE) && BM_select_history_active_get(em->bm, &ese)) {
819                                 BM_editselection_center(&ese, vec);
820                                 calc_tw_center(tbounds, vec);
821                                 totsel = 1;
822                         }
823                         else {
824                                 BMesh *bm = em->bm;
825                                 BMVert *eve;
826
827                                 BMIter iter;
828
829                                 BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
830                                         if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
831                                                 if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
832                                                         totsel++;
833                                                         calc_tw_center(tbounds, eve->co);
834                                                 }
835                                         }
836                                 }
837                         }
838                 } /* end editmesh */
839                 else if (obedit->type == OB_ARMATURE) {
840                         bArmature *arm = obedit->data;
841                         EditBone *ebo;
842
843                         if ((pivot_point == V3D_AROUND_ACTIVE) && (ebo = arm->act_edbone)) {
844                                 /* doesn't check selection or visibility intentionally */
845                                 if (ebo->flag & BONE_TIPSEL) {
846                                         calc_tw_center(tbounds, ebo->tail);
847                                         totsel++;
848                                 }
849                                 if ((ebo->flag & BONE_ROOTSEL) ||
850                                     ((ebo->flag & BONE_TIPSEL) == false))  /* ensure we get at least one point */
851                                 {
852                                         calc_tw_center(tbounds, ebo->head);
853                                         totsel++;
854                                 }
855                                 protectflag_to_drawflags_ebone(rv3d, ebo);
856                         }
857                         else {
858                                 for (ebo = arm->edbo->first; ebo; ebo = ebo->next) {
859                                         if (EBONE_VISIBLE(arm, ebo)) {
860                                                 if (ebo->flag & BONE_TIPSEL) {
861                                                         calc_tw_center(tbounds, ebo->tail);
862                                                         totsel++;
863                                                 }
864                                                 if ((ebo->flag & BONE_ROOTSEL) &&
865                                                     /* don't include same point multiple times */
866                                                     ((ebo->flag & BONE_CONNECTED) &&
867                                                      (ebo->parent != NULL) &&
868                                                      (ebo->parent->flag & BONE_TIPSEL) &&
869                                                      EBONE_VISIBLE(arm, ebo->parent)) == 0)
870                                                 {
871                                                         calc_tw_center(tbounds, ebo->head);
872                                                         totsel++;
873                                                 }
874                                                 if (ebo->flag & BONE_SELECTED) {
875                                                         protectflag_to_drawflags_ebone(rv3d, ebo);
876                                                 }
877                                         }
878                                 }
879                         }
880                 }
881                 else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) {
882                         Curve *cu = obedit->data;
883                         float center[3];
884
885                         if ((pivot_point == V3D_AROUND_ACTIVE) && ED_curve_active_center(cu, center)) {
886                                 calc_tw_center(tbounds, center);
887                                 totsel++;
888                         }
889                         else {
890                                 Nurb *nu;
891                                 BezTriple *bezt;
892                                 BPoint *bp;
893                                 ListBase *nurbs = BKE_curve_editNurbs_get(cu);
894
895                                 nu = nurbs->first;
896                                 while (nu) {
897                                         if (nu->type == CU_BEZIER) {
898                                                 bezt = nu->bezt;
899                                                 a = nu->pntsu;
900                                                 while (a--) {
901                                                         /* exceptions
902                                                          * if handles are hidden then only check the center points.
903                                                          * If the center knot is selected then only use this as the center point.
904                                                          */
905                                                         if (cu->drawflag & CU_HIDE_HANDLES) {
906                                                                 if (bezt->f2 & SELECT) {
907                                                                         calc_tw_center(tbounds, bezt->vec[1]);
908                                                                         totsel++;
909                                                                 }
910                                                         }
911                                                         else if (bezt->f2 & SELECT) {
912                                                                 calc_tw_center(tbounds, bezt->vec[1]);
913                                                                 totsel++;
914                                                         }
915                                                         else {
916                                                                 if (bezt->f1 & SELECT) {
917                                                                         calc_tw_center(
918                                                                                 tbounds, bezt->vec[(pivot_point == V3D_AROUND_LOCAL_ORIGINS) ? 1 : 0]);
919                                                                         totsel++;
920                                                                 }
921                                                                 if (bezt->f3 & SELECT) {
922                                                                         calc_tw_center(
923                                                                                 tbounds, bezt->vec[(pivot_point == V3D_AROUND_LOCAL_ORIGINS) ? 1 : 2]);
924                                                                         totsel++;
925                                                                 }
926                                                         }
927                                                         bezt++;
928                                                 }
929                                         }
930                                         else {
931                                                 bp = nu->bp;
932                                                 a = nu->pntsu * nu->pntsv;
933                                                 while (a--) {
934                                                         if (bp->f1 & SELECT) {
935                                                                 calc_tw_center(tbounds, bp->vec);
936                                                                 totsel++;
937                                                         }
938                                                         bp++;
939                                                 }
940                                         }
941                                         nu = nu->next;
942                                 }
943                         }
944                 }
945                 else if (obedit->type == OB_MBALL) {
946                         MetaBall *mb = (MetaBall *)obedit->data;
947                         MetaElem *ml;
948
949                         if ((pivot_point == V3D_AROUND_ACTIVE) && (ml = mb->lastelem)) {
950                                 calc_tw_center(tbounds, &ml->x);
951                                 totsel++;
952                         }
953                         else {
954                                 for (ml = mb->editelems->first; ml; ml = ml->next) {
955                                         if (ml->flag & SELECT) {
956                                                 calc_tw_center(tbounds, &ml->x);
957                                                 totsel++;
958                                         }
959                                 }
960                         }
961                 }
962                 else if (obedit->type == OB_LATTICE) {
963                         Lattice *lt = ((Lattice *)obedit->data)->editlatt->latt;
964                         BPoint *bp;
965
966                         if ((pivot_point == V3D_AROUND_ACTIVE) && (bp = BKE_lattice_active_point_get(lt))) {
967                                 calc_tw_center(tbounds, bp->vec);
968                                 totsel++;
969                         }
970                         else {
971                                 bp = lt->def;
972                                 a = lt->pntsu * lt->pntsv * lt->pntsw;
973                                 while (a--) {
974                                         if (bp->f1 & SELECT) {
975                                                 calc_tw_center(tbounds, bp->vec);
976                                                 totsel++;
977                                         }
978                                         bp++;
979                                 }
980                         }
981                 }
982
983                 /* selection center */
984                 if (totsel) {
985                         mul_v3_fl(tbounds->center, 1.0f / (float)totsel);   // centroid!
986                         mul_m4_v3(obedit->obmat, tbounds->center);
987                         mul_m4_v3(obedit->obmat, tbounds->min);
988                         mul_m4_v3(obedit->obmat, tbounds->max);
989                 }
990         }
991         else if (ob && (ob->mode & OB_MODE_POSE)) {
992                 bPoseChannel *pchan;
993                 int mode = TFM_ROTATION; // mislead counting bones... bah. We don't know the gizmo mode, could be mixed
994                 bool ok = false;
995
996                 if ((pivot_point == V3D_AROUND_ACTIVE) && (pchan = BKE_pose_channel_active(ob))) {
997                         /* doesn't check selection or visibility intentionally */
998                         Bone *bone = pchan->bone;
999                         if (bone) {
1000                                 calc_tw_center(tbounds, pchan->pose_head);
1001                                 protectflag_to_drawflags_pchan(rv3d, pchan);
1002                                 totsel = 1;
1003                                 ok = true;
1004                         }
1005                 }
1006                 else {
1007                         totsel = count_set_pose_transflags(ob, mode, V3D_AROUND_CENTER_BOUNDS, NULL);
1008
1009                         if (totsel) {
1010                                 /* use channels to get stats */
1011                                 for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
1012                                         Bone *bone = pchan->bone;
1013                                         if (bone && (bone->flag & BONE_TRANSFORM)) {
1014                                                 calc_tw_center(tbounds, pchan->pose_head);
1015                                                 protectflag_to_drawflags_pchan(rv3d, pchan);
1016                                         }
1017                                 }
1018                                 ok = true;
1019                         }
1020                 }
1021
1022                 if (ok) {
1023                         mul_v3_fl(tbounds->center, 1.0f / (float)totsel);   // centroid!
1024                         mul_m4_v3(ob->obmat, tbounds->center);
1025                         mul_m4_v3(ob->obmat, tbounds->min);
1026                         mul_m4_v3(ob->obmat, tbounds->max);
1027                 }
1028         }
1029         else if (ob && (ob->mode & OB_MODE_ALL_PAINT)) {
1030                 /* pass */
1031         }
1032         else if (ob && ob->mode & OB_MODE_PARTICLE_EDIT) {
1033                 PTCacheEdit *edit = PE_get_current(scene, ob);
1034                 PTCacheEditPoint *point;
1035                 PTCacheEditKey *ek;
1036                 int k;
1037
1038                 if (edit) {
1039                         point = edit->points;
1040                         for (a = 0; a < edit->totpoint; a++, point++) {
1041                                 if (point->flag & PEP_HIDE) continue;
1042
1043                                 for (k = 0, ek = point->keys; k < point->totkey; k++, ek++) {
1044                                         if (ek->flag & PEK_SELECT) {
1045                                                 calc_tw_center(tbounds, (ek->flag & PEK_USE_WCO) ? ek->world_co : ek->co);
1046                                                 totsel++;
1047                                         }
1048                                 }
1049                         }
1050
1051                         /* selection center */
1052                         if (totsel)
1053                                 mul_v3_fl(tbounds->center, 1.0f / (float)totsel);  // centroid!
1054                 }
1055         }
1056         else {
1057
1058                 /* we need the one selected object, if its not active */
1059                 base = BASACT(view_layer);
1060                 ob = OBACT(view_layer);
1061                 if (base && ((base->flag & BASE_SELECTED) == 0)) ob = NULL;
1062
1063                 for (base = view_layer->object_bases.first; base; base = base->next) {
1064                         if (!TESTBASELIB(base)) {
1065                                 continue;
1066                         }
1067                         if (ob == NULL) {
1068                                 ob = base->object;
1069                         }
1070                         if (params->use_only_center || base->object->bb == NULL) {
1071                                 calc_tw_center(tbounds, base->object->obmat[3]);
1072                         }
1073                         else {
1074                                 for (uint j = 0; j < 8; j++) {
1075                                         float co[3];
1076                                         mul_v3_m4v3(co, base->object->obmat, base->object->bb->vec[j]);
1077                                         calc_tw_center(tbounds, co);
1078                                 }
1079                         }
1080                         protectflag_to_drawflags(base->object->protectflag, &rv3d->twdrawflag);
1081                         totsel++;
1082                 }
1083
1084                 /* selection center */
1085                 if (totsel) {
1086                         mul_v3_fl(tbounds->center, 1.0f / (float)totsel);   // centroid!
1087                 }
1088         }
1089
1090         if (totsel == 0) {
1091                 unit_m4(rv3d->twmat);
1092         }
1093         else {
1094                 copy_v3_v3(rv3d->tw_axis_min, tbounds->axis_min);
1095                 copy_v3_v3(rv3d->tw_axis_max, tbounds->axis_max);
1096                 copy_m3_m3(rv3d->tw_axis_matrix, tbounds->axis);
1097         }
1098
1099         return totsel;
1100 }
1101
1102 static void gizmo_get_idot(RegionView3D *rv3d, float r_idot[3])
1103 {
1104         float view_vec[3], axis_vec[3];
1105         ED_view3d_global_to_vector(rv3d, rv3d->twmat[3], view_vec);
1106         for (int i = 0; i < 3; i++) {
1107                 normalize_v3_v3(axis_vec, rv3d->twmat[i]);
1108                 r_idot[i] = 1.0f - fabsf(dot_v3v3(view_vec, axis_vec));
1109         }
1110 }
1111
1112 static void gizmo_prepare_mat(
1113         const bContext *C, View3D *v3d, RegionView3D *rv3d, const struct TransformBounds *tbounds)
1114 {
1115         Scene *scene = CTX_data_scene(C);
1116         ViewLayer *view_layer = CTX_data_view_layer(C);
1117
1118         switch (scene->toolsettings->transform_pivot_point) {
1119                 case V3D_AROUND_CENTER_BOUNDS:
1120                 case V3D_AROUND_ACTIVE:
1121                 {
1122                         bGPdata *gpd = CTX_data_gpencil_data(C);
1123                         Object *ob = OBACT(view_layer);
1124
1125                         if (((scene->toolsettings->transform_pivot_point == V3D_AROUND_ACTIVE) &&
1126                              (OBEDIT_FROM_OBACT(ob) == NULL)) &&
1127                             ((gpd == NULL) || !(gpd->flag & GP_DATA_STROKE_EDITMODE)) &&
1128                             (!(ob->mode & OB_MODE_POSE)))
1129                         {
1130                                 copy_v3_v3(rv3d->twmat[3], ob->obmat[3]);
1131                         }
1132                         else {
1133                                 mid_v3_v3v3(rv3d->twmat[3], tbounds->min, tbounds->max);
1134                         }
1135                         break;
1136                 }
1137                 case V3D_AROUND_LOCAL_ORIGINS:
1138                 case V3D_AROUND_CENTER_MEAN:
1139                         copy_v3_v3(rv3d->twmat[3], tbounds->center);
1140                         break;
1141                 case V3D_AROUND_CURSOR:
1142                         copy_v3_v3(rv3d->twmat[3], ED_view3d_cursor3d_get(scene, v3d)->location);
1143                         break;
1144         }
1145 }
1146
1147 /**
1148  * Sets up \a r_start and \a r_len to define arrow line range.
1149  * Needed to adjust line drawing for combined gizmo axis types.
1150  */
1151 static void gizmo_line_range(const int twtype, const short axis_type, float *r_start, float *r_len)
1152 {
1153         const float ofs = 0.2f;
1154
1155         *r_start = 0.2f;
1156         *r_len = 1.0f;
1157
1158         switch (axis_type) {
1159                 case MAN_AXES_TRANSLATE:
1160                         if (twtype & SCE_MANIP_SCALE) {
1161                                 *r_start = *r_len - ofs + 0.075f;
1162                         }
1163                         if (twtype & SCE_MANIP_ROTATE) {
1164                                 *r_len += ofs;
1165                         }
1166                         break;
1167                 case MAN_AXES_SCALE:
1168                         if (twtype & (SCE_MANIP_TRANSLATE | SCE_MANIP_ROTATE)) {
1169                                 *r_len -= ofs + 0.025f;
1170                         }
1171                         break;
1172         }
1173
1174         *r_len -= *r_start;
1175 }
1176
1177 static void gizmo_xform_message_subscribe(
1178         wmGizmoGroup *gzgroup, struct wmMsgBus *mbus,
1179         Scene *scene, bScreen *UNUSED(screen), ScrArea *UNUSED(sa), ARegion *ar, const void *type_fn)
1180 {
1181         /* Subscribe to view properties */
1182         wmMsgSubscribeValue msg_sub_value_gz_tag_refresh = {
1183                 .owner = ar,
1184                 .user_data = gzgroup->parent_gzmap,
1185                 .notify = WM_gizmo_do_msg_notify_tag_refresh,
1186         };
1187
1188         PointerRNA scene_ptr;
1189         RNA_id_pointer_create(&scene->id, &scene_ptr);
1190
1191         {
1192                 extern PropertyRNA rna_Scene_transform_orientation;
1193                 extern PropertyRNA rna_Scene_cursor_location;
1194                 const PropertyRNA *props[] = {
1195                         &rna_Scene_transform_orientation,
1196                         (scene->toolsettings->transform_pivot_point == V3D_AROUND_CURSOR) ? &rna_Scene_cursor_location : NULL,
1197                 };
1198                 for (int i = 0; i < ARRAY_SIZE(props); i++) {
1199                         if (props[i]) {
1200                                 WM_msg_subscribe_rna(mbus, &scene_ptr, props[i], &msg_sub_value_gz_tag_refresh, __func__);
1201                         }
1202                 }
1203         }
1204
1205         PointerRNA toolsettings_ptr;
1206         RNA_pointer_create(&scene->id, &RNA_ToolSettings, scene->toolsettings, &toolsettings_ptr);
1207
1208         if (type_fn == TRANSFORM_GGT_gizmo) {
1209                 extern PropertyRNA rna_ToolSettings_transform_pivot_point;
1210                 extern PropertyRNA rna_ToolSettings_use_gizmo_mode;
1211                 extern PropertyRNA rna_ToolSettings_use_gizmo_apron;
1212                 const PropertyRNA *props[] = {
1213                         &rna_ToolSettings_transform_pivot_point,
1214                         &rna_ToolSettings_use_gizmo_mode,
1215                         &rna_ToolSettings_use_gizmo_apron,
1216                 };
1217                 for (int i = 0; i < ARRAY_SIZE(props); i++) {
1218                         WM_msg_subscribe_rna(mbus, &toolsettings_ptr, props[i], &msg_sub_value_gz_tag_refresh, __func__);
1219                 }
1220         }
1221         else if (type_fn == VIEW3D_GGT_xform_cage) {
1222                 /* pass */
1223         }
1224         else {
1225                 BLI_assert(0);
1226         }
1227
1228         WM_msg_subscribe_rna_anon_prop(mbus, Window, view_layer, &msg_sub_value_gz_tag_refresh);
1229 }
1230
1231 /** \} */
1232
1233
1234 /* -------------------------------------------------------------------- */
1235 /** \name Transform Gizmo
1236  * \{ */
1237
1238 static GizmoGroup *gizmogroup_init(wmGizmoGroup *gzgroup)
1239 {
1240         GizmoGroup *man;
1241
1242         man = MEM_callocN(sizeof(GizmoGroup), "gizmo_data");
1243
1244         const wmGizmoType *gzt_arrow = WM_gizmotype_find("GIZMO_GT_arrow_3d", true);
1245         const wmGizmoType *gzt_dial = WM_gizmotype_find("GIZMO_GT_dial_3d", true);
1246         const wmGizmoType *gzt_prim = WM_gizmotype_find("GIZMO_GT_primitive_3d", true);
1247
1248         /* Fallback action. */
1249         {
1250                 const wmGizmoType *gzt_mask = WM_gizmotype_find("GIZMO_GT_blank_3d", true);
1251                 man->gizmos[MAN_AXIS_APRON_C] = WM_gizmo_new_ptr(gzt_mask, gzgroup, NULL);
1252         }
1253
1254 #define GIZMO_NEW_ARROW(v, draw_style) { \
1255         man->gizmos[v] = WM_gizmo_new_ptr(gzt_arrow, gzgroup, NULL); \
1256         RNA_enum_set(man->gizmos[v]->ptr, "draw_style", draw_style); \
1257 } ((void)0)
1258 #define GIZMO_NEW_DIAL(v, draw_options) { \
1259         man->gizmos[v] = WM_gizmo_new_ptr(gzt_dial, gzgroup, NULL); \
1260         RNA_enum_set(man->gizmos[v]->ptr, "draw_options", draw_options); \
1261 } ((void)0)
1262 #define GIZMO_NEW_PRIM(v, draw_style) { \
1263         man->gizmos[v] = WM_gizmo_new_ptr(gzt_prim, gzgroup, NULL); \
1264         RNA_enum_set(man->gizmos[v]->ptr, "draw_style", draw_style); \
1265 } ((void)0)
1266
1267         /* add/init widgets - order matters! */
1268         GIZMO_NEW_DIAL(MAN_AXIS_ROT_T, ED_GIZMO_DIAL_DRAW_FLAG_FILL);
1269
1270         GIZMO_NEW_DIAL(MAN_AXIS_SCALE_C, ED_GIZMO_DIAL_DRAW_FLAG_NOP);
1271
1272         GIZMO_NEW_ARROW(MAN_AXIS_SCALE_X, ED_GIZMO_ARROW_STYLE_BOX);
1273         GIZMO_NEW_ARROW(MAN_AXIS_SCALE_Y, ED_GIZMO_ARROW_STYLE_BOX);
1274         GIZMO_NEW_ARROW(MAN_AXIS_SCALE_Z, ED_GIZMO_ARROW_STYLE_BOX);
1275
1276         GIZMO_NEW_PRIM(MAN_AXIS_SCALE_XY, ED_GIZMO_PRIMITIVE_STYLE_PLANE);
1277         GIZMO_NEW_PRIM(MAN_AXIS_SCALE_YZ, ED_GIZMO_PRIMITIVE_STYLE_PLANE);
1278         GIZMO_NEW_PRIM(MAN_AXIS_SCALE_ZX, ED_GIZMO_PRIMITIVE_STYLE_PLANE);
1279
1280         GIZMO_NEW_DIAL(MAN_AXIS_ROT_X, ED_GIZMO_DIAL_DRAW_FLAG_CLIP);
1281         GIZMO_NEW_DIAL(MAN_AXIS_ROT_Y, ED_GIZMO_DIAL_DRAW_FLAG_CLIP);
1282         GIZMO_NEW_DIAL(MAN_AXIS_ROT_Z, ED_GIZMO_DIAL_DRAW_FLAG_CLIP);
1283
1284         /* init screen aligned widget last here, looks better, behaves better */
1285         GIZMO_NEW_DIAL(MAN_AXIS_ROT_C, ED_GIZMO_DIAL_DRAW_FLAG_NOP);
1286
1287         GIZMO_NEW_DIAL(MAN_AXIS_TRANS_C, ED_GIZMO_DIAL_DRAW_FLAG_NOP);
1288
1289         GIZMO_NEW_ARROW(MAN_AXIS_TRANS_X, ED_GIZMO_ARROW_STYLE_NORMAL);
1290         GIZMO_NEW_ARROW(MAN_AXIS_TRANS_Y, ED_GIZMO_ARROW_STYLE_NORMAL);
1291         GIZMO_NEW_ARROW(MAN_AXIS_TRANS_Z, ED_GIZMO_ARROW_STYLE_NORMAL);
1292
1293         GIZMO_NEW_PRIM(MAN_AXIS_TRANS_XY, ED_GIZMO_PRIMITIVE_STYLE_PLANE);
1294         GIZMO_NEW_PRIM(MAN_AXIS_TRANS_YZ, ED_GIZMO_PRIMITIVE_STYLE_PLANE);
1295         GIZMO_NEW_PRIM(MAN_AXIS_TRANS_ZX, ED_GIZMO_PRIMITIVE_STYLE_PLANE);
1296
1297         man->gizmos[MAN_AXIS_ROT_T]->flag |= WM_GIZMO_SELECT_BACKGROUND;
1298
1299         return man;
1300 }
1301
1302 /**
1303  * Custom handler for gizmo widgets
1304  */
1305 static int gizmo_modal(
1306         bContext *C, wmGizmo *widget, const wmEvent *event,
1307         eWM_GizmoFlagTweak UNUSED(tweak_flag))
1308 {
1309         /* Avoid unnecessary updates, partially address: T55458. */
1310         if (ELEM(event->type, TIMER, INBETWEEN_MOUSEMOVE)) {
1311                 return OPERATOR_RUNNING_MODAL;
1312         }
1313
1314         const ScrArea *sa = CTX_wm_area(C);
1315         ARegion *ar = CTX_wm_region(C);
1316         View3D *v3d = sa->spacedata.first;
1317         RegionView3D *rv3d = ar->regiondata;
1318         struct TransformBounds tbounds;
1319
1320
1321         if (ED_transform_calc_gizmo_stats(
1322                     C, &(struct TransformCalcParams){
1323                         .use_only_center = true,
1324                     }, &tbounds))
1325         {
1326                 gizmo_prepare_mat(C, v3d, rv3d, &tbounds);
1327                 WM_gizmo_set_matrix_location(widget, rv3d->twmat[3]);
1328         }
1329
1330         ED_region_tag_redraw(ar);
1331
1332         return OPERATOR_RUNNING_MODAL;
1333 }
1334
1335 static void gizmogroup_init_properties_from_twtype(wmGizmoGroup *gzgroup)
1336 {
1337         struct {
1338                 wmOperatorType *translate, *rotate, *trackball, *resize;
1339         } ot_store = {NULL};
1340         GizmoGroup *man = gzgroup->customdata;
1341
1342         if (man->twtype & SCE_MANIP_TRANSLATE) {
1343                 man->axis_type_default = MAN_AXES_TRANSLATE;
1344         }
1345         else if (man->twtype & SCE_MANIP_ROTATE) {
1346                 man->axis_type_default = MAN_AXES_ROTATE;
1347         }
1348         else if (man->twtype & SCE_MANIP_SCALE) {
1349                 man->axis_type_default = MAN_AXES_SCALE;
1350         }
1351         else {
1352                 man->axis_type_default = 0;
1353         }
1354
1355         MAN_ITER_AXES_BEGIN(axis, axis_idx)
1356         {
1357                 const short axis_type = gizmo_get_axis_type(axis_idx, man->axis_type_default);
1358                 bool constraint_axis[3] = {1, 0, 0};
1359                 PointerRNA *ptr = NULL;
1360
1361                 gizmo_get_axis_constraint(axis_idx, constraint_axis);
1362
1363                 /* custom handler! */
1364                 WM_gizmo_set_fn_custom_modal(axis, gizmo_modal);
1365
1366                 switch (axis_idx) {
1367                         case MAN_AXIS_TRANS_X:
1368                         case MAN_AXIS_TRANS_Y:
1369                         case MAN_AXIS_TRANS_Z:
1370                         case MAN_AXIS_SCALE_X:
1371                         case MAN_AXIS_SCALE_Y:
1372                         case MAN_AXIS_SCALE_Z:
1373                                 if (axis_idx >= MAN_AXIS_RANGE_TRANS_START && axis_idx < MAN_AXIS_RANGE_TRANS_END) {
1374                                         int draw_options = 0;
1375                                         if ((man->twtype & (SCE_MANIP_ROTATE | SCE_MANIP_SCALE)) == 0) {
1376                                                 draw_options |= ED_GIZMO_ARROW_DRAW_FLAG_STEM;
1377                                         }
1378                                         RNA_enum_set(axis->ptr, "draw_options", draw_options);
1379                                 }
1380
1381                                 WM_gizmo_set_line_width(axis, GIZMO_AXIS_LINE_WIDTH);
1382                                 break;
1383                         case MAN_AXIS_ROT_X:
1384                         case MAN_AXIS_ROT_Y:
1385                         case MAN_AXIS_ROT_Z:
1386                                 /* increased line width for better display */
1387                                 WM_gizmo_set_line_width(axis, GIZMO_AXIS_LINE_WIDTH + 1.0f);
1388                                 WM_gizmo_set_flag(axis, WM_GIZMO_DRAW_VALUE, true);
1389                                 break;
1390                         case MAN_AXIS_TRANS_XY:
1391                         case MAN_AXIS_TRANS_YZ:
1392                         case MAN_AXIS_TRANS_ZX:
1393                         case MAN_AXIS_SCALE_XY:
1394                         case MAN_AXIS_SCALE_YZ:
1395                         case MAN_AXIS_SCALE_ZX:
1396                         {
1397                                 const float ofs_ax = 7.0f;
1398                                 const float ofs[3] = {ofs_ax, ofs_ax, 0.0f};
1399                                 WM_gizmo_set_scale(axis, 0.07f);
1400                                 WM_gizmo_set_matrix_offset_location(axis, ofs);
1401                                 WM_gizmo_set_flag(axis, WM_GIZMO_DRAW_OFFSET_SCALE, true);
1402                                 break;
1403                         }
1404                         case MAN_AXIS_TRANS_C:
1405                         case MAN_AXIS_ROT_C:
1406                         case MAN_AXIS_SCALE_C:
1407                         case MAN_AXIS_ROT_T:
1408                                 WM_gizmo_set_line_width(axis, GIZMO_AXIS_LINE_WIDTH);
1409                                 if (axis_idx == MAN_AXIS_ROT_T) {
1410                                         WM_gizmo_set_flag(axis, WM_GIZMO_DRAW_HOVER, true);
1411                                 }
1412                                 else if (axis_idx == MAN_AXIS_ROT_C) {
1413                                         WM_gizmo_set_flag(axis, WM_GIZMO_DRAW_VALUE, true);
1414                                         WM_gizmo_set_scale(axis, 1.2f);
1415                                 }
1416                                 else {
1417                                         WM_gizmo_set_scale(axis, 0.2f);
1418                                 }
1419                                 break;
1420                         case MAN_AXIS_APRON_C:
1421                                 WM_gizmo_set_scale(axis, 1.2f);
1422                                 break;
1423                 }
1424
1425                 switch (axis_type) {
1426                         case MAN_AXES_TRANSLATE:
1427                                 if (ot_store.translate == NULL) {
1428                                         ot_store.translate = WM_operatortype_find("TRANSFORM_OT_translate", true);
1429                                 }
1430                                 ptr = WM_gizmo_operator_set(axis, 0, ot_store.translate, NULL);
1431                                 break;
1432                         case MAN_AXES_ROTATE:
1433                         {
1434                                 wmOperatorType *ot_rotate;
1435                                 if (axis_idx == MAN_AXIS_ROT_T) {
1436                                         if (ot_store.trackball == NULL) {
1437                                                 ot_store.trackball = WM_operatortype_find("TRANSFORM_OT_trackball", true);
1438                                         }
1439                                         ot_rotate = ot_store.trackball;
1440                                 }
1441                                 else {
1442                                         if (ot_store.rotate == NULL) {
1443                                                 ot_store.rotate = WM_operatortype_find("TRANSFORM_OT_rotate", true);
1444                                         }
1445                                         ot_rotate = ot_store.rotate;
1446                                 }
1447                                 ptr = WM_gizmo_operator_set(axis, 0, ot_rotate, NULL);
1448                                 break;
1449                         }
1450                         case MAN_AXES_SCALE:
1451                         {
1452                                 if (ot_store.resize == NULL) {
1453                                         ot_store.resize = WM_operatortype_find("TRANSFORM_OT_resize", true);
1454                                 }
1455                                 ptr = WM_gizmo_operator_set(axis, 0, ot_store.resize, NULL);
1456                                 break;
1457                         }
1458                 }
1459
1460                 if (ptr) {
1461                         PropertyRNA *prop;
1462                         if (ELEM(true, UNPACK3(constraint_axis))) {
1463                                 if ((prop = RNA_struct_find_property(ptr, "constraint_axis"))) {
1464                                         RNA_property_boolean_set_array(ptr, prop, constraint_axis);
1465                                 }
1466                         }
1467
1468                         RNA_boolean_set(ptr, "release_confirm", 1);
1469                 }
1470         }
1471         MAN_ITER_AXES_END;
1472 }
1473
1474 static void WIDGETGROUP_gizmo_setup(const bContext *C, wmGizmoGroup *gzgroup)
1475 {
1476         GizmoGroup *man = gizmogroup_init(gzgroup);
1477
1478         gzgroup->customdata = man;
1479
1480         {
1481                 man->twtype = 0;
1482                 ScrArea *sa = CTX_wm_area(C);
1483                 const bToolRef *tref = sa->runtime.tool;
1484
1485                 if (tref == NULL || STREQ(tref->idname, "Transform")) {
1486                         /* Setup all gizmos, they can be toggled via 'ToolSettings.gizmo_flag' */
1487                         man->twtype = SCE_MANIP_TRANSLATE | SCE_MANIP_ROTATE | SCE_MANIP_SCALE;
1488                         man->use_twtype_refresh = true;
1489                 }
1490                 else if (STREQ(tref->idname, "Move")) {
1491                         man->twtype |= SCE_MANIP_TRANSLATE;
1492                 }
1493                 else if (STREQ(tref->idname, "Rotate")) {
1494                         man->twtype |= SCE_MANIP_ROTATE;
1495                 }
1496                 else if (STREQ(tref->idname, "Scale")) {
1497                         man->twtype |= SCE_MANIP_SCALE;
1498                 }
1499                 BLI_assert(man->twtype != 0);
1500                 man->twtype_init = man->twtype;
1501         }
1502
1503         /* *** set properties for axes *** */
1504         gizmogroup_init_properties_from_twtype(gzgroup);
1505 }
1506
1507 static void WIDGETGROUP_gizmo_refresh(const bContext *C, wmGizmoGroup *gzgroup)
1508 {
1509         GizmoGroup *man = gzgroup->customdata;
1510         ScrArea *sa = CTX_wm_area(C);
1511         ARegion *ar = CTX_wm_region(C);
1512         View3D *v3d = sa->spacedata.first;
1513         RegionView3D *rv3d = ar->regiondata;
1514         struct TransformBounds tbounds;
1515
1516         if (man->use_twtype_refresh) {
1517                 Scene *scene = CTX_data_scene(C);
1518                 man->twtype = scene->toolsettings->gizmo_flag & man->twtype_init;
1519                 if (man->twtype != man->twtype_prev) {
1520                         man->twtype_prev = man->twtype;
1521                         gizmogroup_init_properties_from_twtype(gzgroup);
1522                 }
1523         }
1524
1525         /* skip, we don't draw anything anyway */
1526         if ((man->all_hidden =
1527              (ED_transform_calc_gizmo_stats(
1528                      C, &(struct TransformCalcParams){
1529                          .use_only_center = true,
1530                      }, &tbounds) == 0)))
1531         {
1532                 return;
1533         }
1534
1535         gizmo_prepare_mat(C, v3d, rv3d, &tbounds);
1536
1537         /* *** set properties for axes *** */
1538
1539         MAN_ITER_AXES_BEGIN(axis, axis_idx)
1540         {
1541                 const short axis_type = gizmo_get_axis_type(axis_idx, man->axis_type_default);
1542                 const int aidx_norm = gizmo_orientation_axis(axis_idx, NULL);
1543
1544                 WM_gizmo_set_matrix_location(axis, rv3d->twmat[3]);
1545
1546                 switch (axis_idx) {
1547                         case MAN_AXIS_TRANS_X:
1548                         case MAN_AXIS_TRANS_Y:
1549                         case MAN_AXIS_TRANS_Z:
1550                         case MAN_AXIS_SCALE_X:
1551                         case MAN_AXIS_SCALE_Y:
1552                         case MAN_AXIS_SCALE_Z:
1553                         {
1554                                 float start_co[3] = {0.0f, 0.0f, 0.0f};
1555                                 float len;
1556
1557                                 gizmo_line_range(man->twtype, axis_type, &start_co[2], &len);
1558
1559                                 WM_gizmo_set_matrix_rotation_from_z_axis(axis, rv3d->twmat[aidx_norm]);
1560                                 RNA_float_set(axis->ptr, "length", len);
1561
1562                                 if (axis_idx >= MAN_AXIS_RANGE_TRANS_START && axis_idx < MAN_AXIS_RANGE_TRANS_END) {
1563                                         if (man->twtype & SCE_MANIP_ROTATE) {
1564                                                 /* Avoid rotate and translate arrows overlap. */
1565                                                 start_co[2] += 0.215f;
1566                                         }
1567                                 }
1568                                 WM_gizmo_set_matrix_offset_location(axis, start_co);
1569                                 WM_gizmo_set_flag(axis, WM_GIZMO_DRAW_OFFSET_SCALE, true);
1570                                 break;
1571                         }
1572                         case MAN_AXIS_ROT_X:
1573                         case MAN_AXIS_ROT_Y:
1574                         case MAN_AXIS_ROT_Z:
1575                                 WM_gizmo_set_matrix_rotation_from_z_axis(axis, rv3d->twmat[aidx_norm]);
1576                                 break;
1577                         case MAN_AXIS_TRANS_XY:
1578                         case MAN_AXIS_TRANS_YZ:
1579                         case MAN_AXIS_TRANS_ZX:
1580                         case MAN_AXIS_SCALE_XY:
1581                         case MAN_AXIS_SCALE_YZ:
1582                         case MAN_AXIS_SCALE_ZX:
1583                         {
1584                                 const float *y_axis = rv3d->twmat[aidx_norm - 1 < 0 ? 2 : aidx_norm - 1];
1585                                 const float *z_axis = rv3d->twmat[aidx_norm];
1586                                 WM_gizmo_set_matrix_rotation_from_yz_axis(axis, y_axis, z_axis);
1587                                 break;
1588                         }
1589                 }
1590         }
1591         MAN_ITER_AXES_END;
1592 }
1593
1594 static void WIDGETGROUP_gizmo_message_subscribe(
1595         const bContext *C, wmGizmoGroup *gzgroup, struct wmMsgBus *mbus)
1596 {
1597         Scene *scene = CTX_data_scene(C);
1598         bScreen *screen = CTX_wm_screen(C);
1599         ScrArea *sa = CTX_wm_area(C);
1600         ARegion *ar = CTX_wm_region(C);
1601         gizmo_xform_message_subscribe(gzgroup, mbus, scene, screen, sa, ar, TRANSFORM_GGT_gizmo);
1602 }
1603
1604 static void WIDGETGROUP_gizmo_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup)
1605 {
1606         const Scene *scene = CTX_data_scene(C);
1607         GizmoGroup *man = gzgroup->customdata;
1608         // ScrArea *sa = CTX_wm_area(C);
1609         ARegion *ar = CTX_wm_region(C);
1610         // View3D *v3d = sa->spacedata.first;
1611         RegionView3D *rv3d = ar->regiondata;
1612         float idot[3];
1613
1614         /* when looking through a selected camera, the gizmo can be at the
1615          * exact same position as the view, skip so we don't break selection */
1616         if (man->all_hidden || fabsf(ED_view3d_pixel_size(rv3d, rv3d->twmat[3])) < 1e-6f) {
1617                 MAN_ITER_AXES_BEGIN(axis, axis_idx)
1618                 {
1619                         WM_gizmo_set_flag(axis, WM_GIZMO_HIDDEN, true);
1620                 }
1621                 MAN_ITER_AXES_END;
1622                 return;
1623         }
1624         gizmo_get_idot(rv3d, idot);
1625
1626         /* *** set properties for axes *** */
1627
1628         MAN_ITER_AXES_BEGIN(axis, axis_idx)
1629         {
1630                 const short axis_type = gizmo_get_axis_type(axis_idx, man->axis_type_default);
1631                 /* XXX maybe unset _HIDDEN flag on redraw? */
1632
1633                 if (axis_idx == MAN_AXIS_APRON_C) {
1634                         WM_gizmo_set_flag(axis, WM_GIZMO_HIDDEN, (scene->toolsettings->gizmo_flag & SCE_MANIP_DISABLE_APRON) != 0);
1635                 }
1636                 else if (gizmo_is_axis_visible(rv3d, man->twtype, idot, axis_type, axis_idx)) {
1637                         WM_gizmo_set_flag(axis, WM_GIZMO_HIDDEN, false);
1638                 }
1639                 else {
1640                         WM_gizmo_set_flag(axis, WM_GIZMO_HIDDEN, true);
1641                         continue;
1642                 }
1643
1644                 float color[4], color_hi[4];
1645                 gizmo_get_axis_color(axis_idx, idot, color, color_hi);
1646                 WM_gizmo_set_color(axis, color);
1647                 WM_gizmo_set_color_highlight(axis, color_hi);
1648
1649                 switch (axis_idx) {
1650                         case MAN_AXIS_TRANS_C:
1651                         case MAN_AXIS_ROT_C:
1652                         case MAN_AXIS_SCALE_C:
1653                         case MAN_AXIS_ROT_T:
1654                         case MAN_AXIS_APRON_C:
1655                                 WM_gizmo_set_matrix_rotation_from_z_axis(axis, rv3d->viewinv[2]);
1656                                 break;
1657                 }
1658         }
1659         MAN_ITER_AXES_END;
1660 }
1661
1662 static bool WIDGETGROUP_gizmo_poll(const struct bContext *C, struct wmGizmoGroupType *gzgt)
1663 {
1664         /* it's a given we only use this in 3D view */
1665         bToolRef_Runtime *tref_rt = WM_toolsystem_runtime_from_context((bContext *)C);
1666         if ((tref_rt == NULL) ||
1667             !STREQ(gzgt->idname, tref_rt->gizmo_group))
1668         {
1669                 WM_gizmo_group_type_unlink_delayed_ptr(gzgt);
1670                 return false;
1671         }
1672
1673         View3D *v3d = CTX_wm_view3d(C);
1674         if (v3d->gizmo_flag & (V3D_GIZMO_HIDE | V3D_GIZMO_HIDE_TOOL)) {
1675                 return false;
1676         }
1677         return true;
1678 }
1679
1680 void TRANSFORM_GGT_gizmo(wmGizmoGroupType *gzgt)
1681 {
1682         gzgt->name = "Transform Gizmo";
1683         gzgt->idname = "TRANSFORM_GGT_gizmo";
1684
1685         gzgt->flag |= WM_GIZMOGROUPTYPE_3D;
1686
1687         gzgt->gzmap_params.spaceid = SPACE_VIEW3D;
1688         gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW;
1689
1690         gzgt->poll = WIDGETGROUP_gizmo_poll;
1691         gzgt->setup = WIDGETGROUP_gizmo_setup;
1692         gzgt->refresh = WIDGETGROUP_gizmo_refresh;
1693         gzgt->message_subscribe = WIDGETGROUP_gizmo_message_subscribe;
1694         gzgt->draw_prepare = WIDGETGROUP_gizmo_draw_prepare;
1695 }
1696
1697 /** \} */
1698
1699
1700 /* -------------------------------------------------------------------- */
1701 /** \name Scale Cage Gizmo
1702  * \{ */
1703
1704 struct XFormCageWidgetGroup {
1705         wmGizmo *gizmo;
1706 };
1707
1708 static bool WIDGETGROUP_xform_cage_poll(const bContext *C, wmGizmoGroupType *gzgt)
1709 {
1710         bToolRef_Runtime *tref_rt = WM_toolsystem_runtime_from_context((bContext *)C);
1711         if (!STREQ(gzgt->idname, tref_rt->gizmo_group)) {
1712                 WM_gizmo_group_type_unlink_delayed_ptr(gzgt);
1713                 return false;
1714         }
1715         return true;
1716 }
1717
1718 static void WIDGETGROUP_xform_cage_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup)
1719 {
1720         struct XFormCageWidgetGroup *xgzgroup = MEM_mallocN(sizeof(struct XFormCageWidgetGroup), __func__);
1721         const wmGizmoType *gzt_cage = WM_gizmotype_find("GIZMO_GT_cage_3d", true);
1722         xgzgroup->gizmo = WM_gizmo_new_ptr(gzt_cage, gzgroup, NULL);
1723         wmGizmo *gz = xgzgroup->gizmo;
1724
1725         RNA_enum_set(gz->ptr, "transform",
1726                      ED_GIZMO_CAGE2D_XFORM_FLAG_SCALE |
1727                      ED_GIZMO_CAGE2D_XFORM_FLAG_TRANSLATE);
1728
1729         gz->color[0] = 1;
1730         gz->color_hi[0] = 1;
1731
1732         gzgroup->customdata = xgzgroup;
1733
1734         {
1735                 wmOperatorType *ot_resize = WM_operatortype_find("TRANSFORM_OT_resize", true);
1736                 PointerRNA *ptr;
1737
1738                 /* assign operator */
1739                 PropertyRNA *prop_release_confirm = NULL;
1740                 PropertyRNA *prop_constraint_axis = NULL;
1741
1742                 int i = ED_GIZMO_CAGE3D_PART_SCALE_MIN_X_MIN_Y_MIN_Z;
1743                 for (int x = 0; x < 3; x++) {
1744                         for (int y = 0; y < 3; y++) {
1745                                 for (int z = 0; z < 3; z++) {
1746                                         bool constraint[3] = {x != 1, y != 1, z != 1};
1747                                         ptr = WM_gizmo_operator_set(gz, i, ot_resize, NULL);
1748                                         if (prop_release_confirm == NULL) {
1749                                                 prop_release_confirm = RNA_struct_find_property(ptr, "release_confirm");
1750                                                 prop_constraint_axis = RNA_struct_find_property(ptr, "constraint_axis");
1751                                         }
1752                                         RNA_property_boolean_set(ptr, prop_release_confirm, true);
1753                                         RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint);
1754                                         i++;
1755                                 }
1756                         }
1757                 }
1758         }
1759 }
1760
1761 static void WIDGETGROUP_xform_cage_refresh(const bContext *C, wmGizmoGroup *gzgroup)
1762 {
1763         ScrArea *sa = CTX_wm_area(C);
1764         View3D *v3d = sa->spacedata.first;
1765         ARegion *ar = CTX_wm_region(C);
1766         RegionView3D *rv3d = ar->regiondata;
1767
1768         struct XFormCageWidgetGroup *xgzgroup = gzgroup->customdata;
1769         wmGizmo *gz = xgzgroup->gizmo;
1770
1771         struct TransformBounds tbounds;
1772
1773         if ((ED_transform_calc_gizmo_stats(
1774                      C, &(struct TransformCalcParams) {
1775                          .use_local_axis = true,
1776                      }, &tbounds) == 0) ||
1777             equals_v3v3(rv3d->tw_axis_min, rv3d->tw_axis_max))
1778         {
1779                 WM_gizmo_set_flag(gz, WM_GIZMO_HIDDEN, true);
1780         }
1781         else {
1782                 gizmo_prepare_mat(C, v3d, rv3d, &tbounds);
1783
1784                 WM_gizmo_set_flag(gz, WM_GIZMO_HIDDEN, false);
1785                 WM_gizmo_set_flag(gz, WM_GIZMO_MOVE_CURSOR, true);
1786
1787                 float dims[3];
1788                 sub_v3_v3v3(dims, rv3d->tw_axis_max, rv3d->tw_axis_min);
1789                 RNA_float_set_array(gz->ptr, "dimensions", dims);
1790                 mul_v3_fl(dims, 0.5f);
1791
1792                 copy_m4_m3(gz->matrix_offset, rv3d->tw_axis_matrix);
1793                 mid_v3_v3v3(gz->matrix_offset[3], rv3d->tw_axis_max, rv3d->tw_axis_min);
1794                 mul_m3_v3(rv3d->tw_axis_matrix, gz->matrix_offset[3]);
1795
1796                 PropertyRNA *prop_center_override = NULL;
1797                 float center[3];
1798                 float center_global[3];
1799                 int i = ED_GIZMO_CAGE3D_PART_SCALE_MIN_X_MIN_Y_MIN_Z;
1800                 for (int x = 0; x < 3; x++) {
1801                         center[0] = (float)(1 - x) * dims[0];
1802                         for (int y = 0; y < 3; y++) {
1803                                 center[1] = (float)(1 - y) * dims[1];
1804                                 for (int z = 0; z < 3; z++) {
1805                                         center[2] = (float)(1 - z) * dims[2];
1806                                         struct wmGizmoOpElem *mpop = WM_gizmo_operator_get(gz, i);
1807                                         if (prop_center_override == NULL) {
1808                                                 prop_center_override = RNA_struct_find_property(&mpop->ptr, "center_override");
1809                                         }
1810                                         mul_v3_m4v3(center_global, gz->matrix_offset, center);
1811                                         RNA_property_float_set_array(&mpop->ptr, prop_center_override, center_global);
1812                                         i++;
1813                                 }
1814                         }
1815                 }
1816         }
1817 }
1818
1819 static void WIDGETGROUP_xform_cage_message_subscribe(
1820         const bContext *C, wmGizmoGroup *gzgroup, struct wmMsgBus *mbus)
1821 {
1822         Scene *scene = CTX_data_scene(C);
1823         bScreen *screen = CTX_wm_screen(C);
1824         ScrArea *sa = CTX_wm_area(C);
1825         ARegion *ar = CTX_wm_region(C);
1826         gizmo_xform_message_subscribe(gzgroup, mbus, scene, screen, sa, ar, VIEW3D_GGT_xform_cage);
1827 }
1828
1829 static void WIDGETGROUP_xform_cage_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup)
1830 {
1831         struct XFormCageWidgetGroup *xgzgroup = gzgroup->customdata;
1832         wmGizmo *gz = xgzgroup->gizmo;
1833
1834         ViewLayer *view_layer = CTX_data_view_layer(C);
1835         Object *ob = OBACT(view_layer);
1836         if (ob && ob->mode & OB_MODE_EDIT) {
1837                 copy_m4_m4(gz->matrix_space, ob->obmat);
1838         }
1839         else {
1840                 unit_m4(gz->matrix_space);
1841         }
1842 }
1843
1844 void VIEW3D_GGT_xform_cage(wmGizmoGroupType *gzgt)
1845 {
1846         gzgt->name = "Transform Cage";
1847         gzgt->idname = "VIEW3D_GGT_xform_cage";
1848
1849         gzgt->flag |= WM_GIZMOGROUPTYPE_3D;
1850
1851         gzgt->gzmap_params.spaceid = SPACE_VIEW3D;
1852         gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW;
1853
1854         gzgt->poll = WIDGETGROUP_xform_cage_poll;
1855         gzgt->setup = WIDGETGROUP_xform_cage_setup;
1856         gzgt->refresh = WIDGETGROUP_xform_cage_refresh;
1857         gzgt->message_subscribe = WIDGETGROUP_xform_cage_message_subscribe;
1858         gzgt->draw_prepare = WIDGETGROUP_xform_cage_draw_prepare;
1859 }
1860
1861 /** \} */