Merge branch 'master' into blender2.8
[blender.git] / source / blender / editors / transform / transform_manipulator.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  * The Original Code is Copyright (C) 2005 Blender Foundation
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): none yet.
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/editors/transform/transform_manipulator.c
29  *  \ingroup edtransform
30  */
31
32
33 #include <stdlib.h>
34 #include <string.h>
35 #include <math.h>
36 #include <float.h>
37
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_object_types.h"
44 #include "DNA_screen_types.h"
45 #include "DNA_scene_types.h"
46 #include "DNA_view3d_types.h"
47
48 #include "BLI_listbase.h"
49 #include "BLI_math.h"
50 #include "BLI_utildefines.h"
51
52 #include "RNA_access.h"
53
54 #include "BKE_action.h"
55 #include "BKE_context.h"
56 #include "BKE_curve.h"
57 #include "BKE_global.h"
58 #include "BKE_editmesh.h"
59 #include "BKE_lattice.h"
60 #include "BKE_gpencil.h"
61
62 #include "BIF_gl.h"
63
64 #include "WM_api.h"
65 #include "WM_types.h"
66
67 #include "ED_armature.h"
68 #include "ED_curve.h"
69 #include "ED_view3d.h"
70 #include "ED_gpencil.h"
71
72 #include "UI_resources.h"
73
74 /* local module include */
75 #include "transform.h"
76
77 #include "GPU_select.h"
78
79 /* return codes for select, and drawing flags */
80
81 #define MAN_TRANS_X             (1 << 0)
82 #define MAN_TRANS_Y             (1 << 1)
83 #define MAN_TRANS_Z             (1 << 2)
84 #define MAN_TRANS_C             (MAN_TRANS_X | MAN_TRANS_Y | MAN_TRANS_Z)
85
86 #define MAN_ROT_X               (1 << 3)
87 #define MAN_ROT_Y               (1 << 4)
88 #define MAN_ROT_Z               (1 << 5)
89 #define MAN_ROT_V               (1 << 6)
90 #define MAN_ROT_T               (1 << 7)
91 #define MAN_ROT_C               (MAN_ROT_X | MAN_ROT_Y | MAN_ROT_Z | MAN_ROT_V | MAN_ROT_T)
92
93 #define MAN_SCALE_X             (1 << 8)
94 #define MAN_SCALE_Y             (1 << 9)
95 #define MAN_SCALE_Z             (1 << 10)
96 #define MAN_SCALE_C             (MAN_SCALE_X | MAN_SCALE_Y | MAN_SCALE_Z)
97
98 /* color codes */
99
100 #define MAN_RGB     0
101 #define MAN_GHOST   1
102 #define MAN_MOVECOL 2
103
104 /* threshold for testing view aligned manipulator axis */
105 #define TW_AXIS_DOT_MIN 0.02f
106 #define TW_AXIS_DOT_MAX 0.1f
107
108 /* transform widget center calc helper for below */
109 static void calc_tw_center(Scene *scene, const float co[3])
110 {
111         float *twcent = scene->twcent;
112         float *min = scene->twmin;
113         float *max = scene->twmax;
114
115         minmax_v3v3_v3(min, max, co);
116         add_v3_v3(twcent, co);
117 }
118
119 static void protectflag_to_drawflags(short protectflag, short *drawflags)
120 {
121         if (protectflag & OB_LOCK_LOCX)
122                 *drawflags &= ~MAN_TRANS_X;
123         if (protectflag & OB_LOCK_LOCY)
124                 *drawflags &= ~MAN_TRANS_Y;
125         if (protectflag & OB_LOCK_LOCZ)
126                 *drawflags &= ~MAN_TRANS_Z;
127
128         if (protectflag & OB_LOCK_ROTX)
129                 *drawflags &= ~MAN_ROT_X;
130         if (protectflag & OB_LOCK_ROTY)
131                 *drawflags &= ~MAN_ROT_Y;
132         if (protectflag & OB_LOCK_ROTZ)
133                 *drawflags &= ~MAN_ROT_Z;
134
135         if (protectflag & OB_LOCK_SCALEX)
136                 *drawflags &= ~MAN_SCALE_X;
137         if (protectflag & OB_LOCK_SCALEY)
138                 *drawflags &= ~MAN_SCALE_Y;
139         if (protectflag & OB_LOCK_SCALEZ)
140                 *drawflags &= ~MAN_SCALE_Z;
141 }
142
143 /* for pose mode */
144 static void stats_pose(Scene *scene, RegionView3D *rv3d, bPoseChannel *pchan)
145 {
146         Bone *bone = pchan->bone;
147
148         if (bone) {
149                 calc_tw_center(scene, pchan->pose_head);
150                 protectflag_to_drawflags(pchan->protectflag, &rv3d->twdrawflag);
151         }
152 }
153
154 /* for editmode*/
155 static void stats_editbone(RegionView3D *rv3d, EditBone *ebo)
156 {
157         if (ebo->flag & BONE_EDITMODE_LOCKED)
158                 protectflag_to_drawflags(OB_LOCK_LOC | OB_LOCK_ROT | OB_LOCK_SCALE, &rv3d->twdrawflag);
159 }
160
161 /* could move into BLI_math however this is only useful for display/editing purposes */
162 static void axis_angle_to_gimbal_axis(float gmat[3][3], const float axis[3], const float angle)
163 {
164         /* X/Y are arbitrary axies, most importantly Z is the axis of rotation */
165
166         float cross_vec[3];
167         float quat[4];
168
169         /* this is an un-scientific method to get a vector to cross with
170          * XYZ intentionally YZX */
171         cross_vec[0] = axis[1];
172         cross_vec[1] = axis[2];
173         cross_vec[2] = axis[0];
174
175         /* X-axis */
176         cross_v3_v3v3(gmat[0], cross_vec, axis);
177         normalize_v3(gmat[0]);
178         axis_angle_to_quat(quat, axis, angle);
179         mul_qt_v3(quat, gmat[0]);
180
181         /* Y-axis */
182         axis_angle_to_quat(quat, axis, M_PI_2);
183         copy_v3_v3(gmat[1], gmat[0]);
184         mul_qt_v3(quat, gmat[1]);
185
186         /* Z-axis */
187         copy_v3_v3(gmat[2], axis);
188
189         normalize_m3(gmat);
190 }
191
192
193 static int test_rotmode_euler(short rotmode)
194 {
195         return (ELEM(rotmode, ROT_MODE_AXISANGLE, ROT_MODE_QUAT)) ? 0 : 1;
196 }
197
198 bool gimbal_axis(Object *ob, float gmat[3][3])
199 {
200         if (ob) {
201                 if (ob->mode & OB_MODE_POSE) {
202                         bPoseChannel *pchan = BKE_pose_channel_active(ob);
203
204                         if (pchan) {
205                                 float mat[3][3], tmat[3][3], obmat[3][3];
206                                 if (test_rotmode_euler(pchan->rotmode)) {
207                                         eulO_to_gimbal_axis(mat, pchan->eul, pchan->rotmode);
208                                 }
209                                 else if (pchan->rotmode == ROT_MODE_AXISANGLE) {
210                                         axis_angle_to_gimbal_axis(mat, pchan->rotAxis, pchan->rotAngle);
211                                 }
212                                 else { /* quat */
213                                         return 0;
214                                 }
215
216
217                                 /* apply bone transformation */
218                                 mul_m3_m3m3(tmat, pchan->bone->bone_mat, mat);
219
220                                 if (pchan->parent) {
221                                         float parent_mat[3][3];
222
223                                         copy_m3_m4(parent_mat, pchan->parent->pose_mat);
224                                         mul_m3_m3m3(mat, parent_mat, tmat);
225
226                                         /* needed if object transformation isn't identity */
227                                         copy_m3_m4(obmat, ob->obmat);
228                                         mul_m3_m3m3(gmat, obmat, mat);
229                                 }
230                                 else {
231                                         /* needed if object transformation isn't identity */
232                                         copy_m3_m4(obmat, ob->obmat);
233                                         mul_m3_m3m3(gmat, obmat, tmat);
234                                 }
235
236                                 normalize_m3(gmat);
237                                 return 1;
238                         }
239                 }
240                 else {
241                         if (test_rotmode_euler(ob->rotmode)) {
242                                 eulO_to_gimbal_axis(gmat, ob->rot, ob->rotmode);
243                         }
244                         else if (ob->rotmode == ROT_MODE_AXISANGLE) {
245                                 axis_angle_to_gimbal_axis(gmat, ob->rotAxis, ob->rotAngle);
246                         }
247                         else { /* quat */
248                                 return 0;
249                         }
250
251                         if (ob->parent) {
252                                 float parent_mat[3][3];
253                                 copy_m3_m4(parent_mat, ob->parent->obmat);
254                                 normalize_m3(parent_mat);
255                                 mul_m3_m3m3(gmat, parent_mat, gmat);
256                         }
257                         return 1;
258                 }
259         }
260
261         return 0;
262 }
263
264
265 /* centroid, boundbox, of selection */
266 /* returns total items selected */
267 static int calc_manipulator_stats(const bContext *C)
268 {
269         ScrArea *sa = CTX_wm_area(C);
270         ARegion *ar = CTX_wm_region(C);
271         Scene *scene = CTX_data_scene(C);
272         Object *obedit = CTX_data_edit_object(C);
273         View3D *v3d = sa->spacedata.first;
274         RegionView3D *rv3d = ar->regiondata;
275         Base *base;
276         Object *ob = OBACT;
277         bGPdata *gpd = CTX_data_gpencil_data(C);
278         const bool is_gp_edit = ((gpd) && (gpd->flag & GP_DATA_STROKE_EDITMODE));
279         int a, totsel = 0;
280
281         /* transform widget matrix */
282         unit_m4(rv3d->twmat);
283
284         rv3d->twdrawflag = 0xFFFF;
285
286         /* transform widget centroid/center */
287         INIT_MINMAX(scene->twmin, scene->twmax);
288         zero_v3(scene->twcent);
289         
290         if (is_gp_edit) {
291                 float diff_mat[4][4];
292                 float fpt[3];
293
294                 for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
295                         /* only editable and visible layers are considered */
296                         if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
297
298                                 /* calculate difference matrix if parent object */
299                                 if (gpl->parent != NULL) {
300                                         ED_gpencil_parent_location(gpl, diff_mat);
301                                 }
302
303                                 for (bGPDstroke *gps = gpl->actframe->strokes.first; gps; gps = gps->next) {
304                                         /* skip strokes that are invalid for current view */
305                                         if (ED_gpencil_stroke_can_use(C, gps) == false) {
306                                                 continue;
307                                         }
308
309                                         /* we're only interested in selected points here... */
310                                         if (gps->flag & GP_STROKE_SELECT) {
311                                                 bGPDspoint *pt;
312                                                 int i;
313
314                                                 /* Change selection status of all points, then make the stroke match */
315                                                 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
316                                                         if (pt->flag & GP_SPOINT_SELECT) {
317                                                                 if (gpl->parent == NULL) {
318                                                                         calc_tw_center(scene, &pt->x);
319                                                                         totsel++;
320                                                                 }
321                                                                 else {
322                                                                         mul_v3_m4v3(fpt, diff_mat, &pt->x);
323                                                                         calc_tw_center(scene, fpt);
324                                                                         totsel++;
325                                                                 }
326                                                         }
327                                                 }
328                                         }
329                                 }
330                         }
331                 }
332
333
334                 /* selection center */
335                 if (totsel) {
336                         mul_v3_fl(scene->twcent, 1.0f / (float)totsel);   /* centroid! */
337                 }
338         }
339         else if (obedit) {
340                 ob = obedit;
341                 if ((ob->lay & v3d->lay) == 0) return 0;
342
343                 if (obedit->type == OB_MESH) {
344                         BMEditMesh *em = BKE_editmesh_from_object(obedit);
345                         BMEditSelection ese;
346                         float vec[3] = {0, 0, 0};
347
348                         /* USE LAST SELECTE WITH ACTIVE */
349                         if ((v3d->around == V3D_AROUND_ACTIVE) && BM_select_history_active_get(em->bm, &ese)) {
350                                 BM_editselection_center(&ese, vec);
351                                 calc_tw_center(scene, vec);
352                                 totsel = 1;
353                         }
354                         else {
355                                 BMesh *bm = em->bm;
356                                 BMVert *eve;
357
358                                 BMIter iter;
359
360                                 BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
361                                         if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
362                                                 if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
363                                                         totsel++;
364                                                         calc_tw_center(scene, eve->co);
365                                                 }
366                                         }
367                                 }
368                         }
369                 } /* end editmesh */
370                 else if (obedit->type == OB_ARMATURE) {
371                         bArmature *arm = obedit->data;
372                         EditBone *ebo;
373
374                         if ((v3d->around == V3D_AROUND_ACTIVE) && (ebo = arm->act_edbone)) {
375                                 /* doesn't check selection or visibility intentionally */
376                                 if (ebo->flag & BONE_TIPSEL) {
377                                         calc_tw_center(scene, ebo->tail);
378                                         totsel++;
379                                 }
380                                 if ((ebo->flag & BONE_ROOTSEL) ||
381                                     ((ebo->flag & BONE_TIPSEL) == false))  /* ensure we get at least one point */
382                                 {
383                                         calc_tw_center(scene, ebo->head);
384                                         totsel++;
385                                 }
386                                 stats_editbone(rv3d, ebo);
387                         }
388                         else {
389                                 for (ebo = arm->edbo->first; ebo; ebo = ebo->next) {
390                                         if (EBONE_VISIBLE(arm, ebo)) {
391                                                 if (ebo->flag & BONE_TIPSEL) {
392                                                         calc_tw_center(scene, ebo->tail);
393                                                         totsel++;
394                                                 }
395                                                 if ((ebo->flag & BONE_ROOTSEL) &&
396                                                     /* don't include same point multiple times */
397                                                     ((ebo->flag & BONE_CONNECTED) &&
398                                                      (ebo->parent != NULL) &&
399                                                      (ebo->parent->flag & BONE_TIPSEL) &&
400                                                      EBONE_VISIBLE(arm, ebo->parent)) == 0)
401                                                 {
402                                                         calc_tw_center(scene, ebo->head);
403                                                         totsel++;
404                                                 }
405                                                 if (ebo->flag & BONE_SELECTED) {
406                                                         stats_editbone(rv3d, ebo);
407                                                 }
408                                         }
409                                 }
410                         }
411                 }
412                 else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) {
413                         Curve *cu = obedit->data;
414                         float center[3];
415
416                         if (v3d->around == V3D_AROUND_ACTIVE && ED_curve_active_center(cu, center)) {
417                                 calc_tw_center(scene, center);
418                                 totsel++;
419                         }
420                         else {
421                                 Nurb *nu;
422                                 BezTriple *bezt;
423                                 BPoint *bp;
424                                 ListBase *nurbs = BKE_curve_editNurbs_get(cu);
425
426                                 nu = nurbs->first;
427                                 while (nu) {
428                                         if (nu->type == CU_BEZIER) {
429                                                 bezt = nu->bezt;
430                                                 a = nu->pntsu;
431                                                 while (a--) {
432                                                         /* exceptions
433                                                          * if handles are hidden then only check the center points.
434                                                          * If the center knot is selected then only use this as the center point.
435                                                          */
436                                                         if (cu->drawflag & CU_HIDE_HANDLES) {
437                                                                 if (bezt->f2 & SELECT) {
438                                                                         calc_tw_center(scene, bezt->vec[1]);
439                                                                         totsel++;
440                                                                 }
441                                                         }
442                                                         else if (bezt->f2 & SELECT) {
443                                                                 calc_tw_center(scene, bezt->vec[1]);
444                                                                 totsel++;
445                                                         }
446                                                         else {
447                                                                 if (bezt->f1 & SELECT) {
448                                                                         calc_tw_center(scene, bezt->vec[(v3d->around == V3D_AROUND_LOCAL_ORIGINS) ? 1 : 0]);
449                                                                         totsel++;
450                                                                 }
451                                                                 if (bezt->f3 & SELECT) {
452                                                                         calc_tw_center(scene, bezt->vec[(v3d->around == V3D_AROUND_LOCAL_ORIGINS) ? 1 : 2]);
453                                                                         totsel++;
454                                                                 }
455                                                         }
456                                                         bezt++;
457                                                 }
458                                         }
459                                         else {
460                                                 bp = nu->bp;
461                                                 a = nu->pntsu * nu->pntsv;
462                                                 while (a--) {
463                                                         if (bp->f1 & SELECT) {
464                                                                 calc_tw_center(scene, bp->vec);
465                                                                 totsel++;
466                                                         }
467                                                         bp++;
468                                                 }
469                                         }
470                                         nu = nu->next;
471                                 }
472                         }
473                 }
474                 else if (obedit->type == OB_MBALL) {
475                         MetaBall *mb = (MetaBall *)obedit->data;
476                         MetaElem *ml;
477
478                         if ((v3d->around == V3D_AROUND_ACTIVE) && (ml = mb->lastelem)) {
479                                 calc_tw_center(scene, &ml->x);
480                                 totsel++;
481                         }
482                         else {
483                                 for (ml = mb->editelems->first; ml; ml = ml->next) {
484                                         if (ml->flag & SELECT) {
485                                                 calc_tw_center(scene, &ml->x);
486                                                 totsel++;
487                                         }
488                                 }
489                         }
490                 }
491                 else if (obedit->type == OB_LATTICE) {
492                         Lattice *lt = ((Lattice *)obedit->data)->editlatt->latt;
493                         BPoint *bp;
494
495                         if ((v3d->around == V3D_AROUND_ACTIVE) && (bp = BKE_lattice_active_point_get(lt))) {
496                                 calc_tw_center(scene, bp->vec);
497                                 totsel++;
498                         }
499                         else {
500                                 bp = lt->def;
501                                 a = lt->pntsu * lt->pntsv * lt->pntsw;
502                                 while (a--) {
503                                         if (bp->f1 & SELECT) {
504                                                 calc_tw_center(scene, bp->vec);
505                                                 totsel++;
506                                         }
507                                         bp++;
508                                 }
509                         }
510                 }
511
512                 /* selection center */
513                 if (totsel) {
514                         mul_v3_fl(scene->twcent, 1.0f / (float)totsel);   // centroid!
515                         mul_m4_v3(obedit->obmat, scene->twcent);
516                         mul_m4_v3(obedit->obmat, scene->twmin);
517                         mul_m4_v3(obedit->obmat, scene->twmax);
518                 }
519         }
520         else if (ob && (ob->mode & OB_MODE_POSE)) {
521                 bPoseChannel *pchan;
522                 int mode = TFM_ROTATION; // mislead counting bones... bah. We don't know the manipulator mode, could be mixed
523                 bool ok = false;
524
525                 if ((ob->lay & v3d->lay) == 0) return 0;
526
527                 if ((v3d->around == V3D_AROUND_ACTIVE) && (pchan = BKE_pose_channel_active(ob))) {
528                         /* doesn't check selection or visibility intentionally */
529                         Bone *bone = pchan->bone;
530                         if (bone) {
531                                 stats_pose(scene, rv3d, pchan);
532                                 totsel = 1;
533                                 ok = true;
534                         }
535                 }
536                 else {
537                         totsel = count_set_pose_transflags(&mode, 0, ob);
538
539                         if (totsel) {
540                                 /* use channels to get stats */
541                                 for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
542                                         Bone *bone = pchan->bone;
543                                         if (bone && (bone->flag & BONE_TRANSFORM)) {
544                                                 stats_pose(scene, rv3d, pchan);
545                                         }
546                                 }
547                                 ok = true;
548                         }
549                 }
550
551                 if (ok) {
552                         mul_v3_fl(scene->twcent, 1.0f / (float)totsel);   // centroid!
553                         mul_m4_v3(ob->obmat, scene->twcent);
554                         mul_m4_v3(ob->obmat, scene->twmin);
555                         mul_m4_v3(ob->obmat, scene->twmax);
556                 }
557         }
558         else if (ob && (ob->mode & OB_MODE_ALL_PAINT)) {
559                 /* pass */
560         }
561         else {
562
563                 /* we need the one selected object, if its not active */
564                 ob = OBACT;
565                 if (ob && !(ob->flag & SELECT)) ob = NULL;
566
567                 for (base = scene->base.first; base; base = base->next) {
568                         if (TESTBASELIB(v3d, base)) {
569                                 if (ob == NULL)
570                                         ob = base->object;
571                                 calc_tw_center(scene, base->object->obmat[3]);
572                                 protectflag_to_drawflags(base->object->protectflag, &rv3d->twdrawflag);
573                                 totsel++;
574                         }
575                 }
576
577                 /* selection center */
578                 if (totsel) {
579                         mul_v3_fl(scene->twcent, 1.0f / (float)totsel);   // centroid!
580                 }
581         }
582
583         /* global, local or normal orientation? */
584         if (ob && totsel && !is_gp_edit) {
585
586                 switch (v3d->twmode) {
587                 
588                         case V3D_MANIP_GLOBAL:
589                         {
590                                 break; /* nothing to do */
591                         }
592                         case V3D_MANIP_GIMBAL:
593                         {
594                                 float mat[3][3];
595                                 if (gimbal_axis(ob, mat)) {
596                                         copy_m4_m3(rv3d->twmat, mat);
597                                         break;
598                                 }
599                                 /* if not gimbal, fall through to normal */
600                                 /* fall-through */
601                         }
602                         case V3D_MANIP_NORMAL:
603                         {
604                                 if (obedit || ob->mode & OB_MODE_POSE) {
605                                         float mat[3][3];
606                                         ED_getTransformOrientationMatrix(C, mat, v3d->around);
607                                         copy_m4_m3(rv3d->twmat, mat);
608                                         break;
609                                 }
610                                 /* no break we define 'normal' as 'local' in Object mode */
611                                 /* fall-through */
612                         }
613                         case V3D_MANIP_LOCAL:
614                         {
615                                 if (ob->mode & OB_MODE_POSE) {
616                                         /* each bone moves on its own local axis, but  to avoid confusion,
617                                          * use the active pones axis for display [#33575], this works as expected on a single bone
618                                          * and users who select many bones will understand whats going on and what local means
619                                          * when they start transforming */
620                                         float mat[3][3];
621                                         ED_getTransformOrientationMatrix(C, mat, v3d->around);
622                                         copy_m4_m3(rv3d->twmat, mat);
623                                         break;
624                                 }
625                                 copy_m4_m4(rv3d->twmat, ob->obmat);
626                                 normalize_m4(rv3d->twmat);
627                                 break;
628                         }
629                         case V3D_MANIP_VIEW:
630                         {
631                                 float mat[3][3];
632                                 copy_m3_m4(mat, rv3d->viewinv);
633                                 normalize_m3(mat);
634                                 copy_m4_m3(rv3d->twmat, mat);
635                                 break;
636                         }
637                         default: /* V3D_MANIP_CUSTOM */
638                         {
639                                 float mat[3][3];
640                                 if (applyTransformOrientation(C, mat, NULL, v3d->twmode - V3D_MANIP_CUSTOM)) {
641                                         copy_m4_m3(rv3d->twmat, mat);
642                                 }
643                                 break;
644                         }
645                 }
646
647         }
648
649         return totsel;
650 }
651
652 /* don't draw axis perpendicular to the view */
653 static void test_manipulator_axis(const bContext *C)
654 {
655         RegionView3D *rv3d = CTX_wm_region_view3d(C);
656         float view_vec[3], axis_vec[3];
657         float idot;
658         int i;
659
660         const int twdrawflag_axis[3] = {
661             (MAN_TRANS_X | MAN_SCALE_X),
662             (MAN_TRANS_Y | MAN_SCALE_Y),
663             (MAN_TRANS_Z | MAN_SCALE_Z)};
664
665         ED_view3d_global_to_vector(rv3d, rv3d->twmat[3], view_vec);
666
667         for (i = 0; i < 3; i++) {
668                 normalize_v3_v3(axis_vec, rv3d->twmat[i]);
669                 rv3d->tw_idot[i] = idot = 1.0f - fabsf(dot_v3v3(view_vec, axis_vec));
670                 if (idot < TW_AXIS_DOT_MIN) {
671                         rv3d->twdrawflag &= ~twdrawflag_axis[i];
672                 }
673         }
674 }
675
676
677 /* ******************** DRAWING STUFFIES *********** */
678
679 static float screen_aligned(RegionView3D *rv3d, float mat[4][4])
680 {
681         glTranslate3fv(mat[3]);
682
683         /* sets view screen aligned */
684         glRotatef(-360.0f * saacos(rv3d->viewquat[0]) / (float)M_PI, rv3d->viewquat[1], rv3d->viewquat[2], rv3d->viewquat[3]);
685
686         return len_v3(mat[0]); /* draw scale */
687 }
688
689
690 /* radring = radius of doughnut rings
691  * radhole = radius hole
692  * start = starting segment (based on nrings)
693  * end   = end segment
694  * nsides = amount of points in ring
695  * nrigns = amount of rings
696  */
697 static void partial_doughnut(float radring, float radhole, int start, int end, int nsides, int nrings)
698 {
699         float theta, phi, theta1;
700         float cos_theta, sin_theta;
701         float cos_theta1, sin_theta1;
702         float ring_delta, side_delta;
703         int i, j, do_caps = true;
704
705         if (start == 0 && end == nrings) do_caps = false;
706
707         ring_delta = 2.0f * (float)M_PI / (float)nrings;
708         side_delta = 2.0f * (float)M_PI / (float)nsides;
709
710         theta = (float)M_PI + 0.5f * ring_delta;
711         cos_theta = cosf(theta);
712         sin_theta = sinf(theta);
713
714         for (i = nrings - 1; i >= 0; i--) {
715                 theta1 = theta + ring_delta;
716                 cos_theta1 = cosf(theta1);
717                 sin_theta1 = sinf(theta1);
718
719                 if (do_caps && i == start) {  // cap
720                         glBegin(GL_POLYGON);
721                         phi = 0.0;
722                         for (j = nsides; j >= 0; j--) {
723                                 float cos_phi, sin_phi, dist;
724
725                                 phi += side_delta;
726                                 cos_phi = cosf(phi);
727                                 sin_phi = sinf(phi);
728                                 dist = radhole + radring * cos_phi;
729
730                                 glVertex3f(cos_theta1 * dist, -sin_theta1 * dist,  radring * sin_phi);
731                         }
732                         glEnd();
733                 }
734                 if (i >= start && i <= end) {
735                         glBegin(GL_QUAD_STRIP);
736                         phi = 0.0;
737                         for (j = nsides; j >= 0; j--) {
738                                 float cos_phi, sin_phi, dist;
739
740                                 phi += side_delta;
741                                 cos_phi = cosf(phi);
742                                 sin_phi = sinf(phi);
743                                 dist = radhole + radring * cos_phi;
744
745                                 glVertex3f(cos_theta1 * dist, -sin_theta1 * dist, radring * sin_phi);
746                                 glVertex3f(cos_theta * dist, -sin_theta * dist,  radring * sin_phi);
747                         }
748                         glEnd();
749                 }
750
751                 if (do_caps && i == end) {    // cap
752                         glBegin(GL_POLYGON);
753                         phi = 0.0;
754                         for (j = nsides; j >= 0; j--) {
755                                 float cos_phi, sin_phi, dist;
756
757                                 phi -= side_delta;
758                                 cos_phi = cosf(phi);
759                                 sin_phi = sinf(phi);
760                                 dist = radhole + radring * cos_phi;
761
762                                 glVertex3f(cos_theta * dist, -sin_theta * dist,  radring * sin_phi);
763                         }
764                         glEnd();
765                 }
766
767
768                 theta = theta1;
769                 cos_theta = cos_theta1;
770                 sin_theta = sin_theta1;
771         }
772 }
773
774 static char axisBlendAngle(float idot)
775 {
776         if (idot > TW_AXIS_DOT_MAX) {
777                 return 255;
778         }
779         else if (idot < TW_AXIS_DOT_MIN) {
780                 return 0;
781         }
782         else {
783                 return (char)(255.0f * (idot - TW_AXIS_DOT_MIN) / (TW_AXIS_DOT_MAX - TW_AXIS_DOT_MIN));
784         }
785 }
786
787 /* three colors can be set:
788  * gray for ghosting
789  * moving: in transform theme color
790  * else the red/green/blue
791  */
792 static void manipulator_setcolor(View3D *v3d, char axis, int colcode, unsigned char alpha)
793 {
794         unsigned char col[4] = {0};
795         col[3] = alpha;
796
797         if (colcode == MAN_GHOST) {
798                 col[3] = 70;
799         }
800         else if (colcode == MAN_MOVECOL) {
801                 UI_GetThemeColor3ubv(TH_TRANSFORM, col);
802         }
803         else {
804                 switch (axis) {
805                         case 'C':
806                                 UI_GetThemeColor3ubv(TH_TRANSFORM, col);
807                                 if (v3d->twmode == V3D_MANIP_LOCAL) {
808                                         col[0] = col[0] > 200 ? 255 : col[0] + 55;
809                                         col[1] = col[1] > 200 ? 255 : col[1] + 55;
810                                         col[2] = col[2] > 200 ? 255 : col[2] + 55;
811                                 }
812                                 else if (v3d->twmode == V3D_MANIP_NORMAL) {
813                                         col[0] = col[0] < 55 ? 0 : col[0] - 55;
814                                         col[1] = col[1] < 55 ? 0 : col[1] - 55;
815                                         col[2] = col[2] < 55 ? 0 : col[2] - 55;
816                                 }
817                                 break;
818                         case 'X':
819                                 UI_GetThemeColor3ubv(TH_AXIS_X, col);
820                                 break;
821                         case 'Y':
822                                 UI_GetThemeColor3ubv(TH_AXIS_Y, col);
823                                 break;
824                         case 'Z':
825                                 UI_GetThemeColor3ubv(TH_AXIS_Z, col);
826                                 break;
827                         default:
828                                 BLI_assert(0);
829                                 break;
830                 }
831         }
832
833         glColor4ubv(col);
834 }
835
836 static void manipulator_axis_order(RegionView3D *rv3d, int r_axis_order[3])
837 {
838         float axis_values[3];
839         float vec[3];
840
841         ED_view3d_global_to_vector(rv3d, rv3d->twmat[3], vec);
842
843         axis_values[0] = -dot_v3v3(rv3d->twmat[0], vec);
844         axis_values[1] = -dot_v3v3(rv3d->twmat[1], vec);
845         axis_values[2] = -dot_v3v3(rv3d->twmat[2], vec);
846
847         axis_sort_v3(axis_values, r_axis_order);
848 }
849
850 /* viewmatrix should have been set OK, also no shademode! */
851 static void draw_manipulator_axes_single(View3D *v3d, RegionView3D *rv3d, int colcode,
852                                          int flagx, int flagy, int flagz, int axis,
853                                          const bool is_picksel)
854 {
855         switch (axis) {
856                 case 0:
857                         /* axes */
858                         if (flagx) {
859                                 if (is_picksel) {
860                                         if      (flagx & MAN_SCALE_X) GPU_select_load_id(MAN_SCALE_X);
861                                         else if (flagx & MAN_TRANS_X) GPU_select_load_id(MAN_TRANS_X);
862                                 }
863                                 else {
864                                         manipulator_setcolor(v3d, 'X', colcode, axisBlendAngle(rv3d->tw_idot[0]));
865                                 }
866                                 glBegin(GL_LINES);
867                                 glVertex3f(0.2f, 0.0f, 0.0f);
868                                 glVertex3f(1.0f, 0.0f, 0.0f);
869                                 glEnd();
870                         }
871                         break;
872                 case 1:
873                         if (flagy) {
874                                 if (is_picksel) {
875                                         if      (flagy & MAN_SCALE_Y) GPU_select_load_id(MAN_SCALE_Y);
876                                         else if (flagy & MAN_TRANS_Y) GPU_select_load_id(MAN_TRANS_Y);
877                                 }
878                                 else {
879                                         manipulator_setcolor(v3d, 'Y', colcode, axisBlendAngle(rv3d->tw_idot[1]));
880                                 }
881                                 glBegin(GL_LINES);
882                                 glVertex3f(0.0f, 0.2f, 0.0f);
883                                 glVertex3f(0.0f, 1.0f, 0.0f);
884                                 glEnd();
885                         }
886                         break;
887                 case 2:
888                         if (flagz) {
889                                 if (is_picksel) {
890                                         if      (flagz & MAN_SCALE_Z) GPU_select_load_id(MAN_SCALE_Z);
891                                         else if (flagz & MAN_TRANS_Z) GPU_select_load_id(MAN_TRANS_Z);
892                                 }
893                                 else {
894                                         manipulator_setcolor(v3d, 'Z', colcode, axisBlendAngle(rv3d->tw_idot[2]));
895                                 }
896                                 glBegin(GL_LINES);
897                                 glVertex3f(0.0f, 0.0f, 0.2f);
898                                 glVertex3f(0.0f, 0.0f, 1.0f);
899                                 glEnd();
900                         }
901                         break;
902         }
903 }
904 static void draw_manipulator_axes(View3D *v3d, RegionView3D *rv3d, int colcode,
905                                   int flagx, int flagy, int flagz,
906                                   const int axis_order[3], const bool is_picksel)
907 {
908         int i;
909         for (i = 0; i < 3; i++) {
910                 draw_manipulator_axes_single(v3d, rv3d, colcode, flagx, flagy, flagz, axis_order[i], is_picksel);
911         }
912 }
913
914 static void preOrthoFront(const bool ortho, float twmat[4][4], int axis)
915 {
916         if (ortho == false) {
917                 float omat[4][4];
918                 copy_m4_m4(omat, twmat);
919                 orthogonalize_m4(omat, axis);
920                 glPushMatrix();
921                 glMultMatrixf(omat);
922                 glFrontFace(is_negative_m4(omat) ? GL_CW : GL_CCW);
923         }
924 }
925
926 static void postOrtho(const bool ortho)
927 {
928         if (ortho == false) {
929                 glPopMatrix();
930         }
931 }
932
933 BLI_INLINE bool manipulator_rotate_is_visible(const int drawflags)
934 {
935         return (drawflags & (MAN_ROT_X | MAN_ROT_Y | MAN_ROT_Z));
936 }
937
938 static void draw_manipulator_rotate(
939         View3D *v3d, RegionView3D *rv3d, const int drawflags, const int combo,
940         const bool is_moving, const bool is_picksel)
941 {
942         double plane[4];
943         float matt[4][4];
944         float size, unitmat[4][4];
945         float cywid = 0.33f * 0.01f * (float)U.tw_handlesize;
946         float cusize = cywid * 0.65f;
947         int arcs = (G.debug_value != 2);
948         const int colcode = (is_moving) ? MAN_MOVECOL : MAN_RGB;
949         bool ortho;
950
951         /* skip drawing if all axes are locked */
952         if (manipulator_rotate_is_visible(drawflags) == false) return;
953
954         /* Init stuff */
955         glDisable(GL_DEPTH_TEST);
956         unit_m4(unitmat);
957
958         /* prepare for screen aligned draw */
959         size = len_v3(rv3d->twmat[0]);
960         glPushMatrix();
961         glTranslate3fv(rv3d->twmat[3]);
962
963         if (arcs) {
964                 /* clipplane makes nice handles, calc here because of multmatrix but with translate! */
965                 copy_v3db_v3fl(plane, rv3d->viewinv[2]);
966                 plane[3] = -0.02f * size; // clip just a bit more
967                 glClipPlane(GL_CLIP_PLANE0, plane);
968         }
969         /* sets view screen aligned */
970         glRotatef(-360.0f * saacos(rv3d->viewquat[0]) / (float)M_PI, rv3d->viewquat[1], rv3d->viewquat[2], rv3d->viewquat[3]);
971
972         /* Screen aligned help circle */
973         if (arcs) {
974                 if (is_picksel == false) {
975                         UI_ThemeColorShade(TH_BACK, -30);
976                         drawcircball(GL_LINE_LOOP, unitmat[3], size, unitmat);
977                 }
978         }
979
980         /* Screen aligned trackball rot circle */
981         if (drawflags & MAN_ROT_T) {
982                 if (is_picksel) GPU_select_load_id(MAN_ROT_T);
983                 else UI_ThemeColor(TH_TRANSFORM);
984
985                 drawcircball(GL_LINE_LOOP, unitmat[3], 0.2f * size, unitmat);
986         }
987
988         /* Screen aligned view rot circle */
989         if (drawflags & MAN_ROT_V) {
990                 if (is_picksel) GPU_select_load_id(MAN_ROT_V);
991                 else UI_ThemeColor(TH_TRANSFORM);
992                 drawcircball(GL_LINE_LOOP, unitmat[3], 1.2f * size, unitmat);
993
994                 if (is_moving) {
995                         float vec[3];
996                         vec[0] = 0; // XXX (float)(t->mouse.imval[0] - t->center2d[0]);
997                         vec[1] = 0; // XXX (float)(t->mouse.imval[1] - t->center2d[1]);
998                         vec[2] = 0.0f;
999                         normalize_v3_length(vec, 1.2f * size);
1000                         glBegin(GL_LINES);
1001                         glVertex3f(0.0f, 0.0f, 0.0f);
1002                         glVertex3fv(vec);
1003                         glEnd();
1004                 }
1005         }
1006         glPopMatrix();
1007
1008
1009         ortho = is_orthogonal_m4(rv3d->twmat);
1010         
1011         /* apply the transform delta */
1012         if (is_moving) {
1013                 copy_m4_m4(matt, rv3d->twmat); // to copy the parts outside of [3][3]
1014                 // XXX mul_m4_m3m4(matt, t->mat, rv3d->twmat);
1015                 if (ortho) {
1016                         glMultMatrixf(matt);
1017                         glFrontFace(is_negative_m4(matt) ? GL_CW : GL_CCW);
1018                 }
1019         }
1020         else {
1021                 if (ortho) {
1022                         glFrontFace(is_negative_m4(rv3d->twmat) ? GL_CW : GL_CCW);
1023                         glMultMatrixf(rv3d->twmat);
1024                 }
1025         }
1026
1027         /* axes */
1028         if (arcs == 0) {
1029                 if (!is_picksel) {
1030                         if ((combo & V3D_MANIP_SCALE) == 0) {
1031                                 /* axis */
1032                                 if ((drawflags & MAN_ROT_X) || (is_moving && (drawflags & MAN_ROT_Z))) {
1033                                         preOrthoFront(ortho, rv3d->twmat, 2);
1034                                         manipulator_setcolor(v3d, 'X', colcode, 255);
1035                                         glBegin(GL_LINES);
1036                                         glVertex3f(0.2f, 0.0f, 0.0f);
1037                                         glVertex3f(1.0f, 0.0f, 0.0f);
1038                                         glEnd();
1039                                         postOrtho(ortho);
1040                                 }
1041                                 if ((drawflags & MAN_ROT_Y) || (is_moving && (drawflags & MAN_ROT_X))) {
1042                                         preOrthoFront(ortho, rv3d->twmat, 0);
1043                                         manipulator_setcolor(v3d, 'Y', colcode, 255);
1044                                         glBegin(GL_LINES);
1045                                         glVertex3f(0.0f, 0.2f, 0.0f);
1046                                         glVertex3f(0.0f, 1.0f, 0.0f);
1047                                         glEnd();
1048                                         postOrtho(ortho);
1049                                 }
1050                                 if ((drawflags & MAN_ROT_Z) || (is_moving && (drawflags & MAN_ROT_Y))) {
1051                                         preOrthoFront(ortho, rv3d->twmat, 1);
1052                                         manipulator_setcolor(v3d, 'Z', colcode, 255);
1053                                         glBegin(GL_LINES);
1054                                         glVertex3f(0.0f, 0.0f, 0.2f);
1055                                         glVertex3f(0.0f, 0.0f, 1.0f);
1056                                         glEnd();
1057                                         postOrtho(ortho);
1058                                 }
1059                         }
1060                 }
1061         }
1062
1063         if (arcs == 0 && is_moving) {
1064
1065                 /* Z circle */
1066                 if (drawflags & MAN_ROT_Z) {
1067                         preOrthoFront(ortho, matt, 2);
1068                         if (is_picksel) GPU_select_load_id(MAN_ROT_Z);
1069                         else manipulator_setcolor(v3d, 'Z', colcode, 255);
1070                         drawcircball(GL_LINE_LOOP, unitmat[3], 1.0, unitmat);
1071                         postOrtho(ortho);
1072                 }
1073                 /* X circle */
1074                 if (drawflags & MAN_ROT_X) {
1075                         preOrthoFront(ortho, matt, 0);
1076                         if (is_picksel) GPU_select_load_id(MAN_ROT_X);
1077                         else manipulator_setcolor(v3d, 'X', colcode, 255);
1078                         glRotatef(90.0, 0.0, 1.0, 0.0);
1079                         drawcircball(GL_LINE_LOOP, unitmat[3], 1.0, unitmat);
1080                         glRotatef(-90.0, 0.0, 1.0, 0.0);
1081                         postOrtho(ortho);
1082                 }
1083                 /* Y circle */
1084                 if (drawflags & MAN_ROT_Y) {
1085                         preOrthoFront(ortho, matt, 1);
1086                         if (is_picksel) GPU_select_load_id(MAN_ROT_Y);
1087                         else manipulator_setcolor(v3d, 'Y', colcode, 255);
1088                         glRotatef(-90.0, 1.0, 0.0, 0.0);
1089                         drawcircball(GL_LINE_LOOP, unitmat[3], 1.0, unitmat);
1090                         glRotatef(90.0, 1.0, 0.0, 0.0);
1091                         postOrtho(ortho);
1092                 }
1093         }
1094         // donut arcs
1095         if (arcs) {
1096                 glEnable(GL_CLIP_PLANE0);
1097
1098                 /* Z circle */
1099                 if (drawflags & MAN_ROT_Z) {
1100                         preOrthoFront(ortho, rv3d->twmat, 2);
1101                         if (is_picksel) GPU_select_load_id(MAN_ROT_Z);
1102                         else manipulator_setcolor(v3d, 'Z', colcode, 255);
1103                         partial_doughnut(cusize / 4.0f, 1.0f, 0, 48, 8, 48);
1104                         postOrtho(ortho);
1105                 }
1106                 /* X circle */
1107                 if (drawflags & MAN_ROT_X) {
1108                         preOrthoFront(ortho, rv3d->twmat, 0);
1109                         if (is_picksel) GPU_select_load_id(MAN_ROT_X);
1110                         else manipulator_setcolor(v3d, 'X', colcode, 255);
1111                         glRotatef(90.0, 0.0, 1.0, 0.0);
1112                         partial_doughnut(cusize / 4.0f, 1.0f, 0, 48, 8, 48);
1113                         glRotatef(-90.0, 0.0, 1.0, 0.0);
1114                         postOrtho(ortho);
1115                 }
1116                 /* Y circle */
1117                 if (drawflags & MAN_ROT_Y) {
1118                         preOrthoFront(ortho, rv3d->twmat, 1);
1119                         if (is_picksel) GPU_select_load_id(MAN_ROT_Y);
1120                         else manipulator_setcolor(v3d, 'Y', colcode, 255);
1121                         glRotatef(-90.0, 1.0, 0.0, 0.0);
1122                         partial_doughnut(cusize / 4.0f, 1.0f, 0, 48, 8, 48);
1123                         glRotatef(90.0, 1.0, 0.0, 0.0);
1124                         postOrtho(ortho);
1125                 }
1126
1127                 glDisable(GL_CLIP_PLANE0);
1128         }
1129
1130         if (arcs == 0) {
1131
1132                 /* Z handle on X axis */
1133                 if (drawflags & MAN_ROT_Z) {
1134                         preOrthoFront(ortho, rv3d->twmat, 2);
1135                         glPushMatrix();
1136                         if (is_picksel) GPU_select_load_id(MAN_ROT_Z);
1137                         else manipulator_setcolor(v3d, 'Z', colcode, 255);
1138
1139                         partial_doughnut(0.7f * cusize, 1.0f, 31, 33, 8, 64);
1140
1141                         glPopMatrix();
1142                         postOrtho(ortho);
1143                 }
1144
1145                 /* Y handle on X axis */
1146                 if (drawflags & MAN_ROT_Y) {
1147                         preOrthoFront(ortho, rv3d->twmat, 1);
1148                         glPushMatrix();
1149                         if (is_picksel) GPU_select_load_id(MAN_ROT_Y);
1150                         else manipulator_setcolor(v3d, 'Y', colcode, 255);
1151
1152                         glRotatef(90.0, 1.0, 0.0, 0.0);
1153                         glRotatef(90.0, 0.0, 0.0, 1.0);
1154                         partial_doughnut(0.7f * cusize, 1.0f, 31, 33, 8, 64);
1155
1156                         glPopMatrix();
1157                         postOrtho(ortho);
1158                 }
1159
1160                 /* X handle on Z axis */
1161                 if (drawflags & MAN_ROT_X) {
1162                         preOrthoFront(ortho, rv3d->twmat, 0);
1163                         glPushMatrix();
1164                         if (is_picksel) GPU_select_load_id(MAN_ROT_X);
1165                         else manipulator_setcolor(v3d, 'X', colcode, 255);
1166
1167                         glRotatef(-90.0, 0.0, 1.0, 0.0);
1168                         glRotatef(90.0, 0.0, 0.0, 1.0);
1169                         partial_doughnut(0.7f * cusize, 1.0f, 31, 33, 8, 64);
1170
1171                         glPopMatrix();
1172                         postOrtho(ortho);
1173                 }
1174
1175         }
1176
1177         /* restore */
1178         glLoadMatrixf(rv3d->viewmat);
1179         if (v3d->zbuf) glEnable(GL_DEPTH_TEST);
1180
1181 }
1182
1183 static void drawsolidcube(float size)
1184 {
1185         const float cube[8][3] = {
1186                 {-1.0, -1.0, -1.0},
1187                 {-1.0, -1.0,  1.0},
1188                 {-1.0,  1.0,  1.0},
1189                 {-1.0,  1.0, -1.0},
1190                 { 1.0, -1.0, -1.0},
1191                 { 1.0, -1.0,  1.0},
1192                 { 1.0,  1.0,  1.0},
1193                 { 1.0,  1.0, -1.0},
1194         };
1195         float n[3] = {0.0f};
1196
1197         glPushMatrix();
1198         glScalef(size, size, size);
1199
1200         glBegin(GL_QUADS);
1201         n[0] = -1.0;
1202         glNormal3fv(n);
1203         glVertex3fv(cube[0]); glVertex3fv(cube[1]); glVertex3fv(cube[2]); glVertex3fv(cube[3]);
1204         n[0] = 0;
1205         glEnd();
1206
1207         glBegin(GL_QUADS);
1208         n[1] = -1.0;
1209         glNormal3fv(n);
1210         glVertex3fv(cube[0]); glVertex3fv(cube[4]); glVertex3fv(cube[5]); glVertex3fv(cube[1]);
1211         n[1] = 0;
1212         glEnd();
1213
1214         glBegin(GL_QUADS);
1215         n[0] = 1.0;
1216         glNormal3fv(n);
1217         glVertex3fv(cube[4]); glVertex3fv(cube[7]); glVertex3fv(cube[6]); glVertex3fv(cube[5]);
1218         n[0] = 0;
1219         glEnd();
1220
1221         glBegin(GL_QUADS);
1222         n[1] = 1.0;
1223         glNormal3fv(n);
1224         glVertex3fv(cube[7]); glVertex3fv(cube[3]); glVertex3fv(cube[2]); glVertex3fv(cube[6]);
1225         n[1] = 0;
1226         glEnd();
1227
1228         glBegin(GL_QUADS);
1229         n[2] = 1.0;
1230         glNormal3fv(n);
1231         glVertex3fv(cube[1]); glVertex3fv(cube[5]); glVertex3fv(cube[6]); glVertex3fv(cube[2]);
1232         n[2] = 0;
1233         glEnd();
1234
1235         glBegin(GL_QUADS);
1236         n[2] = -1.0;
1237         glNormal3fv(n);
1238         glVertex3fv(cube[7]); glVertex3fv(cube[4]); glVertex3fv(cube[0]); glVertex3fv(cube[3]);
1239         glEnd();
1240
1241         glPopMatrix();
1242 }
1243
1244
1245 static void draw_manipulator_scale(
1246         View3D *v3d, RegionView3D *rv3d, const int drawflags, const int combo, const int colcode,
1247         const bool is_moving, const bool is_picksel)
1248 {
1249         float cywid = 0.25f * 0.01f * (float)U.tw_handlesize;
1250         float cusize = cywid * 0.75f, dz;
1251         int axis_order[3] = {2, 0, 1};
1252         int i;
1253
1254         /* when called while moving in mixed mode, do not draw when... */
1255         if ((drawflags & MAN_SCALE_C) == 0) return;
1256
1257         manipulator_axis_order(rv3d, axis_order);
1258
1259         glDisable(GL_DEPTH_TEST);
1260
1261         /* not in combo mode */
1262         if ((combo & (V3D_MANIP_TRANSLATE | V3D_MANIP_ROTATE)) == 0) {
1263                 float size, unitmat[4][4];
1264                 int shift = 0; // XXX
1265
1266                 /* center circle, do not add to selection when shift is pressed (planar constraint)  */
1267                 if (is_picksel && shift == 0) GPU_select_load_id(MAN_SCALE_C);
1268                 else manipulator_setcolor(v3d, 'C', colcode, 255);
1269
1270                 glPushMatrix();
1271                 size = screen_aligned(rv3d, rv3d->twmat);
1272                 unit_m4(unitmat);
1273                 drawcircball(GL_LINE_LOOP, unitmat[3], 0.2f * size, unitmat);
1274                 glPopMatrix();
1275
1276                 dz = 1.0;
1277         }
1278         else {
1279                 dz = 1.0f - 4.0f * cusize;
1280         }
1281
1282         if (is_moving) {
1283                 float matt[4][4];
1284
1285                 copy_m4_m4(matt, rv3d->twmat); // to copy the parts outside of [3][3]
1286                 // XXX mul_m4_m3m4(matt, t->mat, rv3d->twmat);
1287                 glMultMatrixf(matt);
1288                 glFrontFace(is_negative_m4(matt) ? GL_CW : GL_CCW);
1289         }
1290         else {
1291                 glMultMatrixf(rv3d->twmat);
1292                 glFrontFace(is_negative_m4(rv3d->twmat) ? GL_CW : GL_CCW);
1293         }
1294
1295         /* axis */
1296
1297         /* in combo mode, this is always drawn as first type */
1298         draw_manipulator_axes(v3d, rv3d, colcode,
1299                               drawflags & MAN_SCALE_X, drawflags & MAN_SCALE_Y, drawflags & MAN_SCALE_Z,
1300                               axis_order, is_picksel);
1301
1302
1303         for (i = 0; i < 3; i++) {
1304                 switch (axis_order[i]) {
1305                         case 0: /* X cube */
1306                                 if (drawflags & MAN_SCALE_X) {
1307                                         glTranslatef(dz, 0.0, 0.0);
1308                                         if (is_picksel) GPU_select_load_id(MAN_SCALE_X);
1309                                         else manipulator_setcolor(v3d, 'X', colcode, axisBlendAngle(rv3d->tw_idot[0]));
1310                                         drawsolidcube(cusize);
1311                                         glTranslatef(-dz, 0.0, 0.0);
1312                                 }
1313                                 break;
1314                         case 1: /* Y cube */
1315                                 if (drawflags & MAN_SCALE_Y) {
1316                                         glTranslatef(0.0, dz, 0.0);
1317                                         if (is_picksel) GPU_select_load_id(MAN_SCALE_Y);
1318                                         else manipulator_setcolor(v3d, 'Y', colcode, axisBlendAngle(rv3d->tw_idot[1]));
1319                                         drawsolidcube(cusize);
1320                                         glTranslatef(0.0, -dz, 0.0);
1321                                 }
1322                                 break;
1323                         case 2: /* Z cube */
1324                                 if (drawflags & MAN_SCALE_Z) {
1325                                         glTranslatef(0.0, 0.0, dz);
1326                                         if (is_picksel) GPU_select_load_id(MAN_SCALE_Z);
1327                                         else manipulator_setcolor(v3d, 'Z', colcode, axisBlendAngle(rv3d->tw_idot[2]));
1328                                         drawsolidcube(cusize);
1329                                         glTranslatef(0.0, 0.0, -dz);
1330                                 }
1331                                 break;
1332                 }
1333         }
1334
1335 #if 0 // XXX
1336         /* if shiftkey, center point as last, for selectbuffer order */
1337         if (is_picksel) {
1338                 int shift = 0; // XXX
1339
1340                 if (shift) {
1341                         glTranslatef(0.0, -dz, 0.0);
1342                         GPU_select_load_id(MAN_SCALE_C);
1343                         /* TODO: set glPointSize before drawing center point */
1344                         glBegin(GL_POINTS);
1345                         glVertex3f(0.0, 0.0, 0.0);
1346                         glEnd();
1347                 }
1348         }
1349 #endif
1350
1351         /* restore */
1352         glLoadMatrixf(rv3d->viewmat);
1353
1354         if (v3d->zbuf) glEnable(GL_DEPTH_TEST);
1355         glFrontFace(GL_CCW);
1356 }
1357
1358
1359 static void draw_cone(GLUquadricObj *qobj, float len, float width)
1360 {
1361         glTranslatef(0.0, 0.0, -0.5f * len);
1362         gluCylinder(qobj, width, 0.0, len, 8, 1);
1363         gluQuadricOrientation(qobj, GLU_INSIDE);
1364         gluDisk(qobj, 0.0, width, 8, 1);
1365         gluQuadricOrientation(qobj, GLU_OUTSIDE);
1366         glTranslatef(0.0, 0.0, 0.5f * len);
1367 }
1368
1369 static void draw_cylinder(GLUquadricObj *qobj, float len, float width)
1370 {
1371
1372         width *= 0.8f;   // just for beauty
1373
1374         glTranslatef(0.0, 0.0, -0.5f * len);
1375         gluCylinder(qobj, width, width, len, 8, 1);
1376         gluQuadricOrientation(qobj, GLU_INSIDE);
1377         gluDisk(qobj, 0.0, width, 8, 1);
1378         gluQuadricOrientation(qobj, GLU_OUTSIDE);
1379         glTranslatef(0.0, 0.0, len);
1380         gluDisk(qobj, 0.0, width, 8, 1);
1381         glTranslatef(0.0, 0.0, -0.5f * len);
1382 }
1383
1384
1385 static void draw_manipulator_translate(
1386         View3D *v3d, RegionView3D *rv3d, int drawflags, int combo, int colcode,
1387         const bool UNUSED(is_moving), const bool is_picksel)
1388 {
1389         GLUquadricObj *qobj;
1390         float cylen = 0.01f * (float)U.tw_handlesize;
1391         float cywid = 0.25f * cylen, dz, size;
1392         float unitmat[4][4];
1393         int shift = 0; // XXX
1394         int axis_order[3] = {0, 1, 2};
1395         int i;
1396
1397         /* when called while moving in mixed mode, do not draw when... */
1398         if ((drawflags & MAN_TRANS_C) == 0) return;
1399
1400         manipulator_axis_order(rv3d, axis_order);
1401
1402         // XXX if (moving) glTranslate3fv(t->vec);
1403         glDisable(GL_DEPTH_TEST);
1404
1405         /* center circle, do not add to selection when shift is pressed (planar constraint) */
1406         if (is_picksel && shift == 0) GPU_select_load_id(MAN_TRANS_C);
1407         else manipulator_setcolor(v3d, 'C', colcode, 255);
1408
1409         glPushMatrix();
1410         size = screen_aligned(rv3d, rv3d->twmat);
1411         unit_m4(unitmat);
1412         drawcircball(GL_LINE_LOOP, unitmat[3], 0.2f * size, unitmat);
1413         glPopMatrix();
1414
1415         /* and now apply matrix, we move to local matrix drawing */
1416         glMultMatrixf(rv3d->twmat);
1417
1418         /* axis */
1419         GPU_select_load_id(-1);
1420
1421         // translate drawn as last, only axis when no combo with scale, or for ghosting
1422         if ((combo & V3D_MANIP_SCALE) == 0 || colcode == MAN_GHOST) {
1423                 draw_manipulator_axes(v3d, rv3d, colcode,
1424                                       drawflags & MAN_TRANS_X, drawflags & MAN_TRANS_Y, drawflags & MAN_TRANS_Z,
1425                                       axis_order, is_picksel);
1426         }
1427
1428
1429         /* offset in combo mode, for rotate a bit more */
1430         if (combo & (V3D_MANIP_ROTATE)) dz = 1.0f + 2.0f * cylen;
1431         else if (combo & (V3D_MANIP_SCALE)) dz = 1.0f + 0.5f * cylen;
1432         else dz = 1.0f;
1433
1434         qobj = gluNewQuadric();
1435         gluQuadricDrawStyle(qobj, GLU_FILL);
1436
1437         for (i = 0; i < 3; i++) {
1438                 switch (axis_order[i]) {
1439                         case 0: /* Z Cone */
1440                                 if (drawflags & MAN_TRANS_Z) {
1441                                         glTranslatef(0.0, 0.0, dz);
1442                                         if (is_picksel) GPU_select_load_id(MAN_TRANS_Z);
1443                                         else manipulator_setcolor(v3d, 'Z', colcode, axisBlendAngle(rv3d->tw_idot[2]));
1444                                         draw_cone(qobj, cylen, cywid);
1445                                         glTranslatef(0.0, 0.0, -dz);
1446                                 }
1447                                 break;
1448                         case 1: /* X Cone */
1449                                 if (drawflags & MAN_TRANS_X) {
1450                                         glTranslatef(dz, 0.0, 0.0);
1451                                         if (is_picksel) GPU_select_load_id(MAN_TRANS_X);
1452                                         else manipulator_setcolor(v3d, 'X', colcode, axisBlendAngle(rv3d->tw_idot[0]));
1453                                         glRotatef(90.0, 0.0, 1.0, 0.0);
1454                                         draw_cone(qobj, cylen, cywid);
1455                                         glRotatef(-90.0, 0.0, 1.0, 0.0);
1456                                         glTranslatef(-dz, 0.0, 0.0);
1457                                 }
1458                                 break;
1459                         case 2: /* Y Cone */
1460                                 if (drawflags & MAN_TRANS_Y) {
1461                                         glTranslatef(0.0, dz, 0.0);
1462                                         if (is_picksel) GPU_select_load_id(MAN_TRANS_Y);
1463                                         else manipulator_setcolor(v3d, 'Y', colcode, axisBlendAngle(rv3d->tw_idot[1]));
1464                                         glRotatef(-90.0, 1.0, 0.0, 0.0);
1465                                         draw_cone(qobj, cylen, cywid);
1466                                         glRotatef(90.0, 1.0, 0.0, 0.0);
1467                                         glTranslatef(0.0, -dz, 0.0);
1468                                 }
1469                                 break;
1470                 }
1471         }
1472
1473         gluDeleteQuadric(qobj);
1474         glLoadMatrixf(rv3d->viewmat);
1475
1476         if (v3d->zbuf) glEnable(GL_DEPTH_TEST);
1477
1478 }
1479
1480 static void draw_manipulator_rotate_cyl(
1481         View3D *v3d, RegionView3D *rv3d, int drawflags, const int combo, const int colcode,
1482         const bool is_moving, const bool is_picksel)
1483 {
1484         GLUquadricObj *qobj;
1485         float size;
1486         float cylen = 0.01f * (float)U.tw_handlesize;
1487         float cywid = 0.25f * cylen;
1488         int axis_order[3] = {2, 0, 1};
1489         int i;
1490
1491         /* skip drawing if all axes are locked */
1492         if (manipulator_rotate_is_visible(drawflags) == false) return;
1493
1494         manipulator_axis_order(rv3d, axis_order);
1495
1496         /* prepare for screen aligned draw */
1497         glPushMatrix();
1498         size = screen_aligned(rv3d, rv3d->twmat);
1499
1500         glDisable(GL_DEPTH_TEST);
1501
1502         qobj = gluNewQuadric();
1503
1504         /* Screen aligned view rot circle */
1505         if (drawflags & MAN_ROT_V) {
1506                 float unitmat[4][4];
1507
1508                 unit_m4(unitmat);
1509
1510                 if (is_picksel) GPU_select_load_id(MAN_ROT_V);
1511                 UI_ThemeColor(TH_TRANSFORM);
1512                 drawcircball(GL_LINE_LOOP, unitmat[3], 1.2f * size, unitmat);
1513
1514                 if (is_moving) {
1515                         float vec[3];
1516                         vec[0] = 0; // XXX (float)(t->mouse.imval[0] - t->center2d[0]);
1517                         vec[1] = 0; // XXX (float)(t->mouse.imval[1] - t->center2d[1]);
1518                         vec[2] = 0.0f;
1519                         normalize_v3_length(vec, 1.2f * size);
1520                         glBegin(GL_LINES);
1521                         glVertex3f(0.0, 0.0, 0.0);
1522                         glVertex3fv(vec);
1523                         glEnd();
1524                 }
1525         }
1526         glPopMatrix();
1527
1528         /* apply the transform delta */
1529         if (is_moving) {
1530                 float matt[4][4];
1531                 copy_m4_m4(matt, rv3d->twmat); // to copy the parts outside of [3][3]
1532                 // XXX      if (t->flag & T_USES_MANIPULATOR) {
1533                 // XXX          mul_m4_m3m4(matt, t->mat, rv3d->twmat);
1534                 // XXX }
1535                 glMultMatrixf(matt);
1536         }
1537         else {
1538                 glMultMatrixf(rv3d->twmat);
1539         }
1540
1541         glFrontFace(is_negative_m4(rv3d->twmat) ? GL_CW : GL_CCW);
1542
1543         /* axis */
1544         if (is_picksel == false) {
1545
1546                 // only draw axis when combo didn't draw scale axes
1547                 if ((combo & V3D_MANIP_SCALE) == 0) {
1548                         draw_manipulator_axes(v3d, rv3d, colcode,
1549                                               drawflags & MAN_ROT_X, drawflags & MAN_ROT_Y, drawflags & MAN_ROT_Z,
1550                                               axis_order, is_picksel);
1551                 }
1552
1553                 /* only has to be set when not in picking */
1554                 gluQuadricDrawStyle(qobj, GLU_FILL);
1555         }
1556
1557         for (i = 0; i < 3; i++) {
1558                 switch (axis_order[i]) {
1559                         case 0: /* X cylinder */
1560                                 if (drawflags & MAN_ROT_X) {
1561                                         glTranslatef(1.0, 0.0, 0.0);
1562                                         if (is_picksel) GPU_select_load_id(MAN_ROT_X);
1563                                         glRotatef(90.0, 0.0, 1.0, 0.0);
1564                                         manipulator_setcolor(v3d, 'X', colcode, 255);
1565                                         draw_cylinder(qobj, cylen, cywid);
1566                                         glRotatef(-90.0, 0.0, 1.0, 0.0);
1567                                         glTranslatef(-1.0, 0.0, 0.0);
1568                                 }
1569                                 break;
1570                         case 1: /* Y cylinder */
1571                                 if (drawflags & MAN_ROT_Y) {
1572                                         glTranslatef(0.0, 1.0, 0.0);
1573                                         if (is_picksel) GPU_select_load_id(MAN_ROT_Y);
1574                                         glRotatef(-90.0, 1.0, 0.0, 0.0);
1575                                         manipulator_setcolor(v3d, 'Y', colcode, 255);
1576                                         draw_cylinder(qobj, cylen, cywid);
1577                                         glRotatef(90.0, 1.0, 0.0, 0.0);
1578                                         glTranslatef(0.0, -1.0, 0.0);
1579                                 }
1580                                 break;
1581                         case 2: /* Z cylinder */
1582                                 if (drawflags & MAN_ROT_Z) {
1583                                         glTranslatef(0.0, 0.0, 1.0);
1584                                         if (is_picksel) GPU_select_load_id(MAN_ROT_Z);
1585                                         manipulator_setcolor(v3d, 'Z', colcode, 255);
1586                                         draw_cylinder(qobj, cylen, cywid);
1587                                         glTranslatef(0.0, 0.0, -1.0);
1588                                 }
1589                                 break;
1590                 }
1591         }
1592
1593         /* restore */
1594
1595         gluDeleteQuadric(qobj);
1596         glLoadMatrixf(rv3d->viewmat);
1597
1598         if (v3d->zbuf) glEnable(GL_DEPTH_TEST);
1599
1600 }
1601
1602
1603 /* ********************************************* */
1604
1605 /* main call, does calc centers & orientation too */
1606 static int drawflags = 0xFFFF;       // only for the calls below, belongs in scene...?
1607
1608 void BIF_draw_manipulator(const bContext *C)
1609 {
1610         ScrArea *sa = CTX_wm_area(C);
1611         ARegion *ar = CTX_wm_region(C);
1612         Scene *scene = CTX_data_scene(C);
1613         View3D *v3d = sa->spacedata.first;
1614         RegionView3D *rv3d = ar->regiondata;
1615         int totsel;
1616
1617         const bool is_picksel = false;
1618
1619         if (!(v3d->twflag & V3D_USE_MANIPULATOR)) return;
1620
1621         if ((v3d->twtype & (V3D_MANIP_TRANSLATE | V3D_MANIP_ROTATE | V3D_MANIP_SCALE)) == 0) return;
1622
1623         {
1624                 v3d->twflag &= ~V3D_DRAW_MANIPULATOR;
1625
1626                 totsel = calc_manipulator_stats(C);
1627                 if (totsel == 0) return;
1628
1629                 v3d->twflag |= V3D_DRAW_MANIPULATOR;
1630
1631                 /* now we can define center */
1632                 switch (v3d->around) {
1633                         case V3D_AROUND_CENTER_BOUNDS:
1634                         case V3D_AROUND_ACTIVE:
1635                         {
1636                                 bGPdata *gpd = CTX_data_gpencil_data(C);
1637                                 Object *ob = OBACT;
1638
1639                                 if (((v3d->around == V3D_AROUND_ACTIVE) && (scene->obedit == NULL)) &&
1640                                     ((gpd == NULL) || !(gpd->flag & GP_DATA_STROKE_EDITMODE)) &&
1641                                     (ob && !(ob->mode & OB_MODE_POSE)))
1642                                 {
1643                                         copy_v3_v3(rv3d->twmat[3], ob->obmat[3]);
1644                                 }
1645                                 else {
1646                                         mid_v3_v3v3(rv3d->twmat[3], scene->twmin, scene->twmax);
1647                                 }
1648                                 break;
1649                         }
1650                         case V3D_AROUND_LOCAL_ORIGINS:
1651                         case V3D_AROUND_CENTER_MEAN:
1652                                 copy_v3_v3(rv3d->twmat[3], scene->twcent);
1653                                 break;
1654                         case V3D_AROUND_CURSOR:
1655                                 copy_v3_v3(rv3d->twmat[3], ED_view3d_cursor3d_get(scene, v3d));
1656                                 break;
1657                 }
1658
1659                 mul_mat3_m4_fl(rv3d->twmat, ED_view3d_pixel_size(rv3d, rv3d->twmat[3]) * U.tw_size);
1660         }
1661
1662         /* when looking through a selected camera, the manipulator can be at the
1663          * exact same position as the view, skip so we don't break selection */
1664         if (fabsf(mat4_to_scale(rv3d->twmat)) < 1e-7f)
1665                 return;
1666
1667         test_manipulator_axis(C);
1668         drawflags = rv3d->twdrawflag;    /* set in calc_manipulator_stats */
1669
1670         if (v3d->twflag & V3D_DRAW_MANIPULATOR) {
1671                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1672                 glEnable(GL_BLEND);
1673                 glLineWidth(1.0f);
1674
1675                 if (v3d->twtype & V3D_MANIP_ROTATE) {
1676                         if (G.debug_value == 3) {
1677                                 if (G.moving & (G_TRANSFORM_OBJ | G_TRANSFORM_EDIT))
1678                                         draw_manipulator_rotate_cyl(v3d, rv3d, drawflags, v3d->twtype, MAN_MOVECOL, true, is_picksel);
1679                                 else
1680                                         draw_manipulator_rotate_cyl(v3d, rv3d, drawflags, v3d->twtype, MAN_RGB, false, is_picksel);
1681                         }
1682                         else {
1683                                 draw_manipulator_rotate(v3d, rv3d, drawflags, v3d->twtype, false, is_picksel);
1684                         }
1685                 }
1686                 if (v3d->twtype & V3D_MANIP_SCALE) {
1687                         draw_manipulator_scale(v3d, rv3d, drawflags, v3d->twtype, MAN_RGB, false, is_picksel);
1688                 }
1689                 if (v3d->twtype & V3D_MANIP_TRANSLATE) {
1690                         draw_manipulator_translate(v3d, rv3d, drawflags, v3d->twtype, MAN_RGB, false, is_picksel);
1691                 }
1692
1693                 glDisable(GL_BLEND);
1694         }
1695 }
1696
1697 static int manipulator_selectbuf(ScrArea *sa, ARegion *ar, const int mval[2], float hotspot)
1698 {
1699         View3D *v3d = sa->spacedata.first;
1700         RegionView3D *rv3d = ar->regiondata;
1701         rctf rect, selrect;
1702         GLuint buffer[64];      // max 4 items per select, so large enuf
1703         short hits;
1704         const bool is_picksel = true;
1705         const bool do_passes = GPU_select_query_check_active();
1706
1707         /* XXX check a bit later on this... (ton) */
1708         extern void view3d_winmatrix_set(ARegion *ar, View3D *v3d, rctf *rect);
1709
1710         /* when looking through a selected camera, the manipulator can be at the
1711          * exact same position as the view, skip so we don't break selection */
1712         if (fabsf(mat4_to_scale(rv3d->twmat)) < 1e-7f)
1713                 return 0;
1714
1715         rect.xmin = mval[0] - hotspot;
1716         rect.xmax = mval[0] + hotspot;
1717         rect.ymin = mval[1] - hotspot;
1718         rect.ymax = mval[1] + hotspot;
1719
1720         selrect = rect;
1721
1722         view3d_winmatrix_set(ar, v3d, &rect);
1723         mul_m4_m4m4(rv3d->persmat, rv3d->winmat, rv3d->viewmat);
1724
1725         if (do_passes)
1726                 GPU_select_begin(buffer, 64, &selrect, GPU_SELECT_NEAREST_FIRST_PASS, 0);
1727         else
1728                 GPU_select_begin(buffer, 64, &selrect, GPU_SELECT_ALL, 0);
1729
1730         /* do the drawing */
1731         if (v3d->twtype & V3D_MANIP_ROTATE) {
1732                 if (G.debug_value == 3) draw_manipulator_rotate_cyl(v3d, rv3d, MAN_ROT_C & rv3d->twdrawflag, v3d->twtype, MAN_RGB, false, is_picksel);
1733                 else draw_manipulator_rotate(v3d, rv3d, MAN_ROT_C & rv3d->twdrawflag, v3d->twtype, false, is_picksel);
1734         }
1735         if (v3d->twtype & V3D_MANIP_SCALE)
1736                 draw_manipulator_scale(v3d, rv3d, MAN_SCALE_C & rv3d->twdrawflag, v3d->twtype, MAN_RGB, false, is_picksel);
1737         if (v3d->twtype & V3D_MANIP_TRANSLATE)
1738                 draw_manipulator_translate(v3d, rv3d, MAN_TRANS_C & rv3d->twdrawflag, v3d->twtype, MAN_RGB, false, is_picksel);
1739
1740         hits = GPU_select_end();
1741
1742         if (do_passes) {
1743                 GPU_select_begin(buffer, 64, &selrect, GPU_SELECT_NEAREST_SECOND_PASS, hits);
1744
1745                 /* do the drawing */
1746                 if (v3d->twtype & V3D_MANIP_ROTATE) {
1747                         if (G.debug_value == 3) draw_manipulator_rotate_cyl(v3d, rv3d, MAN_ROT_C & rv3d->twdrawflag, v3d->twtype, MAN_RGB, false, is_picksel);
1748                         else draw_manipulator_rotate(v3d, rv3d, MAN_ROT_C & rv3d->twdrawflag, v3d->twtype, false, is_picksel);
1749                 }
1750                 if (v3d->twtype & V3D_MANIP_SCALE)
1751                         draw_manipulator_scale(v3d, rv3d, MAN_SCALE_C & rv3d->twdrawflag, v3d->twtype, MAN_RGB, false, is_picksel);
1752                 if (v3d->twtype & V3D_MANIP_TRANSLATE)
1753                         draw_manipulator_translate(v3d, rv3d, MAN_TRANS_C & rv3d->twdrawflag, v3d->twtype, MAN_RGB, false, is_picksel);
1754
1755                 GPU_select_end();
1756         }
1757
1758         view3d_winmatrix_set(ar, v3d, NULL);
1759         mul_m4_m4m4(rv3d->persmat, rv3d->winmat, rv3d->viewmat);
1760
1761         if (hits == 1) return buffer[3];
1762         else if (hits > 1) {
1763                 GLuint val, dep, mindep = 0, mindeprot = 0, minval = 0, minvalrot = 0;
1764                 int a;
1765
1766                 /* we compare the hits in buffer, but value centers highest */
1767                 /* we also store the rotation hits separate (because of arcs) and return hits on other widgets if there are */
1768
1769                 for (a = 0; a < hits; a++) {
1770                         dep = buffer[4 * a + 1];
1771                         val = buffer[4 * a + 3];
1772
1773                         if (val == MAN_TRANS_C) {
1774                                 return MAN_TRANS_C;
1775                         }
1776                         else if (val == MAN_SCALE_C) {
1777                                 return MAN_SCALE_C;
1778                         }
1779                         else {
1780                                 if (val & MAN_ROT_C) {
1781                                         if (minvalrot == 0 || dep < mindeprot) {
1782                                                 mindeprot = dep;
1783                                                 minvalrot = val;
1784                                         }
1785                                 }
1786                                 else {
1787                                         if (minval == 0 || dep < mindep) {
1788                                                 mindep = dep;
1789                                                 minval = val;
1790                                         }
1791                                 }
1792                         }
1793                 }
1794
1795                 if (minval)
1796                         return minval;
1797                 else
1798                         return minvalrot;
1799         }
1800         return 0;
1801 }
1802
1803
1804 /* return 0; nothing happened */
1805 int BIF_do_manipulator(bContext *C, const struct wmEvent *event, wmOperator *op)
1806 {
1807         ScrArea *sa = CTX_wm_area(C);
1808         View3D *v3d = sa->spacedata.first;
1809         ARegion *ar = CTX_wm_region(C);
1810         int constraint_axis[3] = {0, 0, 0};
1811         int val;
1812         int shift = event->shift;
1813
1814         if (!(v3d->twflag & V3D_USE_MANIPULATOR)) return 0;
1815         if (!(v3d->twflag & V3D_DRAW_MANIPULATOR)) return 0;
1816
1817         /* Force orientation */
1818         RNA_enum_set(op->ptr, "constraint_orientation", v3d->twmode);
1819
1820         // find the hotspots first test narrow hotspot
1821         val = manipulator_selectbuf(sa, ar, event->mval, 0.5f * (float)U.tw_hotspot);
1822         if (val) {
1823
1824                 // drawflags still global, for drawing call above
1825                 drawflags = manipulator_selectbuf(sa, ar, event->mval, 0.2f * (float)U.tw_hotspot);
1826                 if (drawflags == 0) drawflags = val;
1827
1828                 if (drawflags & MAN_TRANS_C) {
1829                         switch (drawflags) {
1830                                 case MAN_TRANS_C:
1831                                         break;
1832                                 case MAN_TRANS_X:
1833                                         if (shift) {
1834                                                 constraint_axis[1] = 1;
1835                                                 constraint_axis[2] = 1;
1836                                         }
1837                                         else
1838                                                 constraint_axis[0] = 1;
1839                                         break;
1840                                 case MAN_TRANS_Y:
1841                                         if (shift) {
1842                                                 constraint_axis[0] = 1;
1843                                                 constraint_axis[2] = 1;
1844                                         }
1845                                         else
1846                                                 constraint_axis[1] = 1;
1847                                         break;
1848                                 case MAN_TRANS_Z:
1849                                         if (shift) {
1850                                                 constraint_axis[0] = 1;
1851                                                 constraint_axis[1] = 1;
1852                                         }
1853                                         else
1854                                                 constraint_axis[2] = 1;
1855                                         break;
1856                         }
1857                         RNA_boolean_set_array(op->ptr, "constraint_axis", constraint_axis);
1858                         WM_operator_name_call(C, "TRANSFORM_OT_translate", WM_OP_INVOKE_DEFAULT, op->ptr);
1859                 }
1860                 else if (drawflags & MAN_SCALE_C) {
1861                         switch (drawflags) {
1862                                 case MAN_SCALE_X:
1863                                         if (shift) {
1864                                                 constraint_axis[1] = 1;
1865                                                 constraint_axis[2] = 1;
1866                                         }
1867                                         else
1868                                                 constraint_axis[0] = 1;
1869                                         break;
1870                                 case MAN_SCALE_Y:
1871                                         if (shift) {
1872                                                 constraint_axis[0] = 1;
1873                                                 constraint_axis[2] = 1;
1874                                         }
1875                                         else
1876                                                 constraint_axis[1] = 1;
1877                                         break;
1878                                 case MAN_SCALE_Z:
1879                                         if (shift) {
1880                                                 constraint_axis[0] = 1;
1881                                                 constraint_axis[1] = 1;
1882                                         }
1883                                         else
1884                                                 constraint_axis[2] = 1;
1885                                         break;
1886                         }
1887                         RNA_boolean_set_array(op->ptr, "constraint_axis", constraint_axis);
1888                         WM_operator_name_call(C, "TRANSFORM_OT_resize", WM_OP_INVOKE_DEFAULT, op->ptr);
1889                 }
1890                 else if (drawflags == MAN_ROT_T) { /* trackball need special case, init is different */
1891                         /* Do not pass op->ptr!!! trackball has no "constraint" properties!
1892                          * See [#34621], it's a miracle it did not cause more problems!!! */
1893                         /* However, we need to copy the "release_confirm" property, but only if defined, see T41112. */
1894                         PointerRNA props_ptr;
1895                         PropertyRNA *prop;
1896                         wmOperatorType *ot = WM_operatortype_find("TRANSFORM_OT_trackball", true);
1897                         WM_operator_properties_create_ptr(&props_ptr, ot);
1898                         if ((prop = RNA_struct_find_property(op->ptr, "release_confirm")) && RNA_property_is_set(op->ptr, prop)) {
1899                                 RNA_property_boolean_set(&props_ptr, prop, RNA_property_boolean_get(op->ptr, prop));
1900                         }
1901                         WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr);
1902                         WM_operator_properties_free(&props_ptr);
1903                 }
1904                 else if (drawflags & MAN_ROT_C) {
1905                         switch (drawflags) {
1906                                 case MAN_ROT_X:
1907                                         constraint_axis[0] = 1;
1908                                         break;
1909                                 case MAN_ROT_Y:
1910                                         constraint_axis[1] = 1;
1911                                         break;
1912                                 case MAN_ROT_Z:
1913                                         constraint_axis[2] = 1;
1914                                         break;
1915                         }
1916                         RNA_boolean_set_array(op->ptr, "constraint_axis", constraint_axis);
1917                         WM_operator_name_call(C, "TRANSFORM_OT_rotate", WM_OP_INVOKE_DEFAULT, op->ptr);
1918                 }
1919         }
1920         /* after transform, restore drawflags */
1921         drawflags = 0xFFFF;
1922
1923         return val;
1924 }
1925