2 * ***** BEGIN GPL LICENSE BLOCK *****
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.
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.
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.
18 * ***** END GPL LICENSE BLOCK *****
21 /** \file blender/editors/transform/transform_manipulator_3d.c
22 * \ingroup edtransform
24 * \name 3D Transform Manipulator
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"
43 #include "BLI_listbase.h"
45 #include "BLI_utildefines.h"
47 #include "RNA_access.h"
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"
63 #include "DEG_depsgraph.h"
67 #include "WM_message.h"
69 #include "ED_armature.h"
71 #include "ED_object.h"
72 #include "ED_particle.h"
73 #include "ED_view3d.h"
74 #include "ED_gpencil.h"
75 #include "ED_screen.h"
76 #include "ED_manipulator_library.h"
78 #include "UI_resources.h"
80 /* local module include */
81 #include "transform.h"
83 #include "MEM_guardedalloc.h"
85 #include "GPU_select.h"
86 #include "GPU_immediate.h"
87 #include "GPU_matrix.h"
89 #include "DEG_depsgraph_query.h"
91 /* return codes for select, and drawing flags */
93 #define MAN_TRANS_X (1 << 0)
94 #define MAN_TRANS_Y (1 << 1)
95 #define MAN_TRANS_Z (1 << 2)
96 #define MAN_TRANS_C (MAN_TRANS_X | MAN_TRANS_Y | MAN_TRANS_Z)
98 #define MAN_ROT_X (1 << 3)
99 #define MAN_ROT_Y (1 << 4)
100 #define MAN_ROT_Z (1 << 5)
101 #define MAN_ROT_C (MAN_ROT_X | MAN_ROT_Y | MAN_ROT_Z)
103 #define MAN_SCALE_X (1 << 8)
104 #define MAN_SCALE_Y (1 << 9)
105 #define MAN_SCALE_Z (1 << 10)
106 #define MAN_SCALE_C (MAN_SCALE_X | MAN_SCALE_Y | MAN_SCALE_Z)
108 /* threshold for testing view aligned manipulator axis */
109 #define TW_AXIS_DOT_MIN 0.02f
110 #define TW_AXIS_DOT_MAX 0.1f
114 MAN_AXIS_TRANS_X = 0,
122 #define MAN_AXIS_RANGE_TRANS_START MAN_AXIS_TRANS_X
123 #define MAN_AXIS_RANGE_TRANS_END (MAN_AXIS_TRANS_ZX + 1)
129 MAN_AXIS_ROT_T, /* trackball rotation */
130 #define MAN_AXIS_RANGE_ROT_START MAN_AXIS_ROT_X
131 #define MAN_AXIS_RANGE_ROT_END (MAN_AXIS_ROT_T + 1)
140 #define MAN_AXIS_RANGE_SCALE_START MAN_AXIS_SCALE_X
141 #define MAN_AXIS_RANGE_SCALE_END (MAN_AXIS_SCALE_ZX + 1)
143 MAN_AXIS_LAST = MAN_AXIS_RANGE_SCALE_END,
154 /* naming from old blender we may combine. */
156 V3D_MANIP_TRANSLATE = 1,
157 V3D_MANIP_ROTATE = 2,
162 typedef struct ManipulatorGroup {
166 struct wmManipulator *manipulators[MAN_AXIS_LAST];
169 /* -------------------------------------------------------------------- */
174 #define MAN_ITER_AXES_BEGIN(axis, axis_idx) \
176 wmManipulator *axis; \
178 for (axis_idx = 0; axis_idx < MAN_AXIS_LAST; axis_idx++) { \
179 axis = manipulator_get_axis_from_index(man, axis_idx);
181 #define MAN_ITER_AXES_END \
185 static wmManipulator *manipulator_get_axis_from_index(const ManipulatorGroup *man, const short axis_idx)
187 BLI_assert(IN_RANGE_INCL(axis_idx, (float)MAN_AXIS_TRANS_X, (float)MAN_AXIS_LAST));
188 return man->manipulators[axis_idx];
191 static short manipulator_get_axis_type(const int axis_idx)
193 if (axis_idx >= MAN_AXIS_RANGE_TRANS_START && axis_idx < MAN_AXIS_RANGE_TRANS_END) {
194 return MAN_AXES_TRANSLATE;
196 if (axis_idx >= MAN_AXIS_RANGE_ROT_START && axis_idx < MAN_AXIS_RANGE_ROT_END) {
197 return MAN_AXES_ROTATE;
199 if (axis_idx >= MAN_AXIS_RANGE_SCALE_START && axis_idx < MAN_AXIS_RANGE_SCALE_END) {
200 return MAN_AXES_SCALE;
206 static uint manipulator_orientation_axis(const int axis_idx, bool *r_is_plane)
209 case MAN_AXIS_TRANS_YZ:
210 case MAN_AXIS_SCALE_YZ:
215 case MAN_AXIS_TRANS_X:
217 case MAN_AXIS_SCALE_X:
220 case MAN_AXIS_TRANS_ZX:
221 case MAN_AXIS_SCALE_ZX:
226 case MAN_AXIS_TRANS_Y:
228 case MAN_AXIS_SCALE_Y:
231 case MAN_AXIS_TRANS_XY:
232 case MAN_AXIS_SCALE_XY:
237 case MAN_AXIS_TRANS_Z:
239 case MAN_AXIS_SCALE_Z:
245 static bool manipulator_is_axis_visible(
246 const RegionView3D *rv3d, const int twtype,
247 const float idot[3], const int axis_type, const int axis_idx)
249 bool is_plane = false;
250 const uint aidx_norm = manipulator_orientation_axis(axis_idx, &is_plane);
251 /* don't draw axis perpendicular to the view */
253 float idot_axis = idot[aidx_norm];
255 idot_axis = 1.0f - idot_axis;
257 if (idot_axis < TW_AXIS_DOT_MIN) {
262 if ((axis_type == MAN_AXES_TRANSLATE && !(twtype & V3D_MANIP_TRANSLATE)) ||
263 (axis_type == MAN_AXES_ROTATE && !(twtype & V3D_MANIP_ROTATE)) ||
264 (axis_type == MAN_AXES_SCALE && !(twtype & V3D_MANIP_SCALE)))
270 case MAN_AXIS_TRANS_X:
271 return (rv3d->twdrawflag & MAN_TRANS_X);
272 case MAN_AXIS_TRANS_Y:
273 return (rv3d->twdrawflag & MAN_TRANS_Y);
274 case MAN_AXIS_TRANS_Z:
275 return (rv3d->twdrawflag & MAN_TRANS_Z);
276 case MAN_AXIS_TRANS_C:
277 return (rv3d->twdrawflag & MAN_TRANS_C);
279 return (rv3d->twdrawflag & MAN_ROT_X);
281 return (rv3d->twdrawflag & MAN_ROT_Y);
283 return (rv3d->twdrawflag & MAN_ROT_Z);
286 return (rv3d->twdrawflag & MAN_ROT_C);
287 case MAN_AXIS_SCALE_X:
288 return (rv3d->twdrawflag & MAN_SCALE_X);
289 case MAN_AXIS_SCALE_Y:
290 return (rv3d->twdrawflag & MAN_SCALE_Y);
291 case MAN_AXIS_SCALE_Z:
292 return (rv3d->twdrawflag & MAN_SCALE_Z);
293 case MAN_AXIS_SCALE_C:
294 return (rv3d->twdrawflag & MAN_SCALE_C && (twtype & V3D_MANIP_TRANSLATE) == 0);
295 case MAN_AXIS_TRANS_XY:
296 return (rv3d->twdrawflag & MAN_TRANS_X &&
297 rv3d->twdrawflag & MAN_TRANS_Y &&
298 (twtype & V3D_MANIP_ROTATE) == 0);
299 case MAN_AXIS_TRANS_YZ:
300 return (rv3d->twdrawflag & MAN_TRANS_Y &&
301 rv3d->twdrawflag & MAN_TRANS_Z &&
302 (twtype & V3D_MANIP_ROTATE) == 0);
303 case MAN_AXIS_TRANS_ZX:
304 return (rv3d->twdrawflag & MAN_TRANS_Z &&
305 rv3d->twdrawflag & MAN_TRANS_X &&
306 (twtype & V3D_MANIP_ROTATE) == 0);
307 case MAN_AXIS_SCALE_XY:
308 return (rv3d->twdrawflag & MAN_SCALE_X &&
309 rv3d->twdrawflag & MAN_SCALE_Y &&
310 (twtype & V3D_MANIP_TRANSLATE) == 0 &&
311 (twtype & V3D_MANIP_ROTATE) == 0);
312 case MAN_AXIS_SCALE_YZ:
313 return (rv3d->twdrawflag & MAN_SCALE_Y &&
314 rv3d->twdrawflag & MAN_SCALE_Z &&
315 (twtype & V3D_MANIP_TRANSLATE) == 0 &&
316 (twtype & V3D_MANIP_ROTATE) == 0);
317 case MAN_AXIS_SCALE_ZX:
318 return (rv3d->twdrawflag & MAN_SCALE_Z &&
319 rv3d->twdrawflag & MAN_SCALE_X &&
320 (twtype & V3D_MANIP_TRANSLATE) == 0 &&
321 (twtype & V3D_MANIP_ROTATE) == 0);
326 static void manipulator_get_axis_color(
327 const int axis_idx, const float idot[3],
328 float r_col[4], float r_col_hi[4])
330 /* alpha values for normal/highlighted states */
331 const float alpha = 0.6f;
332 const float alpha_hi = 1.0f;
335 bool is_plane = false;
336 const int axis_idx_norm = manipulator_orientation_axis(axis_idx, &is_plane);
337 /* get alpha fac based on axis angle, to fade axis out when hiding it because it points towards view */
338 if (axis_idx_norm < 3) {
339 float idot_axis = idot[axis_idx_norm];
341 idot_axis = 1.0f - idot_axis;
343 alpha_fac = (idot_axis > TW_AXIS_DOT_MAX) ?
344 1.0f : (idot_axis < TW_AXIS_DOT_MIN) ?
345 0.0f : ((idot_axis - TW_AXIS_DOT_MIN) / (TW_AXIS_DOT_MAX - TW_AXIS_DOT_MIN));
348 /* trackball rotation axis is a special case, we only draw a slight overlay */
349 alpha_fac = (axis_idx == MAN_AXIS_ROT_T) ? 0.1f : 1.0f;
353 case MAN_AXIS_TRANS_X:
355 case MAN_AXIS_SCALE_X:
356 case MAN_AXIS_TRANS_YZ:
357 case MAN_AXIS_SCALE_YZ:
358 UI_GetThemeColor4fv(TH_AXIS_X, r_col);
360 case MAN_AXIS_TRANS_Y:
362 case MAN_AXIS_SCALE_Y:
363 case MAN_AXIS_TRANS_ZX:
364 case MAN_AXIS_SCALE_ZX:
365 UI_GetThemeColor4fv(TH_AXIS_Y, r_col);
367 case MAN_AXIS_TRANS_Z:
369 case MAN_AXIS_SCALE_Z:
370 case MAN_AXIS_TRANS_XY:
371 case MAN_AXIS_SCALE_XY:
372 UI_GetThemeColor4fv(TH_AXIS_Z, r_col);
374 case MAN_AXIS_TRANS_C:
376 case MAN_AXIS_SCALE_C:
378 copy_v4_fl(r_col, 1.0f);
382 copy_v4_v4(r_col_hi, r_col);
384 r_col[3] = alpha * alpha_fac;
385 r_col_hi[3] = alpha_hi * alpha_fac;
388 static void manipulator_get_axis_constraint(const int axis_idx, int r_axis[3])
393 case MAN_AXIS_TRANS_X:
395 case MAN_AXIS_SCALE_X:
398 case MAN_AXIS_TRANS_Y:
400 case MAN_AXIS_SCALE_Y:
403 case MAN_AXIS_TRANS_Z:
405 case MAN_AXIS_SCALE_Z:
408 case MAN_AXIS_TRANS_XY:
409 case MAN_AXIS_SCALE_XY:
410 r_axis[0] = r_axis[1] = 1;
412 case MAN_AXIS_TRANS_YZ:
413 case MAN_AXIS_SCALE_YZ:
414 r_axis[1] = r_axis[2] = 1;
416 case MAN_AXIS_TRANS_ZX:
417 case MAN_AXIS_SCALE_ZX:
418 r_axis[2] = r_axis[0] = 1;
426 /* **************** Preparation Stuff **************** */
428 /* transform widget center calc helper for below */
429 static void calc_tw_center(struct TransformBounds *tbounds, const float co[3])
431 minmax_v3v3_v3(tbounds->min, tbounds->max, co);
432 add_v3_v3(tbounds->center, co);
434 for (int i = 0; i < 3; i++) {
435 const float d = dot_v3v3(tbounds->axis[i], co);
436 tbounds->axis_min[i] = min_ff(d, tbounds->axis_min[i]);
437 tbounds->axis_max[i] = max_ff(d, tbounds->axis_max[i]);
441 static void protectflag_to_drawflags(short protectflag, short *drawflags)
443 if (protectflag & OB_LOCK_LOCX)
444 *drawflags &= ~MAN_TRANS_X;
445 if (protectflag & OB_LOCK_LOCY)
446 *drawflags &= ~MAN_TRANS_Y;
447 if (protectflag & OB_LOCK_LOCZ)
448 *drawflags &= ~MAN_TRANS_Z;
450 if (protectflag & OB_LOCK_ROTX)
451 *drawflags &= ~MAN_ROT_X;
452 if (protectflag & OB_LOCK_ROTY)
453 *drawflags &= ~MAN_ROT_Y;
454 if (protectflag & OB_LOCK_ROTZ)
455 *drawflags &= ~MAN_ROT_Z;
457 if (protectflag & OB_LOCK_SCALEX)
458 *drawflags &= ~MAN_SCALE_X;
459 if (protectflag & OB_LOCK_SCALEY)
460 *drawflags &= ~MAN_SCALE_Y;
461 if (protectflag & OB_LOCK_SCALEZ)
462 *drawflags &= ~MAN_SCALE_Z;
466 static void protectflag_to_drawflags_pchan(RegionView3D *rv3d, const bPoseChannel *pchan)
468 protectflag_to_drawflags(pchan->protectflag, &rv3d->twdrawflag);
472 static void protectflag_to_drawflags_ebone(RegionView3D *rv3d, const EditBone *ebo)
474 if (ebo->flag & BONE_EDITMODE_LOCKED) {
475 protectflag_to_drawflags(OB_LOCK_LOC | OB_LOCK_ROT | OB_LOCK_SCALE, &rv3d->twdrawflag);
479 /* could move into BLI_math however this is only useful for display/editing purposes */
480 static void axis_angle_to_gimbal_axis(float gmat[3][3], const float axis[3], const float angle)
482 /* X/Y are arbitrary axies, most importantly Z is the axis of rotation */
487 /* this is an un-scientific method to get a vector to cross with
488 * XYZ intentionally YZX */
489 cross_vec[0] = axis[1];
490 cross_vec[1] = axis[2];
491 cross_vec[2] = axis[0];
494 cross_v3_v3v3(gmat[0], cross_vec, axis);
495 normalize_v3(gmat[0]);
496 axis_angle_to_quat(quat, axis, angle);
497 mul_qt_v3(quat, gmat[0]);
500 axis_angle_to_quat(quat, axis, M_PI_2);
501 copy_v3_v3(gmat[1], gmat[0]);
502 mul_qt_v3(quat, gmat[1]);
505 copy_v3_v3(gmat[2], axis);
511 static bool test_rotmode_euler(short rotmode)
513 return (ELEM(rotmode, ROT_MODE_AXISANGLE, ROT_MODE_QUAT)) ? 0 : 1;
516 bool gimbal_axis(Object *ob, float gmat[3][3])
518 if (ob->mode & OB_MODE_POSE) {
519 bPoseChannel *pchan = BKE_pose_channel_active(ob);
522 float mat[3][3], tmat[3][3], obmat[3][3];
523 if (test_rotmode_euler(pchan->rotmode)) {
524 eulO_to_gimbal_axis(mat, pchan->eul, pchan->rotmode);
526 else if (pchan->rotmode == ROT_MODE_AXISANGLE) {
527 axis_angle_to_gimbal_axis(mat, pchan->rotAxis, pchan->rotAngle);
534 /* apply bone transformation */
535 mul_m3_m3m3(tmat, pchan->bone->bone_mat, mat);
538 float parent_mat[3][3];
540 copy_m3_m4(parent_mat, pchan->parent->pose_mat);
541 mul_m3_m3m3(mat, parent_mat, tmat);
543 /* needed if object transformation isn't identity */
544 copy_m3_m4(obmat, ob->obmat);
545 mul_m3_m3m3(gmat, obmat, mat);
548 /* needed if object transformation isn't identity */
549 copy_m3_m4(obmat, ob->obmat);
550 mul_m3_m3m3(gmat, obmat, tmat);
558 if (test_rotmode_euler(ob->rotmode)) {
559 eulO_to_gimbal_axis(gmat, ob->rot, ob->rotmode);
561 else if (ob->rotmode == ROT_MODE_AXISANGLE) {
562 axis_angle_to_gimbal_axis(gmat, ob->rotAxis, ob->rotAngle);
569 float parent_mat[3][3];
570 copy_m3_m4(parent_mat, ob->parent->obmat);
571 normalize_m3(parent_mat);
572 mul_m3_m3m3(gmat, parent_mat, gmat);
581 /* centroid, boundbox, of selection */
582 /* returns total items selected */
583 int ED_transform_calc_manipulator_stats(
585 const struct TransformCalcParams *params,
586 struct TransformBounds *tbounds)
588 const Depsgraph *depsgraph = CTX_data_depsgraph(C);
589 ScrArea *sa = CTX_wm_area(C);
590 ARegion *ar = CTX_wm_region(C);
591 Scene *scene = CTX_data_scene(C);
592 ViewLayer *view_layer = CTX_data_view_layer(C);
593 Object *obedit = CTX_data_edit_object(C);
594 View3D *v3d = sa->spacedata.first;
595 RegionView3D *rv3d = ar->regiondata;
597 Object *ob = OBACT(view_layer);
598 const Object *ob_eval = NULL;
599 const Object *obedit_eval = NULL;
600 bGPdata *gpd = CTX_data_gpencil_data(C);
601 const bool is_gp_edit = ((gpd) && (gpd->flag & GP_DATA_STROKE_EDITMODE));
603 const int pivot_point = scene->toolsettings->transform_pivot_point;
605 /* transform widget matrix */
606 unit_m4(rv3d->twmat);
608 unit_m3(rv3d->tw_axis_matrix);
609 zero_v3(rv3d->tw_axis_min);
610 zero_v3(rv3d->tw_axis_max);
612 rv3d->twdrawflag = 0xFFFF;
614 ob_eval = DEG_get_evaluated_object(depsgraph, ob);
615 obedit_eval = DEG_get_evaluated_object(depsgraph, obedit);
617 /* global, local or normal orientation?
618 * if we could check 'totsel' now, this should be skipped with no selection. */
619 if (ob && !is_gp_edit) {
620 const short orientation_type = params->orientation_type ? (params->orientation_type - 1) : scene->orientation_type;
622 switch (orientation_type) {
624 case V3D_MANIP_GLOBAL:
626 break; /* nothing to do */
628 case V3D_MANIP_GIMBAL:
631 if (gimbal_axis(ob, mat)) {
632 copy_m4_m3(rv3d->twmat, mat);
635 /* if not gimbal, fall through to normal */
638 case V3D_MANIP_NORMAL:
640 if (obedit || ob->mode & OB_MODE_POSE) {
642 ED_getTransformOrientationMatrix(C, mat, pivot_point);
643 copy_m4_m3(rv3d->twmat, mat);
646 /* no break we define 'normal' as 'local' in Object mode */
649 case V3D_MANIP_LOCAL:
651 if (ob->mode & OB_MODE_POSE) {
652 /* each bone moves on its own local axis, but to avoid confusion,
653 * use the active pones axis for display [#33575], this works as expected on a single bone
654 * and users who select many bones will understand whats going on and what local means
655 * when they start transforming */
657 ED_getTransformOrientationMatrix(C, mat, pivot_point);
658 copy_m4_m3(rv3d->twmat, mat);
661 copy_m4_m4(rv3d->twmat, ob_eval->obmat);
662 normalize_m4(rv3d->twmat);
668 copy_m3_m4(mat, rv3d->viewinv);
670 copy_m4_m3(rv3d->twmat, mat);
673 case V3D_MANIP_CURSOR:
676 ED_view3d_cursor3d_calc_mat3(scene, v3d, mat);
677 copy_m4_m3(rv3d->twmat, mat);
680 case V3D_MANIP_CUSTOM:
682 TransformOrientation *custom_orientation = BKE_scene_transform_orientation_find(
683 scene, scene->orientation_index_custom);
686 if (applyTransformOrientation(custom_orientation, mat, NULL)) {
687 copy_m4_m3(rv3d->twmat, mat);
694 /* transform widget centroid/center */
695 INIT_MINMAX(tbounds->min, tbounds->max);
696 zero_v3(tbounds->center);
698 copy_m3_m4(tbounds->axis, rv3d->twmat);
699 if (params->use_local_axis && (ob && ob->mode & OB_MODE_EDIT)) {
700 float diff_mat[3][3];
701 copy_m3_m4(diff_mat, ob_eval->obmat);
702 normalize_m3(diff_mat);
704 mul_m3_m3m3(tbounds->axis, tbounds->axis, diff_mat);
705 normalize_m3(tbounds->axis);
708 for (int i = 0; i < 3; i++) {
709 tbounds->axis_min[i] = +FLT_MAX;
710 tbounds->axis_max[i] = -FLT_MAX;
714 float diff_mat[4][4];
717 for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
718 /* only editable and visible layers are considered */
719 if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
721 /* calculate difference matrix if parent object */
722 if (gpl->parent != NULL) {
723 ED_gpencil_parent_location(gpl, diff_mat);
726 for (bGPDstroke *gps = gpl->actframe->strokes.first; gps; gps = gps->next) {
727 /* skip strokes that are invalid for current view */
728 if (ED_gpencil_stroke_can_use(C, gps) == false) {
732 /* we're only interested in selected points here... */
733 if (gps->flag & GP_STROKE_SELECT) {
737 /* Change selection status of all points, then make the stroke match */
738 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
739 if (pt->flag & GP_SPOINT_SELECT) {
740 if (gpl->parent == NULL) {
741 calc_tw_center(tbounds, &pt->x);
745 mul_v3_m4v3(fpt, diff_mat, &pt->x);
746 calc_tw_center(tbounds, fpt);
757 /* selection center */
759 mul_v3_fl(tbounds->center, 1.0f / (float)totsel); /* centroid! */
764 ob_eval = obedit_eval;
765 if (obedit->type == OB_MESH) {
766 BMEditMesh *em = BKE_editmesh_from_object(obedit);
768 float vec[3] = {0, 0, 0};
770 /* USE LAST SELECTE WITH ACTIVE */
771 if ((pivot_point == V3D_AROUND_ACTIVE) && BM_select_history_active_get(em->bm, &ese)) {
772 BM_editselection_center(&ese, vec);
773 calc_tw_center(tbounds, vec);
782 BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
783 if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
784 if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
786 calc_tw_center(tbounds, eve->co);
792 else if (obedit->type == OB_ARMATURE) {
793 bArmature *arm = obedit->data;
796 if ((pivot_point == V3D_AROUND_ACTIVE) && (ebo = arm->act_edbone)) {
797 /* doesn't check selection or visibility intentionally */
798 if (ebo->flag & BONE_TIPSEL) {
799 calc_tw_center(tbounds, ebo->tail);
802 if ((ebo->flag & BONE_ROOTSEL) ||
803 ((ebo->flag & BONE_TIPSEL) == false)) /* ensure we get at least one point */
805 calc_tw_center(tbounds, ebo->head);
808 protectflag_to_drawflags_ebone(rv3d, ebo);
811 for (ebo = arm->edbo->first; ebo; ebo = ebo->next) {
812 if (EBONE_VISIBLE(arm, ebo)) {
813 if (ebo->flag & BONE_TIPSEL) {
814 calc_tw_center(tbounds, ebo->tail);
817 if ((ebo->flag & BONE_ROOTSEL) &&
818 /* don't include same point multiple times */
819 ((ebo->flag & BONE_CONNECTED) &&
820 (ebo->parent != NULL) &&
821 (ebo->parent->flag & BONE_TIPSEL) &&
822 EBONE_VISIBLE(arm, ebo->parent)) == 0)
824 calc_tw_center(tbounds, ebo->head);
827 if (ebo->flag & BONE_SELECTED) {
828 protectflag_to_drawflags_ebone(rv3d, ebo);
834 else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) {
835 Curve *cu = obedit->data;
838 if ((pivot_point == V3D_AROUND_ACTIVE) && ED_curve_active_center(cu, center)) {
839 calc_tw_center(tbounds, center);
846 ListBase *nurbs = BKE_curve_editNurbs_get(cu);
850 if (nu->type == CU_BEZIER) {
855 * if handles are hidden then only check the center points.
856 * If the center knot is selected then only use this as the center point.
858 if (cu->drawflag & CU_HIDE_HANDLES) {
859 if (bezt->f2 & SELECT) {
860 calc_tw_center(tbounds, bezt->vec[1]);
864 else if (bezt->f2 & SELECT) {
865 calc_tw_center(tbounds, bezt->vec[1]);
869 if (bezt->f1 & SELECT) {
871 tbounds, bezt->vec[(pivot_point == V3D_AROUND_LOCAL_ORIGINS) ? 1 : 0]);
874 if (bezt->f3 & SELECT) {
876 tbounds, bezt->vec[(pivot_point == V3D_AROUND_LOCAL_ORIGINS) ? 1 : 2]);
885 a = nu->pntsu * nu->pntsv;
887 if (bp->f1 & SELECT) {
888 calc_tw_center(tbounds, bp->vec);
898 else if (obedit->type == OB_MBALL) {
899 MetaBall *mb = (MetaBall *)obedit->data;
902 if ((pivot_point == V3D_AROUND_ACTIVE) && (ml = mb->lastelem)) {
903 calc_tw_center(tbounds, &ml->x);
907 for (ml = mb->editelems->first; ml; ml = ml->next) {
908 if (ml->flag & SELECT) {
909 calc_tw_center(tbounds, &ml->x);
915 else if (obedit->type == OB_LATTICE) {
916 Lattice *lt = ((Lattice *)obedit->data)->editlatt->latt;
919 if ((pivot_point == V3D_AROUND_ACTIVE) && (bp = BKE_lattice_active_point_get(lt))) {
920 calc_tw_center(tbounds, bp->vec);
925 a = lt->pntsu * lt->pntsv * lt->pntsw;
927 if (bp->f1 & SELECT) {
928 calc_tw_center(tbounds, bp->vec);
936 /* selection center */
938 mul_v3_fl(tbounds->center, 1.0f / (float)totsel); // centroid!
939 mul_m4_v3(obedit_eval->obmat, tbounds->center);
940 mul_m4_v3(obedit_eval->obmat, tbounds->min);
941 mul_m4_v3(obedit_eval->obmat, tbounds->max);
944 else if (ob && (ob->mode & OB_MODE_POSE)) {
946 int mode = TFM_ROTATION; // mislead counting bones... bah. We don't know the manipulator mode, could be mixed
949 if ((pivot_point == V3D_AROUND_ACTIVE) && (pchan = BKE_pose_channel_active(ob))) {
950 /* doesn't check selection or visibility intentionally */
951 Bone *bone = pchan->bone;
953 calc_tw_center(tbounds, pchan->pose_head);
954 protectflag_to_drawflags_pchan(rv3d, pchan);
960 totsel = count_set_pose_transflags(&mode, 0, ob);
963 /* use channels to get stats */
964 for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
965 Bone *bone = pchan->bone;
966 if (bone && (bone->flag & BONE_TRANSFORM)) {
967 calc_tw_center(tbounds, pchan->pose_head);
968 protectflag_to_drawflags_pchan(rv3d, pchan);
976 mul_v3_fl(tbounds->center, 1.0f / (float)totsel); // centroid!
977 mul_m4_v3(ob_eval->obmat, tbounds->center);
978 mul_m4_v3(ob_eval->obmat, tbounds->min);
979 mul_m4_v3(ob_eval->obmat, tbounds->max);
982 else if (ob && (ob->mode & OB_MODE_ALL_PAINT)) {
985 else if (ob && ob->mode & OB_MODE_PARTICLE_EDIT) {
986 PTCacheEdit *edit = PE_get_current(scene, ob);
987 PTCacheEditPoint *point;
992 point = edit->points;
993 for (a = 0; a < edit->totpoint; a++, point++) {
994 if (point->flag & PEP_HIDE) continue;
996 for (k = 0, ek = point->keys; k < point->totkey; k++, ek++) {
997 if (ek->flag & PEK_SELECT) {
998 calc_tw_center(tbounds, (ek->flag & PEK_USE_WCO) ? ek->world_co : ek->co);
1004 /* selection center */
1006 mul_v3_fl(tbounds->center, 1.0f / (float)totsel); // centroid!
1011 /* we need the one selected object, if its not active */
1012 base = BASACT(view_layer);
1013 ob = OBACT(view_layer);
1014 if (base && ((base->flag & BASE_SELECTED) == 0)) ob = NULL;
1016 for (base = view_layer->object_bases.first; base; base = base->next) {
1017 if (!TESTBASELIB(base)) {
1020 const Object *base_object_eval = DEG_get_evaluated_object(depsgraph, base->object);
1023 ob_eval = base_object_eval;
1025 if (params->use_only_center || base_object_eval->bb == NULL) {
1026 calc_tw_center(tbounds, base_object_eval->obmat[3]);
1029 for (uint j = 0; j < 8; j++) {
1031 mul_v3_m4v3(co, base_object_eval->obmat, base_object_eval->bb->vec[j]);
1032 calc_tw_center(tbounds, co);
1035 protectflag_to_drawflags(base->object->protectflag, &rv3d->twdrawflag);
1039 /* selection center */
1041 mul_v3_fl(tbounds->center, 1.0f / (float)totsel); // centroid!
1046 unit_m4(rv3d->twmat);
1049 copy_v3_v3(rv3d->tw_axis_min, tbounds->axis_min);
1050 copy_v3_v3(rv3d->tw_axis_max, tbounds->axis_max);
1051 copy_m3_m3(rv3d->tw_axis_matrix, tbounds->axis);
1057 static void manipulator_get_idot(RegionView3D *rv3d, float r_idot[3])
1059 float view_vec[3], axis_vec[3];
1060 ED_view3d_global_to_vector(rv3d, rv3d->twmat[3], view_vec);
1061 for (int i = 0; i < 3; i++) {
1062 normalize_v3_v3(axis_vec, rv3d->twmat[i]);
1063 r_idot[i] = 1.0f - fabsf(dot_v3v3(view_vec, axis_vec));
1067 static void manipulator_prepare_mat(
1068 const bContext *C, View3D *v3d, RegionView3D *rv3d, const struct TransformBounds *tbounds)
1070 Scene *scene = CTX_data_scene(C);
1071 ViewLayer *view_layer = CTX_data_view_layer(C);
1073 switch (scene->toolsettings->transform_pivot_point) {
1074 case V3D_AROUND_CENTER_BOUNDS:
1075 case V3D_AROUND_ACTIVE:
1077 bGPdata *gpd = CTX_data_gpencil_data(C);
1078 Object *ob = OBACT(view_layer);
1080 if (((scene->toolsettings->transform_pivot_point == V3D_AROUND_ACTIVE) &&
1081 (OBEDIT_FROM_OBACT(ob) == NULL)) &&
1082 ((gpd == NULL) || !(gpd->flag & GP_DATA_STROKE_EDITMODE)) &&
1083 (!(ob->mode & OB_MODE_POSE)))
1085 copy_v3_v3(rv3d->twmat[3], ob->obmat[3]);
1088 mid_v3_v3v3(rv3d->twmat[3], tbounds->min, tbounds->max);
1092 case V3D_AROUND_LOCAL_ORIGINS:
1093 case V3D_AROUND_CENTER_MEAN:
1094 copy_v3_v3(rv3d->twmat[3], tbounds->center);
1096 case V3D_AROUND_CURSOR:
1097 copy_v3_v3(rv3d->twmat[3], ED_view3d_cursor3d_get(scene, v3d)->location);
1103 * Sets up \a r_start and \a r_len to define arrow line range.
1104 * Needed to adjust line drawing for combined manipulator axis types.
1106 static void manipulator_line_range(const int twtype, const short axis_type, float *r_start, float *r_len)
1108 const float ofs = 0.2f;
1113 switch (axis_type) {
1114 case MAN_AXES_TRANSLATE:
1115 if (twtype & V3D_MANIP_SCALE) {
1116 *r_start = *r_len - ofs + 0.075f;
1118 if (twtype & V3D_MANIP_ROTATE) {
1122 case MAN_AXES_SCALE:
1123 if (twtype & (V3D_MANIP_TRANSLATE | V3D_MANIP_ROTATE)) {
1124 *r_len -= ofs + 0.025f;
1132 static void manipulator_xform_message_subscribe(
1133 wmManipulatorGroup *mgroup, struct wmMsgBus *mbus,
1134 Scene *scene, bScreen *screen, ScrArea *sa, ARegion *ar, const void *type_fn)
1136 /* Subscribe to view properties */
1137 wmMsgSubscribeValue msg_sub_value_mpr_tag_refresh = {
1139 .user_data = mgroup->parent_mmap,
1140 .notify = WM_manipulator_do_msg_notify_tag_refresh,
1143 PointerRNA scene_ptr;
1144 RNA_id_pointer_create(&scene->id, &scene_ptr);
1147 extern PropertyRNA rna_Scene_transform_orientation;
1148 extern PropertyRNA rna_Scene_cursor_location;
1149 const PropertyRNA *props[] = {
1150 &rna_Scene_transform_orientation,
1151 (scene->toolsettings->transform_pivot_point == V3D_AROUND_CURSOR) ? &rna_Scene_cursor_location : NULL,
1153 for (int i = 0; i < ARRAY_SIZE(props); i++) {
1155 WM_msg_subscribe_rna(mbus, &scene_ptr, props[i], &msg_sub_value_mpr_tag_refresh, __func__);
1160 PointerRNA space_ptr;
1161 RNA_pointer_create(&screen->id, &RNA_SpaceView3D, sa->spacedata.first, &space_ptr);
1163 if (type_fn == TRANSFORM_WGT_manipulator) {
1164 extern PropertyRNA rna_ToolSettings_transform_pivot_point;
1165 const PropertyRNA *props[] = {
1166 &rna_ToolSettings_transform_pivot_point
1168 for (int i = 0; i < ARRAY_SIZE(props); i++) {
1169 WM_msg_subscribe_rna(mbus, &space_ptr, props[i], &msg_sub_value_mpr_tag_refresh, __func__);
1172 else if (type_fn == VIEW3D_WGT_xform_cage) {
1179 WM_msg_subscribe_rna_anon_prop(mbus, Window, view_layer, &msg_sub_value_mpr_tag_refresh);
1185 /* -------------------------------------------------------------------- */
1186 /** \name Transform Manipulator
1189 static ManipulatorGroup *manipulatorgroup_init(wmManipulatorGroup *mgroup)
1191 ManipulatorGroup *man;
1193 man = MEM_callocN(sizeof(ManipulatorGroup), "manipulator_data");
1195 const wmManipulatorType *wt_arrow = WM_manipulatortype_find("MANIPULATOR_WT_arrow_3d", true);
1196 const wmManipulatorType *wt_dial = WM_manipulatortype_find("MANIPULATOR_WT_dial_3d", true);
1197 const wmManipulatorType *wt_prim = WM_manipulatortype_find("MANIPULATOR_WT_primitive_3d", true);
1199 #define MANIPULATOR_NEW_ARROW(v, draw_style) { \
1200 man->manipulators[v] = WM_manipulator_new_ptr(wt_arrow, mgroup, NULL); \
1201 RNA_enum_set(man->manipulators[v]->ptr, "draw_style", draw_style); \
1203 #define MANIPULATOR_NEW_DIAL(v, draw_options) { \
1204 man->manipulators[v] = WM_manipulator_new_ptr(wt_dial, mgroup, NULL); \
1205 RNA_enum_set(man->manipulators[v]->ptr, "draw_options", draw_options); \
1207 #define MANIPULATOR_NEW_PRIM(v, draw_style) { \
1208 man->manipulators[v] = WM_manipulator_new_ptr(wt_prim, mgroup, NULL); \
1209 RNA_enum_set(man->manipulators[v]->ptr, "draw_style", draw_style); \
1212 /* add/init widgets - order matters! */
1213 MANIPULATOR_NEW_DIAL(MAN_AXIS_ROT_T, ED_MANIPULATOR_DIAL_DRAW_FLAG_FILL);
1215 MANIPULATOR_NEW_DIAL(MAN_AXIS_SCALE_C, ED_MANIPULATOR_DIAL_DRAW_FLAG_NOP);
1217 MANIPULATOR_NEW_ARROW(MAN_AXIS_SCALE_X, ED_MANIPULATOR_ARROW_STYLE_BOX);
1218 MANIPULATOR_NEW_ARROW(MAN_AXIS_SCALE_Y, ED_MANIPULATOR_ARROW_STYLE_BOX);
1219 MANIPULATOR_NEW_ARROW(MAN_AXIS_SCALE_Z, ED_MANIPULATOR_ARROW_STYLE_BOX);
1221 MANIPULATOR_NEW_PRIM(MAN_AXIS_SCALE_XY, ED_MANIPULATOR_PRIMITIVE_STYLE_PLANE);
1222 MANIPULATOR_NEW_PRIM(MAN_AXIS_SCALE_YZ, ED_MANIPULATOR_PRIMITIVE_STYLE_PLANE);
1223 MANIPULATOR_NEW_PRIM(MAN_AXIS_SCALE_ZX, ED_MANIPULATOR_PRIMITIVE_STYLE_PLANE);
1225 MANIPULATOR_NEW_DIAL(MAN_AXIS_ROT_X, ED_MANIPULATOR_DIAL_DRAW_FLAG_CLIP);
1226 MANIPULATOR_NEW_DIAL(MAN_AXIS_ROT_Y, ED_MANIPULATOR_DIAL_DRAW_FLAG_CLIP);
1227 MANIPULATOR_NEW_DIAL(MAN_AXIS_ROT_Z, ED_MANIPULATOR_DIAL_DRAW_FLAG_CLIP);
1229 /* init screen aligned widget last here, looks better, behaves better */
1230 MANIPULATOR_NEW_DIAL(MAN_AXIS_ROT_C, ED_MANIPULATOR_DIAL_DRAW_FLAG_NOP);
1232 MANIPULATOR_NEW_DIAL(MAN_AXIS_TRANS_C, ED_MANIPULATOR_DIAL_DRAW_FLAG_NOP);
1234 MANIPULATOR_NEW_ARROW(MAN_AXIS_TRANS_X, ED_MANIPULATOR_ARROW_STYLE_NORMAL);
1235 MANIPULATOR_NEW_ARROW(MAN_AXIS_TRANS_Y, ED_MANIPULATOR_ARROW_STYLE_NORMAL);
1236 MANIPULATOR_NEW_ARROW(MAN_AXIS_TRANS_Z, ED_MANIPULATOR_ARROW_STYLE_NORMAL);
1238 MANIPULATOR_NEW_PRIM(MAN_AXIS_TRANS_XY, ED_MANIPULATOR_PRIMITIVE_STYLE_PLANE);
1239 MANIPULATOR_NEW_PRIM(MAN_AXIS_TRANS_YZ, ED_MANIPULATOR_PRIMITIVE_STYLE_PLANE);
1240 MANIPULATOR_NEW_PRIM(MAN_AXIS_TRANS_ZX, ED_MANIPULATOR_PRIMITIVE_STYLE_PLANE);
1246 * Custom handler for manipulator widgets
1248 static int manipulator_modal(
1249 bContext *C, wmManipulator *widget, const wmEvent *UNUSED(event),
1250 eWM_ManipulatorTweak UNUSED(tweak_flag))
1252 const ScrArea *sa = CTX_wm_area(C);
1253 ARegion *ar = CTX_wm_region(C);
1254 View3D *v3d = sa->spacedata.first;
1255 RegionView3D *rv3d = ar->regiondata;
1256 struct TransformBounds tbounds;
1259 if (ED_transform_calc_manipulator_stats(
1260 C, &(struct TransformCalcParams){
1261 .use_only_center = true,
1264 manipulator_prepare_mat(C, v3d, rv3d, &tbounds);
1265 WM_manipulator_set_matrix_location(widget, rv3d->twmat[3]);
1268 ED_region_tag_redraw(ar);
1270 return OPERATOR_RUNNING_MODAL;
1273 static void WIDGETGROUP_manipulator_setup(const bContext *C, wmManipulatorGroup *mgroup)
1275 ManipulatorGroup *man = manipulatorgroup_init(mgroup);
1277 wmOperatorType *translate, *rotate, *trackball, *resize;
1278 } ot_store = {NULL};
1280 mgroup->customdata = man;
1283 /* TODO: support mixing modes again? - it's supported but tool system makes it unobvious. */
1285 WorkSpace *workspace = CTX_wm_workspace(C);
1286 Scene *scene = CTX_data_scene(C);
1287 ScrArea *sa = CTX_wm_area(C);
1288 const bToolKey tkey = {
1289 .space_type = sa->spacetype,
1290 .mode = WM_toolsystem_mode_from_spacetype(workspace, scene, NULL, sa->spacetype),
1292 bToolRef_Runtime *tref_rt = WM_toolsystem_runtime_find(workspace, &tkey);
1293 wmKeyMap *km = WM_keymap_find_all(C, tref_rt->keymap, sa->spacetype, RGN_TYPE_WINDOW);
1294 /* Weak, check first event */
1295 wmKeyMapItem *kmi = km ? km->items.first : NULL;
1298 man->twtype |= V3D_MANIP_TRANSLATE | V3D_MANIP_ROTATE | V3D_MANIP_SCALE;
1300 else if (STREQ(kmi->idname, "TRANSFORM_OT_translate")) {
1301 man->twtype |= V3D_MANIP_TRANSLATE;
1303 else if (STREQ(kmi->idname, "TRANSFORM_OT_rotate")) {
1304 man->twtype |= V3D_MANIP_ROTATE;
1306 else if (STREQ(kmi->idname, "TRANSFORM_OT_resize")) {
1307 man->twtype |= V3D_MANIP_SCALE;
1309 BLI_assert(man->twtype != 0);
1312 /* *** set properties for axes *** */
1314 MAN_ITER_AXES_BEGIN(axis, axis_idx)
1316 const short axis_type = manipulator_get_axis_type(axis_idx);
1317 int constraint_axis[3] = {1, 0, 0};
1320 manipulator_get_axis_constraint(axis_idx, constraint_axis);
1322 /* custom handler! */
1323 WM_manipulator_set_fn_custom_modal(axis, manipulator_modal);
1326 case MAN_AXIS_TRANS_X:
1327 case MAN_AXIS_TRANS_Y:
1328 case MAN_AXIS_TRANS_Z:
1329 case MAN_AXIS_SCALE_X:
1330 case MAN_AXIS_SCALE_Y:
1331 case MAN_AXIS_SCALE_Z:
1332 WM_manipulator_set_line_width(axis, MANIPULATOR_AXIS_LINE_WIDTH);
1334 case MAN_AXIS_ROT_X:
1335 case MAN_AXIS_ROT_Y:
1336 case MAN_AXIS_ROT_Z:
1337 /* increased line width for better display */
1338 WM_manipulator_set_line_width(axis, MANIPULATOR_AXIS_LINE_WIDTH + 1.0f);
1339 WM_manipulator_set_flag(axis, WM_MANIPULATOR_DRAW_VALUE, true);
1341 case MAN_AXIS_TRANS_XY:
1342 case MAN_AXIS_TRANS_YZ:
1343 case MAN_AXIS_TRANS_ZX:
1344 case MAN_AXIS_SCALE_XY:
1345 case MAN_AXIS_SCALE_YZ:
1346 case MAN_AXIS_SCALE_ZX:
1348 const float ofs_ax = 7.0f;
1349 const float ofs[3] = {ofs_ax, ofs_ax, 0.0f};
1350 WM_manipulator_set_scale(axis, 0.07f);
1351 WM_manipulator_set_matrix_offset_location(axis, ofs);
1352 WM_manipulator_set_flag(axis, WM_MANIPULATOR_DRAW_OFFSET_SCALE, true);
1355 case MAN_AXIS_TRANS_C:
1356 case MAN_AXIS_ROT_C:
1357 case MAN_AXIS_SCALE_C:
1358 case MAN_AXIS_ROT_T:
1359 WM_manipulator_set_line_width(axis, MANIPULATOR_AXIS_LINE_WIDTH);
1360 if (axis_idx == MAN_AXIS_ROT_T) {
1361 WM_manipulator_set_flag(axis, WM_MANIPULATOR_DRAW_HOVER, true);
1363 else if (axis_idx == MAN_AXIS_ROT_C) {
1364 WM_manipulator_set_flag(axis, WM_MANIPULATOR_DRAW_VALUE, true);
1367 WM_manipulator_set_scale(axis, 0.2f);
1372 switch (axis_type) {
1373 case MAN_AXES_TRANSLATE:
1374 if (ot_store.translate == NULL) {
1375 ot_store.translate = WM_operatortype_find("TRANSFORM_OT_translate", true);
1377 ptr = WM_manipulator_operator_set(axis, 0, ot_store.translate, NULL);
1379 case MAN_AXES_ROTATE:
1381 wmOperatorType *ot_rotate;
1382 if (axis_idx == MAN_AXIS_ROT_T) {
1383 if (ot_store.trackball == NULL) {
1384 ot_store.trackball = WM_operatortype_find("TRANSFORM_OT_trackball", true);
1386 ot_rotate = ot_store.trackball;
1389 if (ot_store.rotate == NULL) {
1390 ot_store.rotate = WM_operatortype_find("TRANSFORM_OT_rotate", true);
1392 ot_rotate = ot_store.rotate;
1394 ptr = WM_manipulator_operator_set(axis, 0, ot_rotate, NULL);
1397 case MAN_AXES_SCALE:
1399 if (ot_store.resize == NULL) {
1400 ot_store.resize = WM_operatortype_find("TRANSFORM_OT_resize", true);
1402 ptr = WM_manipulator_operator_set(axis, 0, ot_store.resize, NULL);
1409 if ((prop = RNA_struct_find_property(ptr, "constraint_axis"))) {
1410 RNA_property_boolean_set_array(ptr, prop, constraint_axis);
1414 RNA_boolean_set(ptr, "release_confirm", 1);
1419 static void WIDGETGROUP_manipulator_refresh(const bContext *C, wmManipulatorGroup *mgroup)
1421 ManipulatorGroup *man = mgroup->customdata;
1422 ScrArea *sa = CTX_wm_area(C);
1423 ARegion *ar = CTX_wm_region(C);
1424 View3D *v3d = sa->spacedata.first;
1425 RegionView3D *rv3d = ar->regiondata;
1426 struct TransformBounds tbounds;
1428 /* skip, we don't draw anything anyway */
1429 if ((man->all_hidden =
1430 (ED_transform_calc_manipulator_stats(
1431 C, &(struct TransformCalcParams){
1432 .use_only_center = true,
1433 }, &tbounds) == 0)))
1438 manipulator_prepare_mat(C, v3d, rv3d, &tbounds);
1440 /* *** set properties for axes *** */
1442 MAN_ITER_AXES_BEGIN(axis, axis_idx)
1444 const short axis_type = manipulator_get_axis_type(axis_idx);
1445 const int aidx_norm = manipulator_orientation_axis(axis_idx, NULL);
1447 WM_manipulator_set_matrix_location(axis, rv3d->twmat[3]);
1450 case MAN_AXIS_TRANS_X:
1451 case MAN_AXIS_TRANS_Y:
1452 case MAN_AXIS_TRANS_Z:
1453 case MAN_AXIS_SCALE_X:
1454 case MAN_AXIS_SCALE_Y:
1455 case MAN_AXIS_SCALE_Z:
1457 float start_co[3] = {0.0f, 0.0f, 0.0f};
1460 manipulator_line_range(man->twtype, axis_type, &start_co[2], &len);
1462 WM_manipulator_set_matrix_rotation_from_z_axis(axis, rv3d->twmat[aidx_norm]);
1463 RNA_float_set(axis->ptr, "length", len);
1464 WM_manipulator_set_matrix_offset_location(axis, start_co);
1465 WM_manipulator_set_flag(axis, WM_MANIPULATOR_DRAW_OFFSET_SCALE, true);
1468 case MAN_AXIS_ROT_X:
1469 case MAN_AXIS_ROT_Y:
1470 case MAN_AXIS_ROT_Z:
1471 WM_manipulator_set_matrix_rotation_from_z_axis(axis, rv3d->twmat[aidx_norm]);
1473 case MAN_AXIS_TRANS_XY:
1474 case MAN_AXIS_TRANS_YZ:
1475 case MAN_AXIS_TRANS_ZX:
1476 case MAN_AXIS_SCALE_XY:
1477 case MAN_AXIS_SCALE_YZ:
1478 case MAN_AXIS_SCALE_ZX:
1480 const float *y_axis = rv3d->twmat[aidx_norm - 1 < 0 ? 2 : aidx_norm - 1];
1481 const float *z_axis = rv3d->twmat[aidx_norm];
1482 WM_manipulator_set_matrix_rotation_from_yz_axis(axis, y_axis, z_axis);
1490 static void WIDGETGROUP_manipulator_message_subscribe(
1491 const bContext *C, wmManipulatorGroup *mgroup, struct wmMsgBus *mbus)
1493 Scene *scene = CTX_data_scene(C);
1494 bScreen *screen = CTX_wm_screen(C);
1495 ScrArea *sa = CTX_wm_area(C);
1496 ARegion *ar = CTX_wm_region(C);
1497 manipulator_xform_message_subscribe(mgroup, mbus, scene, screen, sa, ar, TRANSFORM_WGT_manipulator);
1500 static void WIDGETGROUP_manipulator_draw_prepare(const bContext *C, wmManipulatorGroup *mgroup)
1502 ManipulatorGroup *man = mgroup->customdata;
1503 // ScrArea *sa = CTX_wm_area(C);
1504 ARegion *ar = CTX_wm_region(C);
1505 // View3D *v3d = sa->spacedata.first;
1506 RegionView3D *rv3d = ar->regiondata;
1509 /* when looking through a selected camera, the manipulator can be at the
1510 * exact same position as the view, skip so we don't break selection */
1511 if (man->all_hidden || fabsf(ED_view3d_pixel_size(rv3d, rv3d->twmat[3])) < 1e-6f) {
1512 MAN_ITER_AXES_BEGIN(axis, axis_idx)
1514 WM_manipulator_set_flag(axis, WM_MANIPULATOR_HIDDEN, true);
1519 manipulator_get_idot(rv3d, idot);
1521 /* *** set properties for axes *** */
1523 MAN_ITER_AXES_BEGIN(axis, axis_idx)
1525 const short axis_type = manipulator_get_axis_type(axis_idx);
1526 /* XXX maybe unset _HIDDEN flag on redraw? */
1527 if (manipulator_is_axis_visible(rv3d, man->twtype, idot, axis_type, axis_idx)) {
1528 WM_manipulator_set_flag(axis, WM_MANIPULATOR_HIDDEN, false);
1531 WM_manipulator_set_flag(axis, WM_MANIPULATOR_HIDDEN, true);
1535 float color[4], color_hi[4];
1536 manipulator_get_axis_color(axis_idx, idot, color, color_hi);
1537 WM_manipulator_set_color(axis, color);
1538 WM_manipulator_set_color_highlight(axis, color_hi);
1541 case MAN_AXIS_TRANS_C:
1542 case MAN_AXIS_ROT_C:
1543 case MAN_AXIS_SCALE_C:
1544 case MAN_AXIS_ROT_T:
1545 WM_manipulator_set_matrix_rotation_from_z_axis(axis, rv3d->viewinv[2]);
1552 static bool WIDGETGROUP_manipulator_poll(const struct bContext *C, struct wmManipulatorGroupType *wgt)
1554 /* it's a given we only use this in 3D view */
1555 ScrArea *sa = CTX_wm_area(C);
1556 View3D *v3d = sa->spacedata.first;
1557 if (v3d && ((v3d->twflag & V3D_MANIPULATOR_DRAW)) == 0) {
1561 bToolRef_Runtime *tref_rt = WM_toolsystem_runtime_from_context((bContext *)C);
1562 if ((tref_rt == NULL) ||
1563 !STREQ(wgt->idname, tref_rt->manipulator_group))
1565 WM_manipulator_group_type_unlink_delayed_ptr(wgt);
1571 void TRANSFORM_WGT_manipulator(wmManipulatorGroupType *wgt)
1573 wgt->name = "Transform Manipulator";
1574 wgt->idname = "TRANSFORM_WGT_manipulator";
1576 wgt->flag |= WM_MANIPULATORGROUPTYPE_3D;
1578 wgt->mmap_params.spaceid = SPACE_VIEW3D;
1579 wgt->mmap_params.regionid = RGN_TYPE_WINDOW;
1581 wgt->poll = WIDGETGROUP_manipulator_poll;
1582 wgt->setup = WIDGETGROUP_manipulator_setup;
1583 wgt->refresh = WIDGETGROUP_manipulator_refresh;
1584 wgt->message_subscribe = WIDGETGROUP_manipulator_message_subscribe;
1585 wgt->draw_prepare = WIDGETGROUP_manipulator_draw_prepare;
1591 /* -------------------------------------------------------------------- */
1592 /** \name Scale Cage Manipulator
1595 struct XFormCageWidgetGroup {
1596 wmManipulator *manipulator;
1599 static bool WIDGETGROUP_xform_cage_poll(const bContext *C, wmManipulatorGroupType *wgt)
1601 ScrArea *sa = CTX_wm_area(C);
1602 View3D *v3d = sa->spacedata.first;
1603 if (v3d && ((v3d->twflag & V3D_MANIPULATOR_DRAW)) == 0) {
1607 bToolRef_Runtime *tref_rt = WM_toolsystem_runtime_from_context((bContext *)C);
1608 if (!STREQ(wgt->idname, tref_rt->manipulator_group)) {
1609 WM_manipulator_group_type_unlink_delayed_ptr(wgt);
1615 static void WIDGETGROUP_xform_cage_setup(const bContext *UNUSED(C), wmManipulatorGroup *mgroup)
1617 struct XFormCageWidgetGroup *xmgroup = MEM_mallocN(sizeof(struct XFormCageWidgetGroup), __func__);
1618 const wmManipulatorType *wt_cage = WM_manipulatortype_find("MANIPULATOR_WT_cage_3d", true);
1619 xmgroup->manipulator = WM_manipulator_new_ptr(wt_cage, mgroup, NULL);
1620 wmManipulator *mpr = xmgroup->manipulator;
1622 RNA_enum_set(mpr->ptr, "transform",
1623 ED_MANIPULATOR_CAGE2D_XFORM_FLAG_SCALE |
1624 ED_MANIPULATOR_CAGE2D_XFORM_FLAG_TRANSLATE);
1627 mpr->color_hi[0] = 1;
1629 mgroup->customdata = xmgroup;
1632 wmOperatorType *ot_resize = WM_operatortype_find("TRANSFORM_OT_resize", true);
1635 /* assign operator */
1636 PropertyRNA *prop_release_confirm = NULL;
1637 PropertyRNA *prop_constraint_axis = NULL;
1639 int i = ED_MANIPULATOR_CAGE3D_PART_SCALE_MIN_X_MIN_Y_MIN_Z;
1640 for (int x = 0; x < 3; x++) {
1641 for (int y = 0; y < 3; y++) {
1642 for (int z = 0; z < 3; z++) {
1643 int constraint[3] = {x != 1, y != 1, z != 1};
1644 ptr = WM_manipulator_operator_set(mpr, i, ot_resize, NULL);
1645 if (prop_release_confirm == NULL) {
1646 prop_release_confirm = RNA_struct_find_property(ptr, "release_confirm");
1647 prop_constraint_axis = RNA_struct_find_property(ptr, "constraint_axis");
1649 RNA_property_boolean_set(ptr, prop_release_confirm, true);
1650 RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint);
1658 static void WIDGETGROUP_xform_cage_refresh(const bContext *C, wmManipulatorGroup *mgroup)
1660 ScrArea *sa = CTX_wm_area(C);
1661 View3D *v3d = sa->spacedata.first;
1662 ARegion *ar = CTX_wm_region(C);
1663 RegionView3D *rv3d = ar->regiondata;
1665 struct XFormCageWidgetGroup *xmgroup = mgroup->customdata;
1666 wmManipulator *mpr = xmgroup->manipulator;
1668 struct TransformBounds tbounds;
1670 if ((ED_transform_calc_manipulator_stats(
1671 C, &(struct TransformCalcParams) {
1672 .use_local_axis = true,
1673 }, &tbounds) == 0) ||
1674 equals_v3v3(rv3d->tw_axis_min, rv3d->tw_axis_max))
1676 WM_manipulator_set_flag(mpr, WM_MANIPULATOR_HIDDEN, true);
1679 manipulator_prepare_mat(C, v3d, rv3d, &tbounds);
1681 WM_manipulator_set_flag(mpr, WM_MANIPULATOR_HIDDEN, false);
1682 WM_manipulator_set_flag(mpr, WM_MANIPULATOR_GRAB_CURSOR, true);
1685 sub_v3_v3v3(dims, rv3d->tw_axis_max, rv3d->tw_axis_min);
1686 RNA_float_set_array(mpr->ptr, "dimensions", dims);
1687 mul_v3_fl(dims, 0.5f);
1689 copy_m4_m3(mpr->matrix_offset, rv3d->tw_axis_matrix);
1690 mid_v3_v3v3(mpr->matrix_offset[3], rv3d->tw_axis_max, rv3d->tw_axis_min);
1691 mul_m3_v3(rv3d->tw_axis_matrix, mpr->matrix_offset[3]);
1693 PropertyRNA *prop_center_override = NULL;
1695 float center_global[3];
1696 int i = ED_MANIPULATOR_CAGE3D_PART_SCALE_MIN_X_MIN_Y_MIN_Z;
1697 for (int x = 0; x < 3; x++) {
1698 center[0] = (float)(1 - x) * dims[0];
1699 for (int y = 0; y < 3; y++) {
1700 center[1] = (float)(1 - y) * dims[1];
1701 for (int z = 0; z < 3; z++) {
1702 center[2] = (float)(1 - z) * dims[2];
1703 struct wmManipulatorOpElem *mpop = WM_manipulator_operator_get(mpr, i);
1704 if (prop_center_override == NULL) {
1705 prop_center_override = RNA_struct_find_property(&mpop->ptr, "center_override");
1707 mul_v3_m4v3(center_global, mpr->matrix_offset, center);
1708 RNA_property_float_set_array(&mpop->ptr, prop_center_override, center_global);
1716 static void WIDGETGROUP_xform_cage_message_subscribe(
1717 const bContext *C, wmManipulatorGroup *mgroup, struct wmMsgBus *mbus)
1719 Scene *scene = CTX_data_scene(C);
1720 bScreen *screen = CTX_wm_screen(C);
1721 ScrArea *sa = CTX_wm_area(C);
1722 ARegion *ar = CTX_wm_region(C);
1723 manipulator_xform_message_subscribe(mgroup, mbus, scene, screen, sa, ar, VIEW3D_WGT_xform_cage);
1726 static void WIDGETGROUP_xform_cage_draw_prepare(const bContext *C, wmManipulatorGroup *mgroup)
1728 struct XFormCageWidgetGroup *xmgroup = mgroup->customdata;
1729 wmManipulator *mpr = xmgroup->manipulator;
1731 ViewLayer *view_layer = CTX_data_view_layer(C);
1732 Object *ob = OBACT(view_layer);
1733 if (ob && ob->mode & OB_MODE_EDIT) {
1734 copy_m4_m4(mpr->matrix_space, ob->obmat);
1737 unit_m4(mpr->matrix_space);
1741 void VIEW3D_WGT_xform_cage(wmManipulatorGroupType *wgt)
1743 wgt->name = "Transform Cage";
1744 wgt->idname = "VIEW3D_WGT_xform_cage";
1746 wgt->flag |= WM_MANIPULATORGROUPTYPE_3D;
1748 wgt->mmap_params.spaceid = SPACE_VIEW3D;
1749 wgt->mmap_params.regionid = RGN_TYPE_WINDOW;
1751 wgt->poll = WIDGETGROUP_xform_cage_poll;
1752 wgt->setup = WIDGETGROUP_xform_cage_setup;
1753 wgt->refresh = WIDGETGROUP_xform_cage_refresh;
1754 wgt->message_subscribe = WIDGETGROUP_xform_cage_message_subscribe;
1755 wgt->draw_prepare = WIDGETGROUP_xform_cage_draw_prepare;