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 * The Original Code is Copyright (C) 2005 Blender Foundation
19 * All rights reserved.
21 * The Original Code is: all of this file.
23 * Contributor(s): none yet.
25 * ***** END GPL LICENSE BLOCK *****
28 /** \file blender/editors/transform/transform_manipulator.c
29 * \ingroup edtransform
38 #include "DNA_armature_types.h"
39 #include "DNA_curve_types.h"
40 #include "DNA_gpencil_types.h"
41 #include "DNA_lattice_types.h"
42 #include "DNA_meta_types.h"
43 #include "DNA_screen_types.h"
44 #include "DNA_scene_types.h"
45 #include "DNA_view3d_types.h"
47 #include "BLI_listbase.h"
49 #include "BLI_utildefines.h"
51 #include "RNA_access.h"
53 #include "BKE_action.h"
54 #include "BKE_context.h"
55 #include "BKE_curve.h"
56 #include "BKE_global.h"
57 #include "BKE_particle.h"
58 #include "BKE_pointcache.h"
59 #include "BKE_editmesh.h"
60 #include "BKE_lattice.h"
61 #include "BKE_gpencil.h"
62 #include "BKE_workspace.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 /* return codes for select, and drawing flags */
91 #define MAN_TRANS_X (1 << 0)
92 #define MAN_TRANS_Y (1 << 1)
93 #define MAN_TRANS_Z (1 << 2)
94 #define MAN_TRANS_C (MAN_TRANS_X | MAN_TRANS_Y | MAN_TRANS_Z)
96 #define MAN_ROT_X (1 << 3)
97 #define MAN_ROT_Y (1 << 4)
98 #define MAN_ROT_Z (1 << 5)
99 #define MAN_ROT_C (MAN_ROT_X | MAN_ROT_Y | MAN_ROT_Z)
101 #define MAN_SCALE_X (1 << 8)
102 #define MAN_SCALE_Y (1 << 9)
103 #define MAN_SCALE_Z (1 << 10)
104 #define MAN_SCALE_C (MAN_SCALE_X | MAN_SCALE_Y | MAN_SCALE_Z)
106 /* threshold for testing view aligned manipulator axis */
107 #define TW_AXIS_DOT_MIN 0.02f
108 #define TW_AXIS_DOT_MAX 0.1f
112 MAN_AXIS_TRANS_X = 0,
121 MAN_AXIS_ROT_T, /* trackball rotation */
148 typedef struct ManipulatorGroup {
151 struct wmManipulator *translate_x,
163 *rotate_t, /* trackball rotation */
175 /* **************** Utilities **************** */
178 #define MAN_ITER_AXES_BEGIN(axis, axis_idx) \
180 wmManipulator *axis; \
182 for (axis_idx = 0; axis_idx < MAN_AXIS_LAST; axis_idx++) { \
183 axis = manipulator_get_axis_from_index(man, axis_idx);
185 #define MAN_ITER_AXES_END \
189 static wmManipulator *manipulator_get_axis_from_index(const ManipulatorGroup *man, const short axis_idx)
191 BLI_assert(IN_RANGE_INCL(axis_idx, (float)MAN_AXIS_TRANS_X, (float)MAN_AXIS_LAST));
194 case MAN_AXIS_TRANS_X:
195 return man->translate_x;
196 case MAN_AXIS_TRANS_Y:
197 return man->translate_y;
198 case MAN_AXIS_TRANS_Z:
199 return man->translate_z;
200 case MAN_AXIS_TRANS_XY:
201 return man->translate_xy;
202 case MAN_AXIS_TRANS_YZ:
203 return man->translate_yz;
204 case MAN_AXIS_TRANS_ZX:
205 return man->translate_zx;
206 case MAN_AXIS_TRANS_C:
207 return man->translate_c;
209 return man->rotate_x;
211 return man->rotate_y;
213 return man->rotate_z;
215 return man->rotate_c;
217 return man->rotate_t;
218 case MAN_AXIS_SCALE_X:
220 case MAN_AXIS_SCALE_Y:
222 case MAN_AXIS_SCALE_Z:
224 case MAN_AXIS_SCALE_XY:
225 return man->scale_xy;
226 case MAN_AXIS_SCALE_YZ:
227 return man->scale_yz;
228 case MAN_AXIS_SCALE_ZX:
229 return man->scale_zx;
230 case MAN_AXIS_SCALE_C:
237 static short manipulator_get_axis_type(const ManipulatorGroup *man, const wmManipulator *axis)
239 if (ELEM(axis, man->translate_x, man->translate_y, man->translate_z, man->translate_c,
240 man->translate_xy, man->translate_yz, man->translate_zx))
242 return MAN_AXES_TRANSLATE;
244 else if (ELEM(axis, man->rotate_x, man->rotate_y, man->rotate_z, man->rotate_c, man->rotate_t)) {
245 return MAN_AXES_ROTATE;
248 return MAN_AXES_SCALE;
252 /* get index within axis type, so that x == 0, y == 1 and z == 2, no matter which axis type */
253 static uint manipulator_index_normalize(const int axis_idx)
255 if (axis_idx > MAN_AXIS_TRANS_ZX) {
256 return axis_idx - 16;
258 else if (axis_idx > MAN_AXIS_SCALE_C) {
259 return axis_idx - 13;
261 else if (axis_idx > MAN_AXIS_ROT_T) {
264 else if (axis_idx > MAN_AXIS_TRANS_C) {
271 static bool manipulator_is_axis_visible(
272 const View3D *v3d, const RegionView3D *rv3d,
273 const float idot[3], const int axis_type, const int axis_idx)
275 const uint aidx_norm = manipulator_index_normalize(axis_idx);
276 /* don't draw axis perpendicular to the view */
277 if (aidx_norm < 3 && idot[aidx_norm] < TW_AXIS_DOT_MIN) {
281 if ((axis_type == MAN_AXES_TRANSLATE && !(v3d->twtype & V3D_MANIP_TRANSLATE)) ||
282 (axis_type == MAN_AXES_ROTATE && !(v3d->twtype & V3D_MANIP_ROTATE)) ||
283 (axis_type == MAN_AXES_SCALE && !(v3d->twtype & V3D_MANIP_SCALE)))
289 case MAN_AXIS_TRANS_X:
290 return (rv3d->twdrawflag & MAN_TRANS_X);
291 case MAN_AXIS_TRANS_Y:
292 return (rv3d->twdrawflag & MAN_TRANS_Y);
293 case MAN_AXIS_TRANS_Z:
294 return (rv3d->twdrawflag & MAN_TRANS_Z);
295 case MAN_AXIS_TRANS_C:
296 return (rv3d->twdrawflag & MAN_TRANS_C);
298 return (rv3d->twdrawflag & MAN_ROT_X);
300 return (rv3d->twdrawflag & MAN_ROT_Y);
302 return (rv3d->twdrawflag & MAN_ROT_Z);
305 return (rv3d->twdrawflag & MAN_ROT_C);
306 case MAN_AXIS_SCALE_X:
307 return (rv3d->twdrawflag & MAN_SCALE_X);
308 case MAN_AXIS_SCALE_Y:
309 return (rv3d->twdrawflag & MAN_SCALE_Y);
310 case MAN_AXIS_SCALE_Z:
311 return (rv3d->twdrawflag & MAN_SCALE_Z);
312 case MAN_AXIS_SCALE_C:
313 return (rv3d->twdrawflag & MAN_SCALE_C && (v3d->twtype & V3D_MANIP_TRANSLATE) == 0);
314 case MAN_AXIS_TRANS_XY:
315 return (rv3d->twdrawflag & MAN_TRANS_X &&
316 rv3d->twdrawflag & MAN_TRANS_Y &&
317 (v3d->twtype & V3D_MANIP_ROTATE) == 0);
318 case MAN_AXIS_TRANS_YZ:
319 return (rv3d->twdrawflag & MAN_TRANS_Y &&
320 rv3d->twdrawflag & MAN_TRANS_Z &&
321 (v3d->twtype & V3D_MANIP_ROTATE) == 0);
322 case MAN_AXIS_TRANS_ZX:
323 return (rv3d->twdrawflag & MAN_TRANS_Z &&
324 rv3d->twdrawflag & MAN_TRANS_X &&
325 (v3d->twtype & V3D_MANIP_ROTATE) == 0);
326 case MAN_AXIS_SCALE_XY:
327 return (rv3d->twdrawflag & MAN_SCALE_X &&
328 rv3d->twdrawflag & MAN_SCALE_Y &&
329 (v3d->twtype & V3D_MANIP_TRANSLATE) == 0 &&
330 (v3d->twtype & V3D_MANIP_ROTATE) == 0);
331 case MAN_AXIS_SCALE_YZ:
332 return (rv3d->twdrawflag & MAN_SCALE_Y &&
333 rv3d->twdrawflag & MAN_SCALE_Z &&
334 (v3d->twtype & V3D_MANIP_TRANSLATE) == 0 &&
335 (v3d->twtype & V3D_MANIP_ROTATE) == 0);
336 case MAN_AXIS_SCALE_ZX:
337 return (rv3d->twdrawflag & MAN_SCALE_Z &&
338 rv3d->twdrawflag & MAN_SCALE_X &&
339 (v3d->twtype & V3D_MANIP_TRANSLATE) == 0 &&
340 (v3d->twtype & V3D_MANIP_ROTATE) == 0);
345 static void manipulator_get_axis_color(
346 const int axis_idx, const float idot[3],
347 float r_col[4], float r_col_hi[4])
349 /* alpha values for normal/highlighted states */
350 const float alpha = 0.6f;
351 const float alpha_hi = 1.0f;
354 const int axis_idx_norm = manipulator_index_normalize(axis_idx);
355 /* get alpha fac based on axis angle, to fade axis out when hiding it because it points towards view */
356 if (axis_idx_norm < 3) {
357 const float idot_axis = idot[axis_idx_norm];
358 alpha_fac = (idot_axis > TW_AXIS_DOT_MAX) ?
359 1.0f : (idot_axis < TW_AXIS_DOT_MIN) ?
360 0.0f : ((idot_axis - TW_AXIS_DOT_MIN) / (TW_AXIS_DOT_MAX - TW_AXIS_DOT_MIN));
363 /* trackball rotation axis is a special case, we only draw a slight overlay */
364 alpha_fac = (axis_idx == MAN_AXIS_ROT_T) ? 0.1f : 1.0f;
368 case MAN_AXIS_TRANS_X:
370 case MAN_AXIS_SCALE_X:
371 case MAN_AXIS_TRANS_YZ:
372 case MAN_AXIS_SCALE_YZ:
373 UI_GetThemeColor4fv(TH_AXIS_X, r_col);
375 case MAN_AXIS_TRANS_Y:
377 case MAN_AXIS_SCALE_Y:
378 case MAN_AXIS_TRANS_ZX:
379 case MAN_AXIS_SCALE_ZX:
380 UI_GetThemeColor4fv(TH_AXIS_Y, r_col);
382 case MAN_AXIS_TRANS_Z:
384 case MAN_AXIS_SCALE_Z:
385 case MAN_AXIS_TRANS_XY:
386 case MAN_AXIS_SCALE_XY:
387 UI_GetThemeColor4fv(TH_AXIS_Z, r_col);
389 case MAN_AXIS_TRANS_C:
391 case MAN_AXIS_SCALE_C:
393 copy_v4_fl(r_col, 1.0f);
397 copy_v4_v4(r_col_hi, r_col);
399 r_col[3] = alpha * alpha_fac;
400 r_col_hi[3] = alpha_hi * alpha_fac;
403 static void manipulator_get_axis_constraint(const int axis_idx, int r_axis[3])
408 case MAN_AXIS_TRANS_X:
410 case MAN_AXIS_SCALE_X:
413 case MAN_AXIS_TRANS_Y:
415 case MAN_AXIS_SCALE_Y:
418 case MAN_AXIS_TRANS_Z:
420 case MAN_AXIS_SCALE_Z:
423 case MAN_AXIS_TRANS_XY:
424 case MAN_AXIS_SCALE_XY:
425 r_axis[0] = r_axis[1] = 1;
427 case MAN_AXIS_TRANS_YZ:
428 case MAN_AXIS_SCALE_YZ:
429 r_axis[1] = r_axis[2] = 1;
431 case MAN_AXIS_TRANS_ZX:
432 case MAN_AXIS_SCALE_ZX:
433 r_axis[2] = r_axis[0] = 1;
441 /* **************** Preparation Stuff **************** */
443 /* transform widget center calc helper for below */
444 static void calc_tw_center(Scene *scene, const float co[3])
446 float *twcent = scene->twcent;
447 float *min = scene->twmin;
448 float *max = scene->twmax;
450 minmax_v3v3_v3(min, max, co);
451 add_v3_v3(twcent, co);
454 static void protectflag_to_drawflags(short protectflag, short *drawflags)
456 if (protectflag & OB_LOCK_LOCX)
457 *drawflags &= ~MAN_TRANS_X;
458 if (protectflag & OB_LOCK_LOCY)
459 *drawflags &= ~MAN_TRANS_Y;
460 if (protectflag & OB_LOCK_LOCZ)
461 *drawflags &= ~MAN_TRANS_Z;
463 if (protectflag & OB_LOCK_ROTX)
464 *drawflags &= ~MAN_ROT_X;
465 if (protectflag & OB_LOCK_ROTY)
466 *drawflags &= ~MAN_ROT_Y;
467 if (protectflag & OB_LOCK_ROTZ)
468 *drawflags &= ~MAN_ROT_Z;
470 if (protectflag & OB_LOCK_SCALEX)
471 *drawflags &= ~MAN_SCALE_X;
472 if (protectflag & OB_LOCK_SCALEY)
473 *drawflags &= ~MAN_SCALE_Y;
474 if (protectflag & OB_LOCK_SCALEZ)
475 *drawflags &= ~MAN_SCALE_Z;
479 static void protectflag_to_drawflags_pchan(RegionView3D *rv3d, const bPoseChannel *pchan)
481 protectflag_to_drawflags(pchan->protectflag, &rv3d->twdrawflag);
485 static void protectflag_to_drawflags_ebone(RegionView3D *rv3d, const EditBone *ebo)
487 if (ebo->flag & BONE_EDITMODE_LOCKED) {
488 protectflag_to_drawflags(OB_LOCK_LOC | OB_LOCK_ROT | OB_LOCK_SCALE, &rv3d->twdrawflag);
492 /* could move into BLI_math however this is only useful for display/editing purposes */
493 static void axis_angle_to_gimbal_axis(float gmat[3][3], const float axis[3], const float angle)
495 /* X/Y are arbitrary axies, most importantly Z is the axis of rotation */
500 /* this is an un-scientific method to get a vector to cross with
501 * XYZ intentionally YZX */
502 cross_vec[0] = axis[1];
503 cross_vec[1] = axis[2];
504 cross_vec[2] = axis[0];
507 cross_v3_v3v3(gmat[0], cross_vec, axis);
508 normalize_v3(gmat[0]);
509 axis_angle_to_quat(quat, axis, angle);
510 mul_qt_v3(quat, gmat[0]);
513 axis_angle_to_quat(quat, axis, M_PI_2);
514 copy_v3_v3(gmat[1], gmat[0]);
515 mul_qt_v3(quat, gmat[1]);
518 copy_v3_v3(gmat[2], axis);
524 static bool test_rotmode_euler(short rotmode)
526 return (ELEM(rotmode, ROT_MODE_AXISANGLE, ROT_MODE_QUAT)) ? 0 : 1;
529 bool gimbal_axis(Object *ob, float gmat[3][3])
531 if (ob->mode & OB_MODE_POSE) {
532 bPoseChannel *pchan = BKE_pose_channel_active(ob);
535 float mat[3][3], tmat[3][3], obmat[3][3];
536 if (test_rotmode_euler(pchan->rotmode)) {
537 eulO_to_gimbal_axis(mat, pchan->eul, pchan->rotmode);
539 else if (pchan->rotmode == ROT_MODE_AXISANGLE) {
540 axis_angle_to_gimbal_axis(mat, pchan->rotAxis, pchan->rotAngle);
547 /* apply bone transformation */
548 mul_m3_m3m3(tmat, pchan->bone->bone_mat, mat);
551 float parent_mat[3][3];
553 copy_m3_m4(parent_mat, pchan->parent->pose_mat);
554 mul_m3_m3m3(mat, parent_mat, tmat);
556 /* needed if object transformation isn't identity */
557 copy_m3_m4(obmat, ob->obmat);
558 mul_m3_m3m3(gmat, obmat, mat);
561 /* needed if object transformation isn't identity */
562 copy_m3_m4(obmat, ob->obmat);
563 mul_m3_m3m3(gmat, obmat, tmat);
571 if (test_rotmode_euler(ob->rotmode)) {
572 eulO_to_gimbal_axis(gmat, ob->rot, ob->rotmode);
574 else if (ob->rotmode == ROT_MODE_AXISANGLE) {
575 axis_angle_to_gimbal_axis(gmat, ob->rotAxis, ob->rotAngle);
582 float parent_mat[3][3];
583 copy_m3_m4(parent_mat, ob->parent->obmat);
584 normalize_m3(parent_mat);
585 mul_m3_m3m3(gmat, parent_mat, gmat);
594 /* centroid, boundbox, of selection */
595 /* returns total items selected */
596 static int calc_manipulator_stats(const bContext *C)
598 ScrArea *sa = CTX_wm_area(C);
599 ARegion *ar = CTX_wm_region(C);
600 Scene *scene = CTX_data_scene(C);
601 SceneLayer *sl = CTX_data_scene_layer(C);
602 Object *obedit = CTX_data_edit_object(C);
603 View3D *v3d = sa->spacedata.first;
604 RegionView3D *rv3d = ar->regiondata;
606 Object *ob = OBACT_NEW;
607 bGPdata *gpd = CTX_data_gpencil_data(C);
608 const bool is_gp_edit = ((gpd) && (gpd->flag & GP_DATA_STROKE_EDITMODE));
611 /* transform widget matrix */
612 unit_m4(rv3d->twmat);
614 rv3d->twdrawflag = 0xFFFF;
616 /* transform widget centroid/center */
617 INIT_MINMAX(scene->twmin, scene->twmax);
618 zero_v3(scene->twcent);
621 float diff_mat[4][4];
624 for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
625 /* only editable and visible layers are considered */
626 if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
628 /* calculate difference matrix if parent object */
629 if (gpl->parent != NULL) {
630 ED_gpencil_parent_location(gpl, diff_mat);
633 for (bGPDstroke *gps = gpl->actframe->strokes.first; gps; gps = gps->next) {
634 /* skip strokes that are invalid for current view */
635 if (ED_gpencil_stroke_can_use(C, gps) == false) {
639 /* we're only interested in selected points here... */
640 if (gps->flag & GP_STROKE_SELECT) {
644 /* Change selection status of all points, then make the stroke match */
645 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
646 if (pt->flag & GP_SPOINT_SELECT) {
647 if (gpl->parent == NULL) {
648 calc_tw_center(scene, &pt->x);
652 mul_v3_m4v3(fpt, diff_mat, &pt->x);
653 calc_tw_center(scene, fpt);
664 /* selection center */
666 mul_v3_fl(scene->twcent, 1.0f / (float)totsel); /* centroid! */
671 if (obedit->type == OB_MESH) {
672 BMEditMesh *em = BKE_editmesh_from_object(obedit);
674 float vec[3] = {0, 0, 0};
676 /* USE LAST SELECTE WITH ACTIVE */
677 if ((v3d->around == V3D_AROUND_ACTIVE) && BM_select_history_active_get(em->bm, &ese)) {
678 BM_editselection_center(&ese, vec);
679 calc_tw_center(scene, vec);
688 BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
689 if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
690 if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
692 calc_tw_center(scene, eve->co);
698 else if (obedit->type == OB_ARMATURE) {
699 bArmature *arm = obedit->data;
702 if ((v3d->around == V3D_AROUND_ACTIVE) && (ebo = arm->act_edbone)) {
703 /* doesn't check selection or visibility intentionally */
704 if (ebo->flag & BONE_TIPSEL) {
705 calc_tw_center(scene, ebo->tail);
708 if ((ebo->flag & BONE_ROOTSEL) ||
709 ((ebo->flag & BONE_TIPSEL) == false)) /* ensure we get at least one point */
711 calc_tw_center(scene, ebo->head);
714 protectflag_to_drawflags_ebone(rv3d, ebo);
717 for (ebo = arm->edbo->first; ebo; ebo = ebo->next) {
718 if (EBONE_VISIBLE(arm, ebo)) {
719 if (ebo->flag & BONE_TIPSEL) {
720 calc_tw_center(scene, ebo->tail);
723 if ((ebo->flag & BONE_ROOTSEL) &&
724 /* don't include same point multiple times */
725 ((ebo->flag & BONE_CONNECTED) &&
726 (ebo->parent != NULL) &&
727 (ebo->parent->flag & BONE_TIPSEL) &&
728 EBONE_VISIBLE(arm, ebo->parent)) == 0)
730 calc_tw_center(scene, ebo->head);
733 if (ebo->flag & BONE_SELECTED) {
734 protectflag_to_drawflags_ebone(rv3d, ebo);
740 else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) {
741 Curve *cu = obedit->data;
744 if (v3d->around == V3D_AROUND_ACTIVE && ED_curve_active_center(cu, center)) {
745 calc_tw_center(scene, center);
752 ListBase *nurbs = BKE_curve_editNurbs_get(cu);
756 if (nu->type == CU_BEZIER) {
761 * if handles are hidden then only check the center points.
762 * If the center knot is selected then only use this as the center point.
764 if (cu->drawflag & CU_HIDE_HANDLES) {
765 if (bezt->f2 & SELECT) {
766 calc_tw_center(scene, bezt->vec[1]);
770 else if (bezt->f2 & SELECT) {
771 calc_tw_center(scene, bezt->vec[1]);
775 if (bezt->f1 & SELECT) {
776 calc_tw_center(scene, bezt->vec[(v3d->around == V3D_AROUND_LOCAL_ORIGINS) ? 1 : 0]);
779 if (bezt->f3 & SELECT) {
780 calc_tw_center(scene, bezt->vec[(v3d->around == V3D_AROUND_LOCAL_ORIGINS) ? 1 : 2]);
789 a = nu->pntsu * nu->pntsv;
791 if (bp->f1 & SELECT) {
792 calc_tw_center(scene, bp->vec);
802 else if (obedit->type == OB_MBALL) {
803 MetaBall *mb = (MetaBall *)obedit->data;
806 if ((v3d->around == V3D_AROUND_ACTIVE) && (ml = mb->lastelem)) {
807 calc_tw_center(scene, &ml->x);
811 for (ml = mb->editelems->first; ml; ml = ml->next) {
812 if (ml->flag & SELECT) {
813 calc_tw_center(scene, &ml->x);
819 else if (obedit->type == OB_LATTICE) {
820 Lattice *lt = ((Lattice *)obedit->data)->editlatt->latt;
823 if ((v3d->around == V3D_AROUND_ACTIVE) && (bp = BKE_lattice_active_point_get(lt))) {
824 calc_tw_center(scene, bp->vec);
829 a = lt->pntsu * lt->pntsv * lt->pntsw;
831 if (bp->f1 & SELECT) {
832 calc_tw_center(scene, bp->vec);
840 /* selection center */
842 mul_v3_fl(scene->twcent, 1.0f / (float)totsel); // centroid!
843 mul_m4_v3(obedit->obmat, scene->twcent);
844 mul_m4_v3(obedit->obmat, scene->twmin);
845 mul_m4_v3(obedit->obmat, scene->twmax);
848 else if (ob && (ob->mode & OB_MODE_POSE)) {
850 int mode = TFM_ROTATION; // mislead counting bones... bah. We don't know the manipulator mode, could be mixed
853 if ((v3d->around == V3D_AROUND_ACTIVE) && (pchan = BKE_pose_channel_active(ob))) {
854 /* doesn't check selection or visibility intentionally */
855 Bone *bone = pchan->bone;
857 calc_tw_center(scene, pchan->pose_head);
858 protectflag_to_drawflags_pchan(rv3d, pchan);
864 totsel = count_set_pose_transflags(&mode, 0, ob);
867 /* use channels to get stats */
868 for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
869 Bone *bone = pchan->bone;
870 if (bone && (bone->flag & BONE_TRANSFORM)) {
871 calc_tw_center(scene, pchan->pose_head);
872 protectflag_to_drawflags_pchan(rv3d, pchan);
880 mul_v3_fl(scene->twcent, 1.0f / (float)totsel); // centroid!
881 mul_m4_v3(ob->obmat, scene->twcent);
882 mul_m4_v3(ob->obmat, scene->twmin);
883 mul_m4_v3(ob->obmat, scene->twmax);
886 else if (ob && (ob->mode & OB_MODE_ALL_PAINT)) {
889 else if (ob && ob->mode & OB_MODE_PARTICLE_EDIT) {
890 PTCacheEdit *edit = PE_get_current(scene, sl, ob);
891 PTCacheEditPoint *point;
896 point = edit->points;
897 for (a = 0; a < edit->totpoint; a++, point++) {
898 if (point->flag & PEP_HIDE) continue;
900 for (k = 0, ek = point->keys; k < point->totkey; k++, ek++) {
901 if (ek->flag & PEK_SELECT) {
902 calc_tw_center(scene, (ek->flag & PEK_USE_WCO) ? ek->world_co : ek->co);
908 /* selection center */
910 mul_v3_fl(scene->twcent, 1.0f / (float)totsel); // centroid!
915 /* we need the one selected object, if its not active */
918 if (base && ((base->flag & BASE_SELECTED) == 0)) ob = NULL;
920 for (base = sl->object_bases.first; base; base = base->next) {
921 if (TESTBASELIB_NEW(base)) {
924 calc_tw_center(scene, base->object->obmat[3]);
925 protectflag_to_drawflags(base->object->protectflag, &rv3d->twdrawflag);
930 /* selection center */
932 mul_v3_fl(scene->twcent, 1.0f / (float)totsel); // centroid!
936 /* global, local or normal orientation? */
937 if (ob && totsel && !is_gp_edit) {
939 switch (v3d->twmode) {
941 case V3D_MANIP_GLOBAL:
943 break; /* nothing to do */
945 case V3D_MANIP_GIMBAL:
948 if (gimbal_axis(ob, mat)) {
949 copy_m4_m3(rv3d->twmat, mat);
952 /* if not gimbal, fall through to normal */
955 case V3D_MANIP_NORMAL:
957 if (obedit || ob->mode & OB_MODE_POSE) {
959 ED_getTransformOrientationMatrix(C, mat, v3d->around);
960 copy_m4_m3(rv3d->twmat, mat);
963 /* no break we define 'normal' as 'local' in Object mode */
966 case V3D_MANIP_LOCAL:
968 if (ob->mode & OB_MODE_POSE) {
969 /* each bone moves on its own local axis, but to avoid confusion,
970 * use the active pones axis for display [#33575], this works as expected on a single bone
971 * and users who select many bones will understand whats going on and what local means
972 * when they start transforming */
974 ED_getTransformOrientationMatrix(C, mat, v3d->around);
975 copy_m4_m3(rv3d->twmat, mat);
978 copy_m4_m4(rv3d->twmat, ob->obmat);
979 normalize_m4(rv3d->twmat);
985 copy_m3_m4(mat, rv3d->viewinv);
987 copy_m4_m3(rv3d->twmat, mat);
990 case V3D_MANIP_CUSTOM:
992 TransformOrientation *custom_orientation = BKE_workspace_transform_orientation_find(
993 CTX_wm_workspace(C), v3d->custom_orientation_index);
996 if (applyTransformOrientation(custom_orientation, mat, NULL)) {
997 copy_m4_m3(rv3d->twmat, mat);
1008 static void manipulator_get_idot(RegionView3D *rv3d, float r_idot[3])
1010 float view_vec[3], axis_vec[3];
1011 ED_view3d_global_to_vector(rv3d, rv3d->twmat[3], view_vec);
1012 for (int i = 0; i < 3; i++) {
1013 normalize_v3_v3(axis_vec, rv3d->twmat[i]);
1014 r_idot[i] = 1.0f - fabsf(dot_v3v3(view_vec, axis_vec));
1018 static void manipulator_prepare_mat(const bContext *C, View3D *v3d, RegionView3D *rv3d)
1020 Scene *scene = CTX_data_scene(C);
1021 SceneLayer *sl = CTX_data_scene_layer(C);
1023 switch (v3d->around) {
1024 case V3D_AROUND_CENTER_BOUNDS:
1025 case V3D_AROUND_ACTIVE:
1027 bGPdata *gpd = CTX_data_gpencil_data(C);
1028 Object *ob = OBACT_NEW;
1030 if (((v3d->around == V3D_AROUND_ACTIVE) && (scene->obedit == NULL)) &&
1031 ((gpd == NULL) || !(gpd->flag & GP_DATA_STROKE_EDITMODE)) &&
1032 (!(ob->mode & OB_MODE_POSE)))
1034 copy_v3_v3(rv3d->twmat[3], ob->obmat[3]);
1037 mid_v3_v3v3(rv3d->twmat[3], scene->twmin, scene->twmax);
1041 case V3D_AROUND_LOCAL_ORIGINS:
1042 case V3D_AROUND_CENTER_MEAN:
1043 copy_v3_v3(rv3d->twmat[3], scene->twcent);
1045 case V3D_AROUND_CURSOR:
1046 copy_v3_v3(rv3d->twmat[3], ED_view3d_cursor3d_get(scene, v3d));
1052 * Sets up \a r_start and \a r_len to define arrow line range.
1053 * Needed to adjust line drawing for combined manipulator axis types.
1055 static void manipulator_line_range(const View3D *v3d, const short axis_type, float *r_start, float *r_len)
1057 const float ofs = 0.2f;
1062 switch (axis_type) {
1063 case MAN_AXES_TRANSLATE:
1064 if (v3d->twtype & V3D_MANIP_SCALE) {
1065 *r_start = *r_len - ofs + 0.075f;
1067 if (v3d->twtype & V3D_MANIP_ROTATE) {
1071 case MAN_AXES_SCALE:
1072 if (v3d->twtype & (V3D_MANIP_TRANSLATE | V3D_MANIP_ROTATE)) {
1073 *r_len -= ofs + 0.025f;
1081 /* **************** Actual Widget Stuff **************** */
1083 static ManipulatorGroup *manipulatorgroup_init(wmManipulatorGroup *mgroup)
1085 ManipulatorGroup *man;
1087 man = MEM_callocN(sizeof(ManipulatorGroup), "manipulator_data");
1089 const wmManipulatorType *wt_arrow = WM_manipulatortype_find("MANIPULATOR_WT_arrow_3d", true);
1090 const wmManipulatorType *wt_dial = WM_manipulatortype_find("MANIPULATOR_WT_dial_3d", true);
1091 const wmManipulatorType *wt_prim = WM_manipulatortype_find("MANIPULATOR_WT_primitive_3d", true);
1093 #define MANIPULATOR_NEW_ARROW(v, name, draw_style) { \
1094 v = WM_manipulator_new_ptr(wt_arrow, mgroup, name, NULL); \
1095 RNA_enum_set((v)->ptr, "draw_style", draw_style); \
1097 #define MANIPULATOR_NEW_DIAL(v, name, draw_options) { \
1098 v = WM_manipulator_new_ptr(wt_dial, mgroup, name, NULL); \
1099 RNA_enum_set((v)->ptr, "draw_options", draw_options); \
1101 #define MANIPULATOR_NEW_PRIM(v, name, draw_style) { \
1102 v = WM_manipulator_new_ptr(wt_prim, mgroup, name, NULL); \
1103 RNA_enum_set((v)->ptr, "draw_style", draw_style); \
1106 /* add/init widgets - order matters! */
1107 MANIPULATOR_NEW_DIAL(man->rotate_t, "rotate_t", ED_MANIPULATOR_DIAL_DRAW_FLAG_FILL);
1109 MANIPULATOR_NEW_DIAL(man->scale_c, "scale_c", ED_MANIPULATOR_DIAL_DRAW_FLAG_NOP);
1111 MANIPULATOR_NEW_ARROW(man->scale_x, "scale_x", ED_MANIPULATOR_ARROW_STYLE_BOX);
1112 MANIPULATOR_NEW_ARROW(man->scale_y, "scale_y", ED_MANIPULATOR_ARROW_STYLE_BOX);
1113 MANIPULATOR_NEW_ARROW(man->scale_z, "scale_z", ED_MANIPULATOR_ARROW_STYLE_BOX);
1115 MANIPULATOR_NEW_PRIM(man->scale_xy, "scale_xy", ED_MANIPULATOR_PRIMITIVE_STYLE_PLANE);
1116 MANIPULATOR_NEW_PRIM(man->scale_yz, "scale_yz", ED_MANIPULATOR_PRIMITIVE_STYLE_PLANE);
1117 MANIPULATOR_NEW_PRIM(man->scale_zx, "scale_zx", ED_MANIPULATOR_PRIMITIVE_STYLE_PLANE);
1119 MANIPULATOR_NEW_DIAL(man->rotate_x, "rotate_x", ED_MANIPULATOR_DIAL_DRAW_FLAG_CLIP);
1120 MANIPULATOR_NEW_DIAL(man->rotate_y, "rotate_y", ED_MANIPULATOR_DIAL_DRAW_FLAG_CLIP);
1121 MANIPULATOR_NEW_DIAL(man->rotate_z, "rotate_z", ED_MANIPULATOR_DIAL_DRAW_FLAG_CLIP);
1123 /* init screen aligned widget last here, looks better, behaves better */
1124 MANIPULATOR_NEW_DIAL(man->rotate_c, "rotate_c", ED_MANIPULATOR_DIAL_DRAW_FLAG_NOP);
1126 MANIPULATOR_NEW_DIAL(man->translate_c, "translate_c", ED_MANIPULATOR_DIAL_DRAW_FLAG_NOP);
1128 MANIPULATOR_NEW_ARROW(man->translate_x, "translate_x", ED_MANIPULATOR_ARROW_STYLE_NORMAL);
1129 MANIPULATOR_NEW_ARROW(man->translate_y, "translate_y", ED_MANIPULATOR_ARROW_STYLE_NORMAL);
1130 MANIPULATOR_NEW_ARROW(man->translate_z, "translate_z", ED_MANIPULATOR_ARROW_STYLE_NORMAL);
1132 MANIPULATOR_NEW_PRIM(man->translate_xy, "translate_xy", ED_MANIPULATOR_PRIMITIVE_STYLE_PLANE);
1133 MANIPULATOR_NEW_PRIM(man->translate_yz, "translate_yz", ED_MANIPULATOR_PRIMITIVE_STYLE_PLANE);
1134 MANIPULATOR_NEW_PRIM(man->translate_zx, "translate_zx", ED_MANIPULATOR_PRIMITIVE_STYLE_PLANE);
1140 * Custom handler for manipulator widgets
1142 static void manipulator_modal(
1143 bContext *C, wmManipulator *widget, const wmEvent *UNUSED(event), const int UNUSED(flag))
1145 const ScrArea *sa = CTX_wm_area(C);
1146 ARegion *ar = CTX_wm_region(C);
1147 View3D *v3d = sa->spacedata.first;
1148 RegionView3D *rv3d = ar->regiondata;
1150 if (calc_manipulator_stats(C)) {
1151 manipulator_prepare_mat(C, v3d, rv3d);
1152 WM_manipulator_set_matrix_location(widget, rv3d->twmat[3]);
1155 ED_region_tag_redraw(ar);
1158 static void WIDGETGROUP_manipulator_setup(const bContext *UNUSED(C), wmManipulatorGroup *mgroup)
1160 ManipulatorGroup *man = manipulatorgroup_init(mgroup);
1162 wmOperatorType *translate, *rotate, *trackball, *resize;
1163 } ot_store = {NULL};
1165 mgroup->customdata = man;
1167 /* *** set properties for axes *** */
1169 MAN_ITER_AXES_BEGIN(axis, axis_idx)
1171 const short axis_type = manipulator_get_axis_type(man, axis);
1172 int constraint_axis[3] = {1, 0, 0};
1175 manipulator_get_axis_constraint(axis_idx, constraint_axis);
1177 /* custom handler! */
1178 WM_manipulator_set_fn_custom_modal(axis, manipulator_modal);
1181 case MAN_AXIS_TRANS_X:
1182 case MAN_AXIS_TRANS_Y:
1183 case MAN_AXIS_TRANS_Z:
1184 case MAN_AXIS_SCALE_X:
1185 case MAN_AXIS_SCALE_Y:
1186 case MAN_AXIS_SCALE_Z:
1187 WM_manipulator_set_line_width(axis, MANIPULATOR_AXIS_LINE_WIDTH);
1189 case MAN_AXIS_ROT_X:
1190 case MAN_AXIS_ROT_Y:
1191 case MAN_AXIS_ROT_Z:
1192 /* increased line width for better display */
1193 WM_manipulator_set_line_width(axis, MANIPULATOR_AXIS_LINE_WIDTH + 1.0f);
1194 WM_manipulator_set_flag(axis, WM_MANIPULATOR_DRAW_VALUE, true);
1196 case MAN_AXIS_TRANS_XY:
1197 case MAN_AXIS_TRANS_YZ:
1198 case MAN_AXIS_TRANS_ZX:
1199 case MAN_AXIS_SCALE_XY:
1200 case MAN_AXIS_SCALE_YZ:
1201 case MAN_AXIS_SCALE_ZX:
1203 const float ofs_ax = 11.0f;
1204 const float ofs[3] = {ofs_ax, ofs_ax, 0.0f};
1205 WM_manipulator_set_scale(axis, 0.07f);
1206 WM_manipulator_set_matrix_offset_location(axis, ofs);
1209 case MAN_AXIS_TRANS_C:
1210 case MAN_AXIS_ROT_C:
1211 case MAN_AXIS_SCALE_C:
1212 case MAN_AXIS_ROT_T:
1213 WM_manipulator_set_line_width(axis, MANIPULATOR_AXIS_LINE_WIDTH);
1214 if (axis_idx == MAN_AXIS_ROT_T) {
1215 WM_manipulator_set_flag(axis, WM_MANIPULATOR_DRAW_HOVER, true);
1217 else if (axis_idx == MAN_AXIS_ROT_C) {
1218 WM_manipulator_set_flag(axis, WM_MANIPULATOR_DRAW_VALUE, true);
1221 WM_manipulator_set_scale(axis, 0.2f);
1226 switch (axis_type) {
1227 case MAN_AXES_TRANSLATE:
1228 if (ot_store.translate == NULL) {
1229 ot_store.translate = WM_operatortype_find("TRANSFORM_OT_translate", true);
1231 ptr = WM_manipulator_set_operator(axis, ot_store.translate, NULL);
1233 case MAN_AXES_ROTATE:
1235 wmOperatorType *ot_rotate;
1236 if (axis_idx == MAN_AXIS_ROT_T) {
1237 if (ot_store.trackball == NULL) {
1238 ot_store.trackball = WM_operatortype_find("TRANSFORM_OT_trackball", true);
1240 ot_rotate = ot_store.trackball;
1243 if (ot_store.rotate == NULL) {
1244 ot_store.rotate = WM_operatortype_find("TRANSFORM_OT_rotate", true);
1246 ot_rotate = ot_store.rotate;
1248 ptr = WM_manipulator_set_operator(axis, ot_rotate, NULL);
1251 case MAN_AXES_SCALE:
1253 if (ot_store.resize == NULL) {
1254 ot_store.resize = WM_operatortype_find("TRANSFORM_OT_resize", true);
1256 ptr = WM_manipulator_set_operator(axis, ot_store.resize, NULL);
1263 if ((prop = RNA_struct_find_property(ptr, "constraint_axis"))) {
1264 RNA_property_boolean_set_array(ptr, prop, constraint_axis);
1268 RNA_boolean_set(ptr, "release_confirm", 1);
1273 static void WIDGETGROUP_manipulator_refresh(const bContext *C, wmManipulatorGroup *mgroup)
1275 ManipulatorGroup *man = mgroup->customdata;
1276 ScrArea *sa = CTX_wm_area(C);
1277 ARegion *ar = CTX_wm_region(C);
1278 View3D *v3d = sa->spacedata.first;
1279 RegionView3D *rv3d = ar->regiondata;
1281 /* skip, we don't draw anything anyway */
1282 if ((man->all_hidden = (calc_manipulator_stats(C) == 0)))
1285 manipulator_prepare_mat(C, v3d, rv3d);
1287 /* *** set properties for axes *** */
1289 MAN_ITER_AXES_BEGIN(axis, axis_idx)
1291 const short axis_type = manipulator_get_axis_type(man, axis);
1292 const int aidx_norm = manipulator_index_normalize(axis_idx);
1294 WM_manipulator_set_matrix_location(axis, rv3d->twmat[3]);
1297 case MAN_AXIS_TRANS_X:
1298 case MAN_AXIS_TRANS_Y:
1299 case MAN_AXIS_TRANS_Z:
1300 case MAN_AXIS_SCALE_X:
1301 case MAN_AXIS_SCALE_Y:
1302 case MAN_AXIS_SCALE_Z:
1304 float start_co[3] = {0.0f, 0.0f, 0.0f};
1307 manipulator_line_range(v3d, axis_type, &start_co[2], &len);
1309 WM_manipulator_set_matrix_rotation_from_z_axis(axis, rv3d->twmat[aidx_norm]);
1310 RNA_float_set(axis->ptr, "length", len);
1311 WM_manipulator_set_matrix_offset_location(axis, start_co);
1314 case MAN_AXIS_ROT_X:
1315 case MAN_AXIS_ROT_Y:
1316 case MAN_AXIS_ROT_Z:
1317 WM_manipulator_set_matrix_rotation_from_z_axis(axis, rv3d->twmat[aidx_norm]);
1319 case MAN_AXIS_TRANS_XY:
1320 case MAN_AXIS_TRANS_YZ:
1321 case MAN_AXIS_TRANS_ZX:
1322 case MAN_AXIS_SCALE_XY:
1323 case MAN_AXIS_SCALE_YZ:
1324 case MAN_AXIS_SCALE_ZX:
1326 const float *y_axis = rv3d->twmat[aidx_norm + 1 > 2 ? 0 : aidx_norm + 1];
1327 const float *z_axis = rv3d->twmat[aidx_norm - 1 < 0 ? 2 : aidx_norm - 1];
1328 WM_manipulator_set_matrix_rotation_from_yz_axis(axis, y_axis, z_axis);
1336 static void WIDGETGROUP_manipulator_draw_prepare(const bContext *C, wmManipulatorGroup *mgroup)
1338 ManipulatorGroup *man = mgroup->customdata;
1339 ScrArea *sa = CTX_wm_area(C);
1340 ARegion *ar = CTX_wm_region(C);
1341 View3D *v3d = sa->spacedata.first;
1342 RegionView3D *rv3d = ar->regiondata;
1345 /* when looking through a selected camera, the manipulator can be at the
1346 * exact same position as the view, skip so we don't break selection */
1347 if (man->all_hidden || fabsf(ED_view3d_pixel_size(rv3d, rv3d->twmat[3])) < 1e-6f) {
1348 MAN_ITER_AXES_BEGIN(axis, axis_idx)
1350 WM_manipulator_set_flag(axis, WM_MANIPULATOR_HIDDEN, true);
1355 manipulator_get_idot(rv3d, idot);
1357 /* *** set properties for axes *** */
1359 MAN_ITER_AXES_BEGIN(axis, axis_idx)
1361 const short axis_type = manipulator_get_axis_type(man, axis);
1362 /* XXX maybe unset _HIDDEN flag on redraw? */
1363 if (manipulator_is_axis_visible(v3d, rv3d, idot, axis_type, axis_idx)) {
1364 WM_manipulator_set_flag(axis, WM_MANIPULATOR_HIDDEN, false);
1367 WM_manipulator_set_flag(axis, WM_MANIPULATOR_HIDDEN, true);
1371 float col[4], col_hi[4];
1372 manipulator_get_axis_color(axis_idx, idot, col, col_hi);
1373 WM_manipulator_set_color(axis, col);
1374 WM_manipulator_set_color_highlight(axis, col_hi);
1377 case MAN_AXIS_TRANS_C:
1378 case MAN_AXIS_ROT_C:
1379 case MAN_AXIS_SCALE_C:
1380 case MAN_AXIS_ROT_T:
1381 WM_manipulator_set_matrix_rotation_from_z_axis(axis, rv3d->viewinv[2]);
1388 static bool WIDGETGROUP_manipulator_poll(const struct bContext *C, struct wmManipulatorGroupType *UNUSED(wgt))
1390 /* it's a given we only use this in 3D view */
1391 const ScrArea *sa = CTX_wm_area(C);
1392 const View3D *v3d = sa->spacedata.first;
1394 return (((v3d->twflag & V3D_MANIPULATOR_DRAW) != 0) &&
1395 ((v3d->twtype & (V3D_MANIP_TRANSLATE | V3D_MANIP_ROTATE | V3D_MANIP_SCALE)) != 0));
1398 void TRANSFORM_WGT_manipulator(wmManipulatorGroupType *wgt)
1400 wgt->name = "Transform Manipulator";
1401 wgt->idname = "TRANSFORM_WGT_manipulator";
1403 wgt->flag |= (WM_MANIPULATORGROUPTYPE_PERSISTENT |
1404 WM_MANIPULATORGROUPTYPE_3D);
1406 wgt->poll = WIDGETGROUP_manipulator_poll;
1407 wgt->setup = WIDGETGROUP_manipulator_setup;
1408 wgt->refresh = WIDGETGROUP_manipulator_refresh;
1409 wgt->draw_prepare = WIDGETGROUP_manipulator_draw_prepare;