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